-/* xscreensaver, Copyright (c) 1991-2012 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 <stdlib.h>
#import <stdint.h>
+#import <wchar.h>
#ifdef USE_IPHONE
# 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"
+#import "utf8wc.h"
+
+# define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
#undef Assert
#define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
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 {
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.
}
+/* 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);
}
void *
jwxyz_window_view (Window w)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
return w->window.view;
}
void *cgc_arg)
{
CGContextRef cgc = (CGContextRef) cgc_arg;
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
w->frame.origin.x = new_x;
w->frame.origin.y = new_y;
w->frame.size.width = new_width;
CGDisplayCount n;
dpy->cgdpy = 0;
CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
+ // Auuugh!
+ if (! dpy->cgdpy) {
+ p.x = p.y = 0;
+ CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
+ }
Assert (dpy->cgdpy, "unable to find CGDisplay");
}
# endif // USE_IPHONE
-#if 0
+# ifndef USE_BACKBUFFER
+ // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
+ // then this one's faster.
+
{
// Figure out this screen's colorspace, and use that for every CGImage.
//
dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
Assert (dpy->colorspace, "unable to find colorspace");
}
-# else
+# else // USE_BACKBUFFER
// WTF? It's faster if we *do not* use the screen's colorspace!
//
dpy->colorspace = CGColorSpaceCreateDeviceRGB();
-# endif
+# endif // USE_BACKBUFFER
invalidate_drawable_cache (w);
}
void
jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
w->window.last_mouse_x = x;
w->window.last_mouse_y = y;
}
#endif // USE_IPHONE
+void
+jwxyz_flush_context (Display *dpy)
+{
+ // This is only used when USE_BACKBUFFER is off.
+ CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
+}
jwxyz_sources_data *
display_sources_data (Display *dpy)
int
XScreenNumberOfScreen (Screen *s)
{
- return 0;
+ return s->screen_number;
+}
+
+int
+jwxyz_ScreenCount (Display *dpy)
+{
+ return dpy->screen_count;
}
int
*/
#define XDRAWPOINTS_IMAGES
+/* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
+ the bitmap data directly is faster. This only works on Pixmaps, though,
+ not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
+ */
+#define XDRAWPOINTS_CGDATA
+
int
XDrawPoints (Display *dpy, Drawable d, GC gc,
XPoint *points, int count, int mode)
push_fg_gc (d, gc, YES);
-# ifdef XDRAWPOINTS_IMAGES
+# ifdef XDRAWPOINTS_CGDATA
+
+# ifdef USE_BACKBUFFER
+ if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+# else
+ if (d->type == PIXMAP)
+# endif
+ {
+ CGContextRef cgc = d->cgc;
+ void *data = CGBitmapContextGetData (cgc);
+ size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
+ size_t w = CGBitmapContextGetWidth (cgc);
+ size_t h = CGBitmapContextGetHeight (cgc);
- unsigned int 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));
-
- CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
- CGImageRef cgi = CGImageCreate (1, 1,
- 8, 32, 4,
- dpy->colorspace,
- /* Host-ordered, since we're using the
- address of an int as the color data. */
- (kCGImageAlphaNoneSkipFirst |
- kCGBitmapByteOrder32Host),
- prov,
- NULL, /* decode[] */
- NO, /* interpolate */
- kCGRenderingIntentDefault);
- CGDataProviderRelease (prov);
+ Assert (data, "no bitmap data in Drawable");
- CGContextRef cgc = d->cgc;
- CGRect rect;
- rect.size.width = rect.size.height = 1;
- for (i = 0; i < count; i++) {
- if (i > 0 && mode == CoordModePrevious) {
- rect.origin.x += points->x;
- rect.origin.x -= points->y;
+ 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));
+
+ CGFloat x0 = wr.origin.x;
+ CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
+
+ // It's uglier, but faster, to hoist the conditional out of the loop.
+ if (mode == CoordModePrevious) {
+ CGFloat x = x0, y = y0;
+ for (i = 0; i < count; i++, points++) {
+ x += points->x;
+ y += points->y;
+
+ 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 = (unsigned int) argb;
+ }
+ }
} else {
- rect.origin.x = wr.origin.x + points->x;
- rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
+ for (i = 0; i < count; i++, points++) {
+ CGFloat x = x0 + points->x;
+ CGFloat y = y0 + points->y;
+
+ 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 = (unsigned int) argb;
+ }
+ }
}
- //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
- CGContextDrawImage (cgc, rect, cgi);
- points++;
- }
+ } else /* d->type == WINDOW */
+
+# endif /* XDRAWPOINTS_CGDATA */
+ {
- CGImageRelease (cgi);
+# ifdef XDRAWPOINTS_IMAGES
+
+ unsigned int 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));
+
+ CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
+ NULL);
+ CGImageRef cgi = CGImageCreate (1, 1,
+ 8, 32, 4,
+ dpy->colorspace,
+ /* Host-ordered, since we're using the
+ address of an int as the color data. */
+ (kCGImageAlphaNoneSkipFirst |
+ kCGBitmapByteOrder32Host),
+ prov,
+ NULL, /* decode[] */
+ NO, /* interpolate */
+ kCGRenderingIntentDefault);
+ CGDataProviderRelease (prov);
+
+ CGContextRef cgc = d->cgc;
+ CGRect rect;
+ rect.size.width = rect.size.height = 1;
+ for (i = 0; i < count; i++) {
+ if (i > 0 && mode == CoordModePrevious) {
+ rect.origin.x += points->x;
+ rect.origin.x -= points->y;
+ } else {
+ rect.origin.x = wr.origin.x + points->x;
+ rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
+ }
+
+ //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
+ CGContextDrawImage (cgc, rect, cgi);
+ points++;
+ }
+
+ CGImageRelease (cgi);
# else /* ! XDRAWPOINTS_IMAGES */
- CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
- CGRect *r = rects;
+ CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
+ CGRect *r = rects;
- for (i = 0; i < count; i++) {
- r->size.width = r->size.height = 1;
- if (i > 0 && mode == CoordModePrevious) {
- r->origin.x = r[-1].origin.x + points->x;
- r->origin.y = r[-1].origin.x - points->y;
- } else {
- r->origin.x = wr.origin.x + points->x;
- r->origin.y = wr.origin.y + wr.size.height - points->y;
+ for (i = 0; i < count; i++) {
+ r->size.width = r->size.height = 1;
+ if (i > 0 && mode == CoordModePrevious) {
+ r->origin.x = r[-1].origin.x + points->x;
+ r->origin.y = r[-1].origin.x - points->y;
+ } else {
+ r->origin.x = wr.origin.x + points->x;
+ r->origin.y = wr.origin.y + wr.size.height - points->y;
+ }
+ points++;
+ r++;
}
- points++;
- r++;
- }
- CGContextFillRects (d->cgc, rects, count);
- free (rects);
+ CGContextFillRects (d->cgc, rects, count);
+ free (rects);
# endif /* ! XDRAWPOINTS_IMAGES */
+ }
pop_gc (d, gc);
invalidate_drawable_cache (d);
int x, int y, unsigned int width, unsigned int height,
BOOL foreground_p, BOOL fill_p);
+static Bool
+bitmap_context_p (Drawable d)
+{
+# ifdef USE_BACKBUFFER
+ return True;
+# else
+ // Because of the backbuffer, all iPhone Windows work like Pixmaps.
+ return d->type == PIXMAP;
+# endif
+}
+
+static void
+fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
+ size_t fill_width, size_t fill_height)
+{
+ Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
+ while (fill_height) {
+ // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
+ wmemset (dst, fill_data, fill_width);
+ --fill_height;
+ dst = (char *) dst + dst_pitch;
+ }
+}
+
+static void *
+seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
+{
+ return (char *)dst + dst_pitch * y + x * 4;
+}
+
+static unsigned int
+drawable_depth (Drawable d)
+{
+ return (d->type == WINDOW
+ ? visual_depth (NULL, NULL)
+ : d->pixmap.depth);
+}
+
+
int
XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
int src_x, int src_y,
// Clip rects to frames...
//
-// CGRect orig_src_rect = src_rect;
- CGRect orig_dst_rect = dst_rect;
# define CLIP(THIS,THAT,VAL,SIZE) do { \
float off = THIS##_rect.origin.VAL; \
CLIP (dst, src, x, width);
CLIP (dst, src, y, height);
+
+ // Not actually the original dst_rect, just the one before it's clipped to
+ // the src_frame.
+ CGRect orig_dst_rect = dst_rect;
+
CLIP (src, dst, x, width);
CLIP (src, dst, y, height);
# undef CLIP
- if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
+ if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
return 0;
+
+ // Sort-of-special case where no pixels can be grabbed from the source,
+ // and the whole destination is filled with the background color.
+ if (src_rect.size.width < 0 || src_rect.size.height < 0) {
+
+ Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
+ (int)src_rect.size.height == (int)dst_rect.size.height,
+ "size mismatch");
+
+ src_rect.size.width = 0;
+ src_rect.size.height = 0;
+ dst_rect.size.width = 0;
+ dst_rect.size.height = 0;
+ }
NSObject *releaseme = 0;
CGImageRef cgi;
BOOL free_cgi_p = NO;
-#ifndef USE_IPHONE
- // Because of the backbuffer, all iPhone Windows work like Pixmaps.
- if (src->type == PIXMAP)
-# endif
- {
+ /* If we're copying from a bitmap to a bitmap, and there's nothing funny
+ going on with clipping masks or depths or anything, optimize it by
+ just doing a memcpy instead of going through a CGI.
+ */
+ if (bitmap_context_p (src)) {
+
+ if (bitmap_context_p (dst) &&
+ gc->gcv.function == GXcopy &&
+ !gc->gcv.clip_mask &&
+ drawable_depth (src) == drawable_depth (dst)) {
+
+ Assert(!(int)src_frame.origin.x &&
+ !(int)src_frame.origin.y &&
+ !(int)dst_frame.origin.x &&
+ !(int)dst_frame.origin.y,
+ "unexpected non-zero origin");
+
+ char *src_data = CGBitmapContextGetData(src->cgc);
+ char *dst_data = CGBitmapContextGetData(dst->cgc);
+ size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
+ size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
+
+ // Int to float and back again. It's not very safe, but it seems to work.
+ int src_x0 = src_rect.origin.x;
+ int dst_x0 = dst_rect.origin.x;
+
+ // Flip the Y-axis a second time.
+ int src_y0 = (src_frame.origin.y + src_frame.size.height -
+ src_rect.size.height - src_rect.origin.y);
+ int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
+ dst_rect.size.height - dst_rect.origin.y);
+
+ unsigned width0 = (int) src_rect.size.width;
+ unsigned height0 = (int) src_rect.size.height;
+
+ Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
+ (int)src_rect.size.height == (int)dst_rect.size.height,
+ "size mismatch");
+ {
+ char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
+ char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
+ size_t src_pitch0 = src_pitch;
+ size_t dst_pitch0 = dst_pitch;
+ size_t bytes = width0 * 4;
+
+ if (src == dst && dst_y0 > src_y0) {
+ // Copy upwards if the areas might overlap.
+ src_data0 += src_pitch0 * (height0 - 1);
+ dst_data0 += dst_pitch0 * (height0 - 1);
+ src_pitch0 = -src_pitch0;
+ dst_pitch0 = -dst_pitch0;
+ }
+
+ size_t lines0 = height0;
+ while (lines0) {
+ // memcpy is an alias for memmove on OS X.
+ memmove(dst_data0, src_data0, bytes);
+ src_data0 += src_pitch0;
+ dst_data0 += dst_pitch0;
+ --lines0;
+ }
+ }
+
+ if (clipped) {
+ int orig_dst_x = orig_dst_rect.origin.x;
+ int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
+ orig_dst_rect.origin.y - orig_dst_rect.size.height);
+ int orig_width = orig_dst_rect.size.width;
+ int orig_height = orig_dst_rect.size.height;
+
+ Assert (orig_dst_x >= 0 &&
+ orig_dst_x + orig_width <= (int) dst_frame.size.width &&
+ orig_dst_y >= 0 &&
+ orig_dst_y + orig_height <= (int) dst_frame.size.height,
+ "wrong dimensions");
+
+ if (orig_dst_y < dst_y0) {
+ fill_rect_memset (seek_xy (dst_data, dst_pitch,
+ orig_dst_x, orig_dst_y), dst_pitch,
+ (uint32_t) gc->gcv.background, orig_width,
+ dst_y0 - orig_dst_y);
+ }
+
+ if (orig_dst_y + orig_height > dst_y0 + height0) {
+ fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
+ dst_y0 + height0),
+ dst_pitch,
+ (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, (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, (uint32_t) gc->gcv.background,
+ orig_dst_x + orig_width - dst_x0 - width0,
+ height0);
+ }
+ }
+
+ invalidate_drawable_cache (dst);
+ return 0;
+ }
+
// If we are copying from a Pixmap to a Pixmap or Window, we must first
// copy the bits to an intermediary CGImage object, then copy that to the
if (src->type == PIXMAP && src->pixmap.depth == 1)
mask_p = YES;
-# ifndef USE_IPHONE
+# ifndef USE_BACKBUFFER
} else { /* (src->type == WINDOW) */
NSRect nsfrom; // NSRect != CGRect on 10.4
CGDataProviderRelease (prov);
}
-# endif // !USE_IPHONE
+# endif // !USE_BACKBUFFER
}
CGContextRef cgc = dst->cgc;
} else {
// No cgi means src == dst, and both are Windows.
-# ifdef USE_IPHONE
+# ifdef USE_BACKBUFFER
Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
return 0;
-# else // !USE_IPHONE
+# else // !USE_BACKBUFFER
NSRect nsfrom;
nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
nsfrom.origin.y = src_rect.origin.y;
nsto.x = dst_rect.origin.x;
nsto.y = dst_rect.origin.y;
NSCopyBits (0, nsfrom, nsto);
-# endif // !USE_IPHONE
+# endif // !USE_BACKBUFFER
}
pop_gc (dst, gc);
int
XClearWindow (Display *dpy, Window win)
{
- Assert (win->type == WINDOW, "not a window");
+ Assert (win && win->type == WINDOW, "not a window");
CGRect wr = win->frame;
return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
}
int
XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
validate_pixel (pixel, 32, NO);
w->window.background = pixel;
return 0;
int
XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
{
- Assert (win->type == WINDOW, "not a window");
+ Assert (win && win->type == WINDOW, "not a window");
CGContextRef cgc = win->cgc;
set_color (cgc, win->window.background, 32, NO, YES);
draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
Status
XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
memset (xgwa, 0, sizeof(*xgwa));
xgwa->x = w->frame.origin.x;
xgwa->y = w->frame.origin.y;
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, alpha_first_p;
-# ifndef USE_IPHONE
+ size_t depth, ibpp, ibpl;
+ enum { RGBA, ARGB, BGRA } src_format; // As bytes.
+# ifndef USE_BACKBUFFER
NSBitmapImageRep *bm = 0;
# endif
CGContextRef cgc = d->cgc;
-#ifndef USE_IPHONE
+#ifndef USE_BACKBUFFER
// Because of the backbuffer, all iPhone Windows work like Pixmaps.
if (d->type == PIXMAP)
# endif
depth = (d->type == PIXMAP
? d->pixmap.depth
: 32);
- // If it's a pixmap, we created it with kCGImageAlphaNoneSkipFirst.
- // If it's an iPhone window, it's the other way around.
- alpha_first_p = (d->type == PIXMAP);
+ // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
+ src_format = BGRA; // #### Should this be ARGB on PPC?
ibpp = CGBitmapContextGetBitsPerPixel (cgc);
ibpl = CGBitmapContextGetBytesPerRow (cgc);
data = CGBitmapContextGetData (cgc);
Assert (data, "CGBitmapContextGetData failed");
-# ifndef USE_IPHONE
+# ifndef USE_BACKBUFFER
} else { /* (d->type == WINDOW) */
// get the bits (desired sub-rectangle) out of the NSView
NSRect nsfrom;
nsfrom.origin.x = x;
- nsfrom.origin.y = y;
+// nsfrom.origin.y = y;
+ nsfrom.origin.y = d->frame.size.height - height - y;
nsfrom.size.width = width;
nsfrom.size.height = height;
bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
depth = 32;
- alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
+ src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
ibpp = [bm bitsPerPixel];
ibpl = [bm bytesPerRow];
data = [bm bitmapData];
Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
-# endif // !USE_IPHONE
+# endif // !USE_BACKBUFFER
}
// data points at (x,y) with ibpl rowstride. ignore x,y from now on.
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;
const unsigned char *iline2 = iline;
unsigned char *oline2 = oline;
- if (alpha_first_p) // ARGB
+ switch (src_format) {
+ case ARGB:
for (xx = 0; xx < width; xx++) {
unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
unsigned char r = *iline2++;
*((uint32_t *) oline2) = pixel;
oline2 += 4;
}
- else // RGBA
+ break;
+ case RGBA:
for (xx = 0; xx < width; xx++) {
unsigned char r = *iline2++;
unsigned char g = *iline2++;
*((uint32_t *) oline2) = pixel;
oline2 += 4;
}
+ break;
+ case BGRA:
+ for (xx = 0; xx < width; xx++) {
+ unsigned char b = *iline2++;
+ unsigned char g = *iline2++;
+ unsigned char r = *iline2++;
+ unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
+ uint32_t pixel = ((a << 24) |
+ (r << 16) |
+ (g << 8) |
+ (b << 0));
+ *((uint32_t *) oline2) = pixel;
+ oline2 += 4;
+ }
+ break;
+ default:
+ abort();
+ break;
+ }
oline += obpl;
iline += ibpl;
}
}
-# ifndef USE_IPHONE
+# ifndef USE_BACKBUFFER
if (bm) [bm release];
# endif
width * 4, /* bpl */
dpy->colorspace,
// Without this, it returns 0...
- kCGImageAlphaNoneSkipFirst
+ (kCGImageAlphaNoneSkipFirst |
+ kCGBitmapByteOrder32Host)
);
Assert (p->cgc, "could not create CGBitmapContext");
return p;
int
XFreePixmap (Display *d, Pixmap p)
{
- Assert (p->type == PIXMAP, "not a pixmap");
+ Assert (p && p->type == PIXMAP, "not a pixmap");
invalidate_drawable_cache (p);
CGContextRelease (p->cgc);
if (p->pixmap.cgc_buffer)
width * 4, /* bpl */
dpy->colorspace,
// Without this, it returns 0...
- kCGImageAlphaNoneSkipFirst
+ (kCGImageAlphaNoneSkipFirst |
+ kCGBitmapByteOrder32Host)
);
Assert (p2->cgc, "could not create CGBitmapContext");
"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.
*/
+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
XCharStruct *min = &f->min_bounds;
XCharStruct *max = &f->max_bounds;
-#define CEIL(F) ((F) < 0 ? floor(F) : ceil(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
+ 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.
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))
- {
- bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
- kCTFontDefaultOrientation,
- &cgglyph, NULL, 1);
- CGSize adv = { 0, };
- CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
- &cgglyph, &adv, 1);
- advancement.x = adv.width;
- advancement.y = adv.height;
-
- // Seems to be clipping by a pixel or two. Add a margin to be safe.
- bbox.origin.x -= 2;
- bbox.origin.y -= 2;
- bbox.size.width += 4;
- bbox.size.height += 4;
- }
-# endif // USE_IPHONE
-
- /* 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->descent = CEIL(-bbox.origin.y);
- cs->lbearing = CEIL (bbox.origin.x);
- cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
- cs->width = CEIL (advancement.x);
-
- Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
- "bbox w wrong");
- Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
- "bbox h wrong");
+ if (CTFontGetGlyphsForCharacters (ctfont, &i, &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));
+
+# 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 // 0
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=%3d x %3d @ %3d %3d adv=%3d %3d\n",
+ " 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,
- (int) bbox.size.width, (int) bbox.size.height,
- (int) bbox.origin.x, (int) bbox.origin.y,
- (int) advancement.x, (int) advancement.y);
-#endif
+ bbox.size.width, bbox.size.height,
+ bbox.origin.x, bbox.origin.y,
+ 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))
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 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;
}
-static void
-set_font (Display *dpy, CGContextRef cgc, GC gc)
+int
+XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
+ int *dir_ret, int *ascent_ret, int *descent_ret,
+ XCharStruct *cs)
{
- 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?
+ 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;
+ }
}
- CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
- CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+ *dir_ret = 0;
+ *ascent_ret = f->ascent;
+ *descent_ret = f->descent;
+
+ free (utf8);
+ CFRelease (ctfont);
+
+ return 0;
}
-static int
-draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
- const char *str, int len, BOOL clear_background_p)
+int
+Xutf8TextExtents (XFontSet set, const char *str, int num_bytes,
+ XRectangle *overall_ink_return,
+ XRectangle *overall_logical_return)
{
- if (clear_background_p) {
- int ascent, descent, dir;
+ 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;
- 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;
- CGRect wr = d->frame;
+ 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));
-# 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)!
- */
+ logical.width += cs.width;
- CGContextRef cgc = d->cgc;
- push_fg_gc (d, gc, YES);
- set_font (dpy, cgc, gc);
+ ink.height = MAX(ink.height, cs.ascent);
+ ink.y = MIN(ink.y, cs.descent);
- 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);
+ if (i == 0)
+ ink.x = cs.lbearing;
-# else /* !0 */
+ if (i == L-1) {
+ ink.width += cs.rbearing;
+ } else {
+ ink.width += cs.width;
+ }
+ }
+
+ CFRelease (ctfont);
- /* The Cocoa way...
- */
+ 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;
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];
- 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 */
+ // Don't understand why we have to do both set_color and
+ // NSForegroundColorAttributeName, but we do.
+ //
+ set_color (cgc, argb, 32, NO, YES);
+
+ 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;
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)
{
- 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);
}
int *root_x_ret, int *root_y_ret,
int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
# ifdef USE_IPHONE
int x = w->window.last_mouse_x;
int *dest_x_ret, int *dest_y_ret,
Window *child_ret)
{
- Assert (w->type == WINDOW, "not a window");
+ Assert (w && w->type == WINDOW, "not a window");
# ifdef USE_IPHONE
XComposeStatus *xc)
{
KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
- char c = (char) ks; // could be smarter about modifiers here...
+ char c = 0;
+ // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
+ if ((unsigned int) ks <= 255)
+ c = (char) ks;
+
+ // Put control characters in the string. Not meta.
+ if (e->state & ControlMask) {
+ if (c >= 'a' && c <= 'z') // Upcase control.
+ c -= 'a'-'A';
+ if (c >= '@' && c <= '_') // Shift to control page.
+ c -= '@';
+ if (c == ' ') // C-SPC is NULL.
+ c = 0;
+ }
+
if (k_ret) *k_ret = ks;
if (size > 0) buf[0] = c;
if (size > 1) buf[1] = 0;
- return 0;
+ return (size > 0 ? 1 : 0);
}
return TrueColor;
}
+int
+get_bits_per_pixel (Display *dpy, int depth)
+{
+ Assert (depth == 32 || depth == 1, "unexpected 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)