-/* xscreensaver, Copyright (c) 1991-2013 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2014 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
# import <UIKit/UIKit.h>
# import <UIKit/UIScreen.h>
# import <QuartzCore/QuartzCore.h>
-# import <CoreText/CTFont.h>
# define NSView UIView
# define NSRect CGRect
# define NSPoint CGPoint
# define NSWindow UIWindow
# define NSMakeSize CGSizeMake
# define NSBezierPath UIBezierPath
+# define colorWithDeviceRed colorWithRed
#else
# import <Cocoa/Cocoa.h>
#endif
+#import <CoreText/CTFont.h>
+#import <CoreText/CTLine.h>
+
#import "jwxyz.h"
#import "jwxyz-timers.h"
#import "yarandom.h"
struct jwxyz_Display {
Window main_window;
Screen *screen;
+ int screen_count;
struct jwxyz_sources_data *timers_data;
# ifndef USE_IPHONE
struct jwxyz_Screen {
Display *dpy;
Visual *visual;
+ int screen_number;
};
struct jwxyz_GC {
}
+/* We keep a list of all of the Displays that have been created and not
+ yet freed so that they can have sensible display numbers. If three
+ displays are created (0, 1, 2) and then #1 is closed, then the fourth
+ display will be given the now-unused display number 1. (Everything in
+ here assumes a 1:1 Display/Screen mapping.)
+
+ The size of this array is the most number of live displays at one time.
+ So if it's 20, then we'll blow up if the system has 19 monitors and also
+ has System Preferences open (the small preview window).
+
+ Note that xlockmore-style savers tend to allocate big structures, so
+ setting this to 1000 will waste a few megabytes. Also some of them assume
+ that the number of screens never changes, so dynamically expanding this
+ array won't work.
+ */
+# ifndef USE_IPHONE
+static Display *jwxyz_live_displays[20] = { 0, };
+# endif
+
+
Display *
jwxyz_make_display (void *nsview_arg, void *cgc_arg)
{
d->screen = (Screen *) calloc (1, sizeof(Screen));
d->screen->dpy = d;
+ d->screen_count = 1;
+ d->screen->screen_number = 0;
+# ifndef USE_IPHONE
+ {
+ // Find the first empty slot in live_displays and plug us in.
+ int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
+ int i;
+ for (i = 0; i < size; i++) {
+ if (! jwxyz_live_displays[i])
+ break;
+ }
+ if (i >= size) abort();
+ jwxyz_live_displays[i] = d;
+ d->screen_count = size;
+ d->screen->screen_number = i;
+ }
+# endif // !USE_IPHONE
+
Visual *v = (Visual *) calloc (1, sizeof(Visual));
v->class = TrueColor;
v->red_mask = 0x00FF0000;
void
jwxyz_free_display (Display *dpy)
{
- jwxyz_XtRemoveInput_all (dpy);
- // #### jwxyz_XtRemoveTimeOut_all ();
+ jwxyz_sources_free (dpy->timers_data);
+# ifndef USE_IPHONE
+ {
+ // Find us in live_displays and clear that slot.
+ int size = ScreenCount(dpy);
+ int i;
+ for (i = 0; i < size; i++) {
+ if (dpy == jwxyz_live_displays[i]) {
+ jwxyz_live_displays[i] = 0;
+ break;
+ }
+ }
+ if (i >= size) abort();
+ }
+# endif // !USE_IPHONE
+
free (dpy->screen->visual);
free (dpy->screen);
+ CFRelease (dpy->main_window->window.view);
free (dpy->main_window);
free (dpy);
}
int
XScreenNumberOfScreen (Screen *s)
{
- return 0;
+ return s->screen_number;
+}
+
+int
+jwxyz_ScreenCount (Display *dpy)
+{
+ return dpy->screen_count;
}
int
Assert (data, "no bitmap data in Drawable");
- unsigned int argb = gc->gcv.foreground;
+ unsigned long argb = gc->gcv.foreground;
validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
if (gc->depth == 1)
argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
if (x >= 0 && x < w && y >= 0 && y < h) {
unsigned int *p = (unsigned int *)
((char *) data + (size_t) y * bpr + (size_t) x * 4);
- *p = argb;
+ *p = (unsigned int) argb;
}
}
} else {
if (x >= 0 && x < w && y >= 0 && y < h) {
unsigned int *p = (unsigned int *)
((char *) data + (size_t) y * bpr + (size_t) x * 4);
- *p = argb;
+ *p = (unsigned int) argb;
}
}
}
if (orig_dst_y < dst_y0) {
fill_rect_memset (seek_xy (dst_data, dst_pitch,
orig_dst_x, orig_dst_y), dst_pitch,
- gc->gcv.background, orig_width,
+ (uint32_t) gc->gcv.background, orig_width,
dst_y0 - orig_dst_y);
}
fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
dst_y0 + height0),
dst_pitch,
- gc->gcv.background, orig_width,
+ (uint32_t) gc->gcv.background, orig_width,
orig_dst_y + orig_height - dst_y0 - height0);
}
if (orig_dst_x < dst_x0) {
fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
- dst_pitch, gc->gcv.background,
+ dst_pitch, (uint32_t) gc->gcv.background,
dst_x0 - orig_dst_x, height0);
}
if (dst_x0 + width0 < orig_dst_x + orig_width) {
fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
dst_y0),
- dst_pitch, gc->gcv.background,
+ dst_pitch, (uint32_t) gc->gcv.background,
orig_dst_x + orig_width - dst_x0 - width0,
height0);
}
ximage->format = format;
ximage->data = data;
ximage->bitmap_unit = 8;
- ximage->byte_order = MSBFirst;
+ ximage->byte_order = LSBFirst;
ximage->bitmap_bit_order = ximage->byte_order;
ximage->bitmap_pad = bitmap_pad;
ximage->depth = depth;
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
- while (--length > 0)
+ while (length-- > 0)
*out++ = table[*in++];
}
unsigned long plane_mask, int format)
{
const unsigned char *data = 0;
- int depth, ibpp, ibpl;
+ size_t depth, ibpp, ibpl;
enum { RGBA, ARGB, BGRA } src_format; // As bytes.
# ifndef USE_BACKBUFFER
NSBitmapImageRep *bm = 0;
data += (y * ibpl) + (x * (ibpp/8));
format = (depth == 1 ? XYPixmap : ZPixmap);
- XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
- 0, 0);
+ XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
+ format, 0, 0, width, height, 0, 0);
image->data = (char *) malloc (height * image->bytes_per_line);
int obpl = image->bytes_per_line;
XCharStruct *min = &f->min_bounds;
XCharStruct *max = &f->max_bounds;
-#define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
+//#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->default_char = 'M';
f->ascent = CEIL ([fid->nsfont ascender]);
- f->descent = -CEIL ([fid->nsfont descender]);
+ f->descent = -FLOOR ([fid->nsfont descender]);
min->width = 255; // set to smaller values in the loop
min->ascent = 255;
min->rbearing = 255;
f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
- int i;
+ UniChar i;
-# ifndef USE_IPHONE
- NSBezierPath *bpath = [NSBezierPath bezierPath];
-# else // USE_IPHONE
CTFontRef ctfont =
CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
[fid->nsfont pointSize],
NULL);
Assert (ctfont, @"no CTFontRef for UIFont");
-# endif // USE_IPHONE
for (i = first; i <= last; i++) {
- unsigned char str[2];
- str[0] = i;
- str[1] = 0;
-
- NSString *nsstr = [NSString stringWithCString:(char *) str
- encoding:NSISOLatin1StringEncoding];
- NSPoint advancement = { 0, };
- NSRect bbox = {{ 0, }, };
-
-# ifndef USE_IPHONE
-
- /* I can't believe we have to go through this bullshit just to
- convert a 'char' to an NSGlyph!!
-
- You might think that we could do
- NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
- but that doesn't work; my guess is that glyphWithName expects
- full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
- */
- NSGlyph glyph;
- {
- NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
- [ts setFont:fid->nsfont];
- NSLayoutManager *lm = [[NSLayoutManager alloc] init];
-
- /* Without this, the layout manager ends up on a queue somewhere and is
- referenced again after we return to the command loop. Since we don't
- use this layout manager again, by that time it may have been garbage
- collected, and we crash. Setting this seems to cause `lm' to no
- longer be referenced once we exit this block. */
- [lm setBackgroundLayoutEnabled:NO];
-
- NSTextContainer *tc = [[NSTextContainer alloc] init];
- [lm addTextContainer:tc];
- [tc release]; // lm retains tc
- [ts addLayoutManager:lm];
- [lm release]; // ts retains lm
- glyph = [lm glyphAtIndex:0];
- [ts release];
- }
-
- /* Compute the bounding box and advancement by converting the glyph
- to a bezier path. There appears to be *no other way* to find out
- the bounding box of a character: [NSFont boundingRectForGlyph] and
- [NSString sizeWithAttributes] both return an advancement-sized
- rectangle, not a rectangle completely enclosing the glyph's ink.
- */
- advancement.x = advancement.y = 0;
- [bpath removeAllPoints];
- [bpath moveToPoint:advancement];
- [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
- advancement = [bpath currentPoint];
- bbox = [bpath bounds];
-
-# else // USE_IPHONE
+ CGSize advancement = CGSizeMake (0, 0);
+ CGRect bbox = CGRectMake (0, 0, 0, 0);
/* There is no way to get "lbearing", "rbearing" or "descent" out of
NSFont. 'sizeWithFont' gives us "width" and "height" only.
the CoreText library, but there's no non-CoreText way to turn a
unichar into a CGGlyph.
*/
- UniChar uchar = [nsstr characterAtIndex: 0];
CGGlyph cgglyph = 0;
- if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
+ if (CTFontGetGlyphsForCharacters (ctfont, &i, &cgglyph, 1))
{
bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
kCTFontDefaultOrientation,
&cgglyph, NULL, 1);
- CGSize adv = { 0, };
CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
- &cgglyph, &adv, 1);
- advancement.x = adv.width;
- advancement.y = adv.height;
-
- /* A bug that existed was that the GL FPS display was truncating
- characters slightly: commas looked like periods.
-
- At one point, I believed the bounding box was being rounded
- wrong and we needed to add padding to it here.
-
- I think what was actually going on was, I was computing rbearing
- wrong. Also there was an off-by-one error in texfont.c, displaying
- too little of the bitmap.
-
- Adding arbitrarily large padding to the bbox is fine in fontglide
- and FPS display, but screws up BSOD. Increasing bbox width makes
- inverted text print too wide; decreasing origin makes characters
- clip.
-
- I think that all 3 states are correct now with the new lbearing
- computation plus the texfont fix.
- */
-# if 0
- double kludge = 2;
- bbox.origin.x -= kludge;
- bbox.origin.y -= kludge;
- bbox.size.width += kludge;
- bbox.size.height += kludge;
-# endif
+ &cgglyph, &advancement, 1);
+ }
+ else
+ {
+ // This is normal, since Latin1 does not encode 0-31 or 127-159.
+ // NSLog (@"no glyph for character %d\n", i);
}
-# endif // USE_IPHONE
+# if 0
+ if (i == 224) { // Latin1 == "agrave", MacRoman == "daggerdouble".
+ NSString *glyph_name = (NSString *)
+ CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
+ cgglyph);
+ Assert ([glyph_name isEqualToString:@"agrave"], @"wrong encoding");
+ }
+ if (i == 250) { // Latin1 == "uacute", MacRoman == "dotaccent".
+ NSString *glyph_name = (NSString *)
+ CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
+ 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) + 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->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->lbearing;
- cs->width = CEIL (advancement.x);
+ 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");
+// Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
+// "bbox h wrong");
max->width = MAX (max->width, cs->width);
max->ascent = MAX (max->ascent, cs->ascent);
min->rbearing = MIN (min->rbearing, cs->rbearing);
# undef CEIL
+# undef FLOOR
-#if 0
+# if 0
fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
" bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
i, i, cs->width, cs->lbearing, cs->rbearing,
cs->ascent, cs->descent,
bbox.size.width, bbox.size.height,
bbox.origin.x, bbox.origin.y,
- advancement.x, advancement.y);
-#endif
+ advancement.width, advancement.height);
+# endif // 0
}
-# ifdef USE_IPHONE
CFRelease (ctfont);
-# endif
}
*f = fid->metrics;
// copy XCharStruct array
- int size = f->max_char_or_byte2 - f->min_char_or_byte2;
+ int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
memcpy (f->per_char, fid->metrics.per_char,
size * sizeof (XCharStruct));
while (*s2 && (*s2 != '*' && *s2 != '-'))
s2++;
- int L = s2-s;
+ unsigned long L = s2-s;
if (s == s2)
;
# define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
}
-static void
-set_font (Display *dpy, CGContextRef cgc, GC gc)
-{
- Font font = gc->gcv.font;
- if (! font) {
- font = XLoadFont (dpy, 0);
- gc->gcv.font = font;
- [gc->gcv.font->nsfont retain];
- CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
- }
- CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
- CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
-}
-
-
static int
draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
const char *str, int len, BOOL clear_background_p)
}
CGRect wr = d->frame;
-
-# if 1
- /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
- But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
- */
-
CGContextRef cgc = d->cgc;
- push_fg_gc (d, gc, YES);
- set_font (dpy, cgc, gc);
-
- CGContextSetTextDrawingMode (cgc, kCGTextFill);
- if (gc->gcv.antialias_p)
- CGContextSetShouldAntialias (cgc, YES);
- CGContextShowTextAtPoint (cgc,
- wr.origin.x + x,
- wr.origin.y + wr.size.height - y,
- str, len);
- pop_gc (d, gc);
-
-# else /* !0 */
-
- /* The Cocoa way...
- */
unsigned long argb = gc->gcv.foreground;
if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
float g = ((argb >> 8) & 0xFF) / 255.0;
float b = ((argb ) & 0xFF) / 255.0;
NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
+
+ if (!gc->gcv.font) {
+ Assert (0, "no font");
+ return 1;
+ }
+
NSDictionary *attr =
[NSDictionary dictionaryWithObjectsAndKeys:
gc->gcv.font->nsfont, NSFontAttributeName,
fg, NSForegroundColorAttributeName,
nil];
+
+ // Don't understand why we have to do both set_color and
+ // NSForegroundColorAttributeName, but we do.
+ //
+ 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];
- free (s2);
- NSPoint pos;
- pos.x = wr.origin.x + x;
- pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
- [nsstr drawAtPoint:pos withAttributes:attr];
-
-# endif /* 0 */
+ encoding:NSISOLatin1StringEncoding];
+ NSAttributedString *astr = [[NSAttributedString alloc]
+ initWithString:nsstr
+ attributes:attr];
+ CTLineRef dl = CTLineCreateWithAttributedString (
+ (__bridge CFAttributedStringRef) astr);
+ CGContextSetTextPosition (cgc,
+ wr.origin.x + x,
+ wr.origin.y + wr.size.height - y);
+ CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
+ CTLineDraw (dl, cgc);
+ CFRelease (dl);
invalidate_drawable_cache (d);
return 0;
return TrueColor;
}
+int
+get_bits_per_pixel (Display *dpy, int depth)
+{
+ Assert (depth == 32 || depth == 1, "unexpected depth");
+ return depth;
+}
+
// declared in utils/grabclient.h
Bool
use_subwindow_mode_p (Screen *screen, Window window)