1 /* xscreensaver, Copyright (c) 1991-2015 Jamie Zawinski <jwz@jwz.org>
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
14 But it's a bunch of function definitions that bear some resemblance to
15 Xlib and that do Cocoa-ish things that bear some resemblance to the
16 things that Xlib might have done.
24 # import <UIKit/UIKit.h>
25 # import <UIKit/UIScreen.h>
26 # import <QuartzCore/QuartzCore.h>
27 # define NSView UIView
28 # define NSRect CGRect
29 # define NSPoint CGPoint
30 # define NSSize CGSize
31 # define NSColor UIColor
32 # define NSImage UIImage
33 # define NSEvent UIEvent
34 # define NSFont UIFont
35 # define NSGlyph CGGlyph
36 # define NSWindow UIWindow
37 # define NSMakeSize CGSizeMake
38 # define NSBezierPath UIBezierPath
39 # define colorWithDeviceRed colorWithRed
41 # define NSFontTraitMask UIFontDescriptorSymbolicTraits
42 // The values for the flags for NSFontTraitMask and
43 // UIFontDescriptorSymbolicTraits match up, not that it really matters here.
44 # define NSBoldFontMask UIFontDescriptorTraitBold
45 # define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
46 # define NSItalicFontMask UIFontDescriptorTraitItalic
48 # import <Cocoa/Cocoa.h>
51 #import <CoreText/CTFont.h>
52 #import <CoreText/CTLine.h>
53 #import <CoreText/CTRun.h>
56 #import "jwxyz-timers.h"
61 # define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
64 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
68 # define MAX(a,b) ((a)>(b)?(a):(b))
69 # define MIN(a,b) ((a)<(b)?(a):(b))
72 struct jwxyz_Drawable {
73 enum { WINDOW, PIXMAP } type;
80 unsigned long background;
81 int last_mouse_x, last_mouse_y;
85 void *cgc_buffer; // the bits to which CGContextRef renders
90 struct jwxyz_Display {
94 struct jwxyz_sources_data *timers_data;
97 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
98 This can change if the window is dragged to
99 a different screen. */
102 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
103 our images with this to avoid translation
107 struct jwxyz_Screen {
109 CGBitmapInfo bitmap_info;
110 unsigned long black, white;
118 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
125 float size; // points
128 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
129 // But we need the metrics on both of them, so they go here.
133 struct jwxyz_XFontSet {
138 /* Instead of calling abort(), throw a real exception, so that
139 XScreenSaverView can catch it and display a dialog.
142 jwxyz_abort (const char *fmt, ...)
150 va_start (args, fmt);
151 vsprintf (s, fmt, args);
154 [[NSException exceptionWithName: NSInternalInconsistencyException
155 reason: [NSString stringWithCString: s
156 encoding:NSUTF8StringEncoding]
159 abort(); // not reached
162 // 24/32bpp -> 32bpp image conversion.
163 // Any of RGBA, BGRA, ABGR, or ARGB can be represented by a rotate of 0/8/16/24
164 // bits and an optional byte order swap.
166 // This type encodes such a conversion.
167 typedef unsigned convert_mode_t;
169 // It's rotate, then swap.
170 // A rotation here shifts bytes forward in memory. On x86/ARM, that's a left
171 // rotate, and on PowerPC, a rightward rotation.
172 static const convert_mode_t CONVERT_MODE_ROTATE_MASK = 0x3;
173 static const convert_mode_t CONVERT_MODE_SWAP = 0x4;
176 // Converts an array of pixels ('src') from one format to another, placing the
177 // result in 'dest', according to the pixel conversion mode 'mode'.
179 convert_row (uint32_t *dest, const void *src, size_t count,
180 convert_mode_t mode, size_t src_bpp)
182 Assert (src_bpp == 24 || src_bpp == 32, "weird bpp");
184 // This works OK iff src == dest or src and dest do not overlap.
188 memcpy (dest, src, count * 4);
192 // This is correct, but not fast.
193 convert_mode_t rot = (mode & CONVERT_MODE_ROTATE_MASK) * 8;
194 convert_mode_t flip = mode & CONVERT_MODE_SWAP;
198 uint32_t *dest_end = dest + count;
199 while (dest != dest_end) {
203 x = *(const uint32_t *)src;
204 else { // src_bpp == 3
205 const uint8_t *src8 = (const uint8_t *)src;
206 // __LITTLE/BIG_ENDIAN__ are defined by the compiler.
207 # if defined __LITTLE_ENDIAN__
208 x = src8[0] | (src8[1] << 8) | (src8[2] << 16) | 0xff000000;
209 # elif defined __BIG_ENDIAN__
210 x = (src8[0] << 24) | (src8[1] << 16) | (src8[2] << 8) | 0xff;
212 # error "Can't determine system endianness."
216 src = (const uint8_t *)src + src_bpp;
218 /* The naive (i.e. ubiquitous) portable implementation of bitwise rotation,
219 for 32-bit integers, is:
221 (x << rot) | (x >> (32 - rot))
223 This works nearly everywhere. Compilers on x86 wil generally recognize
224 the idiom and convert it to a ROL instruction. But there's a problem
225 here: according to the C specification, bit shifts greater than or equal
226 to the length of the integer are undefined. And if rot = 0:
227 1. (x << 0) | (x >> (32 - 0))
228 2. (x << 0) | (x >> 32)
229 3. (x << 0) | (Undefined!)
231 Still, when the compiler converts this to a ROL on x86, everything works
232 as intended. But, there are two additional problems when Clang does
233 compile-time constant expression evaluation with the (x >> 32)
235 1. Instead of evaluating it to something reasonable (either 0, like a
236 human would intuitively expect, or x, like x86 would with SHR), Clang
237 seems to pull a value out of nowhere, like -1, or some other random
239 2. Clang's warning for this, -Wshift-count-overflow, only works when the
240 shift count is a literal constant, as opposed to an arbitrary
241 expression that is optimized down to a constant.
242 Put together, this means that the assertions in jwxyz_make_display with
243 convert_px break with the above naive rotation, but only for a release
246 http://blog.regehr.org/archives/1063
247 http://llvm.org/bugs/show_bug.cgi?id=17332
248 As described in those links, there is a solution here: Masking the
249 undefined shift with '& 31' as below makes the experesion well-defined
250 again. And LLVM is set to pick up on this safe version of the idiom and
251 use a rotation instruction on architectures (like x86) that support it,
252 just like it does with the unsafe version.
254 Too bad LLVM doesn't want to pick up on that particular optimization
255 here. Oh well. At least this code usually isn't critical w.r.t.
259 # if defined __LITTLE_ENDIAN__
260 x = (x << rot) | (x >> ((32 - rot) & 31));
261 # elif defined __BIG_ENDIAN__
262 x = (x >> rot) | (x << ((32 - rot) & 31));
266 x = __builtin_bswap32(x); // LLVM/GCC built-in function.
274 // Converts a single pixel.
276 convert_px (uint32_t px, convert_mode_t mode)
278 convert_row (&px, &px, 1, mode, 32);
283 // This returns the inverse conversion mode, such that:
285 // == convert_px(convert_px(pixel, mode), convert_mode_invert(mode))
286 // == convert_px(convert_px(pixel, convert_mode_invert(mode)), mode)
287 static convert_mode_t
288 convert_mode_invert (convert_mode_t mode)
290 // swap(0); rot(n) == rot(n); swap(0)
291 // swap(1); rot(n) == rot(-n); swap(1)
292 return mode & CONVERT_MODE_SWAP ? mode : CONVERT_MODE_ROTATE_MASK & -mode;
296 // This combines two conversions into one, such that:
297 // convert_px(convert_px(pixel, mode0), mode1)
298 // == convert_px(pixel, convert_mode_merge(mode0, mode1))
299 static convert_mode_t
300 convert_mode_merge (convert_mode_t m0, convert_mode_t m1)
302 // rot(r0); swap(s0); rot(r1); swap(s1)
303 // rot(r0); rot(s0 ? -r1 : r1); swap(s0); swap(s1)
304 // rot(r0 + (s0 ? -r1 : r1)); swap(s0 + s1)
306 ((m0 + (m0 & CONVERT_MODE_SWAP ? -m1 : m1)) & CONVERT_MODE_ROTATE_MASK) |
307 ((m0 ^ m1) & CONVERT_MODE_SWAP);
311 // This returns a conversion mode that converts an arbitrary 32-bit format
312 // specified by bitmap_info to RGBA.
313 static convert_mode_t
314 convert_mode_to_rgba (CGBitmapInfo bitmap_info)
316 // Former default: kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little
319 // green = 0x0000FF00;
320 // blue = 0x000000FF;
322 // RGBA: kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big
324 CGImageAlphaInfo alpha_info =
325 (CGImageAlphaInfo)(bitmap_info & kCGBitmapAlphaInfoMask);
327 Assert (! (bitmap_info & kCGBitmapFloatComponents),
328 "kCGBitmapFloatComponents unsupported");
329 Assert (alpha_info != kCGImageAlphaOnly, "kCGImageAlphaOnly not supported");
331 convert_mode_t rot = alpha_info == kCGImageAlphaFirst ||
332 alpha_info == kCGImageAlphaPremultipliedFirst ||
333 alpha_info == kCGImageAlphaNoneSkipFirst ?
336 CGBitmapInfo byte_order = bitmap_info & kCGBitmapByteOrderMask;
338 Assert (byte_order == kCGBitmapByteOrder32Little ||
339 byte_order == kCGBitmapByteOrder32Big,
340 "byte order not supported");
342 convert_mode_t swap = byte_order == kCGBitmapByteOrder32Little ?
343 CONVERT_MODE_SWAP : 0;
345 rot = CONVERT_MODE_ROTATE_MASK & -rot;
358 alloc_color (Display *dpy, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
360 union color_bytes color;
362 /* Instead of (int)(c / 256.0), another possibility is
363 (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
364 uint8_t integer_math(uint16_t c) {
365 unsigned c0 = c + 128;
366 return (c0 - (c0 >> 8)) >> 8;
370 color.bytes[0] = r >> 8;
371 color.bytes[1] = g >> 8;
372 color.bytes[2] = b >> 8;
373 color.bytes[3] = a >> 8;
376 convert_px (color.pixel,
377 convert_mode_invert (convert_mode_to_rgba (dpy->screen->bitmap_info)));
382 query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
384 union color_bytes color;
385 color.pixel = convert_px ((uint32_t)pixel,
386 convert_mode_to_rgba (dpy->screen->bitmap_info));
387 for (unsigned i = 0; i != 4; ++i)
388 rgba[i] = color.bytes[i];
393 query_color_float (Display *dpy, unsigned long pixel, float *rgba)
396 query_color (dpy, pixel, rgba8);
397 for (unsigned i = 0; i != 4; ++i)
398 rgba[i] = rgba8[i] * (1.0f / 255.0f);
402 /* We keep a list of all of the Displays that have been created and not
403 yet freed so that they can have sensible display numbers. If three
404 displays are created (0, 1, 2) and then #1 is closed, then the fourth
405 display will be given the now-unused display number 1. (Everything in
406 here assumes a 1:1 Display/Screen mapping.)
408 The size of this array is the most number of live displays at one time.
409 So if it's 20, then we'll blow up if the system has 19 monitors and also
410 has System Preferences open (the small preview window).
412 Note that xlockmore-style savers tend to allocate big structures, so
413 setting this to 1000 will waste a few megabytes. Also some of them assume
414 that the number of screens never changes, so dynamically expanding this
418 static Display *jwxyz_live_displays[20] = { 0, };
423 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
425 CGContextRef cgc = (CGContextRef) cgc_arg;
426 NSView *view = (NSView *) nsview_arg;
427 Assert (view, "no view");
430 Display *d = (Display *) calloc (1, sizeof(*d));
431 d->screen = (Screen *) calloc (1, sizeof(Screen));
435 d->screen->screen_number = 0;
438 // Find the first empty slot in live_displays and plug us in.
439 int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
441 for (i = 0; i < size; i++) {
442 if (! jwxyz_live_displays[i])
445 if (i >= size) abort();
446 jwxyz_live_displays[i] = d;
447 d->screen_count = size;
448 d->screen->screen_number = i;
450 # endif // !USE_IPHONE
452 # ifdef USE_BACKBUFFER
453 d->screen->bitmap_info = CGBitmapContextGetBitmapInfo (cgc);
455 d->screen->bitmap_info = (kCGImageAlphaNoneSkipFirst |
456 kCGBitmapByteOrder32Little);
458 d->screen->black = alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
459 d->screen->white = alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
462 // Tests for the image conversion modes.
464 const uint32_t key = 0x04030201;
465 # ifdef __LITTLE_ENDIAN__
466 assert (convert_px (key, 0) == key);
467 assert (convert_px (key, 1) == 0x03020104);
468 assert (convert_px (key, 3) == 0x01040302);
469 assert (convert_px (key, 4) == 0x01020304);
470 assert (convert_px (key, 5) == 0x04010203);
472 for (unsigned i = 0; i != 8; ++i) {
473 assert (convert_px(convert_px(key, i), convert_mode_invert(i)) == key);
474 assert (convert_mode_invert(convert_mode_invert(i)) == i);
477 for (unsigned i = 0; i != 8; ++i) {
478 for (unsigned j = 0; j != 8; ++j)
479 assert (convert_px(convert_px(key, i), j) ==
480 convert_px(key, convert_mode_merge(i, j)));
485 Visual *v = (Visual *) calloc (1, sizeof(Visual));
486 v->class = TrueColor;
487 v->red_mask = alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
488 v->green_mask = alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
489 v->blue_mask = alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
490 CGBitmapInfo byte_order = d->screen->bitmap_info & kCGBitmapByteOrderMask;
491 Assert ( ! (d->screen->bitmap_info & kCGBitmapFloatComponents) &&
492 (byte_order == kCGBitmapByteOrder32Little ||
493 byte_order == kCGBitmapByteOrder32Big),
494 "invalid bits per channel");
496 d->screen->visual = v;
498 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
501 Window w = (Window) calloc (1, sizeof(*w));
503 w->window.view = view;
504 CFRetain (w->window.view); // needed for garbage collection?
505 w->window.background = BlackPixel(d,0);
512 cgc = [[[view window] graphicsContext] graphicsPort];
518 Assert (cgc, "no CGContext");
523 jwxyz_free_display (Display *dpy)
525 jwxyz_sources_free (dpy->timers_data);
529 // Find us in live_displays and clear that slot.
530 int size = ScreenCount(dpy);
532 for (i = 0; i < size; i++) {
533 if (dpy == jwxyz_live_displays[i]) {
534 jwxyz_live_displays[i] = 0;
538 if (i >= size) abort();
540 # endif // !USE_IPHONE
542 free (dpy->screen->visual);
544 CFRelease (dpy->main_window->window.view);
545 free (dpy->main_window);
551 jwxyz_window_view (Window w)
553 Assert (w && w->type == WINDOW, "not a window");
554 return w->window.view;
558 /* Call this after any modification to the bits on a Pixmap or Window.
559 Most Pixmaps are used frequently as sources and infrequently as
560 destinations, so it pays to cache the data as a CGImage as needed.
563 invalidate_drawable_cache (Drawable d)
566 CGImageRelease (d->cgi);
572 /* Call this when the View changes size or position.
575 jwxyz_window_resized (Display *dpy, Window w,
576 int new_x, int new_y, int new_width, int new_height,
579 CGContextRef cgc = (CGContextRef) cgc_arg;
580 Assert (w && w->type == WINDOW, "not a window");
581 w->frame.origin.x = new_x;
582 w->frame.origin.y = new_y;
583 w->frame.size.width = new_width;
584 w->frame.size.height = new_height;
586 if (cgc) w->cgc = cgc;
587 Assert (w->cgc, "no CGContext");
590 // Figure out which screen the window is currently on.
593 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
599 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
603 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
605 Assert (dpy->cgdpy, "unable to find CGDisplay");
607 # endif // USE_IPHONE
609 # ifndef USE_BACKBUFFER
610 // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
611 // then this one's faster.
614 // Figure out this screen's colorspace, and use that for every CGImage.
616 CMProfileRef profile = 0;
617 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
618 Assert (profile, "unable to find colorspace profile");
619 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
620 Assert (dpy->colorspace, "unable to find colorspace");
622 # else // USE_BACKBUFFER
624 // WTF? It's faster if we *do not* use the screen's colorspace!
626 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
627 # endif // USE_BACKBUFFER
629 invalidate_drawable_cache (w);
635 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
637 Assert (w && w->type == WINDOW, "not a window");
638 w->window.last_mouse_x = x;
639 w->window.last_mouse_y = y;
645 jwxyz_flush_context (Display *dpy)
647 // This is only used when USE_BACKBUFFER is off.
648 // CGContextSynchronize is another possibility.
649 CGContextFlush(dpy->main_window->cgc);
653 display_sources_data (Display *dpy)
655 return dpy->timers_data;
660 XRootWindow (Display *dpy, int screen)
662 return dpy->main_window;
666 XDefaultScreenOfDisplay (Display *dpy)
672 XDefaultVisualOfScreen (Screen *screen)
674 return screen->visual;
678 XDisplayOfScreen (Screen *s)
684 XDisplayNumberOfScreen (Screen *s)
690 XScreenNumberOfScreen (Screen *s)
692 return s->screen_number;
696 jwxyz_ScreenCount (Display *dpy)
698 return dpy->screen_count;
702 XDisplayWidth (Display *dpy, int screen)
704 return (int) dpy->main_window->frame.size.width;
708 XDisplayHeight (Display *dpy, int screen)
710 return (int) dpy->main_window->frame.size.height;
714 XBlackPixelOfScreen(Screen *screen)
716 return screen->black;
720 XWhitePixelOfScreen(Screen *screen)
722 return screen->white;
726 XCellsOfScreen(Screen *screen)
728 Visual *v = screen->visual;
729 return v->red_mask | v->green_mask | v->blue_mask;
733 validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth,
734 BOOL alpha_allowed_p)
737 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
738 else if (!alpha_allowed_p)
739 Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)),
740 "bogus color pixel");
745 set_color (Display *dpy, CGContextRef cgc, unsigned long argb,
746 unsigned int depth, BOOL alpha_allowed_p, BOOL fill_p)
748 validate_pixel (dpy, argb, depth, alpha_allowed_p);
751 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
753 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
756 query_color_float (dpy, argb, rgba);
758 CGContextSetRGBFillColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
760 CGContextSetRGBStrokeColor (cgc, rgba[0], rgba[1], rgba[2], rgba[3]);
765 set_line_mode (CGContextRef cgc, XGCValues *gcv)
767 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
768 CGContextSetLineJoin (cgc,
769 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
770 gcv->join_style == JoinRound ? kCGLineJoinRound :
772 CGContextSetLineCap (cgc,
773 gcv->cap_style == CapNotLast ? kCGLineCapButt :
774 gcv->cap_style == CapButt ? kCGLineCapButt :
775 gcv->cap_style == CapRound ? kCGLineCapRound :
780 set_clip_mask (Drawable d, GC gc)
782 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
784 Pixmap p = gc->gcv.clip_mask;
786 Assert (p->type == PIXMAP, "not a pixmap");
788 CGRect wr = d->frame;
790 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
791 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
792 - p->frame.size.height;
793 to.size.width = p->frame.size.width;
794 to.size.height = p->frame.size.height;
796 CGContextClipToMask (d->cgc, to, gc->clip_mask);
800 /* Pushes a GC context; sets BlendMode and ClipMask.
803 push_gc (Drawable d, GC gc)
805 CGContextRef cgc = d->cgc;
806 CGContextSaveGState (cgc);
808 switch (gc->gcv.function) {
811 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
812 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
813 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
814 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
815 default: Assert(0, "unknown gcv function"); break;
818 if (gc->gcv.clip_mask)
819 set_clip_mask (d, gc);
822 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
825 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
828 push_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color,
829 BOOL antialias_p, Bool fill_p)
833 int depth = gc->depth;
834 switch (gc->gcv.function) {
835 case GXset: color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
836 case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
839 CGContextRef cgc = d->cgc;
840 set_color (dpy, cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
841 CGContextSetShouldAntialias (cgc, antialias_p);
845 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
848 push_fg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
850 push_color_gc (dpy, d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
853 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
856 push_bg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
858 push_color_gc (dpy, d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
863 /* You've got to be fucking kidding me!
865 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
866 with repeated calls to CGContextDrawImage than it is to make a single
867 call to CGContextFillRects() with a list of 1x1 rectangles!
869 I still wouldn't call it *fast*, however...
871 #define XDRAWPOINTS_IMAGES
873 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
874 the bitmap data directly is faster. This only works on Pixmaps, though,
875 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
877 #define XDRAWPOINTS_CGDATA
880 XDrawPoints (Display *dpy, Drawable d, GC gc,
881 XPoint *points, int count, int mode)
884 CGRect wr = d->frame;
886 # ifdef XDRAWPOINTS_CGDATA
888 # ifdef USE_BACKBUFFER
889 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
891 if (d->type == PIXMAP)
894 CGContextRef cgc = d->cgc;
895 void *data = CGBitmapContextGetData (cgc);
896 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
897 size_t w = CGBitmapContextGetWidth (cgc);
898 size_t h = CGBitmapContextGetHeight (cgc);
900 Assert (data, "no bitmap data in Drawable");
902 unsigned long argb = gc->gcv.foreground;
903 validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
905 argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
907 CGFloat x0 = wr.origin.x;
908 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
910 // It's uglier, but faster, to hoist the conditional out of the loop.
911 if (mode == CoordModePrevious) {
912 CGFloat x = x0, y = y0;
913 for (i = 0; i < count; i++, points++) {
917 if (x >= 0 && x < w && y >= 0 && y < h) {
918 unsigned int *p = (unsigned int *)
919 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
920 *p = (unsigned int) argb;
924 for (i = 0; i < count; i++, points++) {
925 CGFloat x = x0 + points->x;
926 CGFloat y = y0 + points->y;
928 if (x >= 0 && x < w && y >= 0 && y < h) {
929 unsigned int *p = (unsigned int *)
930 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
931 *p = (unsigned int) argb;
936 } else /* d->type == WINDOW */
938 # endif /* XDRAWPOINTS_CGDATA */
940 push_fg_gc (dpy, d, gc, YES);
942 # ifdef XDRAWPOINTS_IMAGES
944 unsigned int argb = gc->gcv.foreground;
945 validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
947 argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
949 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
951 CGImageRef cgi = CGImageCreate (1, 1,
954 /* Host-ordered, since we're using the
955 address of an int as the color data. */
956 dpy->screen->bitmap_info,
959 NO, /* interpolate */
960 kCGRenderingIntentDefault);
961 CGDataProviderRelease (prov);
963 CGContextRef cgc = d->cgc;
965 rect.size.width = rect.size.height = 1;
966 for (i = 0; i < count; i++) {
967 if (i > 0 && mode == CoordModePrevious) {
968 rect.origin.x += points->x;
969 rect.origin.x -= points->y;
971 rect.origin.x = wr.origin.x + points->x;
972 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
975 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
976 CGContextDrawImage (cgc, rect, cgi);
980 CGImageRelease (cgi);
982 # else /* ! XDRAWPOINTS_IMAGES */
984 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
987 for (i = 0; i < count; i++) {
988 r->size.width = r->size.height = 1;
989 if (i > 0 && mode == CoordModePrevious) {
990 r->origin.x = r[-1].origin.x + points->x;
991 r->origin.y = r[-1].origin.x - points->y;
993 r->origin.x = wr.origin.x + points->x;
994 r->origin.y = wr.origin.y + wr.size.height - points->y;
1000 CGContextFillRects (d->cgc, rects, count);
1003 # endif /* ! XDRAWPOINTS_IMAGES */
1008 invalidate_drawable_cache (d);
1015 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
1020 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
1024 static void draw_rect (Display *, Drawable, GC,
1025 int x, int y, unsigned int width, unsigned int height,
1026 BOOL foreground_p, BOOL fill_p);
1029 bitmap_context_p (Drawable d)
1031 # ifdef USE_BACKBUFFER
1034 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
1035 return d->type == PIXMAP;
1040 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
1041 size_t fill_width, size_t fill_height)
1043 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
1044 while (fill_height) {
1045 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
1046 wmemset (dst, fill_data, fill_width);
1048 dst = (char *) dst + dst_pitch;
1053 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
1055 return (char *)dst + dst_pitch * y + x * 4;
1059 drawable_depth (Drawable d)
1061 return (d->type == WINDOW
1062 ? visual_depth (NULL, NULL)
1068 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
1069 int src_x, int src_y,
1070 unsigned int width, unsigned int height,
1071 int dst_x, int dst_y)
1073 Assert (gc, "no GC");
1074 Assert ((width < 65535), "improbably large width");
1075 Assert ((height < 65535), "improbably large height");
1076 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1077 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1078 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
1079 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
1081 if (width == 0 || height == 0)
1084 if (gc->gcv.function == GXset ||
1085 gc->gcv.function == GXclear) {
1086 // "set" and "clear" are dumb drawing modes that ignore the source
1087 // bits and just draw solid rectangles.
1088 set_color (dpy, dst->cgc,
1089 (gc->gcv.function == GXset
1090 ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
1091 : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))),
1092 gc->depth, gc->gcv.alpha_allowed_p, YES);
1093 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
1097 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
1098 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
1099 // bounds of their drawables.
1100 BOOL clipped = NO; // Whether we did any clipping of the rects.
1102 src_frame = src->frame;
1103 dst_frame = dst->frame;
1105 // Initialize src_rect...
1107 src_rect.origin.x = src_frame.origin.x + src_x;
1108 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
1110 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
1111 src_rect.size.width = width;
1112 src_rect.size.height = height;
1114 // Initialize dst_rect...
1116 dst_rect.origin.x = dst_frame.origin.x + dst_x;
1117 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
1119 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
1120 dst_rect.size.width = width;
1121 dst_rect.size.height = height;
1123 // Clip rects to frames...
1126 # define CLIP(THIS,THAT,VAL,SIZE) do { \
1127 float off = THIS##_rect.origin.VAL; \
1130 THIS##_rect.size.SIZE += off; \
1131 THAT##_rect.size.SIZE += off; \
1132 THIS##_rect.origin.VAL -= off; \
1133 THAT##_rect.origin.VAL -= off; \
1135 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
1136 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
1139 THIS##_rect.size.SIZE -= off; \
1140 THAT##_rect.size.SIZE -= off; \
1143 CLIP (dst, src, x, width);
1144 CLIP (dst, src, y, height);
1146 // Not actually the original dst_rect, just the one before it's clipped to
1148 CGRect orig_dst_rect = dst_rect;
1150 CLIP (src, dst, x, width);
1151 CLIP (src, dst, y, height);
1154 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
1157 // Sort-of-special case where no pixels can be grabbed from the source,
1158 // and the whole destination is filled with the background color.
1159 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
1161 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
1162 (int)src_rect.size.height == (int)dst_rect.size.height,
1165 src_rect.size.width = 0;
1166 src_rect.size.height = 0;
1167 dst_rect.size.width = 0;
1168 dst_rect.size.height = 0;
1171 NSObject *releaseme = 0;
1174 BOOL free_cgi_p = NO;
1177 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
1178 going on with clipping masks or depths or anything, optimize it by
1179 just doing a memcpy instead of going through a CGI.
1181 if (bitmap_context_p (src)) {
1183 if (bitmap_context_p (dst) &&
1184 gc->gcv.function == GXcopy &&
1185 !gc->gcv.clip_mask &&
1186 drawable_depth (src) == drawable_depth (dst)) {
1188 Assert(!(int)src_frame.origin.x &&
1189 !(int)src_frame.origin.y &&
1190 !(int)dst_frame.origin.x &&
1191 !(int)dst_frame.origin.y,
1192 "unexpected non-zero origin");
1194 char *src_data = CGBitmapContextGetData(src->cgc);
1195 char *dst_data = CGBitmapContextGetData(dst->cgc);
1196 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
1197 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
1199 // Int to float and back again. It's not very safe, but it seems to work.
1200 int src_x0 = src_rect.origin.x;
1201 int dst_x0 = dst_rect.origin.x;
1203 // Flip the Y-axis a second time.
1204 int src_y0 = (src_frame.origin.y + src_frame.size.height -
1205 src_rect.size.height - src_rect.origin.y);
1206 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
1207 dst_rect.size.height - dst_rect.origin.y);
1209 unsigned width0 = (int) src_rect.size.width;
1210 unsigned height0 = (int) src_rect.size.height;
1212 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
1213 (int)src_rect.size.height == (int)dst_rect.size.height,
1216 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
1217 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
1218 size_t src_pitch0 = src_pitch;
1219 size_t dst_pitch0 = dst_pitch;
1220 size_t bytes = width0 * 4;
1222 if (src == dst && dst_y0 > src_y0) {
1223 // Copy upwards if the areas might overlap.
1224 src_data0 += src_pitch0 * (height0 - 1);
1225 dst_data0 += dst_pitch0 * (height0 - 1);
1226 src_pitch0 = -src_pitch0;
1227 dst_pitch0 = -dst_pitch0;
1230 size_t lines0 = height0;
1232 // memcpy is an alias for memmove on OS X.
1233 memmove(dst_data0, src_data0, bytes);
1234 src_data0 += src_pitch0;
1235 dst_data0 += dst_pitch0;
1241 int orig_dst_x = orig_dst_rect.origin.x;
1242 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
1243 orig_dst_rect.origin.y - orig_dst_rect.size.height);
1244 int orig_width = orig_dst_rect.size.width;
1245 int orig_height = orig_dst_rect.size.height;
1247 Assert (orig_dst_x >= 0 &&
1248 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
1250 orig_dst_y + orig_height <= (int) dst_frame.size.height,
1251 "wrong dimensions");
1253 if (orig_dst_y < dst_y0) {
1254 fill_rect_memset (seek_xy (dst_data, dst_pitch,
1255 orig_dst_x, orig_dst_y), dst_pitch,
1256 (uint32_t) gc->gcv.background, orig_width,
1257 dst_y0 - orig_dst_y);
1260 if (orig_dst_y + orig_height > dst_y0 + height0) {
1261 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
1264 (uint32_t) gc->gcv.background, orig_width,
1265 orig_dst_y + orig_height - dst_y0 - height0);
1268 if (orig_dst_x < dst_x0) {
1269 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
1270 dst_pitch, (uint32_t) gc->gcv.background,
1271 dst_x0 - orig_dst_x, height0);
1274 if (dst_x0 + width0 < orig_dst_x + orig_width) {
1275 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
1277 dst_pitch, (uint32_t) gc->gcv.background,
1278 orig_dst_x + orig_width - dst_x0 - width0,
1283 invalidate_drawable_cache (dst);
1288 // If we are copying from a Pixmap to a Pixmap or Window, we must first
1289 // copy the bits to an intermediary CGImage object, then copy that to the
1290 // destination drawable's CGContext.
1292 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
1293 // case of copying from a Pixmap back to itself, but I don't think that
1294 // happens very often anyway.)
1296 // First we get a CGImage out of the pixmap CGContext -- it's the whole
1297 // pixmap, but it presumably shares the data pointer instead of copying
1298 // it. We then cache that CGImage it inside the Pixmap object. Note:
1299 // invalidate_drawable_cache() must be called to discard this any time a
1300 // modification is made to the pixmap, or we'll end up re-using old bits.
1303 src->cgi = CGBitmapContextCreateImage (src->cgc);
1306 // if doing a sub-rect, trim it down.
1307 if (src_rect.origin.x != src_frame.origin.x ||
1308 src_rect.origin.y != src_frame.origin.y ||
1309 src_rect.size.width != src_frame.size.width ||
1310 src_rect.size.height != src_frame.size.height) {
1311 // #### I don't understand why this is needed...
1312 src_rect.origin.y = (src_frame.size.height -
1313 src_rect.size.height - src_rect.origin.y);
1314 // This does not copy image data, so it should be fast.
1315 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
1319 if (src->type == PIXMAP && src->pixmap.depth == 1)
1322 # ifndef USE_BACKBUFFER
1323 } else { /* (src->type == WINDOW) */
1325 NSRect nsfrom; // NSRect != CGRect on 10.4
1326 nsfrom.origin.x = src_rect.origin.x;
1327 nsfrom.origin.y = src_rect.origin.y;
1328 nsfrom.size.width = src_rect.size.width;
1329 nsfrom.size.height = src_rect.size.height;
1333 // If we are copying from a window to itself, we can use NSCopyBits()
1334 // without first copying the rectangle to an intermediary CGImage.
1335 // This is ~28% faster (but I *expected* it to be twice as fast...)
1336 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
1342 // If we are copying from a Window to a Pixmap, we must first copy
1343 // the bits to an intermediary CGImage object, then copy that to the
1344 // Pixmap's CGContext.
1346 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
1347 initWithFocusedViewRect:nsfrom];
1348 unsigned char *data = [bm bitmapData];
1349 int bps = [bm bitsPerSample];
1350 int bpp = [bm bitsPerPixel];
1351 int bpl = [bm bytesPerRow];
1354 // create a CGImage from those bits.
1355 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
1356 // but that method didn't exist in 10.4.)
1358 CGDataProviderRef prov =
1359 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
1361 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
1364 /* Use whatever default bit ordering we got from
1365 initWithFocusedViewRect. I would have assumed
1366 that it was (kCGImageAlphaNoneSkipFirst |
1367 kCGBitmapByteOrder32Host), but on Intel,
1372 NULL, /* decode[] */
1373 NO, /* interpolate */
1374 kCGRenderingIntentDefault);
1376 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1377 CGDataProviderRelease (prov);
1380 # endif // !USE_BACKBUFFER
1383 CGContextRef cgc = dst->cgc;
1385 if (mask_p) { // src depth == 1
1387 push_bg_gc (dpy, dst, gc, YES);
1389 // fill the destination rectangle with solid background...
1390 CGContextFillRect (cgc, orig_dst_rect);
1392 Assert (cgc, "no CGC with 1-bit XCopyArea");
1394 // then fill in a solid rectangle of the fg color, using the image as an
1395 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1396 set_color (dpy, cgc, gc->gcv.foreground, gc->depth,
1397 gc->gcv.alpha_allowed_p, YES);
1398 CGContextClipToMask (cgc, dst_rect, cgi);
1399 CGContextFillRect (cgc, dst_rect);
1403 } else { // src depth > 1
1407 // If either the src or dst rects did not lie within their drawables,
1408 // then we have adjusted both the src and dst rects to account for
1409 // the clipping; that means we need to first clear to the background,
1410 // so that clipped bits end up in the bg color instead of simply not
1414 set_color (dpy, cgc, gc->gcv.background, gc->depth,
1415 gc->gcv.alpha_allowed_p, YES);
1416 CGContextFillRect (cgc, orig_dst_rect);
1420 // copy the CGImage onto the destination CGContext
1421 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1422 CGContextDrawImage (cgc, dst_rect, cgi);
1424 // No cgi means src == dst, and both are Windows.
1426 # ifdef USE_BACKBUFFER
1427 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1429 # else // !USE_BACKBUFFER
1431 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1432 nsfrom.origin.y = src_rect.origin.y;
1433 nsfrom.size.width = src_rect.size.width;
1434 nsfrom.size.height = src_rect.size.height;
1436 nsto.x = dst_rect.origin.x;
1437 nsto.y = dst_rect.origin.y;
1438 NSCopyBits (0, nsfrom, nsto);
1439 # endif // !USE_BACKBUFFER
1445 if (free_cgi_p) CGImageRelease (cgi);
1447 if (releaseme) [releaseme release];
1448 invalidate_drawable_cache (dst);
1454 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1455 int src_x, int src_y,
1456 unsigned width, int height,
1457 int dest_x, int dest_y, unsigned long plane)
1459 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1461 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1462 // not to white/black.
1463 return XCopyArea (dpy, src, dest, gc,
1464 src_x, src_y, width, height, dest_x, dest_y);
1469 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1471 // when drawing a zero-length line, obey line-width and cap-style.
1472 if (x1 == x2 && y1 == y2) {
1473 int w = gc->gcv.line_width;
1476 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1477 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1479 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1482 CGRect wr = d->frame;
1484 p.x = wr.origin.x + x1;
1485 p.y = wr.origin.y + wr.size.height - y1;
1487 push_fg_gc (dpy, d, gc, NO);
1489 CGContextRef cgc = d->cgc;
1490 set_line_mode (cgc, &gc->gcv);
1491 CGContextBeginPath (cgc);
1492 CGContextMoveToPoint (cgc, p.x, p.y);
1493 p.x = wr.origin.x + x2;
1494 p.y = wr.origin.y + wr.size.height - y2;
1495 CGContextAddLineToPoint (cgc, p.x, p.y);
1496 CGContextStrokePath (cgc);
1498 invalidate_drawable_cache (d);
1503 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1508 CGRect wr = d->frame;
1509 push_fg_gc (dpy, d, gc, NO);
1511 CGContextRef cgc = d->cgc;
1513 set_line_mode (cgc, &gc->gcv);
1515 // if the first and last points coincide, use closepath to get
1516 // the proper line-joining.
1517 BOOL closed_p = (points[0].x == points[count-1].x &&
1518 points[0].y == points[count-1].y);
1519 if (closed_p) count--;
1521 p.x = wr.origin.x + points->x;
1522 p.y = wr.origin.y + wr.size.height - points->y;
1524 CGContextBeginPath (cgc);
1525 CGContextMoveToPoint (cgc, p.x, p.y);
1526 for (i = 1; i < count; i++) {
1527 if (mode == CoordModePrevious) {
1531 p.x = wr.origin.x + points->x;
1532 p.y = wr.origin.y + wr.size.height - points->y;
1534 CGContextAddLineToPoint (cgc, p.x, p.y);
1537 if (closed_p) CGContextClosePath (cgc);
1538 CGContextStrokePath (cgc);
1540 invalidate_drawable_cache (d);
1546 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1549 CGRect wr = d->frame;
1551 CGContextRef cgc = d->cgc;
1553 push_fg_gc (dpy, d, gc, NO);
1554 set_line_mode (cgc, &gc->gcv);
1555 CGContextBeginPath (cgc);
1556 for (i = 0; i < count; i++) {
1557 CGContextMoveToPoint (cgc,
1558 wr.origin.x + segments->x1,
1559 wr.origin.y + wr.size.height - segments->y1);
1560 CGContextAddLineToPoint (cgc,
1561 wr.origin.x + segments->x2,
1562 wr.origin.y + wr.size.height - segments->y2);
1565 CGContextStrokePath (cgc);
1567 invalidate_drawable_cache (d);
1573 XClearWindow (Display *dpy, Window win)
1575 Assert (win && win->type == WINDOW, "not a window");
1576 CGRect wr = win->frame;
1577 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1581 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1583 Assert (w && w->type == WINDOW, "not a window");
1584 validate_pixel (dpy, pixel, 32, NO);
1585 w->window.background = pixel;
1590 draw_rect (Display *dpy, Drawable d, GC gc,
1591 int x, int y, unsigned int width, unsigned int height,
1592 BOOL foreground_p, BOOL fill_p)
1594 CGRect wr = d->frame;
1596 r.origin.x = wr.origin.x + x;
1597 r.origin.y = wr.origin.y + wr.size.height - y - height;
1598 r.size.width = width;
1599 r.size.height = height;
1603 push_fg_gc (dpy, d, gc, fill_p);
1605 push_bg_gc (dpy, d, gc, fill_p);
1608 CGContextRef cgc = d->cgc;
1610 CGContextFillRect (cgc, r);
1613 set_line_mode (cgc, &gc->gcv);
1614 CGContextStrokeRect (cgc, r);
1619 invalidate_drawable_cache (d);
1624 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1625 unsigned int width, unsigned int height)
1627 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1632 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1633 unsigned int width, unsigned int height)
1635 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1640 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1642 CGRect wr = d->frame;
1644 CGContextRef cgc = d->cgc;
1645 push_fg_gc (dpy, d, gc, YES);
1646 for (i = 0; i < n; i++) {
1648 r.origin.x = wr.origin.x + rects->x;
1649 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1650 r.size.width = rects->width;
1651 r.size.height = rects->height;
1652 CGContextFillRect (cgc, r);
1656 invalidate_drawable_cache (d);
1662 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1664 Assert (win && win->type == WINDOW, "not a window");
1665 CGContextRef cgc = win->cgc;
1666 set_color (dpy, cgc, win->window.background, 32, NO, YES);
1667 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1673 XFillPolygon (Display *dpy, Drawable d, GC gc,
1674 XPoint *points, int npoints, int shape, int mode)
1676 CGRect wr = d->frame;
1678 push_fg_gc (dpy, d, gc, YES);
1679 CGContextRef cgc = d->cgc;
1680 CGContextBeginPath (cgc);
1682 for (i = 0; i < npoints; i++) {
1683 if (i > 0 && mode == CoordModePrevious) {
1687 x = wr.origin.x + points[i].x;
1688 y = wr.origin.y + wr.size.height - points[i].y;
1692 CGContextMoveToPoint (cgc, x, y);
1694 CGContextAddLineToPoint (cgc, x, y);
1696 CGContextClosePath (cgc);
1697 if (gc->gcv.fill_rule == EvenOddRule)
1698 CGContextEOFillPath (cgc);
1700 CGContextFillPath (cgc);
1702 invalidate_drawable_cache (d);
1706 #define radians(DEG) ((DEG) * M_PI / 180.0)
1707 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1710 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1711 unsigned int width, unsigned int height, int angle1, int angle2,
1714 CGRect wr = d->frame;
1716 bound.origin.x = wr.origin.x + x;
1717 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1718 bound.size.width = width;
1719 bound.size.height = height;
1722 ctr.x = bound.origin.x + bound.size.width /2;
1723 ctr.y = bound.origin.y + bound.size.height/2;
1725 float r1 = radians (angle1/64.0);
1726 float r2 = radians (angle2/64.0) + r1;
1727 BOOL clockwise = angle2 < 0;
1728 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1730 push_fg_gc (dpy, d, gc, fill_p);
1732 CGContextRef cgc = d->cgc;
1733 CGContextBeginPath (cgc);
1735 CGContextSaveGState(cgc);
1736 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1737 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1739 CGContextMoveToPoint (cgc, 0, 0);
1741 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1742 CGContextRestoreGState (cgc); // restore before stroke, for line width
1745 CGContextClosePath (cgc); // for proper line joining
1748 CGContextFillPath (cgc);
1750 set_line_mode (cgc, &gc->gcv);
1751 CGContextStrokePath (cgc);
1755 invalidate_drawable_cache (d);
1760 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1761 unsigned int width, unsigned int height, int angle1, int angle2)
1763 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1767 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1768 unsigned int width, unsigned int height, int angle1, int angle2)
1770 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1774 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1777 for (i = 0; i < narcs; i++)
1778 draw_arc (dpy, d, gc,
1779 arcs[i].x, arcs[i].y,
1780 arcs[i].width, arcs[i].height,
1781 arcs[i].angle1, arcs[i].angle2,
1787 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1790 for (i = 0; i < narcs; i++)
1791 draw_arc (dpy, d, gc,
1792 arcs[i].x, arcs[i].y,
1793 arcs[i].width, arcs[i].height,
1794 arcs[i].angle1, arcs[i].angle2,
1801 gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
1803 memset (gcv, 0, sizeof(*gcv));
1804 gcv->function = GXcopy;
1805 gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
1806 gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
1807 gcv->line_width = 1;
1808 gcv->cap_style = CapNotLast;
1809 gcv->join_style = JoinMiter;
1810 gcv->fill_rule = EvenOddRule;
1812 gcv->alpha_allowed_p = NO;
1813 gcv->antialias_p = YES;
1817 set_gcv (Display *dpy, GC gc, XGCValues *from, unsigned long mask)
1820 Assert (gc && from, "no gc");
1821 if (!gc || !from) return;
1823 if (mask & GCFunction) gc->gcv.function = from->function;
1824 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1825 if (mask & GCBackground) gc->gcv.background = from->background;
1826 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1827 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1828 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1829 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1830 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1831 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1832 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1834 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1835 if (mask & GCFont) XSetFont (0, gc, from->font);
1837 if (mask & GCForeground) validate_pixel (dpy, from->foreground, gc->depth,
1838 gc->gcv.alpha_allowed_p);
1839 if (mask & GCBackground) validate_pixel (dpy, from->background, gc->depth,
1840 gc->gcv.alpha_allowed_p);
1842 Assert ((! (mask & (GCLineStyle |
1849 GCGraphicsExposures |
1853 "unimplemented gcvalues mask");
1858 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1860 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1861 if (d->type == WINDOW) {
1863 } else { /* (d->type == PIXMAP) */
1864 gc->depth = d->pixmap.depth;
1867 gcv_defaults (dpy, &gc->gcv, gc->depth);
1868 set_gcv (dpy, gc, xgcv, mask);
1873 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1875 set_gcv (dpy, gc, gcv, mask);
1881 XFreeGC (Display *dpy, GC gc)
1884 XUnloadFont (dpy, gc->gcv.font);
1886 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1888 if (gc->gcv.clip_mask) {
1889 XFreePixmap (dpy, gc->gcv.clip_mask);
1890 CGImageRelease (gc->clip_mask);
1898 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1900 Assert (w && w->type == WINDOW, "not a window");
1901 memset (xgwa, 0, sizeof(*xgwa));
1902 xgwa->x = w->frame.origin.x;
1903 xgwa->y = w->frame.origin.y;
1904 xgwa->width = w->frame.size.width;
1905 xgwa->height = w->frame.size.height;
1907 xgwa->screen = dpy->screen;
1908 xgwa->visual = dpy->screen->visual;
1913 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1914 int *x_ret, int *y_ret,
1915 unsigned int *w_ret, unsigned int *h_ret,
1916 unsigned int *bw_ret, unsigned int *d_ret)
1918 *x_ret = d->frame.origin.x;
1919 *y_ret = d->frame.origin.y;
1920 *w_ret = d->frame.size.width;
1921 *h_ret = d->frame.size.height;
1922 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1923 *root_ret = RootWindow (dpy, 0);
1930 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1932 color->pixel = alloc_color (dpy,
1933 color->red, color->green, color->blue, 0xFFFF);
1938 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1939 unsigned long *pmret, unsigned int npl,
1940 unsigned long *pxret, unsigned int npx)
1946 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1948 Assert(0, "XStoreColors called");
1953 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1955 Assert(0, "XStoreColor called");
1960 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1961 unsigned long planes)
1967 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1969 unsigned char r=0, g=0, b=0;
1970 if (*spec == '#' && strlen(spec) == 7) {
1971 static unsigned const char hex[] = { // yeah yeah, shoot me.
1972 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1973 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
1974 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1975 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1976 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1977 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1978 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1979 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1980 r = (hex[spec[1]] << 4) | hex[spec[2]];
1981 g = (hex[spec[3]] << 4) | hex[spec[4]];
1982 b = (hex[spec[5]] << 4) | hex[spec[6]];
1983 } else if (!strcasecmp(spec,"black")) {
1985 } else if (!strcasecmp(spec,"white")) {
1987 } else if (!strcasecmp(spec,"red")) {
1989 } else if (!strcasecmp(spec,"green")) {
1991 } else if (!strcasecmp(spec,"blue")) {
1993 } else if (!strcasecmp(spec,"cyan")) {
1995 } else if (!strcasecmp(spec,"magenta")) {
1997 } else if (!strcasecmp(spec,"yellow")) {
2003 ret->red = (r << 8) | r;
2004 ret->green = (g << 8) | g;
2005 ret->blue = (b << 8) | b;
2006 ret->flags = DoRed|DoGreen|DoBlue;
2011 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
2012 XColor *screen_ret, XColor *exact_ret)
2014 if (! XParseColor (dpy, cmap, name, screen_ret))
2016 *exact_ret = *screen_ret;
2017 return XAllocColor (dpy, cmap, screen_ret);
2021 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
2023 validate_pixel (dpy, color->pixel, 32, NO);
2025 query_color(dpy, color->pixel, rgba);
2026 color->red = (rgba[0] << 8) | rgba[0];
2027 color->green = (rgba[1] << 8) | rgba[1];
2028 color->blue = (rgba[2] << 8) | rgba[2];
2029 color->flags = DoRed|DoGreen|DoBlue;
2034 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
2037 for (i = 0; i < n; i++)
2038 XQueryColor (dpy, cmap, &c[i]);
2043 static unsigned long
2044 ximage_getpixel_1 (XImage *ximage, int x, int y)
2046 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
2050 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
2053 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
2055 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
2060 static unsigned long
2061 ximage_getpixel_32 (XImage *ximage, int x, int y)
2063 return ((unsigned long)
2064 *((uint32_t *) ximage->data +
2065 (y * (ximage->bytes_per_line >> 2)) +
2070 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
2072 *((uint32_t *) ximage->data +
2073 (y * (ximage->bytes_per_line >> 2)) +
2074 x) = (uint32_t) pixel;
2080 XInitImage (XImage *ximage)
2082 if (!ximage->bytes_per_line)
2083 ximage->bytes_per_line = (ximage->depth == 1
2084 ? (ximage->width + 7) / 8
2085 : ximage->width * 4);
2087 if (ximage->depth == 1) {
2088 ximage->f.put_pixel = ximage_putpixel_1;
2089 ximage->f.get_pixel = ximage_getpixel_1;
2090 } else if (ximage->depth == 32 || ximage->depth == 24) {
2091 ximage->f.put_pixel = ximage_putpixel_32;
2092 ximage->f.get_pixel = ximage_getpixel_32;
2094 Assert (0, "unknown depth");
2101 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
2102 int format, int offset, char *data,
2103 unsigned int width, unsigned int height,
2104 int bitmap_pad, int bytes_per_line)
2106 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
2107 ximage->width = width;
2108 ximage->height = height;
2109 ximage->format = format;
2110 ximage->data = data;
2111 ximage->bitmap_unit = 8;
2112 ximage->byte_order = LSBFirst;
2113 ximage->bitmap_bit_order = ximage->byte_order;
2114 ximage->bitmap_pad = bitmap_pad;
2115 ximage->depth = depth;
2116 ximage->red_mask = (depth == 1 ? 0 : dpy->screen->visual->red_mask);
2117 ximage->green_mask = (depth == 1 ? 0 : dpy->screen->visual->green_mask);
2118 ximage->blue_mask = (depth == 1 ? 0 : dpy->screen->visual->blue_mask);
2119 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
2120 ximage->bytes_per_line = bytes_per_line;
2122 XInitImage (ximage);
2127 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
2129 XImage *to = (XImage *) malloc (sizeof(*to));
2130 memcpy (to, from, sizeof(*from));
2133 to->bytes_per_line = 0;
2136 to->data = (char *) malloc (h * to->bytes_per_line);
2138 if (x >= from->width)
2140 else if (x+w > from->width)
2141 w = from->width - x;
2143 if (y >= from->height)
2145 else if (y+h > from->height)
2146 h = from->height - y;
2149 for (ty = 0; ty < h; ty++)
2150 for (tx = 0; tx < w; tx++)
2151 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
2156 XPixmapFormatValues *
2157 XListPixmapFormats (Display *dpy, int *n_ret)
2159 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
2161 ret[0].bits_per_pixel = 32;
2162 ret[0].scanline_pad = 8;
2164 ret[1].bits_per_pixel = 1;
2165 ret[1].scanline_pad = 8;
2172 XGetPixel (XImage *ximage, int x, int y)
2174 return ximage->f.get_pixel (ximage, x, y);
2179 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
2181 return ximage->f.put_pixel (ximage, x, y, pixel);
2185 XDestroyImage (XImage *ximage)
2187 if (ximage->data) free (ximage->data);
2194 flipbits (unsigned const char *in, unsigned char *out, int length)
2196 static const unsigned char table[256] = {
2197 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
2198 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
2199 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
2200 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
2201 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
2202 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
2203 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
2204 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
2205 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
2206 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
2207 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
2208 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
2209 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
2210 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
2211 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
2212 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
2213 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
2214 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
2215 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
2216 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
2217 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
2218 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
2219 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
2220 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
2221 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
2222 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
2223 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
2224 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
2225 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
2226 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
2227 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
2228 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
2230 while (length-- > 0)
2231 *out++ = table[*in++];
2236 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
2237 int src_x, int src_y, int dest_x, int dest_y,
2238 unsigned int w, unsigned int h)
2240 CGRect wr = d->frame;
2242 Assert (gc, "no GC");
2243 Assert ((w < 65535), "improbably large width");
2244 Assert ((h < 65535), "improbably large height");
2245 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
2246 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
2247 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
2248 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
2250 // Clip width and height to the bounds of the Drawable
2252 if (dest_x + w > wr.size.width) {
2253 if (dest_x > wr.size.width)
2255 w = wr.size.width - dest_x;
2257 if (dest_y + h > wr.size.height) {
2258 if (dest_y > wr.size.height)
2260 h = wr.size.height - dest_y;
2262 if (w <= 0 || h <= 0)
2265 // Clip width and height to the bounds of the XImage
2267 if (src_x + w > ximage->width) {
2268 if (src_x > ximage->width)
2270 w = ximage->width - src_x;
2272 if (src_y + h > ximage->height) {
2273 if (src_y > ximage->height)
2275 h = ximage->height - src_y;
2277 if (w <= 0 || h <= 0)
2280 CGContextRef cgc = d->cgc;
2282 if (gc->gcv.function == GXset ||
2283 gc->gcv.function == GXclear) {
2284 // "set" and "clear" are dumb drawing modes that ignore the source
2285 // bits and just draw solid rectangles.
2286 set_color (dpy, cgc, (gc->gcv.function == GXset
2287 ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
2288 : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))),
2289 gc->depth, gc->gcv.alpha_allowed_p, YES);
2290 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
2294 int bpl = ximage->bytes_per_line;
2295 int bpp = ximage->bits_per_pixel;
2296 int bsize = bpl * h;
2297 char *data = ximage->data;
2300 r.origin.x = wr.origin.x + dest_x;
2301 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
2307 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
2308 to create a CGImage from a sub-rectagle of the XImage.
2310 data += (src_y * bpl) + (src_x * 4);
2311 CGDataProviderRef prov =
2312 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
2314 CGImageRef cgi = CGImageCreate (w, h,
2317 dpy->screen->bitmap_info,
2319 NULL, /* decode[] */
2320 NO, /* interpolate */
2321 kCGRenderingIntentDefault);
2322 CGDataProviderRelease (prov);
2323 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2324 CGContextDrawImage (cgc, r, cgi);
2325 CGImageRelease (cgi);
2327 } else { // (bpp == 1)
2329 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
2331 #### However, the bit order within a byte in a 1bpp XImage is
2332 the wrong way around from what Quartz expects, so first we
2333 have to copy the data to reverse it. Shit! Maybe it
2334 would be worthwhile to go through the hacks and #ifdef
2335 each one that diddles 1bpp XImage->data directly...
2337 Assert ((src_x % 8) == 0,
2338 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
2340 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
2341 unsigned char *flipped = (unsigned char *) malloc (bsize);
2343 flipbits ((unsigned char *) data, flipped, bsize);
2345 CGDataProviderRef prov =
2346 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
2347 CGImageRef mask = CGImageMaskCreate (w, h,
2350 NULL, /* decode[] */
2351 NO); /* interpolate */
2352 push_fg_gc (dpy, d, gc, YES);
2354 CGContextFillRect (cgc, r); // foreground color
2355 CGContextClipToMask (cgc, r, mask);
2356 set_color (dpy, cgc, gc->gcv.background, gc->depth, NO, YES);
2357 CGContextFillRect (cgc, r); // background color
2361 CGDataProviderRelease (prov);
2362 CGImageRelease (mask);
2365 invalidate_drawable_cache (d);
2372 XGetImage (Display *dpy, Drawable d, int x, int y,
2373 unsigned int width, unsigned int height,
2374 unsigned long plane_mask, int format)
2376 const unsigned char *data = 0;
2377 size_t depth, ibpp, ibpl;
2378 convert_mode_t mode;
2379 # ifndef USE_BACKBUFFER
2380 NSBitmapImageRep *bm = 0;
2383 Assert ((width < 65535), "improbably large width");
2384 Assert ((height < 65535), "improbably large height");
2385 Assert ((x < 65535 && x > -65535), "improbably large x");
2386 Assert ((y < 65535 && y > -65535), "improbably large y");
2388 CGContextRef cgc = d->cgc;
2390 #ifndef USE_BACKBUFFER
2391 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2392 if (d->type == PIXMAP)
2395 depth = (d->type == PIXMAP
2398 mode = convert_mode_to_rgba (dpy->screen->bitmap_info);
2399 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2400 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2401 data = CGBitmapContextGetData (cgc);
2402 Assert (data, "CGBitmapContextGetData failed");
2404 # ifndef USE_BACKBUFFER
2405 } else { /* (d->type == WINDOW) */
2407 // get the bits (desired sub-rectangle) out of the NSView
2409 nsfrom.origin.x = x;
2410 // nsfrom.origin.y = y;
2411 nsfrom.origin.y = d->frame.size.height - height - y;
2412 nsfrom.size.width = width;
2413 nsfrom.size.height = height;
2414 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2416 mode = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? 3 : 0;
2417 ibpp = [bm bitsPerPixel];
2418 ibpl = [bm bytesPerRow];
2419 data = [bm bitmapData];
2420 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2421 # endif // !USE_BACKBUFFER
2424 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2425 data += (y * ibpl) + (x * (ibpp/8));
2427 format = (depth == 1 ? XYPixmap : ZPixmap);
2428 XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2429 format, 0, 0, width, height, 0, 0);
2430 image->data = (char *) malloc (height * image->bytes_per_line);
2432 int obpl = image->bytes_per_line;
2434 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2435 means that on Intel it is BGRA when viewed by bytes (And BGR
2436 when using 24bpp packing).
2438 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2439 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2440 indicator of this latest kink.
2444 const unsigned char *iline = data;
2445 for (yy = 0; yy < height; yy++) {
2447 const unsigned char *iline2 = iline;
2448 for (xx = 0; xx < width; xx++) {
2450 iline2++; // ignore R or A or A or B
2451 iline2++; // ignore G or B or R or G
2452 unsigned char r = *iline2++; // use B or G or G or R
2453 if (ibpp == 32) iline2++; // ignore A or R or B or A
2455 XPutPixel (image, xx, yy, (r ? 1 : 0));
2460 const unsigned char *iline = data;
2461 unsigned char *oline = (unsigned char *) image->data;
2463 mode = convert_mode_merge (mode,
2464 convert_mode_invert (
2465 convert_mode_to_rgba (dpy->screen->bitmap_info)));
2467 for (yy = 0; yy < height; yy++) {
2469 convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
2476 # ifndef USE_BACKBUFFER
2477 if (bm) [bm release];
2485 /* Returns a transformation matrix to do rotation as per the provided
2486 EXIF "Orientation" value.
2488 static CGAffineTransform
2489 exif_rotate (int rot, CGSize rect)
2491 CGAffineTransform trans = CGAffineTransformIdentity;
2493 case 2: // flip horizontal
2494 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2495 trans = CGAffineTransformScale (trans, -1, 1);
2498 case 3: // rotate 180
2499 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2500 trans = CGAffineTransformRotate (trans, M_PI);
2503 case 4: // flip vertical
2504 trans = CGAffineTransformMakeTranslation (0, rect.height);
2505 trans = CGAffineTransformScale (trans, 1, -1);
2508 case 5: // transpose (UL-to-LR axis)
2509 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2510 trans = CGAffineTransformScale (trans, -1, 1);
2511 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2514 case 6: // rotate 90
2515 trans = CGAffineTransformMakeTranslation (0, rect.width);
2516 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2519 case 7: // transverse (UR-to-LL axis)
2520 trans = CGAffineTransformMakeScale (-1, 1);
2521 trans = CGAffineTransformRotate (trans, M_PI / 2);
2524 case 8: // rotate 270
2525 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2526 trans = CGAffineTransformRotate (trans, M_PI / 2);
2538 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2539 Bool nsimg_p, void *img_arg,
2540 XRectangle *geom_ret, int exif_rotation)
2544 CGImageSourceRef cgsrc;
2545 # endif // USE_IPHONE
2548 CGContextRef cgc = d->cgc;
2552 NSImage *nsimg = (NSImage *) img_arg;
2553 imgr = [nsimg size];
2556 // convert the NSImage to a CGImage via the toll-free-bridging
2557 // of NSData and CFData...
2559 NSData *nsdata = [NSBitmapImageRep
2560 TIFFRepresentationOfImageRepsInArray:
2561 [nsimg representations]];
2562 CFDataRef cfdata = (CFDataRef) nsdata;
2563 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2564 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2565 # else // USE_IPHONE
2566 cgi = nsimg.CGImage;
2567 # endif // USE_IPHONE
2570 cgi = (CGImageRef) img_arg;
2571 imgr.width = CGImageGetWidth (cgi);
2572 imgr.height = CGImageGetHeight (cgi);
2575 Bool rot_p = (exif_rotation >= 5);
2578 imgr = NSMakeSize (imgr.height, imgr.width);
2580 CGRect winr = d->frame;
2581 float rw = winr.size.width / imgr.width;
2582 float rh = winr.size.height / imgr.height;
2583 float r = (rw < rh ? rw : rh);
2586 dst.size.width = imgr.width * r;
2587 dst.size.height = imgr.height * r;
2588 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2589 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2591 dst2.origin.x = dst2.origin.y = 0;
2593 dst2.size.width = dst.size.height;
2594 dst2.size.height = dst.size.width;
2596 dst2.size = dst.size;
2599 // Clear the part not covered by the image to background or black.
2601 if (d->type == WINDOW)
2602 XClearWindow (dpy, d);
2604 set_color (dpy, cgc, BlackPixel(dpy,0), 32, NO, YES);
2605 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2608 CGAffineTransform trans =
2609 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2611 CGContextSaveGState (cgc);
2612 CGContextConcatCTM (cgc,
2613 CGAffineTransformMakeTranslation (dst.origin.x,
2615 CGContextConcatCTM (cgc, trans);
2616 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2617 CGContextDrawImage (cgc, dst2, cgi);
2618 CGContextRestoreGState (cgc);
2623 CGImageRelease (cgi);
2625 # endif // USE_IPHONE
2628 geom_ret->x = dst.origin.x;
2629 geom_ret->y = dst.origin.y;
2630 geom_ret->width = dst.size.width;
2631 geom_ret->height = dst.size.height;
2634 invalidate_drawable_cache (d);
2640 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2642 unsigned int w, unsigned int h,
2643 unsigned long fg, unsigned int bg,
2646 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2647 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2648 (char *) data, w, h, 0, 0);
2650 gcv.foreground = fg;
2651 gcv.background = bg;
2652 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2653 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2656 XDestroyImage (image);
2661 XCreatePixmap (Display *dpy, Drawable d,
2662 unsigned int width, unsigned int height, unsigned int depth)
2664 char *data = (char *) malloc (width * height * 4);
2665 if (! data) return 0;
2667 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2669 p->frame.size.width = width;
2670 p->frame.size.height = height;
2671 p->pixmap.depth = depth;
2672 p->pixmap.cgc_buffer = data;
2674 /* Quartz doesn't have a 1bpp image type.
2675 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2676 don't support that! So we always use 32bpp, regardless of depth. */
2678 p->cgc = CGBitmapContextCreate (data, width, height,
2679 8, /* bits per component */
2680 width * 4, /* bpl */
2682 dpy->screen->bitmap_info);
2683 Assert (p->cgc, "could not create CGBitmapContext");
2689 XFreePixmap (Display *d, Pixmap p)
2691 Assert (p && p->type == PIXMAP, "not a pixmap");
2692 invalidate_drawable_cache (p);
2693 CGContextRelease (p->cgc);
2694 if (p->pixmap.cgc_buffer)
2695 free (p->pixmap.cgc_buffer);
2702 copy_pixmap (Display *dpy, Pixmap p)
2705 Assert (p->type == PIXMAP, "not a pixmap");
2707 int width = p->frame.size.width;
2708 int height = p->frame.size.height;
2709 char *data = (char *) malloc (width * height * 4);
2710 if (! data) return 0;
2712 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2714 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2717 p2->pixmap.cgc_buffer = data;
2718 p2->cgc = CGBitmapContextCreate (data, width, height,
2719 8, /* bits per component */
2720 width * 4, /* bpl */
2722 dpy->screen->bitmap_info);
2723 Assert (p2->cgc, "could not create CGBitmapContext");
2730 XGetAtomName (Display *dpy, Atom atom)
2732 if (atom == XA_FONT)
2733 return strdup ("FONT");
2735 // Note that atoms (that aren't predefined) are just char *.
2736 return strdup ((char *) atom);
2740 /* Font metric terminology, as used by X11:
2742 "lbearing" is the distance from the logical origin to the leftmost pixel.
2743 If a character's ink extends to the left of the origin, it is negative.
2745 "rbearing" is the distance from the logical origin to the rightmost pixel.
2747 "descent" is the distance from the logical origin to the bottommost pixel.
2748 For characters with descenders, it is positive. For superscripts, it
2751 "ascent" is the distance from the logical origin to the topmost pixel.
2752 It is the number of pixels above the baseline.
2754 "width" is the distance from the logical origin to the position where
2755 the logical origin of the next character should be placed.
2757 If "rbearing" is greater than "width", then this character overlaps the
2758 following character. If smaller, then there is trailing blank space.
2761 utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs)
2763 // Returns the metrics of the multi-character, single-line UTF8 string.
2765 NSFont *nsfont = fid->nsfont;
2766 Drawable d = XRootWindow (fid->dpy, 0);
2768 CGContextRef cgc = d->cgc;
2769 NSDictionary *attr =
2770 [NSDictionary dictionaryWithObjectsAndKeys:
2771 nsfont, NSFontAttributeName,
2773 NSAttributedString *astr = [[NSAttributedString alloc]
2774 initWithString:nsstr
2776 CTLineRef ctline = CTLineCreateWithAttributedString (
2777 (__bridge CFAttributedStringRef) astr);
2778 CGContextSetTextPosition (cgc, 0, 0);
2779 CGContextSetShouldAntialias (cgc, True); // #### Guess?
2781 memset (cs, 0, sizeof(*cs));
2783 // "CTRun represents set of consecutive glyphs sharing the same
2784 // attributes and direction".
2786 // We also get multiple runs any time font subsitution happens:
2787 // E.g., if the current font is Verdana-Bold, a ← character
2788 // in the NSString will actually be rendered in LucidaGrande-Bold.
2791 for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
2792 CTRunRef run = (CTRunRef) runid;
2794 CGRect bbox = CTRunGetImageBounds (run, cgc, r);
2795 CGFloat ascent, descent, leading;
2796 CGFloat advancement =
2797 CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
2800 // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
2801 bbox.origin.x -= 2.0/3.0;
2802 bbox.size.width += 4.0/3.0;
2803 bbox.size.height += 1.0/2.0;
2806 // Create the metrics for this run:
2808 cc.ascent = ceil (bbox.origin.y + bbox.size.height);
2809 cc.descent = ceil (-bbox.origin.y);
2810 cc.lbearing = floor (bbox.origin.x);
2811 cc.rbearing = ceil (bbox.origin.x + bbox.size.width);
2812 cc.width = floor (advancement + 0.5);
2814 // Add those metrics into the cumulative metrics:
2819 cs->ascent = MAX (cs->ascent, cc.ascent);
2820 cs->descent = MAX (cs->descent, cc.descent);
2821 cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing);
2822 cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing);
2823 cs->width = MAX (cs->width, cs->width + cc.width);
2826 // Why no y? What about vertical text?
2827 // XCharStruct doesn't encapsulate that but XGlyphInfo does.
2837 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2840 query_font (Font fid)
2842 if (!fid || !fid->nsfont) {
2843 Assert (0, "no NSFont in fid");
2846 if (![fid->nsfont fontName]) {
2847 Assert(0, @"broken NSFont in fid");
2854 XFontStruct *f = &fid->metrics;
2855 XCharStruct *min = &f->min_bounds;
2856 XCharStruct *max = &f->max_bounds;
2859 f->min_char_or_byte2 = first;
2860 f->max_char_or_byte2 = last;
2861 f->default_char = 'M';
2862 f->ascent = ceil ([fid->nsfont ascender]);
2863 f->descent = -floor ([fid->nsfont descender]);
2865 min->width = 32767; // set to smaller values in the loop
2866 min->ascent = 32767;
2867 min->descent = 32767;
2868 min->lbearing = 32767;
2869 min->rbearing = 32767;
2871 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2873 for (int i = first; i <= last; i++) {
2874 XCharStruct *cs = &f->per_char[i-first];
2879 NSString *nsstr = [NSString stringWithCString:s2
2880 encoding:NSISOLatin1StringEncoding];
2881 utf8_metrics (fid, nsstr, cs);
2883 max->width = MAX (max->width, cs->width);
2884 max->ascent = MAX (max->ascent, cs->ascent);
2885 max->descent = MAX (max->descent, cs->descent);
2886 max->lbearing = MAX (max->lbearing, cs->lbearing);
2887 max->rbearing = MAX (max->rbearing, cs->rbearing);
2889 min->width = MIN (min->width, cs->width);
2890 min->ascent = MIN (min->ascent, cs->ascent);
2891 min->descent = MIN (min->descent, cs->descent);
2892 min->lbearing = MIN (min->lbearing, cs->lbearing);
2893 min->rbearing = MIN (min->rbearing, cs->rbearing);
2896 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2897 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2898 i, i, cs->width, cs->lbearing, cs->rbearing,
2899 cs->ascent, cs->descent,
2900 bbox.size.width, bbox.size.height,
2901 bbox.origin.x, bbox.origin.y,
2902 advancement.width, advancement.height);
2908 // Since 'Font' includes the metrics, this just makes a copy of that.
2911 XQueryFont (Display *dpy, Font fid)
2914 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2918 f->n_properties = 1;
2919 f->properties = malloc (sizeof(*f->properties) * f->n_properties);
2920 f->properties[0].name = XA_FONT;
2921 Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
2922 "atoms probably needs a real implementation");
2923 // If XInternAtom is ever implemented, use it here.
2924 f->properties[0].card32 = (char *)fid->xa_font;
2926 // copy XCharStruct array
2927 int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
2928 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2929 memcpy (f->per_char, fid->metrics.per_char,
2930 size * sizeof (XCharStruct));
2937 copy_font (Font fid)
2939 // copy 'Font' struct
2940 Font fid2 = (Font) malloc (sizeof(*fid2));
2943 // copy XCharStruct array
2944 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2945 fid2->metrics.per_char = (XCharStruct *)
2946 malloc ((size + 2) * sizeof (XCharStruct));
2947 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2948 size * sizeof (XCharStruct));
2950 // copy the other pointers
2951 fid2->ps_name = strdup (fid->ps_name);
2952 fid2->xa_font = strdup (fid->xa_font);
2953 // [fid2->nsfont retain];
2954 fid2->metrics.fid = fid2;
2961 font_family_members (NSString *family_name)
2964 return [[NSFontManager sharedFontManager]
2965 availableMembersOfFontFamily:family_name];
2967 return [UIFont fontNamesForFamilyName:family_name];
2973 default_font_family (NSFontTraitMask require)
2975 return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
2980 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
2981 NSString *family_name, float size,
2984 Assert (size > 0, "zero font size");
2986 NSArray *family_members = font_family_members (family_name);
2987 if (!family_members.count)
2988 family_members = font_family_members (default_font_family (traits));
2991 for (unsigned k = 0; k != family_members.count; ++k) {
2993 NSArray *member = [family_members objectAtIndex:k];
2994 NSFontTraitMask font_mask =
2995 [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
2997 if ((font_mask & mask) == traits) {
2999 NSString *name = [member objectAtIndex:0];
3000 NSFont *f = [NSFont fontWithName:name size:size];
3004 /* Don't use this font if it (probably) doesn't include ASCII characters.
3006 NSStringEncoding enc = [f mostCompatibleStringEncoding];
3007 if (! (enc == NSUTF8StringEncoding ||
3008 enc == NSISOLatin1StringEncoding ||
3009 enc == NSNonLossyASCIIStringEncoding ||
3010 enc == NSISOLatin2StringEncoding ||
3011 enc == NSUnicodeStringEncoding ||
3012 enc == NSWindowsCP1250StringEncoding ||
3013 enc == NSWindowsCP1252StringEncoding ||
3014 enc == NSMacOSRomanStringEncoding)) {
3015 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
3018 // NSLog(@"using \"%@\": %d", name, enc);
3020 // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
3021 *name_ret = strdup (name.UTF8String);
3025 # else // USE_IPHONE
3027 for (NSString *fn in family_members) {
3029 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
3032 // The magic invocation for getting font names is
3033 // [[UIFontDescriptor
3034 // fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
3036 // ...but this only works on iOS 7 and later.
3037 NSFontTraitMask font_mask = 0;
3039 font_mask |= NSBoldFontMask;
3040 if (MATCH(@"Italic") || MATCH(@"Oblique"))
3041 font_mask |= NSItalicFontMask;
3043 if ((font_mask & mask) == traits) {
3045 /* Check if it can do ASCII. No good way to accomplish this!
3046 These are fonts present in iPhone Simulator as of June 2012
3047 that don't include ASCII.
3049 if (MATCH(@"AppleGothic") || // Korean
3050 MATCH(@"Dingbats") || // Dingbats
3051 MATCH(@"Emoji") || // Emoticons
3052 MATCH(@"Geeza") || // Arabic
3053 MATCH(@"Hebrew") || // Hebrew
3054 MATCH(@"HiraKaku") || // Japanese
3055 MATCH(@"HiraMin") || // Japanese
3056 MATCH(@"Kailasa") || // Tibetan
3057 MATCH(@"Ornaments") || // Dingbats
3058 MATCH(@"STHeiti") // Chinese
3062 *name_ret = strdup (fn.UTF8String);
3063 return [UIFont fontWithName:fn size:size];
3074 try_native_font (const char *name, float scale,
3075 char **name_ret, float *size_ret, char **xa_font)
3077 if (!name) return 0;
3078 const char *spc = strrchr (name, ' ');
3081 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
3084 if (size <= 4) return 0;
3088 char *name2 = strdup (name);
3089 name2[strlen(name2) - strlen(spc)] = 0;
3090 NSString *nsname = [NSString stringWithCString:name2
3091 encoding:NSUTF8StringEncoding];
3092 NSFont *f = [NSFont fontWithName:nsname size:size];
3096 *xa_font = strdup (name); // Maybe this should be an XLFD?
3105 /* Returns a random font in the given size and face.
3108 random_font (NSFontTraitMask traits, NSFontTraitMask mask,
3109 float size, NSString **family_ret, char **name_ret)
3113 // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
3114 // returns an empty list, at least on a system with default fonts only.
3115 NSArray *families = [[NSFontManager sharedFontManager]
3116 availableFontFamilies];
3117 if (!families) return 0;
3119 NSArray *families = [UIFont familyNames];
3121 // There are many dups in the families array -- uniquify it.
3123 NSArray *sorted_families =
3124 [families sortedArrayUsingSelector:@selector(compare:)];
3125 NSMutableArray *new_families =
3126 [NSMutableArray arrayWithCapacity:sorted_families.count];
3128 NSString *prev_family = nil;
3129 for (NSString *family in sorted_families) {
3130 if ([family compare:prev_family])
3131 [new_families addObject:family];
3134 families = new_families;
3136 # endif // USE_IPHONE
3138 long n = [families count];
3139 if (n <= 0) return 0;
3142 for (j = 0; j < n; j++) {
3143 int i = random() % n;
3144 NSString *family_name = [families objectAtIndex:i];
3146 NSFont *result = try_font (traits, mask, family_name, size, name_ret);
3148 [*family_ret release];
3149 *family_ret = family_name;
3150 [*family_ret retain];
3155 // None of the fonts support ASCII?
3160 // Fonts need this. XDisplayHeightMM and friends should probably be consistent
3161 // with this as well if they're ever implemented.
3162 static const unsigned dpi = 75;
3166 xlfd_field_end (const char *s)
3168 const char *s2 = strchr(s, '-');
3176 xlfd_next (const char **s, const char **s2)
3181 Assert (**s2 == '-', "xlfd parse error");
3183 *s2 = xlfd_field_end (*s);
3191 try_xlfd_font (const char *name, float scale,
3192 char **name_ret, float *size_ret, char **xa_font)
3195 NSString *family_name = nil;
3196 NSFontTraitMask require = 0, forbid = 0;
3201 const char *s = (name ? name : "");
3203 size_t L = strlen (s);
3204 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
3205 # define UNSPEC (L == 0 || L == 1 && *s == '*')
3206 if (CMP ("6x10")) size = 8, require |= NSFixedPitchFontMask;
3207 else if (CMP ("6x10bold")) size = 8, require |= NSFixedPitchFontMask | NSBoldFontMask;
3208 else if (CMP ("fixed")) size = 12, require |= NSFixedPitchFontMask;
3209 else if (CMP ("9x15")) size = 12, require |= NSFixedPitchFontMask;
3210 else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
3211 else if (CMP ("vga")) size = 12, require |= NSFixedPitchFontMask;
3212 else if (CMP ("console")) size = 12, require |= NSFixedPitchFontMask;
3213 else if (CMP ("gallant")) size = 12, require |= NSFixedPitchFontMask;
3216 // Incorrect fields are ignored.
3220 const char *s2 = xlfd_field_end(s);
3224 L = xlfd_next (&s, &s2); // Family name
3225 // This used to substitute Georgia for Times. Now it doesn't.
3226 if (CMP ("random")) {
3228 } else if (CMP ("fixed")) {
3229 require |= NSFixedPitchFontMask;
3230 family_name = @"Courier";
3231 } else if (!UNSPEC) {
3232 family_name = [[[NSString alloc] initWithBytes:s
3234 encoding:NSUTF8StringEncoding]
3238 L = xlfd_next (&s, &s2); // Weight name
3239 if (CMP ("bold") || CMP ("demibold"))
3240 require |= NSBoldFontMask;
3241 else if (CMP ("medium") || CMP ("regular"))
3242 forbid |= NSBoldFontMask;
3244 L = xlfd_next (&s, &s2); // Slant
3245 if (CMP ("i") || CMP ("o"))
3246 require |= NSItalicFontMask;
3248 forbid |= NSItalicFontMask;
3250 xlfd_next (&s, &s2); // Set width name (ignore)
3251 xlfd_next (&s, &s2); // Add style name (ignore)
3253 xlfd_next (&s, &s2); // Pixel size (ignore)
3255 xlfd_next (&s, &s2); // Point size
3257 uintmax_t n = strtoumax(s, &s3, 10);
3261 xlfd_next (&s, &s2); // Resolution X (ignore)
3262 xlfd_next (&s, &s2); // Resolution Y (ignore)
3264 xlfd_next (&s, &s2); // Spacing
3266 forbid |= NSFixedPitchFontMask;
3267 else if (CMP ("m") || CMP ("c"))
3268 require |= NSFixedPitchFontMask;
3270 // Don't care about average_width or charset registry.
3275 if (!family_name && !rand)
3276 family_name = default_font_family (require);
3278 if (size < 6 || size > 1000)
3283 NSFontTraitMask mask = require | forbid;
3286 nsfont = random_font (require, mask, size, &family_name, &ps_name);
3287 [family_name autorelease];
3291 nsfont = try_font (require, mask, family_name, size, &ps_name);
3293 // if that didn't work, turn off attibutes until it does
3294 // (e.g., there is no "Monaco-Bold".)
3296 if (!nsfont && (mask & NSItalicFontMask)) {
3297 require &= ~NSItalicFontMask;
3298 mask &= ~NSItalicFontMask;
3299 nsfont = try_font (require, mask, family_name, size, &ps_name);
3301 if (!nsfont && (mask & NSBoldFontMask)) {
3302 require &= ~NSBoldFontMask;
3303 mask &= ~NSBoldFontMask;
3304 nsfont = try_font (require, mask, family_name, size, &ps_name);
3306 if (!nsfont && (mask & NSFixedPitchFontMask)) {
3307 require &= ~NSFixedPitchFontMask;
3308 mask &= ~NSFixedPitchFontMask;
3309 nsfont = try_font (require, mask, family_name, size, &ps_name);
3313 *name_ret = ps_name;
3315 float actual_size = size / scale;
3316 asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
3317 family_name.UTF8String,
3318 (require & NSBoldFontMask) ? "bold" : "medium",
3319 (require & NSItalicFontMask) ? 'o' : 'r',
3320 (unsigned)(dpi * actual_size / 72.27 + 0.5),
3321 (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
3322 (require & NSFixedPitchFontMask) ? 'm' : 'p');
3331 XLoadFont (Display *dpy, const char *name)
3333 Font fid = (Font) calloc (1, sizeof(*fid));
3338 /* Since iOS screens are physically smaller than desktop screens, scale up
3339 the fonts to make them more readable.
3341 Note that X11 apps on iOS also have the backbuffer sized in points
3342 instead of pixels, resulting in an effective X11 screen size of 768x1024
3343 or so, even if the display has significantly higher resolution. That is
3344 unrelated to this hack, which is really about DPI.
3350 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
3353 if (!fid->nsfont && name &&
3354 strchr (name, ' ') &&
3355 !strchr (name, '*')) {
3356 // If name contains a space but no stars, it is a native font spec --
3357 // return NULL so that we know it really didn't exist. Else, it is an
3358 // XLFD font, so keep trying.
3359 XUnloadFont (dpy, fid);
3364 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size,
3367 // We should never return NULL for XLFD fonts.
3369 Assert (0, "no font");
3372 CFRetain (fid->nsfont); // needed for garbage collection?
3374 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
3383 XLoadQueryFont (Display *dpy, const char *name)
3385 Font fid = XLoadFont (dpy, name);
3387 return XQueryFont (dpy, fid);
3391 XUnloadFont (Display *dpy, Font fid)
3394 free (fid->ps_name);
3395 if (fid->metrics.per_char)
3396 free (fid->metrics.per_char);
3398 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3399 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3400 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3401 // They're probably not very big...
3403 // [fid->nsfont release];
3404 // CFRelease (fid->nsfont);
3411 XFreeFontInfo (char **names, XFontStruct *info, int n)
3415 for (i = 0; i < n; i++)
3416 if (names[i]) free (names[i]);
3420 for (i = 0; i < n; i++)
3421 if (info[i].per_char) {
3422 free (info[i].per_char);
3423 free (info[i].properties);
3431 XFreeFont (Display *dpy, XFontStruct *f)
3434 XFreeFontInfo (0, f, 1);
3435 XUnloadFont (dpy, fid);
3441 XSetFont (Display *dpy, GC gc, Font fid)
3444 XUnloadFont (dpy, gc->gcv.font);
3445 gc->gcv.font = copy_font (fid);
3446 [gc->gcv.font->nsfont retain];
3447 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3453 XCreateFontSet (Display *dpy, char *name,
3454 char ***missing_charset_list_return,
3455 int *missing_charset_count_return,
3456 char **def_string_return)
3458 char *name2 = strdup (name);
3459 char *s = strchr (name, ",");
3462 XFontStruct *f = XLoadQueryFont (dpy, name2);
3465 set = (XFontSet) calloc (1, sizeof(*set));
3469 if (missing_charset_list_return) *missing_charset_list_return = 0;
3470 if (missing_charset_count_return) *missing_charset_count_return = 0;
3471 if (def_string_return) *def_string_return = 0;
3477 XFreeFontSet (Display *dpy, XFontSet set)
3479 XFreeFont (dpy, set->font);
3485 jwxyz_nativeFontName (Font f, float *size)
3487 if (size) *size = f->size;
3493 XFreeStringList (char **list)
3497 for (i = 0; list[i]; i++)
3503 // Returns the verbose Unicode name of this character, like "agrave" or
3504 // "daggerdouble". Used by fontglide debugMetrics.
3507 jwxyz_unicode_character_name (Font fid, unsigned long uc)
3511 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
3512 [fid->nsfont pointSize],
3514 Assert (ctfont, @"no CTFontRef for UIFont");
3517 if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
3518 NSString *name = (NSString *)
3519 CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
3521 ret = (name ? strdup ([name UTF8String]) : 0);
3529 // Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored.
3530 // We have to do this because stringWithCString returns NULL if there are
3531 // any invalid characters at all.
3534 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
3536 int out_len = in_len * 4; // length of string might increase
3537 char *s2 = (char *) malloc (out_len);
3539 const char *in_end = in + in_len;
3540 const char *out_end = out + out_len;
3541 Bool latin1_p = True;
3546 long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
3547 long L2 = utf8_encode (uc, out, out_end - out);
3550 if (uc > 255) latin1_p = False;
3554 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
3556 if (latin1_pP) *latin1_pP = latin1_p;
3557 return (nsstr ? nsstr : @"");
3562 XTextExtents (XFontStruct *f, const char *s, int length,
3563 int *dir_ret, int *ascent_ret, int *descent_ret,
3566 // Unfortunately, adding XCharStructs together to get the extents for a
3567 // string doesn't work: Cocoa uses non-integral character advancements, but
3568 // XCharStruct.width is an integer. Plus that doesn't take into account
3569 // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
3572 NSString *nsstr = [[[NSString alloc] initWithBytes:s
3574 encoding:NSISOLatin1StringEncoding]
3576 utf8_metrics (f->fid, nsstr, cs);
3578 *ascent_ret = f->ascent;
3579 *descent_ret = f->descent;
3584 XTextWidth (XFontStruct *f, const char *s, int length)
3586 int ascent, descent, dir;
3588 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3594 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
3595 int *dir_ret, int *ascent_ret, int *descent_ret,
3598 Bool latin1_p = True;
3599 int i, utf8_len = 0;
3600 char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized
3602 for (i = 0; i < length; i++)
3603 if (s[i].byte1 > 0) {
3609 NSString *nsstr = [NSString stringWithCString:utf8
3610 encoding:NSUTF8StringEncoding];
3611 utf8_metrics (f->fid, nsstr, cs);
3615 *ascent_ret = f->ascent;
3616 *descent_ret = f->descent;
3622 /* "Returns the distance in pixels in the primary draw direction from
3623 the drawing origin to the origin of the next character to be drawn."
3625 "overall_ink_return is set to the bbox of the string's character ink."
3627 "The overall_ink_return for a nondescending, horizontally drawn Latin
3628 character is conventionally entirely above the baseline; that is,
3629 overall_ink_return.height <= -overall_ink_return.y."
3631 [So this means that y is the top of the ink, and height grows down:
3632 For above-the-baseline characters, y is negative.]
3634 "The overall_ink_return for a nonkerned character is entirely at, and to
3635 the right of, the origin; that is, overall_ink_return.x >= 0."
3637 [So this means that x is the left of the ink, and width grows right.
3638 For left-of-the-origin characters, x is negative.]
3640 "A character consisting of a single pixel at the origin would set
3641 overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
3644 Xutf8TextExtents (XFontSet set, const char *str, int len,
3645 XRectangle *overall_ink_return,
3646 XRectangle *overall_logical_return)
3649 NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
3652 utf8_metrics (set->font->fid, nsstr, &cs);
3654 /* "The overall_logical_return is the bounding box that provides minimum
3655 spacing to other graphical features for the string. Other graphical
3656 features, for example, a border surrounding the text, should not
3657 intersect this rectangle."
3659 So I think that means they're the same? Or maybe "ink" is the bounding
3660 box, and "logical" is the advancement? But then why is the return value
3663 if (overall_ink_return)
3664 XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
3665 if (overall_logical_return)
3666 XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
3673 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3676 if (! nsstr) return 1;
3678 CGRect wr = d->frame;
3679 CGContextRef cgc = d->cgc;
3681 unsigned long argb = gc->gcv.foreground;
3682 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3684 query_color_float (dpy, argb, rgba);
3685 NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
3690 if (!gc->gcv.font) {
3691 Assert (0, "no font");
3695 /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
3696 NSFontAttributeName, and NSAttributedString are only present on iOS 6
3697 and later. We could resurrect the Quartz code from v5.29 and do a
3698 runtime conditional on that, but that would be a pain in the ass.
3699 Probably time to just make iOS 6 a requirement.
3702 NSDictionary *attr =
3703 [NSDictionary dictionaryWithObjectsAndKeys:
3704 gc->gcv.font->nsfont, NSFontAttributeName,
3705 fg, NSForegroundColorAttributeName,
3708 // Don't understand why we have to do both set_color and
3709 // NSForegroundColorAttributeName, but we do.
3711 set_color (dpy, cgc, argb, 32, NO, YES);
3713 NSAttributedString *astr = [[NSAttributedString alloc]
3714 initWithString:nsstr
3716 CTLineRef dl = CTLineCreateWithAttributedString (
3717 (__bridge CFAttributedStringRef) astr);
3719 // Not sure why this is necessary, but xoff is positive when the first
3720 // character on the line has a negative lbearing. Without this, the
3721 // string is rendered with the first ink at 0 instead of at lbearing.
3722 // I have not seen xoff be negative, so I'm not sure if that can happen.
3724 // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
3727 CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
3728 Assert (xoff >= 0, "unexpected CTLineOffset");
3731 CGContextSetTextPosition (cgc,
3733 wr.origin.y + wr.size.height - y);
3734 CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
3736 CTLineDraw (dl, cgc);
3739 invalidate_drawable_cache (d);
3745 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3746 const char *str, int len)
3748 char *s2 = (char *) malloc (len + 1);
3749 strncpy (s2, str, len);
3751 NSString *nsstr = [NSString stringWithCString:s2
3752 encoding:NSISOLatin1StringEncoding];
3753 int ret = draw_string (dpy, d, gc, x, y, nsstr);
3760 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
3761 const XChar2b *str, int len)
3763 char *s2 = XChar2b_to_utf8 (str, 0); // already sanitized
3765 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
3766 int ret = draw_string (dpy, d, gc, x, y, nsstr);
3773 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
3774 int x, int y, const char *str, int len)
3776 char *s2 = (char *) malloc (len + 1);
3777 strncpy (s2, str, len);
3779 NSString *nsstr = sanitize_utf8 (str, len, 0);
3780 draw_string (dpy, d, gc, x, y, nsstr);
3786 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3787 const char *str, int len)
3789 int ascent, descent, dir;
3791 XTextExtents (&gc->gcv.font->metrics, str, len,
3792 &dir, &ascent, &descent, &cs);
3793 draw_rect (dpy, d, gc,
3794 x + MIN (0, cs.lbearing),
3795 y - MAX (0, ascent),
3796 MAX (MAX (0, cs.rbearing) -
3797 MIN (0, cs.lbearing),
3799 MAX (0, ascent) + MAX (0, descent),
3801 return XDrawString (dpy, d, gc, x, y, str, len);
3806 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3808 validate_pixel (dpy, fg, gc->depth, gc->gcv.alpha_allowed_p);
3809 gc->gcv.foreground = fg;
3815 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3817 validate_pixel (dpy, bg, gc->depth, gc->gcv.alpha_allowed_p);
3818 gc->gcv.background = bg;
3823 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3825 gc->gcv.alpha_allowed_p = allowed;
3830 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3832 gc->gcv.antialias_p = antialias_p;
3838 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3839 int line_style, int cap_style, int join_style)
3841 gc->gcv.line_width = line_width;
3842 Assert (line_style == LineSolid, "only LineSolid implemented");
3843 // gc->gcv.line_style = line_style;
3844 gc->gcv.cap_style = cap_style;
3845 gc->gcv.join_style = join_style;
3850 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3856 XSetFunction (Display *dpy, GC gc, int which)
3858 gc->gcv.function = which;
3863 XSetSubwindowMode (Display *dpy, GC gc, int which)
3865 gc->gcv.subwindow_mode = which;
3870 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3872 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3874 if (gc->gcv.clip_mask) {
3875 XFreePixmap (dpy, gc->gcv.clip_mask);
3876 CGImageRelease (gc->clip_mask);
3879 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3880 if (gc->gcv.clip_mask)
3882 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3890 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3892 gc->gcv.clip_x_origin = x;
3893 gc->gcv.clip_y_origin = y;
3899 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3900 int *root_x_ret, int *root_y_ret,
3901 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3903 Assert (w && w->type == WINDOW, "not a window");
3906 int x = w->window.last_mouse_x;
3907 int y = w->window.last_mouse_y;
3908 if (root_x_ret) *root_x_ret = x;
3909 if (root_y_ret) *root_y_ret = y;
3910 if (win_x_ret) *win_x_ret = x;
3911 if (win_y_ret) *win_y_ret = y;
3913 # else // !USE_IPHONE
3915 NSWindow *nsw = [w->window.view window];
3917 // get bottom left of window on screen, from bottom left
3918 wpos.x = wpos.y = 0;
3919 wpos = [nsw convertBaseToScreen:wpos];
3922 // get bottom left of view on window, from bottom left
3923 vpos.x = vpos.y = 0;
3924 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3926 // get bottom left of view on screen, from bottom left
3930 // get top left of view on screen, from bottom left
3931 vpos.y += w->frame.size.height;
3933 // get top left of view on screen, from top left
3934 NSArray *screens = [NSScreen screens];
3935 NSScreen *screen = (screens && [screens count] > 0
3936 ? [screens objectAtIndex:0]
3937 : [NSScreen mainScreen]);
3938 NSRect srect = [screen frame];
3939 vpos.y = srect.size.height - vpos.y;
3941 // get the mouse position on window, from bottom left
3942 NSEvent *e = [NSApp currentEvent];
3943 NSPoint p = [e locationInWindow];
3945 // get mouse position on screen, from bottom left
3949 // get mouse position on screen, from top left
3950 p.y = srect.size.height - p.y;
3952 if (root_x_ret) *root_x_ret = (int) p.x;
3953 if (root_y_ret) *root_y_ret = (int) p.y;
3954 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3955 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3956 # endif // !USE_IPHONE
3958 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3959 if (root_ret) *root_ret = 0;
3960 if (child_ret) *child_ret = 0;
3965 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3966 int src_x, int src_y,
3967 int *dest_x_ret, int *dest_y_ret,
3970 Assert (w && w->type == WINDOW, "not a window");
3978 # else // !USE_IPHONE
3980 NSWindow *nsw = [w->window.view window];
3982 // get bottom left of window on screen, from bottom left
3983 wpos.x = wpos.y = 0;
3984 wpos = [nsw convertBaseToScreen:wpos];
3987 // get bottom left of view on window, from bottom left
3988 vpos.x = vpos.y = 0;
3989 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3991 // get bottom left of view on screen, from bottom left
3995 // get top left of view on screen, from bottom left
3996 vpos.y += w->frame.size.height;
3998 // get top left of view on screen, from top left
3999 NSArray *screens = [NSScreen screens];
4000 NSScreen *screen = (screens && [screens count] > 0
4001 ? [screens objectAtIndex:0]
4002 : [NSScreen mainScreen]);
4003 NSRect srect = [screen frame];
4004 vpos.y = srect.size.height - vpos.y;
4006 // point starts out relative to top left of view
4011 // get point relative to top left of screen
4014 # endif // !USE_IPHONE
4025 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
4031 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
4034 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
4036 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
4037 if ((unsigned int) ks <= 255)
4040 // Put control characters in the string. Not meta.
4041 if (e->state & ControlMask) {
4042 if (c >= 'a' && c <= 'z') // Upcase control.
4044 if (c >= '@' && c <= '_') // Shift to control page.
4046 if (c == ' ') // C-SPC is NULL.
4050 if (k_ret) *k_ret = ks;
4051 if (size > 0) buf[0] = c;
4052 if (size > 1) buf[1] = 0;
4053 return (size > 0 ? 1 : 0);
4058 XFlush (Display *dpy)
4060 // Just let the event loop take care of this on its own schedule.
4065 XSync (Display *dpy, Bool flush)
4067 return XFlush (dpy);
4071 // declared in utils/visual.h
4073 has_writable_cells (Screen *s, Visual *v)
4079 visual_depth (Screen *s, Visual *v)
4085 visual_cells (Screen *s, Visual *v)
4087 return (int)(v->red_mask | v->green_mask | v->blue_mask);
4091 visual_class (Screen *s, Visual *v)
4097 get_bits_per_pixel (Display *dpy, int depth)
4099 Assert (depth == 32 || depth == 1, "unexpected depth");
4104 screen_number (Screen *screen)
4106 Display *dpy = DisplayOfScreen (screen);
4108 for (i = 0; i < ScreenCount (dpy); i++)
4109 if (ScreenOfDisplay (dpy, i) == screen)
4115 // declared in utils/grabclient.h
4117 use_subwindow_mode_p (Screen *screen, Window window)