From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / jwxyz / jwxyz-gl.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 OpenGL-ish things that bear some resemblance to the
16    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 /* Be advised, this is all very much a work in progress.
23
24    TODO: The following should be implemented before OpenGL can be considered
25    practical here:
26    - Above all, pick the smallest not-yet working hack that utilizes the
27      needed functionality.
28      - Half-ass the drawing functions.
29    - [OK] What Interference needs
30    - Fast Pixmaps
31    - Whatever clipping is used in XScreenSaver (shape and/or bitmap clipping)
32    - Delayed context creation to support anti-aliasing/multisampling
33    - Everything these hacks need:
34      - FuzzyFlakes (needs wide lines)
35      - Greynetic
36      - [OK] Deluxe
37    - [OK] Get DangerBall going.
38    - [OK] iOS.
39      - [Interference, so far...] And fast, too.
40    - And text really needs to work for the FPS display. */
41
42 /* Also, Take note that OS X can actually run with 256 colors. */
43
44 /* TODO:
45    - malloc error checking
46    - Check max texture sizes for XGet/PutImage, XCopyArea.
47    - Optional 5:5:5 16-bit color
48 */
49
50 /* Techniques & notes:
51    - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
52    - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
53    - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
54    - glLogicOp is an actual thing that should work for GCs.
55    - Pixmaps can be any of the following, depending on GL implementation.
56      - This requires offscreen rendering. Fortunately, this is always
57        available.
58        - OS X: Drawable objects, including: pixel buffers and offscreen
59          memory.
60          - Offscreen buffers w/ CGLSetOffScreen (10.0+)
61          - http://lists.apple.com/archives/mac-opengl/2002/Jun/msg00087.html
62            provides a very ugly solution for hardware-accelerated offscreen
63            rendering with CGLSetParameter(*, kCGLCPSurfaceTexture, *) on OS X
64            10.1+. Apple docs say it's actually for OS X 10.3+, instead.
65          - Pixel buffers w/ CGLSetPBuffer (10.3+, now deprecated)
66            - Requires APPLE_pixel_buffer.
67              - Available in software on x86 only.
68              - Always available on hardware.
69            - Need to blit? Use OpenGL pixel buffers. (GL_PIXEL_PACK_BUFFER)
70          - Framebuffer objects w/ GL_(ARB|EXT)_framebuffer_object
71            - TODO: Can EXT_framebuffers be different sizes?
72            - Preferred on 10.7+
73        - iOS: Use OES_framebuffer_object, it's always present.
74  */
75
76 /* OpenGL hacks call a number of X11 functions, including
77  * XCopyArea, XDrawString, XGetImage
78  * XCreatePixmap, XCreateGC, XCreateImage
79  * XPutPixel
80  * Check these, of course. */
81
82 #ifdef JWXYZ_GL /* entire file */
83
84 #include <math.h>
85 #include <limits.h>
86 #include <stddef.h>
87 #include <stdlib.h>
88 #include <stdint.h>
89 #include <string.h>
90 #include <wchar.h>
91
92 #ifdef HAVE_COCOA
93 # ifdef USE_IPHONE
94 #  import <QuartzCore/QuartzCore.h>
95 #  include <OpenGLES/ES1/gl.h>
96 #  include <OpenGLES/ES1/glext.h>
97
98 #  define NSView  UIView
99 #  define NSRect  CGRect
100 #  define NSPoint CGPoint
101 #  define NSSize  CGSize
102 #  define NSColor UIColor
103 #  define NSImage UIImage
104 #  define NSEvent UIEvent
105 #  define NSFont  UIFont
106 #  define NSGlyph CGGlyph
107 #  define NSWindow UIWindow
108 #  define NSMakeSize   CGSizeMake
109 #  define NSBezierPath UIBezierPath
110 #  define colorWithDeviceRed colorWithRed
111
112 #  define NSFontTraitMask      UIFontDescriptorSymbolicTraits
113 // The values for the flags for NSFontTraitMask and
114 // UIFontDescriptorSymbolicTraits match up, not that it really matters here.
115 #  define NSBoldFontMask       UIFontDescriptorTraitBold
116 #  define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
117 #  define NSItalicFontMask     UIFontDescriptorTraitItalic
118
119 #  define NSOpenGLContext EAGLContext
120
121 # else
122 #  include <OpenGL/glu.h>
123 # endif
124 #else
125 /* TODO: Does this work on iOS? */
126 # ifndef HAVE_JWZGLES
127 #  include <gl/glu.h>
128 # else
129 #  include <GLES/gl.h>
130 #  include <GLES/glext.h>
131 # endif
132 #endif
133
134 #ifdef HAVE_JWZGLES
135 # include "jwzglesI.h"
136 #endif
137
138 #ifdef HAVE_ANDROID
139 # include <android/log.h>
140 #endif
141
142 #include "jwxyzI.h"
143 #include "jwxyz-timers.h"
144 #include "yarandom.h"
145 #include "utf8wc.h"
146 #include "xft.h"
147
148 #if defined HAVE_COCOA
149 # include <CoreGraphics/CGGeometry.h>
150 #else
151
152
153 #ifdef HAVE_ANDROID
154  extern void Log(const char *fmt, ...);
155 #else
156 static void
157 Log (const char *fmt, ...)
158 {
159   va_list args;
160   va_start (args, fmt);
161   vfprintf (stderr, fmt, args);
162   va_end (args);
163 }
164 #endif
165
166 struct CGPoint {
167     float x;
168     float y;
169 };
170 typedef struct CGPoint CGPoint;
171
172 struct CGSize {
173     float width;
174     float height;
175 };
176 typedef struct CGSize CGSize;
177
178 struct CGRect {
179     CGPoint origin;
180     CGSize size;
181 };
182 typedef struct CGRect CGRect;
183
184 #endif
185
186 # undef MAX
187 # undef MIN
188 # define MAX(a,b) ((a)>(b)?(a):(b))
189 # define MIN(a,b) ((a)<(b)?(a):(b))
190
191 union color_bytes
192 {
193   /* On 64-bit systems, high bits of the 32-bit pixel are available as scratch
194      space. I doubt if any screen savers need it, but just in case... */
195   unsigned long pixel;
196   uint8_t bytes[4];
197 };
198
199 struct jwxyz_Display {
200   Window main_window;
201   Screen *screen;
202   int screen_count;
203   struct jwxyz_sources_data *timers_data;
204
205   Bool gl_texture_npot_p;
206   /* Bool opengl_core_p */;
207   GLenum gl_texture_target;
208   
209 // #if defined USE_IPHONE
210   GLuint rect_texture; // Also can work on the desktop.
211 // #endif
212
213   unsigned long window_background;
214 };
215
216 struct jwxyz_Screen {
217   Display *dpy;
218   GLenum pixel_format, pixel_type;
219   unsigned long black, white;
220   Visual *visual;
221   int screen_number;
222 };
223
224 struct jwxyz_GC {
225   XGCValues gcv;
226   unsigned int depth;
227   // CGImageRef clip_mask;  // CGImage copy of the Pixmap in gcv.clip_mask
228 };
229
230 struct jwxyz_Font {
231   Display *dpy;
232   char *ps_name;
233   void *native_font;
234   int refcount; // for deciding when to release the native font
235   float size;   // points
236   int ascent, descent;
237   char *xa_font;
238
239   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
240   // But we need the metrics on both of them, so they go here.
241   XFontStruct metrics;
242 };
243
244 struct jwxyz_XFontSet {
245   XFontStruct *font;
246 };
247
248
249 /* XGetImage in CoreGraphics JWXYZ has to deal with funky pixel formats
250    necessitating fast & flexible pixel conversion. OpenGL does image format
251    conversion itself, so alloc_color and query_color are mercifully simple.
252  */
253 uint32_t
254 jwxyz_alloc_color (Display *dpy,
255                    uint16_t r, uint16_t g, uint16_t b, uint16_t a)
256 {
257   union color_bytes color;
258
259   /* Instead of (int)(c / 256.0), another possibility is
260      (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
261      uint8_t integer_math(uint16_t c) {
262        unsigned c0 = c + 128;
263        return (c0 - (c0 >> 8)) >> 8;
264      }
265    */
266
267   color.bytes[0] = r >> 8;
268   color.bytes[1] = g >> 8;
269   color.bytes[2] = b >> 8;
270   color.bytes[3] = a >> 8;
271
272   if (dpy->screen->pixel_format == GL_BGRA_EXT) {
273     color.pixel = color.bytes[2] |
274                   (color.bytes[1] << 8) |
275                   (color.bytes[0] << 16) |
276                   (color.bytes[3] << 24);
277   } else {
278     Assert(dpy->screen->pixel_format == GL_RGBA,
279            "jwxyz_alloc_color: Unknown pixel_format");
280   }
281
282   return (uint32_t)color.pixel;
283 }
284
285 // Converts an array of pixels ('src') from one format to another, placing the
286 // result in 'dest', according to the pixel conversion mode 'mode'.
287 void
288 jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
289 {
290   union color_bytes color;
291
292   if(dpy->screen->pixel_format == GL_RGBA)
293   {
294     color.pixel = pixel;
295     for (unsigned i = 0; i != 4; ++i)
296       rgba[i] = color.bytes[i];
297     return;
298   }
299
300   Assert (dpy->screen->pixel_format == GL_BGRA_EXT,
301           "jwxyz_query_color: Unknown pixel format");
302   /* TODO: Cross-check with XAllocColor. */
303   rgba[0] = (pixel >> 16) & 0xFF;
304   rgba[1] = (pixel >>  8) & 0xFF;
305   rgba[2] = (pixel >>  0) & 0xFF;
306   rgba[3] = (pixel >> 24) & 0xFF;
307 }
308
309
310 void
311 jwxyz_assert_display(Display *dpy)
312 {
313   if(!dpy)
314     return;
315   jwxyz_assert_gl ();
316   jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
317 }
318
319
320 void
321 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
322                     Bool window_p)
323 {
324   /* TODO: Check registration pattern from Interference with rectangles instead of points. */
325
326   // The projection matrix is always set as follows. The modelview matrix is
327   // usually identity, but for points and thin lines, it's translated by 0.5.
328   glMatrixMode(GL_PROJECTION);
329   glLoadIdentity();
330   
331 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
332
333   if (window_p && ignore_rotation_p(dpy)) {
334     int o = (int) current_device_rotation();
335     glRotatef (-o, 0, 0, 1);
336   }
337
338   // glPointSize(1); // This is the default.
339   
340 #ifdef HAVE_JWZGLES
341   glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
342 #else
343   glOrtho
344 #endif
345     (0, width, height, 0, -1, 1);
346
347   glMatrixMode(GL_MODELVIEW);
348 # endif // HAVE_MOBILE
349 }
350
351 #ifndef HAVE_JWZGLES
352
353 struct gl_version
354 {
355   // iOS always uses OpenGL ES 1.1.
356   unsigned major;
357   unsigned minor;
358 };
359
360 static GLboolean
361 gl_check_ver (const struct gl_version *caps,
362               unsigned gl_major,
363               unsigned gl_minor)
364 {
365   return caps->major > gl_major ||
366            (caps->major == gl_major && caps->minor >= gl_minor);
367 }
368
369 /*
370 static GLboolean gl_check_ext(const struct gl_caps *caps,
371                               unsigned gl_major,
372                               unsigned gl_minor,
373                               const char *extension)
374 {
375   return
376     gl_check_ver(caps, gl_major, gl_minor) ||
377     gluCheckExtension(extension, caps->extensions);
378 }
379 */
380
381 #endif
382
383
384 // NSOpenGLContext *jwxyz_debug_context;
385
386 /* We keep a list of all of the Displays that have been created and not
387    yet freed so that they can have sensible display numbers.  If three
388    displays are created (0, 1, 2) and then #1 is closed, then the fourth
389    display will be given the now-unused display number 1. (Everything in
390    here assumes a 1:1 Display/Screen mapping.)
391
392    The size of this array is the most number of live displays at one time.
393    So if it's 20, then we'll blow up if the system has 19 monitors and also
394    has System Preferences open (the small preview window).
395
396    Note that xlockmore-style savers tend to allocate big structures, so
397    setting this to 1000 will waste a few megabytes.  Also some of them assume
398    that the number of screens never changes, so dynamically expanding this
399    array won't work.
400  */
401 # ifndef USE_IPHONE
402 static Display *jwxyz_live_displays[20] = { 0, };
403 # endif
404
405
406 Display *
407 jwxyz_make_display (Window w)
408 {
409   Display *d = (Display *) calloc (1, sizeof(*d));
410   d->screen = (Screen *) calloc (1, sizeof(Screen));
411   d->screen->dpy = d;
412   
413   d->screen_count = 1;
414   d->screen->screen_number = 0;
415 # ifndef USE_IPHONE
416   {
417     // Find the first empty slot in live_displays and plug us in.
418     int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
419     int i;
420     for (i = 0; i < size; i++) {
421       if (! jwxyz_live_displays[i])
422         break;
423     }
424     if (i >= size) abort();
425     jwxyz_live_displays[i] = d;
426     d->screen_count = size;
427     d->screen->screen_number = i;
428   }
429 # endif // !USE_IPHONE
430
431 # ifndef HAVE_JWZGLES
432   struct gl_version version;
433
434   {
435     const GLubyte *version_str = glGetString (GL_VERSION);
436
437     /* iPhone is always OpenGL ES 1.1. */
438     if (sscanf ((const char *) version_str, "%u.%u",
439                 &version.major, &version.minor) < 2)
440     {
441       version.major = 1;
442       version.minor = 1;
443     }
444   }
445 # endif // !HAVE_JWZGLES
446
447   const GLubyte *extensions = glGetString (GL_EXTENSIONS);
448
449   /* See:
450      - Apple TN2080: Understanding and Detecting OpenGL Functionality.
451      - OpenGL Programming Guide for the Mac - Best Practices for Working with
452        Texture Data - Optimal Data Formats and Types
453    */
454
455   // If a video adapter suports BGRA textures, then that's probably as fast as
456   // you're gonna get for getting a texture onto the screen.
457 # ifdef HAVE_JWZGLES
458   /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
459   d->screen->pixel_format = GL_RGBA; /*
460     gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
461                        extensions) ? GL_BGRA_EXT : GL_RGBA; */
462   d->screen->pixel_type = GL_UNSIGNED_BYTE;
463   // See also OES_read_format.
464 # else  // !HAVE_JWZGLES
465   if (gl_check_ver (&version, 1, 2) ||
466       (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
467        gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
468                           extensions))) {
469     d->screen->pixel_format = GL_BGRA_EXT;
470     // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
471     d->screen->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
472   } else {
473     d->screen->pixel_format = GL_RGBA;
474     d->screen->pixel_type = GL_UNSIGNED_BYTE;
475   }
476   // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
477   // sense on PowerPC.
478 # endif // !HAVE_JWZGLES
479
480   // On really old systems, it would make sense to split the texture
481   // into subsections.
482 # ifndef HAVE_JWZGLES
483   d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
484                                             "GL_ARB_texture_rectangle",
485                                             extensions);
486   d->gl_texture_target = d->gl_texture_npot_p ?
487                          GL_TEXTURE_RECTANGLE_EXT :
488                          GL_TEXTURE_2D;
489 # else
490   d->gl_texture_npot_p = jwzgles_gluCheckExtension
491       ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
492     jwzgles_gluCheckExtension
493       ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
494     jwzgles_gluCheckExtension // From PixelFlinger 1.4
495       ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
496
497   d->gl_texture_target = GL_TEXTURE_2D;
498 # endif
499
500   d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
501   d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
502
503   Visual *v = (Visual *) calloc (1, sizeof(Visual));
504   v->class      = TrueColor;
505   v->red_mask   = jwxyz_alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
506   v->green_mask = jwxyz_alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
507   v->blue_mask  = jwxyz_alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
508   v->bits_per_rgb = 8;
509   d->screen->visual = v;
510
511   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
512
513   d->window_background = BlackPixel(d,0);
514
515   d->main_window = w;
516   {
517     fputs((char *)glGetString(GL_VENDOR), stderr);
518     fputc(' ', stderr);
519     fputs((char *)glGetString(GL_RENDERER), stderr);
520     fputc(' ', stderr);
521     fputs((char *)glGetString(GL_VERSION), stderr);
522     fputc('\n', stderr);
523 //  puts(caps.extensions);
524     GLint max_texture_size;
525     glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
526     printf ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size);
527   }
528  
529   // In case a GL hack wants to use X11 to draw offscreen, the rect_texture is available.
530   Assert (d->main_window == w, "Uh-oh.");
531   glGenTextures (1, &d->rect_texture);
532   // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
533   // Rectangle textures should be present on OS X with the following exceptions:
534   // - Generic renderer on PowerPC OS X 10.4 and earlier
535   // - ATI Rage 128
536   glBindTexture (d->gl_texture_target, d->rect_texture);
537   // TODO: This is all somewhere else. Refactor.
538   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
539   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
540
541   // This might be redundant for rectangular textures.
542 # ifndef HAVE_JWZGLES
543   const GLint wrap = GL_CLAMP;
544 # else  // HAVE_JWZGLES
545   const GLint wrap = GL_CLAMP_TO_EDGE;
546 # endif // HAVE_JWZGLES
547
548   // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
549   // This is always present with OpenGL ES.
550   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
551   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
552
553   jwxyz_assert_display(d);
554   return d;
555 }
556
557 void
558 jwxyz_free_display (Display *dpy)
559 {
560   /* TODO: Go over everything. */
561
562   jwxyz_sources_free (dpy->timers_data);
563
564 # ifndef USE_IPHONE
565   {
566     // Find us in live_displays and clear that slot.
567     int size = ScreenCount(dpy);
568     int i;
569     for (i = 0; i < size; i++) {
570       if (dpy == jwxyz_live_displays[i]) {
571         jwxyz_live_displays[i] = 0;
572         break;
573       }
574     }
575     if (i >= size) abort();
576   }
577 # endif // !USE_IPHONE
578
579   free (dpy->screen->visual);
580   free (dpy->screen);
581   free (dpy);
582 }
583
584
585 /* Call this after any modification to the bits on a Pixmap or Window.
586    Most Pixmaps are used frequently as sources and infrequently as
587    destinations, so it pays to cache the data as a CGImage as needed.
588  */
589 static void
590 invalidate_drawable_cache (Drawable d)
591 {
592   /* TODO: Kill this outright. jwxyz_bind_drawable handles any potential 
593      invalidation.
594    */
595
596   /*
597   if (d && d->cgi) {
598     abort();
599     CGImageRelease (d->cgi);
600     d->cgi = 0;
601   }
602  */
603 }
604
605
606 /* Call this when the View changes size or position.
607  */
608 void
609 jwxyz_window_resized (Display *dpy)
610 {
611   const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
612   unsigned new_width = new_frame->width;
613   unsigned new_height = new_frame->height;
614
615   Assert (new_width, "jwxyz_window_resized: No width.");
616   Assert (new_height, "jwxyz_window_resized: No height.");
617
618 /*if (cgc) w->cgc = cgc;
619   Assert (w->cgc, "no CGContext"); */
620
621   Log("resize: %d, %d\n", new_width, new_height);
622
623   jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
624
625   // TODO: What does the iPhone need?
626   
627   // iOS only: If the main_window is not the current_drawable, then set_matrices
628   // was already called in bind_drawable.
629   jwxyz_set_matrices (dpy, new_width, new_height, True);
630
631 /*
632   GLint draw_buffer;
633   glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
634   
635   glDrawBuffer (GL_FRONT);
636   glClearColor (1, 0, 1, 0);
637   glClear (GL_COLOR_BUFFER_BIT);
638   glDrawBuffer (GL_BACK);
639   glClearColor (0, 1, 1, 0);
640   glClear (GL_COLOR_BUFFER_BIT);
641   
642   glDrawBuffer (draw_buffer); */
643   
644   // Stylish and attractive purple!
645   // glClearColor (1, 0, 1, 0.5);
646   // glClear (GL_COLOR_BUFFER_BIT);
647   
648   invalidate_drawable_cache (dpy->main_window);
649 }
650
651
652 jwxyz_sources_data *
653 display_sources_data (Display *dpy)
654 {
655   return dpy->timers_data;
656 }
657
658
659 Window
660 XRootWindow (Display *dpy, int screen)
661 {
662   return dpy ? dpy->main_window : 0;
663 }
664
665 Screen *
666 XDefaultScreenOfDisplay (Display *dpy)
667 {
668   return dpy ? dpy->screen : 0;
669 }
670
671 Visual *
672 XDefaultVisualOfScreen (Screen *screen)
673 {
674   return screen ? screen->visual : 0;
675 }
676
677 Display *
678 XDisplayOfScreen (Screen *s)
679 {
680   return s ? s->dpy : 0;
681 }
682
683 int
684 XDisplayNumberOfScreen (Screen *s)
685 {
686   return 0;
687 }
688
689 int
690 XScreenNumberOfScreen (Screen *s)
691 {
692   return s? s->screen_number : 0;
693 }
694
695 int
696 jwxyz_ScreenCount (Display *dpy)
697 {
698   return dpy ? dpy->screen_count : 0;
699 }
700
701 unsigned long
702 XBlackPixelOfScreen(Screen *screen)
703 {
704   return screen->black;
705 }
706
707 unsigned long
708 XWhitePixelOfScreen(Screen *screen)
709 {
710   return screen->white;
711 }
712
713 unsigned long
714 XCellsOfScreen(Screen *screen)
715 {
716   Visual *v = screen->visual;
717   return v->red_mask | v->green_mask | v->blue_mask;
718 }
719
720
721 /* GC attributes by usage and OpenGL implementation:
722  *
723  * All drawing functions:
724  * function                                | glLogicOp w/ GL_COLOR_LOGIC_OP
725  * clip_x_origin, clip_y_origin, clip_mask | Stencil mask
726  *
727  * Shape drawing functions:
728  * foreground, background                  | glColor*
729  *
730  * XDrawLines, XDrawRectangles, XDrawSegments:
731  * line_width, cap_style, join_style       | Lotsa vertices
732  *
733  * XFillPolygon:
734  * fill_rule                               | Multiple GL_TRIANGLE_FANs
735  *
736  * XDrawText:
737  * font                                    | Cocoa, then OpenGL display lists.
738  *
739  * alpha_allowed_p                         | TODO
740  * antialias_p                             | TODO
741  *
742  * Nothing, really:
743  * subwindow_mode
744 */
745
746 static void
747 set_clip_mask (GC gc)
748 {
749   Assert (!gc->gcv.clip_mask, "set_gc: TODO");
750 }
751
752 static void
753 set_function (int function)
754 {
755   Assert (function == GXcopy, "set_gc: (TODO) Stubbed gcv function");
756   
757   /* TODO: The GL_COLOR_LOGIC_OP extension is exactly what is needed here. (OpenGL 1.1)
758    Fun fact: The glLogicOp opcode constants are the same as the X11 GX* function constants | GL_CLEAR.
759    */
760   
761 #if 0
762   switch (gc->gcv.function) {
763     case GXset:
764     case GXclear:
765     case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/   break;
766     case GXxor:   CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
767     case GXor:    CGContextSetBlendMode (cgc, kCGBlendModeLighten);    break;
768     case GXand:   CGContextSetBlendMode (cgc, kCGBlendModeDarken);     break;
769     default: Assert(0, "unknown gcv function"); break;
770   }
771 #endif
772 }
773
774
775 static void
776 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
777            Bool alpha_allowed_p)
778 {
779   jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
780
781   if (depth == 1) {
782     GLfloat f = pixel;
783     glColor4f (f, f, f, 1);
784   } else {
785     /* TODO: alpha_allowed_p */
786     uint8_t rgba[4];
787     jwxyz_query_color (dpy, pixel, rgba);
788 #ifdef HAVE_JWZGLES
789     glColor4f (rgba[0] / 255.0f, rgba[1] / 255.0f,
790                rgba[2] / 255.0f, rgba[3] / 255.0f);
791 #else
792     glColor4ubv (rgba);
793 #endif
794   }
795 }
796
797 /* Pushes a GC context; sets Function, ClipMask, and color.
798  */
799 static void
800 set_color_gc (Display *dpy, GC gc, unsigned long color)
801 {
802   // GC is NULL for XClearArea and XClearWindow.
803   unsigned int depth;
804   int function;
805   if (gc) {
806     function = gc->gcv.function;
807     depth = gc->depth;
808     set_clip_mask (gc);
809   } else {
810     function = GXcopy;
811     depth = visual_depth (NULL, NULL);
812     // TODO: Set null clip mask here.
813   }
814
815   set_function (function);
816
817   switch (function) {
818     case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
819     case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
820   }
821
822   set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
823   
824   /* TODO: Antialiasing. */
825   /* CGContextSetShouldAntialias (cgc, antialias_p); */
826 }
827
828 /* Pushes a GC context; sets color to the foreground color.
829  */
830 static void
831 set_fg_gc (Display *dpy, GC gc)
832 {
833   set_color_gc (dpy, gc, gc->gcv.foreground);
834 }
835
836 static void
837 next_point(short *v, XPoint p, int mode)
838 {
839   switch (mode) {
840     case CoordModeOrigin:
841       v[0] = p.x;
842       v[1] = p.y;
843       break;
844     case CoordModePrevious:
845       v[0] += p.x;
846       v[1] += p.y;
847       break;
848     default:
849       Assert (False, "next_point: bad mode");
850       break;
851   }
852 }
853
854 int
855 XDrawPoints (Display *dpy, Drawable d, GC gc, 
856              XPoint *points, int count, int mode)
857 {
858   jwxyz_bind_drawable (dpy, dpy->main_window, d);
859   set_fg_gc (dpy, gc);
860
861 /*
862   
863   glBegin(GL_POINTS);
864   for (unsigned i = 0; i < count; i++) {
865     next_point(v, points[i], mode);
866     glVertex2f(v[0] + 0.5f, v[1] + 0.5f);
867   }
868   glEnd();
869  */
870
871   short v[2] = {0, 0};
872
873   // TODO: XPoints can be fed directly to OpenGL.
874   GLshort *gl_points = malloc (count * 2 * sizeof(GLshort)); // TODO: malloc returns NULL.
875   for (unsigned i = 0; i < count; i++) {
876     next_point (v, points[i], mode);
877     gl_points[2 * i] = v[0];
878     gl_points[2 * i + 1] = v[1];
879   }
880   
881   glMatrixMode (GL_MODELVIEW);
882   glTranslatef (0.5, 0.5, 0);
883   
884   glEnableClientState (GL_VERTEX_ARRAY);
885   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
886   glVertexPointer (2, GL_SHORT, 0, gl_points);
887   glDrawArrays (GL_POINTS, 0, count);
888   
889   free (gl_points);
890   
891   glLoadIdentity ();
892   
893   return 0;
894 }
895
896
897 static GLint
898 texture_internalformat(Display *dpy)
899 {
900 #ifdef HAVE_JWZGLES
901   return dpy->screen->pixel_format;
902 #else
903   return GL_RGBA;
904 #endif
905 }
906
907 static GLenum gl_pixel_type(const Display *dpy)
908 {
909   return dpy->screen->pixel_type;
910 }
911
912 static void
913 clear_texture (Display *dpy)
914 {
915   glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
916                 0, dpy->screen->pixel_format, gl_pixel_type (dpy), NULL);
917 }
918
919 static void set_white (void)
920 {
921 #ifdef HAVE_JWZGLES
922   glColor4f (1, 1, 1, 1);
923 #else
924   glColor3ub (0xff, 0xff, 0xff);
925 #endif
926 }
927
928
929 static GLsizei to_pow2 (size_t x);
930
931
932 void
933 jwxyz_gl_copy_area_copy_tex_image (Display *dpy, Drawable src, Drawable dst,
934                                    GC gc, int src_x, int src_y,
935                                    unsigned int width, unsigned int height,
936                                    int dst_x, int dst_y)
937 {
938   const XRectangle *src_frame = jwxyz_frame (src);
939
940   Assert(gc->gcv.function == GXcopy, "XCopyArea: Unknown function");
941
942   jwxyz_bind_drawable (dpy, dpy->main_window, src);
943
944 #  if defined HAVE_COCOA && !defined USE_IPHONE
945   /* TODO: Does this help? */
946   /* glFinish(); */
947 #  endif
948
949   unsigned tex_w = width, tex_h = height;
950   if (!dpy->gl_texture_npot_p) {
951     tex_w = to_pow2(tex_w);
952     tex_h = to_pow2(tex_h);
953   }
954
955   GLint internalformat = texture_internalformat(dpy);
956
957   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
958
959   if (tex_w == width && tex_h == height) {
960     glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
961                       src_x, src_frame->height - src_y - height,
962                       width, height, 0);
963   } else {
964     glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
965                   0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
966     glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
967                          src_x, src_frame->height - src_y - height,
968                          width, height);
969   }
970
971   jwxyz_bind_drawable (dpy, dpy->main_window, dst);
972   set_white ();
973   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
974   glEnable (dpy->gl_texture_target);
975
976   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
977   glEnableClientState (GL_VERTEX_ARRAY);
978     
979   /* TODO: Copied from XPutImage. Refactor. */
980   /* TODO: EXT_draw_texture or whatever it's called. */
981   GLfloat vertices[4][2] =
982   {
983     {dst_x, dst_y},
984     {dst_x, dst_y + height},
985     {dst_x + width, dst_y + height},
986     {dst_x + width, dst_y}
987   };
988   
989 #ifdef HAVE_JWZGLES
990   static const GLshort tex_coords[4][2] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
991 #else
992   GLshort tex_coords[4][2] = {{0, height}, {0, 0}, {width, 0}, {width, height}};
993 #endif
994
995   glVertexPointer (2, GL_FLOAT, 0, vertices);
996   glTexCoordPointer (2, GL_SHORT, 0, tex_coords);
997   glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
998   
999   clear_texture (dpy);
1000   
1001   glDisable (dpy->gl_texture_target);
1002 }
1003
1004 void
1005 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
1006                                 GC gc, int src_x, int src_y,
1007                                 unsigned int width, unsigned int height,
1008                                 int dst_x, int dst_y)
1009 {
1010 # if 1
1011   XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
1012   XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
1013   XDestroyImage (img);
1014 # endif
1015
1016 # if 0
1017   /* Something may or may not be broken in here. (shrug) */
1018   bind_drawable(dpy, src);
1019
1020   /* Error checking would be nice. */
1021   void *pixels = malloc (src_rect.size.width * 4 * src_rect.size.height);
1022
1023   glPixelStorei (GL_PACK_ROW_LENGTH, 0);
1024   glPixelStorei (GL_PACK_ALIGNMENT, 4);
1025
1026   glReadPixels (src_rect.origin.x, dst_frame.size.height - (src_rect.origin.y + src_rect.size.height),
1027                 src_rect.size.width, src_rect.size.height,
1028                 GL_RGBA, GL_UNSIGNED_BYTE, // TODO: Pick better formats.
1029                 pixels);
1030
1031   bind_drawable (dpy, dst);
1032
1033   glPixelZoom (1.0f, 1.0f);
1034
1035   glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
1036   glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1037
1038   glRasterPos2i (dst_rect.origin.x, dst_rect.origin.y + dst_rect.size.height);
1039   glDrawPixels (dst_rect.size.width, dst_rect.size.height,
1040                 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
1041
1042   free(pixels);
1043 # endif
1044 }
1045
1046
1047 #if 0
1048 // TODO: Make sure offset works in super-sampled mode.
1049 static void
1050 adjust_point_for_line (GC gc, CGPoint *p)
1051 {
1052   // Here's the authoritative discussion on how X draws lines:
1053   // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width
1054   if (gc->gcv.line_width <= 1) {
1055     /* Thin lines are "drawn using an unspecified, device-dependent
1056        algorithm", but seriously though, Bresenham's algorithm. Bresenham's
1057        algorithm runs to and from pixel centers.
1058
1059        There's a few screenhacks (Maze, at the very least) that set line_width
1060        to 1 when it probably should be set to 0, so it's line_width <= 1
1061        instead of < 1.
1062      */
1063     p->x += 0.5;
1064     p->y -= 0.5;
1065   } else {
1066     /* Thick lines OTOH run from the upper-left corners of pixels. This means
1067        that a horizontal thick line of width 1 straddles two scan lines.
1068        Aliasing requires one of these scan lines be chosen; the following
1069        nudges the point so that the right choice is made. */
1070     p->y -= 1e-3;
1071   }
1072 }
1073 #endif
1074
1075
1076 int
1077 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1078 {
1079   // TODO: XDrawLine == XDrawSegments(nlines == 1), also in jwxyz.m
1080   XSegment segment;
1081   segment.x1 = x1;
1082   segment.y1 = y1;
1083   segment.x2 = x2;
1084   segment.y2 = y2;
1085   XDrawSegments (dpy, d, gc, &segment, 1);
1086
1087   // when drawing a zero-length line, obey line-width and cap-style.
1088 /* if (x1 == x2 && y1 == y2) {
1089     int w = gc->gcv.line_width;
1090     x1 -= w/2;
1091     y1 -= w/2;
1092     if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1093       return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1094     else {
1095       if (!w)
1096         w = 1; // Actually show zero-length lines.
1097       return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1098     }
1099   }
1100
1101   CGPoint p = point_for_line (d, gc, x1, y1);
1102
1103   push_fg_gc (dpy, d, gc, NO);
1104
1105   CGContextRef cgc = d->cgc;
1106   set_line_mode (cgc, &gc->gcv);
1107   CGContextBeginPath (cgc);
1108   CGContextMoveToPoint (cgc, p.x, p.y);
1109   p = point_for_line(d, gc, x2, y2);
1110   CGContextAddLineToPoint (cgc, p.x, p.y);
1111   CGContextStrokePath (cgc);
1112   pop_gc (d, gc);
1113   invalidate_drawable_cache (d); */
1114   return 0;
1115 }
1116
1117 int
1118 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1119             int mode)
1120 {
1121   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1122   set_fg_gc (dpy, gc);
1123
1124   /* TODO: Thick lines
1125    * Zero-length line segments
1126    * Paths with zero length total (Remember line width, cap style.)
1127    * Closed loops
1128    */
1129   
1130   if (!count)
1131     return 0;
1132
1133   GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
1134   
1135   glMatrixMode (GL_MODELVIEW);
1136   glTranslatef (0.5f, 0.5f, 0);
1137   
1138   short p[2] = {0, 0};
1139   for (unsigned i = 0; i < count; i++) {
1140     next_point (p, points[i], mode);
1141     vertices[2 * i] = p[0];
1142     vertices[2 * i + 1] = p[1];
1143   }
1144   
1145   glEnableClientState (GL_VERTEX_ARRAY);
1146   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1147   glVertexPointer (2, GL_SHORT, 0, vertices);
1148   glDrawArrays (GL_LINE_STRIP, 0, count);
1149   
1150   free (vertices);
1151
1152   if (gc->gcv.cap_style != CapNotLast) {
1153     // TODO: How does this look with multisampling?
1154     // TODO: Disable me for closed loops.
1155     glVertexPointer (2, GL_SHORT, 0, p);
1156     glDrawArrays (GL_POINTS, 0, 1);
1157   }
1158
1159   glLoadIdentity ();
1160   
1161   return 0;
1162 }
1163
1164
1165 int
1166 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1167 {
1168   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1169   set_fg_gc (dpy, gc);
1170   
1171   /* TODO: Thick lines. */
1172   /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
1173   
1174   glMatrixMode (GL_MODELVIEW);
1175   glTranslatef (0.5, 0.5, 0);
1176
1177   glEnableClientState (GL_VERTEX_ARRAY);
1178   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1179   
1180   Assert (sizeof(XSegment) == sizeof(short) * 4, "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
1181   Assert (sizeof(GLshort) == sizeof(short), "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
1182   Assert (offsetof(XSegment, x1) == 0, "XDrawSegments: Data alignment mix-up.");
1183   Assert (offsetof(XSegment, x2) == 4, "XDrawSegments: Data alignment mix-up.");
1184   glVertexPointer (2, GL_SHORT, 0, segments);
1185   glDrawArrays (GL_LINES, 0, count * 2);
1186   
1187   if (gc->gcv.cap_style != CapNotLast) {
1188     glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4, (const GLshort *)segments + 2);
1189     glDrawArrays (GL_POINTS, 0, count);
1190   }
1191   
1192   glLoadIdentity ();
1193   
1194 /* CGRect wr = d->frame;
1195   push_fg_gc (dpy, d, gc, NO);
1196   set_line_mode (cgc, &gc->gcv);
1197   CGContextBeginPath (cgc);
1198   for (i = 0; i < count; i++) {
1199     CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
1200     CGContextMoveToPoint (cgc, p.x, p.y);
1201     p = point_for_line (d, gc, segments->x2, segments->y2);
1202     CGContextAddLineToPoint (cgc, p.x, p.y);
1203     segments++;
1204   }
1205   CGContextStrokePath (cgc);
1206   pop_gc (d, gc);
1207   invalidate_drawable_cache (d); */
1208   return 0;
1209 }
1210
1211
1212 int
1213 XClearWindow (Display *dpy, Window win)
1214 {
1215   Assert (win == dpy->main_window, "not a window");
1216   const XRectangle *wr = jwxyz_frame (win);
1217   /* TODO: Use glClear if there's no background pixmap. */
1218   return XClearArea (dpy, win, 0, 0, wr->width, wr->height, 0);
1219 }
1220
1221 unsigned long
1222 jwxyz_window_background (Display *dpy)
1223 {
1224   return dpy->window_background;
1225 }
1226
1227 int
1228 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1229 {
1230   Assert (w == dpy->main_window, "not a window");
1231   jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False);
1232   dpy->window_background = pixel;
1233   return 0;
1234 }
1235
1236 void
1237 jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
1238                   const XRectangle *rectangles, unsigned long nrectangles,
1239                   unsigned long pixel)
1240 {
1241   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1242   set_color_gc (dpy, gc, pixel);
1243 /*
1244   glBegin(GL_QUADS);
1245   for (unsigned i = 0; i != nrectangles; ++i) {
1246     const XRectangle *r = &rectangles[i];
1247     glVertex2i(r->x, r->y);
1248     glVertex2i(r->x, r->y + r->height);
1249     glVertex2i(r->x + r->width, r->y + r->height);
1250     glVertex2i(r->x + r->width, r->y);
1251   }
1252   glEnd(); */
1253   
1254   glEnableClientState (GL_VERTEX_ARRAY);
1255   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1256     
1257   for (unsigned long i = 0; i != nrectangles; ++i)
1258   {
1259     const XRectangle *r = &rectangles[i];
1260     
1261     GLfloat coords[4][2] =
1262     {
1263       {r->x, r->y},
1264       {r->x, r->y + r->height},
1265       {r->x + r->width, r->y + r->height},
1266       {r->x + r->width, r->y}
1267     };
1268     
1269     // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads.
1270     glVertexPointer (2, GL_FLOAT, 0, coords);
1271     jwxyz_assert_gl ();
1272     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1273     jwxyz_assert_gl ();
1274   }
1275 }
1276
1277
1278 int
1279 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1280 {
1281   Assert(win == dpy->main_window, "XClearArea: not a window");
1282   Assert(!exp, "XClearArea: exposures unsupported");
1283
1284   jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
1285   return 0;
1286 }
1287
1288
1289 int
1290 XFillPolygon (Display *dpy, Drawable d, GC gc, 
1291               XPoint *points, int npoints, int shape, int mode)
1292 {
1293   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1294   set_fg_gc(dpy, gc);
1295   
1296   // TODO: Re-implement the GLU tesselation functions.
1297
1298   /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1299    * Nonconvex: Goop, Pacman, Rocks, Speedmine
1300    */
1301   
1302   Assert(shape == Convex, "XFillPolygon: (TODO) Unimplemented shape");
1303   
1304   // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1305   GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1306   short v[2] = {0, 0};
1307   
1308   for (unsigned i = 0; i < npoints; i++) {
1309     next_point(v, points[i], mode);
1310     vertices[2 * i] = v[0];
1311     vertices[2 * i + 1] = v[1];
1312   }
1313
1314   glEnableClientState (GL_VERTEX_ARRAY);
1315   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1316   
1317   glVertexPointer (2, GL_SHORT, 0, vertices);
1318   glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1319   
1320   free(vertices);
1321
1322   /*
1323   CGRect wr = d->frame;
1324   int i;
1325   push_fg_gc (dpy, d, gc, YES);
1326   CGContextRef cgc = d->cgc;
1327   CGContextBeginPath (cgc);
1328   float x = 0, y = 0;
1329   for (i = 0; i < npoints; i++) {
1330     if (i > 0 && mode == CoordModePrevious) {
1331       x += points[i].x;
1332       y -= points[i].y;
1333     } else {
1334       x = wr.origin.x + points[i].x;
1335       y = wr.origin.y + wr.size.height - points[i].y;
1336     }
1337         
1338     if (i == 0)
1339       CGContextMoveToPoint (cgc, x, y);
1340     else
1341       CGContextAddLineToPoint (cgc, x, y);
1342   }
1343   CGContextClosePath (cgc);
1344   if (gc->gcv.fill_rule == EvenOddRule)
1345     CGContextEOFillPath (cgc);
1346   else
1347     CGContextFillPath (cgc);
1348   pop_gc (d, gc);
1349   invalidate_drawable_cache (d);
1350   */
1351   return 0;
1352 }
1353
1354 #define radians(DEG) ((DEG) * M_PI / 180.0)
1355 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1356
1357 static void
1358 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1359 {
1360   p[0] = cos(theta) * w2 + cx;
1361   p[1] = -sin(theta) * h2 + cy;
1362 }
1363
1364 static unsigned
1365 mod_neg(int a, unsigned b)
1366 {
1367   /* Normal modulus is implementation defined for negative numbers. This is 
1368    * well-defined such that the repeating pattern for a >= 0 is maintained for 
1369    * a < 0. */
1370   return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1371 }
1372
1373 int
1374 jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1375                 unsigned int width, unsigned int height,
1376                 int angle1, int angle2, Bool fill_p)
1377 {
1378   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1379   set_fg_gc(dpy, gc);
1380
1381   /* Let's say the number of line segments needed to make a convincing circle is
1382      4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1383
1384   double w2 = width * 0.5f, h2 = height * 0.5f;
1385   double a, b; /* Semi-major/minor axes. */
1386   if(w2 > h2) {
1387     a = w2;
1388     b = h2;
1389   } else {
1390     a = h2;
1391     b = w2;
1392   }
1393   
1394   const double two_pi = 2 * M_PI;
1395
1396   double amb = a - b, apb = a + b;
1397   double h = (amb * amb) / (apb * apb);
1398   // TODO: Math cleanup.
1399   double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1400   double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1401
1402   // TODO: Explain how drawing works what with the points of overlapping arcs
1403   // matching up.
1404  
1405 #if 1
1406   unsigned segments_360 = segments_f;
1407   
1408   /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1409   /* TODO: color, thick lines, CapNotLast for thin lines */
1410   /* TODO: Transformations. */
1411
1412   double segment_angle = two_pi / segments_360;
1413
1414   const unsigned deg64 = 360 * 64;
1415   const double rad_from_deg64 = two_pi / deg64;
1416   
1417   if (angle2 < 0) {
1418     angle1 += angle2;
1419     angle2 = -angle2;
1420   }
1421
1422   angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1423
1424   if (angle2 > deg64)
1425     angle2 = deg64; // TODO: Handle circles special.
1426   
1427   double
1428     angle1_f = angle1 * rad_from_deg64,
1429     angle2_f = angle2 * rad_from_deg64;
1430   
1431   if (angle2_f > two_pi) // TODO: Move this up.
1432     angle2_f = two_pi;
1433   
1434   double segment1_angle_part = fmodf(angle1_f, segment_angle);
1435   
1436   unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1437
1438   double angle_2r = angle2_f - segment1_angle_part;
1439   unsigned segments = angle_2r / segment_angle;
1440   
1441   GLfloat cx = x + w2, cy = y + h2;
1442
1443   GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1444   
1445   GLfloat *data_ptr = data;
1446   if (fill_p) {
1447     data_ptr[0] = cx;
1448     data_ptr[1] = cy;
1449     data_ptr += 2;
1450   }
1451   
1452   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1453   data_ptr += 2;
1454   
1455   for (unsigned s = 0; s != segments; ++s) {
1456     // TODO: Make sure values of theta for the following arc_xy call are between
1457     // angle1_f and angle1_f + angle2_f.
1458     arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1459     data_ptr += 2;
1460   }
1461   
1462   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1463   data_ptr += 2;
1464
1465   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1466   glEnableClientState (GL_VERTEX_ARRAY);
1467   
1468   glVertexPointer (2, GL_FLOAT, 0, data);
1469   glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1470                 0,
1471                 (GLsizei)((data_ptr - data) / 2));
1472
1473   free(data);
1474   
1475 #endif
1476   
1477 #if 0
1478   unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1479  
1480   glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1481   
1482   if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1483     glVertex2f (cx, cy);
1484   
1485   /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1486   
1487   float to_radians = 2 * M_PI / (360 * 64);
1488   float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1489   
1490   for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1491   {
1492     glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1493     theta += d_theta;
1494   }
1495   
1496   glEnd ();
1497 #endif
1498   
1499   return 0;
1500 }
1501
1502
1503 XGCValues *
1504 jwxyz_gc_gcv (GC gc)
1505 {
1506   return &gc->gcv;
1507 }
1508
1509
1510 unsigned int
1511 jwxyz_gc_depth (GC gc)
1512 {
1513   return gc->depth;
1514 }
1515
1516
1517 GC
1518 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1519 {
1520   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1521   gc->depth = jwxyz_drawable_depth (d);
1522
1523   jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1524   XChangeGC (dpy, gc, mask, xgcv);
1525   return gc;
1526 }
1527
1528
1529 int
1530 XFreeGC (Display *dpy, GC gc)
1531 {
1532   if (gc->gcv.font)
1533     XUnloadFont (dpy, gc->gcv.font);
1534
1535   // TODO
1536 /*
1537   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1538
1539   if (gc->gcv.clip_mask) {
1540     XFreePixmap (dpy, gc->gcv.clip_mask);
1541     CGImageRelease (gc->clip_mask);
1542   }
1543 */
1544   free (gc);
1545   return 0;
1546 }
1547
1548
1549 /*
1550 static void
1551 flipbits (unsigned const char *in, unsigned char *out, int length)
1552 {
1553   static const unsigned char table[256] = {
1554     0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
1555     0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
1556     0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 
1557     0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
1558     0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 
1559     0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
1560     0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 
1561     0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
1562     0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 
1563     0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
1564     0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 
1565     0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
1566     0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 
1567     0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
1568     0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
1569     0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
1570     0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 
1571     0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
1572     0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 
1573     0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
1574     0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 
1575     0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
1576     0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 
1577     0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
1578     0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 
1579     0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
1580     0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1581     0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
1582     0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 
1583     0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
1584     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
1585     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1586   };
1587   while (length-- > 0)
1588     *out++ = table[*in++];
1589 }
1590 */
1591
1592 // Copied and pasted from OSX/XScreenSaverView.m
1593 static GLsizei
1594 to_pow2 (size_t x)
1595 {
1596   if (x <= 1)
1597     return 1;
1598
1599   size_t mask = (size_t)-1;
1600   unsigned bits = sizeof(x) * CHAR_BIT;
1601   unsigned log2 = bits;
1602
1603   --x;
1604   while (bits) {
1605     if (!(x & mask)) {
1606       log2 -= bits;
1607       x <<= bits;
1608     }
1609
1610     bits >>= 1;
1611     mask <<= bits;
1612   }
1613
1614   return 1 << log2;
1615 }
1616
1617
1618 int
1619 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1620            int src_x, int src_y, int dest_x, int dest_y,
1621            unsigned int w, unsigned int h)
1622 {
1623   jwxyz_assert_display (dpy);
1624  
1625   const XRectangle *wr = jwxyz_frame (d);
1626
1627   Assert (gc, "no GC");
1628   Assert ((w < 65535), "improbably large width");
1629   Assert ((h < 65535), "improbably large height");
1630   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1631   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1632   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1633   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1634
1635   // Clip width and height to the bounds of the Drawable
1636   //
1637   if (dest_x + w > wr->width) {
1638     if (dest_x > wr->width)
1639       return 0;
1640     w = wr->width - dest_x;
1641   }
1642   if (dest_y + h > wr->height) {
1643     if (dest_y > wr->height)
1644       return 0;
1645     h = wr->height - dest_y;
1646   }
1647   if (w <= 0 || h <= 0)
1648     return 0;
1649
1650   // Clip width and height to the bounds of the XImage
1651   //
1652   if (src_x + w > ximage->width) {
1653     if (src_x > ximage->width)
1654       return 0;
1655     w = ximage->width - src_x;
1656   }
1657   if (src_y + h > ximage->height) {
1658     if (src_y > ximage->height)
1659       return 0;
1660     h = ximage->height - src_y;
1661   }
1662   if (w <= 0 || h <= 0)
1663     return 0;
1664
1665   /* Assert (d->win */
1666
1667   if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1668     return 0;
1669
1670   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1671   int bpl = ximage->bytes_per_line;
1672   int bpp = ximage->bits_per_pixel;
1673   /* int bsize = bpl * h; */
1674   char *data = ximage->data;
1675
1676 /*
1677   CGRect r;
1678   r.origin.x = wr->x + dest_x;
1679   r.origin.y = wr->y + wr->height - dest_y - h;
1680   r.size.width = w;
1681   r.size.height = h;
1682 */
1683
1684   Assert (gc->gcv.function == GXcopy, "XPutImage: (TODO) GC function not supported");
1685   Assert (!gc->gcv.clip_mask, "XPutImage: (TODO) GC clip mask not supported");
1686   
1687   if (bpp == 32) {
1688
1689     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1690        to create a CGImage from a sub-rectagle of the XImage.
1691      */
1692     data += (src_y * bpl) + (src_x * 4);
1693
1694     jwxyz_assert_display(dpy);
1695     
1696     /* There probably won't be any hacks that do this, but... */
1697     Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1698     
1699     unsigned src_w = bpl / 4;
1700
1701     /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1702 # ifndef HAVE_JWZGLES
1703     glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1704     src_w = w;
1705 # endif
1706
1707     glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1708
1709 # if 1 // defined HAVE_JWZGLES
1710     // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1711     // TODO: Make use of OES_draw_texture.
1712     // TODO: Coords might be wrong; things might be upside-down or backwards
1713     //       or whatever.
1714
1715     unsigned tex_w = src_w, tex_h = h;
1716     if (!dpy->gl_texture_npot_p) {
1717       tex_w = to_pow2(tex_w);
1718       tex_h = to_pow2(tex_h);
1719     }
1720
1721     GLint internalformat = texture_internalformat(dpy);
1722
1723     glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
1724
1725     if (tex_w == src_w && tex_h == h) {
1726       glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
1727                     0, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1728     } else {
1729       // TODO: Sampling the last row might be a problem if src_x != 0.
1730       glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
1731                     0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
1732       glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h,
1733                        dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1734     }
1735     
1736     set_white ();
1737     // glEnable (dpy->gl_texture_target);
1738     // glColor4f (0.5, 0, 1, 1);
1739     glEnable (dpy->gl_texture_target);
1740     glEnableClientState (GL_VERTEX_ARRAY);
1741     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
1742
1743     // TODO: Why are these ever turned on in the first place?
1744     glDisableClientState (GL_COLOR_ARRAY);
1745     glDisableClientState (GL_NORMAL_ARRAY);
1746     // glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1747
1748     GLfloat vertices[4][2] =
1749     {
1750       {dest_x, dest_y},
1751       {dest_x, dest_y + h},
1752       {dest_x + w, dest_y + h},
1753       {dest_x + w, dest_y}
1754     };
1755
1756     GLfloat texcoord_w, texcoord_h;
1757 #  ifndef HAVE_JWZGLES
1758     if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
1759       texcoord_w = w;
1760       texcoord_h = h;
1761     } else
1762 #  endif /* HAVE_JWZGLES */
1763     {
1764       texcoord_w = (double)w / tex_w;
1765       texcoord_h = (double)h / tex_h;
1766     }
1767
1768     GLfloat tex_coords[4][2];
1769     tex_coords[0][0] = 0;
1770     tex_coords[0][1] = 0;
1771     tex_coords[1][0] = 0;
1772     tex_coords[1][1] = texcoord_h;
1773     tex_coords[2][0] = texcoord_w;
1774     tex_coords[2][1] = texcoord_h;
1775     tex_coords[3][0] = texcoord_w;
1776     tex_coords[3][1] = 0;
1777
1778     glVertexPointer (2, GL_FLOAT, 0, vertices);
1779     glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1780
1781     // Respect the alpha channel in the XImage
1782     glEnable (GL_BLEND);
1783     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1784
1785     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1786
1787     glDisable (GL_BLEND);
1788
1789 //  clear_texture();
1790     glDisable (dpy->gl_texture_target);
1791 # else
1792     glRasterPos2i (dest_x, dest_y);
1793     glPixelZoom (1, -1);
1794     jwxyz_assert_display (dpy);
1795     glDrawPixels (w, h, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1796 # endif
1797   } else {   // (bpp == 1)
1798
1799     // Assert(FALSE, "XPutImage: TODO");
1800     // Check out ximage_(get|put)pixel_1
1801     
1802 #if 0
1803     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1804
1805        #### However, the bit order within a byte in a 1bpp XImage is
1806             the wrong way around from what Quartz expects, so first we
1807             have to copy the data to reverse it.  Shit!  Maybe it
1808             would be worthwhile to go through the hacks and #ifdef
1809             each one that diddles 1bpp XImage->data directly...
1810      */
1811     Assert ((src_x % 8) == 0,
1812             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1813
1814     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
1815     unsigned char *flipped = (unsigned char *) malloc (bsize);
1816
1817     flipbits ((unsigned char *) data, flipped, bsize);
1818
1819     CGDataProviderRef prov = 
1820       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1821     CGImageRef mask = CGImageMaskCreate (w, h, 
1822                                          1, bpp, bpl,
1823                                          prov,
1824                                          NULL,  /* decode[] */
1825                                          GL_FALSE); /* interpolate */
1826     push_fg_gc (dpy, d, gc, GL_TRUE);
1827
1828     CGContextFillRect (cgc, r);                         // foreground color
1829     CGContextClipToMask (cgc, r, mask);
1830     set_color (dpy, cgc, gc->gcv.background, gc->depth, GL_FALSE, GL_TRUE);
1831     CGContextFillRect (cgc, r);                         // background color
1832     pop_gc (d, gc);
1833
1834     free (flipped);
1835     CGDataProviderRelease (prov);
1836     CGImageRelease (mask);
1837 #endif
1838   }
1839  
1840   jwxyz_assert_gl ();
1841   invalidate_drawable_cache (d);
1842
1843   return 0;
1844 }
1845
1846 /* At the moment nothing actually uses XGetSubImage. */
1847 /* #### Actually lots of things call XGetImage, which calls XGetSubImage.
1848    E.g., Twang calls XGetImage on the window intending to get a
1849    buffer full of black.  This is returning a buffer full of white
1850    instead of black for some reason. */
1851 static XImage *
1852 XGetSubImage (Display *dpy, Drawable d, int x, int y,
1853               unsigned int width, unsigned int height,
1854               unsigned long plane_mask, int format,
1855               XImage *dest_image, int dest_x, int dest_y)
1856 {
1857   Assert ((width  < 65535), "improbably large width");
1858   Assert ((height < 65535), "improbably large height");
1859   Assert ((x < 65535 && x > -65535), "improbably large x");
1860   Assert ((y < 65535 && y > -65535), "improbably large y");
1861
1862   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1863   
1864   // TODO: What if this reads off the edge? What is supposed to happen?
1865
1866   {
1867     // In case the caller tries to write off the edge.
1868     int
1869       max_width = dest_image->width - dest_x,
1870       max_height = dest_image->height - dest_y;
1871
1872     if (width > max_width) {
1873       width = max_width;
1874     }
1875     
1876     if (height > max_height) {
1877       height = max_height;
1878     }
1879   }
1880   
1881   Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1882   
1883   if (dest_image->depth == visual_depth (NULL, NULL)) {
1884     Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1885     unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1886 #ifdef HAVE_JWZGLES
1887     Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1888 #else
1889     glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1890 #endif
1891     glPixelStorei (GL_PACK_ALIGNMENT, 4);
1892     
1893     uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1894     
1895     glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1896                   dpy->screen->pixel_format, gl_pixel_type(dpy), dest_data);
1897
1898     /* Flip this upside down. :( */
1899     uint32_t *top = dest_data;
1900     uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1901     for (unsigned y = height / 2; y; --y) {
1902       for (unsigned x = 0; x != width; ++x) {
1903         uint32_t px = top[x];
1904         top[x] = bottom[x];
1905         bottom[x] = px;
1906       }
1907       top += pixels_per_line;
1908       bottom -= pixels_per_line;
1909     }
1910   } else {
1911
1912     /* TODO: Actually get pixels. */
1913
1914     Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1915     uint8_t *dest_data =
1916       (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1917       + dest_x / 8;
1918     for (unsigned y = height / 2; y; --y) {
1919       memset (dest_data, y & 1 ? 0x55 : 0xAA, width / 8);
1920       dest_data += dest_image->bytes_per_line;
1921     }
1922   }
1923
1924   return dest_image;
1925 }
1926
1927 XImage *
1928 XGetImage (Display *dpy, Drawable d, int x, int y,
1929            unsigned int width, unsigned int height,
1930            unsigned long plane_mask, int format)
1931 {
1932   unsigned depth = jwxyz_drawable_depth (d);
1933   XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1934                                 0, 0);
1935   image->data = (char *) malloc (height * image->bytes_per_line);
1936
1937   return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format,
1938                        image, 0, 0);
1939 }
1940
1941 /* Returns a transformation matrix to do rotation as per the provided
1942    EXIF "Orientation" value.
1943  */
1944 /*
1945 static CGAffineTransform
1946 exif_rotate (int rot, CGSize rect)
1947 {
1948   CGAffineTransform trans = CGAffineTransformIdentity;
1949   switch (rot) {
1950   case 2:               // flip horizontal
1951     trans = CGAffineTransformMakeTranslation (rect.width, 0);
1952     trans = CGAffineTransformScale (trans, -1, 1);
1953     break;
1954
1955   case 3:               // rotate 180
1956     trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1957     trans = CGAffineTransformRotate (trans, M_PI);
1958     break;
1959
1960   case 4:               // flip vertical
1961     trans = CGAffineTransformMakeTranslation (0, rect.height);
1962     trans = CGAffineTransformScale (trans, 1, -1);
1963     break;
1964
1965   case 5:               // transpose (UL-to-LR axis)
1966     trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1967     trans = CGAffineTransformScale (trans, -1, 1);
1968     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1969     break;
1970
1971   case 6:               // rotate 90
1972     trans = CGAffineTransformMakeTranslation (0, rect.width);
1973     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1974     break;
1975
1976   case 7:               // transverse (UR-to-LL axis)
1977     trans = CGAffineTransformMakeScale (-1, 1);
1978     trans = CGAffineTransformRotate (trans, M_PI / 2);
1979     break;
1980
1981   case 8:               // rotate 270
1982     trans = CGAffineTransformMakeTranslation (rect.height, 0);
1983     trans = CGAffineTransformRotate (trans, M_PI / 2);
1984     break;
1985
1986   default: 
1987     break;
1988   }
1989
1990   return trans;
1991 }
1992 */
1993
1994 void
1995 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
1996                                 Bool nsimg_p, void *img_arg,
1997                                XRectangle *geom_ret, int exif_rotation)
1998 {
1999   Assert (False, "jwxyz_draw_NSImage_or_CGImage: TODO stub");
2000 #if 0
2001   CGImageRef cgi;
2002 # ifndef USE_IPHONE
2003   CGImageSourceRef cgsrc;
2004 # endif // USE_IPHONE
2005   NSSize imgr;
2006
2007   CGContextRef cgc = d->cgc;
2008
2009   if (nsimg_p) {
2010
2011     NSImage *nsimg = (NSImage *) img_arg;
2012     imgr = [nsimg size];
2013
2014 # ifndef USE_IPHONE
2015     // convert the NSImage to a CGImage via the toll-free-bridging 
2016     // of NSData and CFData...
2017     //
2018     NSData *nsdata = [NSBitmapImageRep
2019                        TIFFRepresentationOfImageRepsInArray:
2020                          [nsimg representations]];
2021     CFDataRef cfdata = (CFDataRef) nsdata;
2022     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2023     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2024 # else  // USE_IPHONE
2025     cgi = nsimg.CGImage;
2026 # endif // USE_IPHONE
2027
2028   } else {
2029     cgi = (CGImageRef) img_arg;
2030     imgr.width  = CGImageGetWidth (cgi);
2031     imgr.height = CGImageGetHeight (cgi);
2032   }
2033
2034   Bool rot_p = (exif_rotation >= 5);
2035
2036   if (rot_p)
2037     imgr = NSMakeSize (imgr.height, imgr.width);
2038
2039   CGRect winr = d->frame;
2040   float rw = winr.size.width  / imgr.width;
2041   float rh = winr.size.height / imgr.height;
2042   float r = (rw < rh ? rw : rh);
2043
2044   CGRect dst, dst2;
2045   dst.size.width  = imgr.width  * r;
2046   dst.size.height = imgr.height * r;
2047   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
2048   dst.origin.y = (winr.size.height - dst.size.height) / 2;
2049
2050   dst2.origin.x = dst2.origin.y = 0;
2051   if (rot_p) {
2052     dst2.size.width = dst.size.height; 
2053     dst2.size.height = dst.size.width;
2054   } else {
2055     dst2.size = dst.size;
2056   }
2057
2058   // Clear the part not covered by the image to background or black.
2059   //
2060   if (d->type == WINDOW)
2061     XClearWindow (dpy, d);
2062   else {
2063     jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height,
2064                      drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
2065   }
2066
2067   CGAffineTransform trans = 
2068     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2069
2070   CGContextSaveGState (cgc);
2071   CGContextConcatCTM (cgc, 
2072                       CGAffineTransformMakeTranslation (dst.origin.x,
2073                                                         dst.origin.y));
2074   CGContextConcatCTM (cgc, trans);
2075   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2076   CGContextDrawImage (cgc, dst2, cgi);
2077   CGContextRestoreGState (cgc);
2078
2079 # ifndef USE_IPHONE
2080   if (nsimg_p) {
2081     CFRelease (cgsrc);
2082     CGImageRelease (cgi);
2083   }
2084 # endif // USE_IPHONE
2085
2086   if (geom_ret) {
2087     geom_ret->x = dst.origin.x;
2088     geom_ret->y = dst.origin.y;
2089     geom_ret->width  = dst.size.width;
2090     geom_ret->height = dst.size.height;
2091   }
2092
2093   invalidate_drawable_cache (d);
2094 #endif
2095 }
2096
2097 #ifndef HAVE_JWZGLES
2098
2099 /*
2100 static void
2101 create_rectangle_texture (GLuint *texture)
2102 {
2103   glGenTextures(1, texture);
2104   glBindTexture(GL_TEXTURE_RECTANGLE_EXT, *texture);
2105   glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2106   glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2107 }
2108 */
2109
2110 #endif
2111
2112
2113 #if 0
2114 static Pixmap
2115 copy_pixmap (Display *dpy, Pixmap p)
2116 {
2117   if (!p) return 0;
2118   Assert (p->type == PIXMAP, "not a pixmap");
2119
2120   Pixmap p2 = 0;
2121
2122   Window root;
2123   int x, y;
2124   unsigned int width, height, border_width, depth;
2125   if (XGetGeometry (dpy, p, &root,
2126                     &x, &y, &width, &height, &border_width, &depth)) {
2127     XGCValues gcv;
2128     gcv.function = GXcopy;
2129     GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
2130     if (gc) {
2131       p2 = XCreatePixmap (dpy, p, width, height, depth);
2132       if (p2)
2133         XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
2134       XFreeGC (dpy, gc);
2135     }
2136   }
2137
2138   Assert (p2, "could not copy pixmap");
2139
2140   return p2;
2141 }
2142 #endif
2143
2144
2145 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2146 //
2147 static void
2148 query_font (Font fid)
2149 {
2150   if (!fid || !fid->native_font) {
2151     Assert (0, "no native font in fid");
2152     return;
2153   }
2154
2155   int first = 32;
2156   int last = 255;
2157
2158   Display *dpy = fid->dpy;
2159   void *native_font = fid->native_font;
2160
2161   XFontStruct *f = &fid->metrics;
2162   XCharStruct *min = &f->min_bounds;
2163   XCharStruct *max = &f->max_bounds;
2164
2165   f->fid               = fid;
2166   f->min_char_or_byte2 = first;
2167   f->max_char_or_byte2 = last;
2168   f->default_char      = 'M';
2169   f->ascent            = fid->ascent;
2170   f->descent           = fid->descent;
2171
2172   min->width    = 32767;  // set to smaller values in the loop
2173   min->ascent   = 32767;
2174   min->descent  = 32767;
2175   min->lbearing = 32767;
2176   min->rbearing = 32767;
2177
2178   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2179
2180   for (int i = first; i <= last; i++) {
2181     XCharStruct *cs = &f->per_char[i-first];
2182     char s = (char) i;
2183     jwxyz_render_text (dpy, native_font, &s, 1, GL_FALSE, cs, 0);
2184
2185     max->width    = MAX (max->width,    cs->width);
2186     max->ascent   = MAX (max->ascent,   cs->ascent);
2187     max->descent  = MAX (max->descent,  cs->descent);
2188     max->lbearing = MAX (max->lbearing, cs->lbearing);
2189     max->rbearing = MAX (max->rbearing, cs->rbearing);
2190
2191     min->width    = MIN (min->width,    cs->width);
2192     min->ascent   = MIN (min->ascent,   cs->ascent);
2193     min->descent  = MIN (min->descent,  cs->descent);
2194     min->lbearing = MIN (min->lbearing, cs->lbearing);
2195     min->rbearing = MIN (min->rbearing, cs->rbearing);
2196 /*
2197     Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d\n",
2198          i, i, cs->width, cs->lbearing, cs->rbearing, 
2199          cs->ascent, cs->descent);
2200  */
2201   }
2202 }
2203
2204
2205 // Since 'Font' includes the metrics, this just makes a copy of that.
2206 //
2207 XFontStruct *
2208 XQueryFont (Display *dpy, Font fid)
2209 {
2210   // copy XFontStruct
2211   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2212   *f = fid->metrics;
2213   f->fid = fid;
2214
2215   // build XFontProps
2216   f->n_properties = 1;
2217   f->properties = malloc (sizeof(*f->properties) * f->n_properties);
2218   f->properties[0].name = XA_FONT;
2219   Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
2220           "atoms probably needs a real implementation");
2221   // If XInternAtom is ever implemented, use it here.
2222   f->properties[0].card32 = (unsigned long)(char *)fid->xa_font;
2223
2224   // copy XCharStruct array
2225   int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
2226   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2227
2228   memcpy (f->per_char, fid->metrics.per_char,
2229           size * sizeof (XCharStruct));
2230
2231   return f;
2232 }
2233
2234
2235 static Font
2236 copy_font (Font fid)
2237 {
2238   fid->refcount++;
2239   return fid;
2240 }
2241
2242
2243 #if 0
2244
2245
2246 static NSArray *
2247 font_family_members (NSString *family_name)
2248 {
2249 # ifndef USE_IPHONE
2250   return [[NSFontManager sharedFontManager]
2251           availableMembersOfFontFamily:family_name];
2252 # else
2253   return [UIFont fontNamesForFamilyName:family_name];
2254 # endif
2255 }
2256
2257
2258 static NSString *
2259 default_font_family (NSFontTraitMask require)
2260 {
2261   return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
2262 }
2263
2264
2265 static NSFont *
2266 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
2267           NSString *family_name, float size,
2268           char **name_ret)
2269 {
2270   Assert (size > 0, "zero font size");
2271
2272   NSArray *family_members = font_family_members (family_name);
2273   if (!family_members.count)
2274     family_members = font_family_members (default_font_family (traits));
2275
2276 # ifndef USE_IPHONE
2277   for (unsigned k = 0; k != family_members.count; ++k) {
2278
2279     NSArray *member = [family_members objectAtIndex:k];
2280     NSFontTraitMask font_mask =
2281     [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
2282
2283     if ((font_mask & mask) == traits) {
2284
2285       NSString *name = [member objectAtIndex:0];
2286       NSFont *f = [NSFont fontWithName:name size:size];
2287       if (!f)
2288         break;
2289
2290       /* Don't use this font if it (probably) doesn't include ASCII characters.
2291        */
2292       NSStringEncoding enc = [f mostCompatibleStringEncoding];
2293       if (! (enc == NSUTF8StringEncoding ||
2294              enc == NSISOLatin1StringEncoding ||
2295              enc == NSNonLossyASCIIStringEncoding ||
2296              enc == NSISOLatin2StringEncoding ||
2297              enc == NSUnicodeStringEncoding ||
2298              enc == NSWindowsCP1250StringEncoding ||
2299              enc == NSWindowsCP1252StringEncoding ||
2300              enc == NSMacOSRomanStringEncoding)) {
2301         // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2302         break;
2303       }
2304       // NSLog(@"using \"%@\": %d", name, enc);
2305
2306       // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2307       *name_ret = strdup (name.UTF8String);
2308       return f;
2309     }
2310   }
2311 # else // USE_IPHONE
2312
2313   for (NSString *fn in family_members) {
2314 # define MATCH(X) \
2315          ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2316          != NSNotFound)
2317
2318     // The magic invocation for getting font names is
2319     // [[UIFontDescriptor
2320     //   fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
2321     //  symbolicTraits]
2322     // ...but this only works on iOS 7 and later.
2323     NSFontTraitMask font_mask = 0;
2324     if (MATCH(@"Bold"))
2325       font_mask |= NSBoldFontMask;
2326     if (MATCH(@"Italic") || MATCH(@"Oblique"))
2327       font_mask |= NSItalicFontMask;
2328
2329     if ((font_mask & mask) == traits) {
2330
2331       /* Check if it can do ASCII.  No good way to accomplish this!
2332          These are fonts present in iPhone Simulator as of June 2012
2333          that don't include ASCII.
2334        */
2335       if (MATCH(@"AppleGothic") ||      // Korean
2336           MATCH(@"Dingbats") ||         // Dingbats
2337           MATCH(@"Emoji") ||            // Emoticons
2338           MATCH(@"Geeza") ||            // Arabic
2339           MATCH(@"Hebrew") ||           // Hebrew
2340           MATCH(@"HiraKaku") ||         // Japanese
2341           MATCH(@"HiraMin") ||          // Japanese
2342           MATCH(@"Kailasa") ||          // Tibetan
2343           MATCH(@"Ornaments") ||        // Dingbats
2344           MATCH(@"STHeiti")             // Chinese
2345        )
2346          break;
2347
2348       *name_ret = strdup (fn.UTF8String);
2349       return [UIFont fontWithName:fn size:size];
2350     }
2351 # undef MATCH
2352   }
2353
2354 # endif
2355
2356   return NULL;
2357 }
2358
2359
2360
2361 /* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
2362    of XLFD strings; also they can be comma-separated strings with multiple
2363    font names.  First one that exists wins.
2364  */
2365 static NSFont *
2366 try_native_font (const char *name, float scale,
2367                  char **name_ret, float *size_ret, char **xa_font)
2368 {
2369   if (!name) return 0;
2370   const char *spc = strrchr (name, ' ');
2371   if (!spc) return 0;
2372
2373   NSFont *f = 0;
2374   char *token = strdup (name);
2375   char *name2;
2376
2377   while ((name2 = strtok (token, ","))) {
2378     token = 0;
2379
2380     while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
2381       name2++;
2382
2383     spc = strrchr (name2, ' ');
2384     if (!spc) continue;
2385
2386     int dsize = 0;
2387     if (1 != sscanf (spc, " %d ", &dsize))
2388       continue;
2389     float size = dsize;
2390
2391     if (size <= 4) continue;
2392
2393     size *= scale;
2394
2395     name2[strlen(name2) - strlen(spc)] = 0;
2396
2397     NSString *nsname = [NSString stringWithCString:name2
2398                                           encoding:NSUTF8StringEncoding];
2399     f = [NSFont fontWithName:nsname size:size];
2400     if (f) {
2401       *name_ret = name2;
2402       *size_ret = size;
2403       *xa_font = strdup (name); // Maybe this should be an XLFD?
2404       break;
2405     } else {
2406       NSLog(@"No native font: \"%@\" %.0f", nsname, size);
2407     }
2408   }
2409
2410   free (token);
2411   return f;
2412 }
2413
2414
2415 /* Returns a random font in the given size and face.
2416  */
2417 static NSFont *
2418 random_font (NSFontTraitMask traits, NSFontTraitMask mask,
2419              float size, NSString **family_ret, char **name_ret)
2420 {
2421
2422 # ifndef USE_IPHONE
2423   // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
2424   // returns an empty list, at least on a system with default fonts only.
2425   NSArray *families = [[NSFontManager sharedFontManager]
2426                        availableFontFamilies];
2427   if (!families) return 0;
2428 # else
2429   NSArray *families = [UIFont familyNames];
2430
2431   // There are many dups in the families array -- uniquify it.
2432   {
2433     NSArray *sorted_families =
2434     [families sortedArrayUsingSelector:@selector(compare:)];
2435     NSMutableArray *new_families =
2436     [NSMutableArray arrayWithCapacity:sorted_families.count];
2437
2438     NSString *prev_family = nil;
2439     for (NSString *family in sorted_families) {
2440       if ([family compare:prev_family])
2441         [new_families addObject:family];
2442     }
2443
2444     families = new_families;
2445   }
2446 # endif // USE_IPHONE
2447
2448   long n = [families count];
2449   if (n <= 0) return 0;
2450
2451   int j;
2452   for (j = 0; j < n; j++) {
2453     int i = random() % n;
2454     NSString *family_name = [families objectAtIndex:i];
2455
2456     NSFont *result = try_font (traits, mask, family_name, size, name_ret);
2457     if (result) {
2458       [*family_ret release];
2459       *family_ret = family_name;
2460       [*family_ret retain];
2461       return result;
2462     }
2463   }
2464
2465   // None of the fonts support ASCII?
2466   return 0;
2467 }
2468
2469
2470 // Fonts need this. XDisplayHeightMM and friends should probably be consistent
2471 // with this as well if they're ever implemented.
2472 static const unsigned dpi = 75;
2473
2474
2475 static const char *
2476 xlfd_field_end (const char *s)
2477 {
2478   const char *s2 = strchr(s, '-');
2479   if (!s2)
2480     s2 = s + strlen(s);
2481   return s2;
2482 }
2483
2484
2485 static size_t
2486 xlfd_next (const char **s, const char **s2)
2487 {
2488   if (!**s2) {
2489     *s = *s2;
2490   } else {
2491     Assert (**s2 == '-', "xlfd parse error");
2492     *s = *s2 + 1;
2493     *s2 = xlfd_field_end (*s);
2494   }
2495
2496   return *s2 - *s;
2497 }
2498
2499 static NSFont *
2500 try_xlfd_font (const char *name, float scale,
2501                char **name_ret, float *size_ret, char **xa_font)
2502 {
2503   NSFont *nsfont = 0;
2504   NSString *family_name = nil;
2505   NSFontTraitMask require = 0, forbid = 0;
2506   GLboolean rand  = GL_FALSE;
2507   float size = 0;
2508   char *ps_name = 0;
2509
2510   const char *s = (name ? name : "");
2511
2512   size_t L = strlen (s);
2513 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2514 # define UNSPEC   (L == 0 || L == 1 && *s == '*')
2515   if      (CMP ("6x10"))     size = 8,  require |= NSFixedPitchFontMask;
2516   else if (CMP ("6x10bold")) size = 8,  require |= NSFixedPitchFontMask | NSBoldFontMask;
2517   else if (CMP ("fixed"))    size = 12, require |= NSFixedPitchFontMask;
2518   else if (CMP ("9x15"))     size = 12, require |= NSFixedPitchFontMask;
2519   else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
2520   else if (CMP ("vga"))      size = 12, require |= NSFixedPitchFontMask;
2521   else if (CMP ("console"))  size = 12, require |= NSFixedPitchFontMask;
2522   else if (CMP ("gallant"))  size = 12, require |= NSFixedPitchFontMask;
2523   else {
2524
2525     // Incorrect fields are ignored.
2526
2527     if (*s == '-')
2528       ++s;
2529     const char *s2 = xlfd_field_end(s);
2530
2531     // Foundry (ignore)
2532
2533     L = xlfd_next (&s, &s2); // Family name
2534     // This used to substitute Georgia for Times. Now it doesn't.
2535     if (CMP ("random")) {
2536       rand = GL_TRUE;
2537     } else if (CMP ("fixed")) {
2538       require |= NSFixedPitchFontMask;
2539       family_name = @"Courier";
2540     } else if (!UNSPEC) {
2541       family_name = [[[NSString alloc] initWithBytes:s
2542                                               length:L
2543                                             encoding:NSUTF8StringEncoding]
2544                      autorelease];
2545     }
2546
2547     L = xlfd_next (&s, &s2); // Weight name
2548     if (CMP ("bold") || CMP ("demibold"))
2549       require |= NSBoldFontMask;
2550     else if (CMP ("medium") || CMP ("regular"))
2551       forbid |= NSBoldFontMask;
2552
2553     L = xlfd_next (&s, &s2); // Slant
2554     if (CMP ("i") || CMP ("o"))
2555       require |= NSItalicFontMask;
2556     else if (CMP ("r"))
2557       forbid |= NSItalicFontMask;
2558
2559     xlfd_next (&s, &s2); // Set width name (ignore)
2560     xlfd_next (&s, &s2); // Add style name (ignore)
2561
2562     xlfd_next (&s, &s2); // Pixel size (ignore)
2563
2564     xlfd_next (&s, &s2); // Point size
2565     char *s3;
2566     uintmax_t n = strtoumax(s, &s3, 10);
2567     if (s2 == s3)
2568       size = n / 10.0;
2569
2570     xlfd_next (&s, &s2); // Resolution X (ignore)
2571     xlfd_next (&s, &s2); // Resolution Y (ignore)
2572
2573     xlfd_next (&s, &s2); // Spacing
2574     if (CMP ("p"))
2575       forbid |= NSFixedPitchFontMask;
2576     else if (CMP ("m") || CMP ("c"))
2577       require |= NSFixedPitchFontMask;
2578
2579     // Don't care about average_width or charset registry.
2580   }
2581 # undef CMP
2582 # undef UNSPEC
2583
2584   if (!family_name && !rand)
2585     family_name = default_font_family (require);
2586
2587   if (size < 6 || size > 1000)
2588     size = 12;
2589
2590   size *= scale;
2591
2592   NSFontTraitMask mask = require | forbid;
2593
2594   if (rand) {
2595     nsfont   = random_font (require, mask, size, &family_name, &ps_name);
2596     [family_name autorelease];
2597   }
2598
2599   if (!nsfont)
2600     nsfont   = try_font (require, mask, family_name, size, &ps_name);
2601
2602   // if that didn't work, turn off attibutes until it does
2603   // (e.g., there is no "Monaco-Bold".)
2604   //
2605   if (!nsfont && (mask & NSItalicFontMask)) {
2606     require &= ~NSItalicFontMask;
2607     mask &= ~NSItalicFontMask;
2608     nsfont = try_font (require, mask, family_name, size, &ps_name);
2609   }
2610   if (!nsfont && (mask & NSBoldFontMask)) {
2611     require &= ~NSBoldFontMask;
2612     mask &= ~NSBoldFontMask;
2613     nsfont = try_font (require, mask, family_name, size, &ps_name);
2614   }
2615   if (!nsfont && (mask & NSFixedPitchFontMask)) {
2616     require &= ~NSFixedPitchFontMask;
2617     mask &= ~NSFixedPitchFontMask;
2618     nsfont = try_font (require, mask, family_name, size, &ps_name);
2619   }
2620
2621   if (nsfont) {
2622     *name_ret = ps_name;
2623     *size_ret = size;
2624     float actual_size = size / scale;
2625     asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
2626              family_name.UTF8String,
2627              (require & NSBoldFontMask) ? "bold" : "medium",
2628              (require & NSItalicFontMask) ? 'o' : 'r',
2629              (unsigned)(dpi * actual_size / 72.27 + 0.5),
2630              (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
2631              (require & NSFixedPitchFontMask) ? 'm' : 'p');
2632     return nsfont;
2633   } else {
2634     return 0;
2635   }
2636 }
2637
2638 #endif
2639
2640 Font
2641 XLoadFont (Display *dpy, const char *name)
2642 {
2643   Font fid = (Font) calloc (1, sizeof(*fid));
2644   fid->refcount = 1;
2645   fid->dpy = dpy;
2646
2647   // (TODO) float scale = 1;
2648
2649 # ifdef USE_IPHONE
2650   /* Since iOS screens are physically smaller than desktop screens, scale up
2651      the fonts to make them more readable.
2652
2653      Note that X11 apps on iOS also have the backbuffer sized in points
2654      instead of pixels, resulting in an effective X11 screen size of 768x1024
2655      or so, even if the display has significantly higher resolution.  That is
2656      unrelated to this hack, which is really about DPI.
2657    */
2658   /* scale = 2; */
2659 # endif
2660
2661   fid->dpy = dpy;
2662   fid->native_font = jwxyz_load_native_font (dpy, name,
2663                                              &fid->ps_name, &fid->size,
2664                                              &fid->ascent, &fid->descent);
2665   if (!fid->native_font) {
2666     free (fid);
2667     return 0;
2668   }
2669   query_font (fid);
2670
2671   return fid;
2672 }
2673
2674
2675 XFontStruct *
2676 XLoadQueryFont (Display *dpy, const char *name)
2677 {
2678   Font fid = XLoadFont (dpy, name);
2679   if (!fid) return 0;
2680   return XQueryFont (dpy, fid);
2681 }
2682
2683 int
2684 XUnloadFont (Display *dpy, Font fid)
2685 {
2686   if (--fid->refcount < 0) abort();
2687   if (fid->refcount > 0) return 0;
2688
2689   if (fid->native_font)
2690     jwxyz_release_native_font (fid->dpy, fid->native_font);
2691
2692   if (fid->ps_name)
2693     free (fid->ps_name);
2694   if (fid->metrics.per_char)
2695     free (fid->metrics.per_char);
2696
2697   // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
2698   //      crashes in [NSFont ascender] <- query_font, and it seems to go away
2699   //      if I never release the nsfont.  So, fuck it, we'll just leak fonts.
2700   //      They're probably not very big...
2701   //
2702   //  [fid->nsfont release];
2703   //  CFRelease (fid->nsfont);
2704
2705   free (fid);
2706   return 0;
2707 }
2708
2709 int
2710 XFreeFontInfo (char **names, XFontStruct *info, int n)
2711 {
2712   int i;
2713   if (names) {
2714     for (i = 0; i < n; i++)
2715       if (names[i]) free (names[i]);
2716     free (names);
2717   }
2718   if (info) {
2719     for (i = 0; i < n; i++)
2720       if (info[i].per_char) {
2721         free (info[i].per_char);
2722         free (info[i].properties);
2723       }
2724     free (info);
2725   }
2726   return 0;
2727 }
2728
2729 int
2730 XFreeFont (Display *dpy, XFontStruct *f)
2731 {
2732   Font fid = f->fid;
2733   XFreeFontInfo (0, f, 1);
2734   XUnloadFont (dpy, fid);
2735   return 0;
2736 }
2737
2738
2739 int
2740 XSetFont (Display *dpy, GC gc, Font fid)
2741 {
2742   Font font2 = copy_font (fid);
2743   if (gc->gcv.font)
2744     XUnloadFont (dpy, gc->gcv.font);
2745   gc->gcv.font = font2;
2746   return 0;
2747 }
2748
2749
2750 XFontSet
2751 XCreateFontSet (Display *dpy, char *name, 
2752                 char ***missing_charset_list_return,
2753                 int *missing_charset_count_return,
2754                 char **def_string_return)
2755 {
2756   char *name2 = strdup (name);
2757   char *s = strchr (name, ',');
2758   if (s) *s = 0;
2759   XFontSet set = 0;
2760   XFontStruct *f = XLoadQueryFont (dpy, name2);
2761   if (f)
2762     {
2763       set = (XFontSet) calloc (1, sizeof(*set));
2764       set->font = f;
2765     }
2766   free (name2);
2767   if (missing_charset_list_return)  *missing_charset_list_return = 0;
2768   if (missing_charset_count_return) *missing_charset_count_return = 0;
2769   if (def_string_return) *def_string_return = 0;
2770   return set;
2771 }
2772
2773
2774 void
2775 XFreeFontSet (Display *dpy, XFontSet set)
2776 {
2777   XFreeFont (dpy, set->font);
2778   free (set);
2779 }
2780
2781
2782 const char *
2783 jwxyz_nativeFontName (Font f, float *size)
2784 {
2785   if (size) *size = f->size;
2786   return f->ps_name;
2787 }
2788
2789
2790 void
2791 XFreeStringList (char **list)
2792 {
2793   int i;
2794   if (!list) return;
2795   for (i = 0; list[i]; i++)
2796     XFree (list[i]);
2797   XFree (list);
2798 }
2799
2800
2801 // Returns the verbose Unicode name of this character, like "agrave" or
2802 // "daggerdouble".  Used by fontglide debugMetrics.
2803 //
2804 char *
2805 jwxyz_unicode_character_name (Font fid, unsigned long uc)
2806 {
2807   /* TODO Fonts
2808   char *ret = 0;
2809   CTFontRef ctfont =
2810     CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2811                           [fid->nsfont pointSize],
2812                           NULL);
2813   Assert (ctfont, @"no CTFontRef for UIFont");
2814
2815   CGGlyph cgglyph;
2816   if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
2817     NSString *name = (NSString *)
2818       CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
2819                                    cgglyph);
2820     ret = (name ? strdup ([name UTF8String]) : 0);
2821   }
2822
2823   CFRelease (ctfont);
2824   return ret;
2825    */
2826   return NULL;
2827 }
2828
2829
2830 // Given a UTF8 string, return an NSString.  Bogus UTF8 characters are ignored.
2831 // We have to do this because stringWithCString returns NULL if there are
2832 // any invalid characters at all.
2833 //
2834 /* TODO
2835 static NSString *
2836 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
2837 {
2838   int out_len = in_len * 4;   // length of string might increase
2839   char *s2 = (char *) malloc (out_len);
2840   char *out = s2;
2841   const char *in_end  = in  + in_len;
2842   const char *out_end = out + out_len;
2843   Bool latin1_p = True;
2844
2845   while (in < in_end)
2846     {
2847       unsigned long uc;
2848       long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
2849       long L2 = utf8_encode (uc, out, out_end - out);
2850       in  += L1;
2851       out += L2;
2852       if (uc > 255) latin1_p = False;
2853     }
2854   *out = 0;
2855   NSString *nsstr =
2856     [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2857   free (s2);
2858   if (latin1_pP) *latin1_pP = latin1_p;
2859   return (nsstr ? nsstr : @"");
2860 }
2861 */
2862
2863 int
2864 XTextExtents (XFontStruct *f, const char *s, int length,
2865               int *dir_ret, int *ascent_ret, int *descent_ret,
2866               XCharStruct *cs)
2867 {
2868   // Unfortunately, adding XCharStructs together to get the extents for a
2869   // string doesn't work: Cocoa uses non-integral character advancements, but
2870   // XCharStruct.width is an integer. Plus that doesn't take into account
2871   // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
2872   // Zapfino.
2873
2874   Font ff = f->fid;
2875   Display *dpy = ff->dpy;
2876   jwxyz_render_text (dpy, ff->native_font, s, length, GL_FALSE, cs, 0);
2877   *dir_ret = 0;
2878   *ascent_ret  = f->ascent;
2879   *descent_ret = f->descent;
2880   return 0;
2881 }
2882
2883 int
2884 XTextWidth (XFontStruct *f, const char *s, int length)
2885 {
2886   int ascent, descent, dir;
2887   XCharStruct cs;
2888   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2889   return cs.width;
2890 }
2891
2892
2893 int
2894 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
2895                 int *dir_ret, int *ascent_ret, int *descent_ret,
2896                 XCharStruct *cs)
2897 {
2898   // Bool latin1_p = True;
2899   int i, utf8_len = 0;
2900   char *utf8 = XChar2b_to_utf8 (s, &utf8_len);   // already sanitized
2901
2902   for (i = 0; i < length; i++)
2903     if (s[i].byte1 > 0) {
2904       // latin1_p = False;
2905       break;
2906     }
2907
2908   {
2909     Font ff = f->fid;
2910     Display *dpy = ff->dpy;
2911     jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8),
2912                        GL_TRUE, cs, 0);
2913   }
2914
2915   *dir_ret = 0;
2916   *ascent_ret  = f->ascent;
2917   *descent_ret = f->descent;
2918   free (utf8);
2919   return 0;
2920 }
2921
2922
2923 /* "Returns the distance in pixels in the primary draw direction from
2924    the drawing origin to the origin of the next character to be drawn."
2925
2926    "overall_ink_return is set to the bbox of the string's character ink."
2927
2928    "The overall_ink_return for a nondescending, horizontally drawn Latin
2929    character is conventionally entirely above the baseline; that is,
2930    overall_ink_return.height <= -overall_ink_return.y."
2931
2932      [So this means that y is the top of the ink, and height grows down:
2933       For above-the-baseline characters, y is negative.]
2934
2935    "The overall_ink_return for a nonkerned character is entirely at, and to
2936    the right of, the origin; that is, overall_ink_return.x >= 0."
2937
2938      [So this means that x is the left of the ink, and width grows right.
2939       For left-of-the-origin characters, x is negative.]
2940
2941    "A character consisting of a single pixel at the origin would set
2942    overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
2943  */
2944 int
2945 Xutf8TextExtents (XFontSet set, const char *str, int len,
2946                   XRectangle *overall_ink_return,
2947                   XRectangle *overall_logical_return)
2948 {
2949 #if 0
2950   Bool latin1_p;
2951   NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
2952   XCharStruct cs;
2953
2954   utf8_metrics (set->font->fid, nsstr, &cs);
2955
2956   /* "The overall_logical_return is the bounding box that provides minimum
2957      spacing to other graphical features for the string. Other graphical
2958      features, for example, a border surrounding the text, should not
2959      intersect this rectangle."
2960
2961      So I think that means they're the same?  Or maybe "ink" is the bounding
2962      box, and "logical" is the advancement?  But then why is the return value
2963      the advancement?
2964    */
2965   if (overall_ink_return)
2966     XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
2967   if (overall_logical_return)
2968     XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
2969
2970   return cs.width;
2971 #endif
2972   abort();
2973 }
2974
2975
2976 static int
2977 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2978              const char *str, size_t len, GLboolean utf8)
2979 {
2980   Font ff = gc->gcv.font;
2981   XCharStruct cs;
2982
2983   char *data = 0;
2984   jwxyz_render_text (dpy, ff->native_font, str, len, utf8, &cs, &data);
2985   int w = cs.rbearing - cs.lbearing;
2986   int h = cs.ascent + cs.descent;
2987
2988   if (w < 0 || h < 0) abort();
2989   if (w == 0 || h == 0) {
2990     if (data) free(data);
2991     return 0;
2992   }
2993
2994   XImage *img = XCreateImage (dpy, dpy->screen->visual, 32,
2995                               ZPixmap, 0, data, w, h, 0, 0);
2996
2997   /* The image of text is a 32-bit image, in white.
2998      Take the red channel for intensity and use that as alpha.
2999      replace RGB with the GC's foreground color.
3000      This expects that XPutImage respects alpha and only writes
3001      the bits that are not masked out.
3002      This also assumes that XPutImage expects ARGB.
3003    */
3004   {
3005     char *s = data;
3006     char *end = s + (w * h * 4);
3007     uint8_t rgba[4];
3008     jwxyz_query_color (dpy, gc->gcv.foreground, rgba);
3009     while (s < end) {
3010
3011       s[3] = s[1];
3012       s[0] = rgba[0];
3013       s[1] = rgba[1];
3014       s[2] = rgba[2];
3015       s += 4;
3016     }
3017   }
3018
3019   XPutImage (dpy, d, gc, img, 0, 0, 
3020              x + cs.lbearing,
3021              y - cs.ascent,
3022              w, h);
3023   XDestroyImage (img);
3024
3025   return 0;
3026 }
3027
3028 int
3029 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3030              const char  *str, int len)
3031 {
3032   return draw_string (dpy, d, gc, x, y, str, len, GL_FALSE);
3033 }
3034
3035
3036 int
3037 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
3038              const XChar2b *str, int len)
3039 {
3040   XChar2b *b2 = malloc ((len + 1) * sizeof(*b2));
3041   char *s2;
3042   int ret;
3043   memcpy (b2, str, len * sizeof(*b2));
3044   b2[len].byte1 = b2[len].byte2 = 0;
3045   s2 = XChar2b_to_utf8 (b2, 0);
3046   free (b2);
3047   ret = draw_string (dpy, d, gc, x, y, s2, strlen(s2), GL_TRUE);
3048   free (s2);
3049   return ret;
3050 }
3051
3052
3053 void
3054 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
3055                  int x, int y, const char *str, int len)
3056 {
3057   draw_string (dpy, d, gc, x, y, str, len, GL_TRUE);
3058 }
3059
3060
3061 int
3062 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3063                   const char *str, int len)
3064 {
3065   int ascent, descent, dir;
3066   XCharStruct cs;
3067   XTextExtents (&gc->gcv.font->metrics, str, len,
3068                 &dir, &ascent, &descent, &cs);
3069   jwxyz_fill_rect (dpy, d, gc,
3070                    x + MIN (0, cs.lbearing),
3071                    y - MAX (0, ascent),
3072                    MAX (MAX (0, cs.rbearing) -
3073                         MIN (0, cs.lbearing),
3074                         cs.width),
3075                    MAX (0, ascent) + MAX (0, descent),
3076                    gc->gcv.background);
3077   return XDrawString (dpy, d, gc, x, y, str, len);
3078 }
3079
3080
3081 int
3082 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3083 {
3084 //####  abort();
3085 /*
3086   TODO
3087
3088   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3089
3090   if (gc->gcv.clip_mask) {
3091     XFreePixmap (dpy, gc->gcv.clip_mask);
3092     CGImageRelease (gc->clip_mask);
3093   }
3094
3095   gc->gcv.clip_mask = copy_pixmap (dpy, m);
3096   if (gc->gcv.clip_mask)
3097     gc->clip_mask =
3098       CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3099   else
3100     gc->clip_mask = 0;
3101 */
3102   
3103   return 0;
3104 }
3105
3106 int
3107 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3108 {
3109   gc->gcv.clip_x_origin = x;
3110   gc->gcv.clip_y_origin = y;
3111   return 0;
3112 }
3113
3114 #endif /* JWXYZ_GL -- entire file */