From http://www.jwz.org/xscreensaver/xscreensaver-5.31.tar.gz
[xscreensaver] / OSX / jwxyz.m
index 330d0632adfab3a5d34fe4f39daf074629c5d0e0..78a9bc56043bbf7bf8eac7d2601e37aa4eff1831 100644 (file)
@@ -47,6 +47,7 @@
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
 #import "yarandom.h"
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
 #import "yarandom.h"
+#import "utf8wc.h"
 
 # define USE_BACKBUFFER  /* must be in sync with XScreenSaverView.h */
 
 
 # define USE_BACKBUFFER  /* must be in sync with XScreenSaverView.h */
 
@@ -116,6 +117,10 @@ struct jwxyz_Font {
   XFontStruct metrics;
 };
 
   XFontStruct metrics;
 };
 
+struct jwxyz_XFontSet {
+  Font font;
+};
+
 
 /* Instead of calling abort(), throw a real exception, so that
    XScreenSaverView can catch it and display a dialog.
 
 /* Instead of calling abort(), throw a real exception, so that
    XScreenSaverView can catch it and display a dialog.
@@ -2477,7 +2482,8 @@ copy_pixmap (Display *dpy, Pixmap p)
    "rbearing" is the distance from the logical origin to the rightmost pixel.
 
    "descent" is the distance from the logical origin to the bottommost pixel.
    "rbearing" is the distance from the logical origin to the rightmost pixel.
 
    "descent" is the distance from the logical origin to the bottommost pixel.
-   For characters with descenders, it is negative.
+   For characters with descenders, it is positive.  For superscripts, it
+   is negative.
 
    "ascent" is the distance from the logical origin to the topmost pixel.
    It is the number of pixels above the baseline.
 
    "ascent" is the distance from the logical origin to the topmost pixel.
    It is the number of pixels above the baseline.
@@ -2490,6 +2496,46 @@ copy_pixmap (Display *dpy, Pixmap p)
  */
 
 
  */
 
 
+static void
+glyph_metrics (CTFontRef ctfont, CGGlyph cgglyph, XCharStruct *cs)
+{
+  CGRect bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
+                                                 kCTFontDefaultOrientation,
+                                                 &cgglyph, NULL, 1);
+  CGSize advancement;
+  CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
+                              &cgglyph, &advancement, 1);
+
+# ifndef USE_IPHONE
+  // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
+  bbox.origin.x    -= 2.0/3.0;
+  bbox.size.width  += 4.0/3.0;
+  bbox.size.height += 1.0/2.0;
+# endif
+
+//#define CEIL(F)  ((F) < 0 ? floor(F) : ceil(F))
+//#define FLOOR(F) ((F) < 0 ? ceil(F) : floor(F))
+#define CEIL(F)  ceil(F)
+#define FLOOR(F) floor(F)
+
+  /* Now that we know the advancement and bounding box, we can compute
+     the lbearing and rbearing.
+   */
+//cs->ascent   = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
+  cs->ascent   = CEIL (bbox.origin.y + bbox.size.height);
+  cs->descent  = CEIL(-bbox.origin.y);
+  cs->lbearing = FLOOR (bbox.origin.x);
+//cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
+  cs->rbearing = CEIL (bbox.origin.x + bbox.size.width);
+  cs->width    = FLOOR (advancement.width + 0.5);
+
+  //  Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
+  //          "bbox w wrong");
+  //  Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
+  //          "bbox h wrong");
+}
+
+
 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
 //
 static void
 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
 //
 static void
@@ -2511,11 +2557,6 @@ query_font (Font fid)
   XCharStruct *min = &f->min_bounds;
   XCharStruct *max = &f->max_bounds;
 
   XCharStruct *min = &f->min_bounds;
   XCharStruct *max = &f->max_bounds;
 
-//#define CEIL(F)  ((F) < 0 ? floor(F) : ceil(F))
-//#define FLOOR(F) ((F) < 0 ? ceil(F) : floor(F))
-#define CEIL(F)  ceil(F)
-#define FLOOR(F) floor(F)
-
   f->fid               = fid;
   f->min_char_or_byte2 = first;
   f->max_char_or_byte2 = last;
   f->fid               = fid;
   f->min_char_or_byte2 = first;
   f->max_char_or_byte2 = last;
@@ -2539,8 +2580,7 @@ query_font (Font fid)
   Assert (ctfont, @"no CTFontRef for UIFont");
 
   for (i = first; i <= last; i++) {
   Assert (ctfont, @"no CTFontRef for UIFont");
 
   for (i = first; i <= last; i++) {
-    CGSize advancement = CGSizeMake (0, 0);
-    CGRect bbox = CGRectMake (0, 0, 0, 0);
+    XCharStruct *cs = &f->per_char[i-first];
 
     /* There is no way to get "lbearing", "rbearing" or "descent" out of
        NSFont.  'sizeWithFont' gives us "width" and "height" only.
 
     /* There is no way to get "lbearing", "rbearing" or "descent" out of
        NSFont.  'sizeWithFont' gives us "width" and "height" only.
@@ -2554,18 +2594,10 @@ query_font (Font fid)
     CGGlyph cgglyph = 0;
 
     if (CTFontGetGlyphsForCharacters (ctfont, &i, &cgglyph, 1))
     CGGlyph cgglyph = 0;
 
     if (CTFontGetGlyphsForCharacters (ctfont, &i, &cgglyph, 1))
-      {
-        bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
-                                                kCTFontDefaultOrientation,
-                                                &cgglyph, NULL, 1);
-        CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
-                                    &cgglyph, &advancement, 1);
-      }
+      glyph_metrics (ctfont, cgglyph, cs);
     else
     else
-      {
-        // This is normal, since Latin1 does not encode 0-31 or 127-159.
-        // NSLog (@"no glyph for character %d\n", i);
-      }
+      // This is normal, since Latin1 does not encode 0-31 or 127-159.
+      memset (cs, 0, sizeof(*cs));
 
 # if 0
     if (i == 224) {  // Latin1 == "agrave", MacRoman == "daggerdouble".
 
 # if 0
     if (i == 224) {  // Latin1 == "agrave", MacRoman == "daggerdouble".
@@ -2580,32 +2612,7 @@ query_font (Font fid)
                                      cgglyph);
       Assert ([glyph_name isEqualToString:@"uacute"], @"wrong encoding");
     }
                                      cgglyph);
       Assert ([glyph_name isEqualToString:@"uacute"], @"wrong encoding");
     }
-# endif // 9
-
-# ifndef USE_IPHONE
-    // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
-    bbox.origin.x    -= 2.0/3.0;
-    bbox.size.width  += 4.0/3.0;
-    bbox.size.height += 1.0/2.0;
-# endif
-    /* Now that we know the advancement and bounding box, we can compute
-       the lbearing and rbearing.
-     */
-    XCharStruct *cs = &f->per_char[i-first];
-
-//    cs->ascent   = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
-    cs->ascent   = CEIL (bbox.origin.y + bbox.size.height);
-    cs->descent  = CEIL(-bbox.origin.y);
-    cs->lbearing = FLOOR (bbox.origin.x);
-//  cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
-    cs->rbearing = CEIL (bbox.origin.x + bbox.size.width);
-    cs->width    = FLOOR (advancement.width + 0.5);
-
-//  Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width), 
-//          "bbox w wrong");
-//  Assert (cs->ascent   + cs->descent  == CEIL(bbox.size.height),
-//          "bbox h wrong");
+# endif // 0
 
     max->width    = MAX (max->width,    cs->width);
     max->ascent   = MAX (max->ascent,   cs->ascent);
 
     max->width    = MAX (max->width,    cs->width);
     max->ascent   = MAX (max->ascent,   cs->ascent);
@@ -3061,14 +3068,57 @@ XSetFont (Display *dpy, GC gc, Font fid)
   return 0;
 }
 
   return 0;
 }
 
+
+XFontSet
+XCreateFontSet (Display *dpy, char *name, 
+                char ***missing_charset_list_return,
+                int *missing_charset_count_return,
+                char **def_string_return)
+{
+  char *name2 = strdup (name);
+  char *s = strchr (name, ",");
+  if (s) *s = 0;
+  XFontSet set = 0;
+  Font f = XLoadFont (dpy, name2);
+  if (f)
+    {
+      set = (XFontSet) calloc (1, sizeof(*set));
+      set->font = f;
+    }
+  free (name2);
+  if (missing_charset_list_return)  *missing_charset_list_return = 0;
+  if (missing_charset_count_return) *missing_charset_count_return = 0;
+  if (def_string_return) *def_string_return = 0;
+  return set;
+}
+
+
+void
+XFreeFontSet (Display *dpy, XFontSet set)
+{
+  XUnloadFont (dpy, set->font);
+  free (set);
+}
+
+
+void
+XFreeStringList (char **list)
+{
+  int i;
+  if (!list) return;
+  for (i = 0; list[i]; i++)
+    XFree (list[i]);
+  XFree (list);
+}
+
+
 int
 XTextExtents (XFontStruct *f, const char *s, int length,
               int *dir_ret, int *ascent_ret, int *descent_ret,
               XCharStruct *cs)
 {
   memset (cs, 0, sizeof(*cs));
 int
 XTextExtents (XFontStruct *f, const char *s, int length,
               int *dir_ret, int *ascent_ret, int *descent_ret,
               XCharStruct *cs)
 {
   memset (cs, 0, sizeof(*cs));
-  int i;
-  for (i = 0; i < length; i++) {
+  for (int i = 0; i < length; i++) {
     unsigned char c = (unsigned char) s[i];
     if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
       c = f->default_char;
     unsigned char c = (unsigned char) s[i];
     if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
       c = f->default_char;
@@ -3099,24 +3149,118 @@ XTextWidth (XFontStruct *f, const char *s, int length)
 }
 
 
 }
 
 
-static int
-draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
-             const char  *str, int len, BOOL clear_background_p)
+int
+XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
+                int *dir_ret, int *ascent_ret, int *descent_ret,
+                XCharStruct *cs)
 {
 {
-  if (clear_background_p) {
-    int ascent, descent, dir;
+  Font fid = f->fid;
+  CTFontRef ctfont =
+    CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+                          [fid->nsfont pointSize],
+                          NULL);
+  Assert (ctfont, @"no CTFontRef for UIFont");
+
+  int utf8_len = 0;
+  char *utf8 = XChar2b_to_utf8 (s, &utf8_len);
+  NSString *nsstr = [NSString stringWithCString:utf8
+                                       encoding:NSUTF8StringEncoding];
+  NSUInteger L = [nsstr length];
+
+  for (int i = 0; i < L; i++) {
+    unichar c = [nsstr characterAtIndex:i];
+    XCharStruct cc;
+    CGGlyph cgglyph = 0;
+
+    if (CTFontGetGlyphsForCharacters (ctfont, &c, &cgglyph, 1))
+      glyph_metrics (ctfont, cgglyph, &cc);
+    else
+      // This is normal, since Latin1 does not encode 0-31 or 127-159.
+      memset (&cc, 0, sizeof(cc));
+
+    if (i == 0) {
+      *cs = cc;
+    } else {
+      cs->ascent   = MAX (cs->ascent,   cc.ascent);
+      cs->descent  = MAX (cs->descent,  cc.descent);
+      cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing);
+      cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing);
+      cs->width   += cc.width;
+    }
+  }
+  *dir_ret = 0;
+  *ascent_ret  = f->ascent;
+  *descent_ret = f->descent;
+
+  free (utf8);
+  CFRelease (ctfont);
+
+  return 0;
+}
+
+
+int
+Xutf8TextExtents (XFontSet set, const char *str, int num_bytes,
+                  XRectangle *overall_ink_return,
+                  XRectangle *overall_logical_return)
+{
+  Font fid = set->font;
+  CTFontRef ctfont =
+    CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
+                          [fid->nsfont pointSize],
+                          NULL);
+  Assert (ctfont, @"no CTFontRef for UIFont");
+
+  NSString *nsstr = [NSString stringWithCString:str
+                                       encoding:NSUTF8StringEncoding];
+  NSUInteger L = [nsstr length];
+
+  XRectangle ink = { 0, };
+  XRectangle logical = { 0, };
+
+  logical.height = fid->metrics.ascent;
+
+  for (int i = 0; i < L; i++) {
+    unichar c = [nsstr characterAtIndex:i];
     XCharStruct cs;
     XCharStruct cs;
-    XTextExtents (&gc->gcv.font->metrics, str, len,
-                  &dir, &ascent, &descent, &cs);
-    draw_rect (dpy, d, gc,
-               x + MIN (0, cs.lbearing),
-               y - MAX (0, ascent),
-               MAX (MAX (0, cs.rbearing) -
-                    MIN (0, cs.lbearing),
-                    cs.width),
-               MAX (0, ascent) + MAX (0, descent),
-               NO, YES);
+    CGGlyph cgglyph = 0;
+
+    if (CTFontGetGlyphsForCharacters (ctfont, &c, &cgglyph, 1))
+      glyph_metrics (ctfont, cgglyph, &cs);
+    else
+      // This is normal, since Latin1 does not encode 0-31 or 127-159.
+      memset (&cs, 0, sizeof(cs));
+
+    logical.width += cs.width;
+
+    ink.height = MAX(ink.height, cs.ascent);
+    ink.y      = MIN(ink.y,      cs.descent);
+
+    if (i == 0)
+      ink.x = cs.lbearing;
+
+    if (i == L-1) {
+      ink.width += cs.rbearing;
+    } else {
+      ink.width += cs.width;
+    }
   }
   }
+    
+  CFRelease (ctfont);
+
+  if (overall_ink_return)
+    *overall_ink_return = ink;
+  if (overall_logical_return)
+    *overall_logical_return = logical;
+  return 0;
+}
+
+
+static int
+draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
+             NSString *nsstr)
+{
+  if (! nsstr) return 1;
 
   CGRect wr = d->frame;
   CGContextRef cgc = d->cgc;
 
   CGRect wr = d->frame;
   CGContextRef cgc = d->cgc;
@@ -3145,11 +3289,6 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
   //
   set_color (cgc, argb, 32, NO, YES);
 
   //
   set_color (cgc, argb, 32, NO, YES);
 
-  char *s2 = (char *) malloc (len + 1);
-  strncpy (s2, str, len);
-  s2[len] = 0;
-  NSString *nsstr = [NSString stringWithCString:s2
-                                       encoding:NSISOLatin1StringEncoding];
   NSAttributedString *astr = [[NSAttributedString alloc]
                                initWithString:nsstr
                                    attributes:attr];
   NSAttributedString *astr = [[NSAttributedString alloc]
                                initWithString:nsstr
                                    attributes:attr];
@@ -3171,14 +3310,69 @@ int
 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
              const char  *str, int len)
 {
 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
              const char  *str, int len)
 {
-  return draw_string (dpy, d, gc, x, y, str, len, NO);
+  char *s2 = (char *) malloc (len + 1);
+  strncpy (s2, str, len);
+  s2[len] = 0;
+  NSString *nsstr = [NSString stringWithCString:s2
+                                       encoding:NSISOLatin1StringEncoding];
+  int ret = draw_string (dpy, d, gc, x, y, nsstr);
+  free (s2);
+  return ret;
+}
+
+
+int
+XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
+             const XChar2b *str, int len)
+{
+  char *s2 = XChar2b_to_utf8 (str, 0);
+  NSString *nsstr = [NSString stringWithCString:s2
+                                       encoding:NSUTF8StringEncoding];
+  if (! nsstr)
+    /* If the C string has invalid UTF8 bytes in it, the result is
+       "undefined", which turns out to mean "return a null string"
+       instead of just omitting the bogus characters. Greaaat.
+       So try it again as Latin1, I guess. */
+    nsstr = [NSString stringWithCString:s2 encoding:NSISOLatin1StringEncoding];
+  int ret = draw_string (dpy, d, gc, x, y, nsstr);
+  free (s2);
+  return ret;
+}
+
+
+void
+Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
+                 int x, int y, const char *str, int len)
+{
+  char *s2 = (char *) malloc (len + 1);
+  strncpy (s2, str, len);
+  s2[len] = 0;
+  NSString *nsstr = [NSString stringWithCString:s2
+                                       encoding:NSUTF8StringEncoding];
+  if (! nsstr)
+    nsstr = [NSString stringWithCString:s2 encoding:NSISOLatin1StringEncoding];
+  draw_string (dpy, d, gc, x, y, nsstr);
+  free (s2);
 }
 
 }
 
+
 int
 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
                   const char *str, int len)
 {
 int
 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
                   const char *str, int len)
 {
-  return draw_string (dpy, d, gc, x, y, str, len, YES);
+  int ascent, descent, dir;
+  XCharStruct cs;
+  XTextExtents (&gc->gcv.font->metrics, str, len,
+                &dir, &ascent, &descent, &cs);
+  draw_rect (dpy, d, gc,
+             x + MIN (0, cs.lbearing),
+             y - MAX (0, ascent),
+             MAX (MAX (0, cs.rbearing) -
+                  MIN (0, cs.lbearing),
+                  cs.width),
+             MAX (0, ascent) + MAX (0, descent),
+             NO, YES);
+  return XDrawString (dpy, d, gc, x, y, str, len);
 }
 
 
 }
 
 
@@ -3490,6 +3684,18 @@ get_bits_per_pixel (Display *dpy, int depth)
   return depth;
 }
 
   return depth;
 }
 
+int
+screen_number (Screen *screen)
+{
+  Display *dpy = DisplayOfScreen (screen);
+  int i;
+  for (i = 0; i < ScreenCount (dpy); i++)
+    if (ScreenOfDisplay (dpy, i) == screen)
+      return i;
+  abort ();
+  return 0;
+}
+
 // declared in utils/grabclient.h
 Bool
 use_subwindow_mode_p (Screen *screen, Window window)
 // declared in utils/grabclient.h
 Bool
 use_subwindow_mode_p (Screen *screen, Window window)