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);
862 bitmap_context_p (Drawable d)
864 # ifdef USE_BACKBUFFER
867 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
868 return d->type == PIXMAP;
874 /* You've got to be fucking kidding me!
876 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
877 with repeated calls to CGContextDrawImage than it is to make a single
878 call to CGContextFillRects() with a list of 1x1 rectangles!
880 I still wouldn't call it *fast*, however...
882 #define XDRAWPOINTS_IMAGES
884 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
885 the bitmap data directly is faster. This only works on Pixmaps, though,
886 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
888 #define XDRAWPOINTS_CGDATA
891 XDrawPoints (Display *dpy, Drawable d, GC gc,
892 XPoint *points, int count, int mode)
895 CGRect wr = d->frame;
897 # ifdef XDRAWPOINTS_CGDATA
899 if (bitmap_context_p (d))
901 CGContextRef cgc = d->cgc;
902 void *data = CGBitmapContextGetData (cgc);
903 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
904 size_t w = CGBitmapContextGetWidth (cgc);
905 size_t h = CGBitmapContextGetHeight (cgc);
907 Assert (data, "no bitmap data in Drawable");
909 unsigned long argb = gc->gcv.foreground;
910 validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
912 argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
914 CGFloat x0 = wr.origin.x;
915 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
917 // It's uglier, but faster, to hoist the conditional out of the loop.
918 if (mode == CoordModePrevious) {
919 CGFloat x = x0, y = y0;
920 for (i = 0; i < count; i++, points++) {
924 if (x >= 0 && x < w && y >= 0 && y < h) {
925 unsigned int *p = (unsigned int *)
926 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
927 *p = (unsigned int) argb;
931 for (i = 0; i < count; i++, points++) {
932 CGFloat x = x0 + points->x;
933 CGFloat y = y0 + points->y;
935 if (x >= 0 && x < w && y >= 0 && y < h) {
936 unsigned int *p = (unsigned int *)
937 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
938 *p = (unsigned int) argb;
943 } else /* d->type == WINDOW */
945 # endif /* XDRAWPOINTS_CGDATA */
947 push_fg_gc (dpy, d, gc, YES);
949 # ifdef XDRAWPOINTS_IMAGES
951 unsigned int argb = gc->gcv.foreground;
952 validate_pixel (dpy, argb, gc->depth, gc->gcv.alpha_allowed_p);
954 argb = (gc->gcv.foreground ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
956 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
958 CGImageRef cgi = CGImageCreate (1, 1,
961 /* Host-ordered, since we're using the
962 address of an int as the color data. */
963 dpy->screen->bitmap_info,
966 NO, /* interpolate */
967 kCGRenderingIntentDefault);
968 CGDataProviderRelease (prov);
970 CGContextRef cgc = d->cgc;
972 rect.size.width = rect.size.height = 1;
973 for (i = 0; i < count; i++) {
974 if (i > 0 && mode == CoordModePrevious) {
975 rect.origin.x += points->x;
976 rect.origin.x -= points->y;
978 rect.origin.x = wr.origin.x + points->x;
979 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
982 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
983 CGContextDrawImage (cgc, rect, cgi);
987 CGImageRelease (cgi);
989 # else /* ! XDRAWPOINTS_IMAGES */
991 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
994 for (i = 0; i < count; i++) {
995 r->size.width = r->size.height = 1;
996 if (i > 0 && mode == CoordModePrevious) {
997 r->origin.x = r[-1].origin.x + points->x;
998 r->origin.y = r[-1].origin.x - points->y;
1000 r->origin.x = wr.origin.x + points->x;
1001 r->origin.y = wr.origin.y + wr.size.height - points->y;
1007 CGContextFillRects (d->cgc, rects, count);
1010 # endif /* ! XDRAWPOINTS_IMAGES */
1015 invalidate_drawable_cache (d);
1022 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
1027 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
1031 static void draw_rects (Display *dpy, Drawable d, GC gc,
1032 const XRectangle *rectangles, unsigned nrectangles,
1033 unsigned long pixel, BOOL fill_p);
1035 static void draw_rect (Display *, Drawable, GC,
1036 int x, int y, unsigned int width, unsigned int height,
1037 unsigned long pixel, BOOL fill_p);
1040 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
1042 return (char *)dst + dst_pitch * y + x * 4;
1046 drawable_depth (Drawable d)
1048 return (d->type == WINDOW
1049 ? visual_depth (NULL, NULL)
1055 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
1056 int src_x, int src_y,
1057 unsigned int width, unsigned int height,
1058 int dst_x, int dst_y)
1060 Assert (gc, "no GC");
1061 Assert ((width < 65535), "improbably large width");
1062 Assert ((height < 65535), "improbably large height");
1063 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1064 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1065 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
1066 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
1068 if (width == 0 || height == 0)
1071 if (gc->gcv.function == GXset ||
1072 gc->gcv.function == GXclear) {
1073 // "set" and "clear" are dumb drawing modes that ignore the source
1074 // bits and just draw solid rectangles.
1075 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height,
1076 (gc->gcv.function == GXset
1077 ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
1078 : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), YES);
1082 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
1083 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
1084 // bounds of their drawables.
1085 BOOL clipped = NO; // Whether we did any clipping of the rects.
1087 src_frame = src->frame;
1088 dst_frame = dst->frame;
1090 // Initialize src_rect...
1092 src_rect.origin.x = src_frame.origin.x + src_x;
1093 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
1095 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
1096 src_rect.size.width = width;
1097 src_rect.size.height = height;
1099 // Initialize dst_rect...
1101 dst_rect.origin.x = dst_frame.origin.x + dst_x;
1102 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
1104 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
1105 dst_rect.size.width = width;
1106 dst_rect.size.height = height;
1108 // Clip rects to frames...
1111 # define CLIP(THIS,THAT,VAL,SIZE) do { \
1112 float off = THIS##_rect.origin.VAL; \
1115 THIS##_rect.size.SIZE += off; \
1116 THAT##_rect.size.SIZE += off; \
1117 THIS##_rect.origin.VAL -= off; \
1118 THAT##_rect.origin.VAL -= off; \
1120 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
1121 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
1124 THIS##_rect.size.SIZE -= off; \
1125 THAT##_rect.size.SIZE -= off; \
1128 CLIP (dst, src, x, width);
1129 CLIP (dst, src, y, height);
1131 // Not actually the original dst_rect, just the one before it's clipped to
1133 CGRect orig_dst_rect = dst_rect;
1135 CLIP (src, dst, x, width);
1136 CLIP (src, dst, y, height);
1139 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
1142 // Sort-of-special case where no pixels can be grabbed from the source,
1143 // and the whole destination is filled with the background color.
1144 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
1146 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
1147 (int)src_rect.size.height == (int)dst_rect.size.height,
1150 src_rect.size.width = 0;
1151 src_rect.size.height = 0;
1152 dst_rect.size.width = 0;
1153 dst_rect.size.height = 0;
1156 BOOL mask_p = src->type == PIXMAP && src->pixmap.depth == 1;
1159 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
1160 going on with clipping masks or depths or anything, optimize it by
1161 just doing a memcpy instead of going through a CGI.
1163 if (bitmap_context_p (src) &&
1164 bitmap_context_p (dst) &&
1165 gc->gcv.function == GXcopy &&
1166 !gc->gcv.clip_mask &&
1167 drawable_depth (src) == drawable_depth (dst)) {
1169 Assert(!(int)src_frame.origin.x &&
1170 !(int)src_frame.origin.y &&
1171 !(int)dst_frame.origin.x &&
1172 !(int)dst_frame.origin.y,
1173 "unexpected non-zero origin");
1175 char *src_data = CGBitmapContextGetData(src->cgc);
1176 char *dst_data = CGBitmapContextGetData(dst->cgc);
1177 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
1178 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
1180 // Int to float and back again. It's not very safe, but it seems to work.
1181 int src_x0 = src_rect.origin.x;
1182 int dst_x0 = dst_rect.origin.x;
1184 // Flip the Y-axis a second time.
1185 int src_y0 = (src_frame.origin.y + src_frame.size.height -
1186 src_rect.size.height - src_rect.origin.y);
1187 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
1188 dst_rect.size.height - dst_rect.origin.y);
1190 unsigned width0 = (int) src_rect.size.width;
1191 unsigned height0 = (int) src_rect.size.height;
1193 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
1194 (int)src_rect.size.height == (int)dst_rect.size.height,
1197 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
1198 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
1199 size_t src_pitch0 = src_pitch;
1200 size_t dst_pitch0 = dst_pitch;
1201 size_t bytes = width0 * 4;
1203 if (src == dst && dst_y0 > src_y0) {
1204 // Copy upwards if the areas might overlap.
1205 src_data0 += src_pitch0 * (height0 - 1);
1206 dst_data0 += dst_pitch0 * (height0 - 1);
1207 src_pitch0 = -src_pitch0;
1208 dst_pitch0 = -dst_pitch0;
1211 size_t lines0 = height0;
1213 // memcpy is an alias for memmove on OS X.
1214 memmove(dst_data0, src_data0, bytes);
1215 src_data0 += src_pitch0;
1216 dst_data0 += dst_pitch0;
1220 # ifndef USE_BACKBUFFER
1221 } else if (src->type == WINDOW && src == dst && !mask_p) {
1223 // If we are copying from a window to itself, we can use NSCopyBits()
1224 // without first copying the rectangle to an intermediary CGImage.
1225 // This is ~28% faster (but I *expected* it to be twice as fast...)
1226 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
1232 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1233 nsfrom.origin.y = src_rect.origin.y;
1234 nsfrom.size.width = src_rect.size.width;
1235 nsfrom.size.height = src_rect.size.height;
1237 nsto.x = dst_rect.origin.x;
1238 nsto.y = dst_rect.origin.y;
1239 NSCopyBits (0, nsfrom, nsto);
1246 NSObject *releaseme = 0;
1248 BOOL free_cgi_p = NO;
1250 if (bitmap_context_p (src)) {
1252 // If we are copying from a Pixmap to a Pixmap or Window, we must first
1253 // copy the bits to an intermediary CGImage object, then copy that to the
1254 // destination drawable's CGContext.
1256 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
1257 // case of copying from a Pixmap back to itself, but I don't think that
1258 // happens very often anyway.)
1260 // First we get a CGImage out of the pixmap CGContext -- it's the whole
1261 // pixmap, but it presumably shares the data pointer instead of copying
1262 // it. We then cache that CGImage it inside the Pixmap object. Note:
1263 // invalidate_drawable_cache() must be called to discard this any time a
1264 // modification is made to the pixmap, or we'll end up re-using old bits.
1267 src->cgi = CGBitmapContextCreateImage (src->cgc);
1270 // if doing a sub-rect, trim it down.
1271 if (src_rect.origin.x != src_frame.origin.x ||
1272 src_rect.origin.y != src_frame.origin.y ||
1273 src_rect.size.width != src_frame.size.width ||
1274 src_rect.size.height != src_frame.size.height) {
1275 // #### I don't understand why this is needed...
1276 src_rect.origin.y = (src_frame.size.height -
1277 src_rect.size.height - src_rect.origin.y);
1278 // This does not copy image data, so it should be fast.
1279 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
1283 # ifndef USE_BACKBUFFER
1284 } else { /* (src->type == WINDOW) */
1286 NSRect nsfrom; // NSRect != CGRect on 10.4
1287 nsfrom.origin.x = src_rect.origin.x;
1288 nsfrom.origin.y = src_rect.origin.y;
1289 nsfrom.size.width = src_rect.size.width;
1290 nsfrom.size.height = src_rect.size.height;
1292 // If we are copying from a Window to a Pixmap, we must first copy
1293 // the bits to an intermediary CGImage object, then copy that to the
1294 // Pixmap's CGContext.
1296 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
1297 initWithFocusedViewRect:nsfrom];
1298 unsigned char *data = [bm bitmapData];
1299 int bps = [bm bitsPerSample];
1300 int bpp = [bm bitsPerPixel];
1301 int bpl = [bm bytesPerRow];
1304 // create a CGImage from those bits.
1305 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
1306 // but that method didn't exist in 10.4.)
1308 CGDataProviderRef prov =
1309 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
1311 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
1314 /* Use whatever default bit ordering we got from
1315 initWithFocusedViewRect. I would have assumed
1316 that it was (kCGImageAlphaNoneSkipFirst |
1317 kCGBitmapByteOrder32Host), but on Intel,
1322 NULL, /* decode[] */
1323 NO, /* interpolate */
1324 kCGRenderingIntentDefault);
1326 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1327 CGDataProviderRelease (prov);
1329 # endif // !USE_BACKBUFFER
1332 CGContextRef cgc = dst->cgc;
1334 if (mask_p) { // src depth == 1
1336 push_bg_gc (dpy, dst, gc, YES);
1338 // fill the destination rectangle with solid background...
1339 CGContextFillRect (cgc, dst_rect);
1341 Assert (cgc, "no CGC with 1-bit XCopyArea");
1343 // then fill in a solid rectangle of the fg color, using the image as an
1344 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1345 set_color (dpy, cgc, gc->gcv.foreground, gc->depth,
1346 gc->gcv.alpha_allowed_p, YES);
1347 CGContextClipToMask (cgc, dst_rect, cgi);
1348 CGContextFillRect (cgc, dst_rect);
1352 } else { // src depth > 1
1356 // copy the CGImage onto the destination CGContext
1357 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1358 CGContextDrawImage (cgc, dst_rect, cgi);
1363 if (free_cgi_p) CGImageRelease (cgi);
1365 if (releaseme) [releaseme release];
1368 // If either the src or dst rects did not lie within their drawables, then
1369 // we have adjusted both the src and dst rects to account for the clipping;
1370 // that means we need to clear to the background, so that clipped bits end
1371 // up in the bg color instead of simply not being copied.
1373 // This has to happen after the copy, because if it happens before, the
1374 // cleared area will get grabbed if it overlaps with the source rectangle.
1376 if (clipped && dst->type == WINDOW) {
1377 // Int to float and back again. It's not very safe, but it seems to work.
1378 int dst_x0 = dst_rect.origin.x;
1380 // Flip the Y-axis a second time.
1381 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
1382 dst_rect.size.height - dst_rect.origin.y);
1384 unsigned width0 = (int) src_rect.size.width;
1385 unsigned height0 = (int) src_rect.size.height;
1387 int orig_dst_x = orig_dst_rect.origin.x;
1388 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
1389 orig_dst_rect.origin.y - orig_dst_rect.size.height);
1390 int orig_width = orig_dst_rect.size.width;
1391 int orig_height = orig_dst_rect.size.height;
1393 Assert (orig_dst_x >= 0 &&
1394 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
1396 orig_dst_y + orig_height <= (int) dst_frame.size.height,
1397 "wrong dimensions");
1399 XRectangle rects[4];
1400 XRectangle *rects_end = rects;
1402 if (orig_dst_y < dst_y0) {
1403 rects_end->x = orig_dst_x;
1404 rects_end->y = orig_dst_y;
1405 rects_end->width = orig_width;
1406 rects_end->height = dst_y0 - orig_dst_y;
1410 if (orig_dst_y + orig_height > dst_y0 + height0) {
1411 rects_end->x = orig_dst_x;
1412 rects_end->y = dst_y0 + height0;
1413 rects_end->width = orig_width;
1414 rects_end->height = orig_dst_y + orig_height - dst_y0 - height0;
1418 if (orig_dst_x < dst_x0) {
1419 rects_end->x = orig_dst_x;
1420 rects_end->y = dst_y0;
1421 rects_end->width = dst_x0 - orig_dst_x;
1422 rects_end->height = height0;
1426 if (dst_x0 + width0 < orig_dst_x + orig_width) {
1427 rects_end->x = dst_x0 + width0;
1428 rects_end->y = dst_y0;
1429 rects_end->width = orig_dst_x + orig_width - dst_x0 - width0;
1430 rects_end->height = height0;
1434 unsigned long old_function = gc->gcv.function;
1435 gc->gcv.function = GXcopy;
1436 draw_rects (dpy, dst, gc, rects, rects_end - rects,
1437 dst->window.background,
1439 gc->gcv.function = old_function;
1442 invalidate_drawable_cache (dst);
1448 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1449 int src_x, int src_y,
1450 unsigned width, int height,
1451 int dest_x, int dest_y, unsigned long plane)
1453 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1455 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1456 // not to white/black.
1457 return XCopyArea (dpy, src, dest, gc,
1458 src_x, src_y, width, height, dest_x, dest_y);
1463 map_point (Drawable d, int x, int y)
1465 const CGRect *wr = &d->frame;
1467 p.x = wr->origin.x + x;
1468 p.y = wr->origin.y + wr->size.height - y;
1474 adjust_point_for_line (GC gc, CGPoint *p)
1476 // Here's the authoritative discussion on how X draws lines:
1477 // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width
1478 if (gc->gcv.line_width <= 1) {
1479 /* Thin lines are "drawn using an unspecified, device-dependent
1480 algorithm", but seriously though, Bresenham's algorithm. Bresenham's
1481 algorithm runs to and from pixel centers.
1483 There's a few screenhacks (Maze, at the very least) that set line_width
1484 to 1 when it probably should be set to 0, so it's line_width <= 1
1490 /* Thick lines OTOH run from the upper-left corners of pixels. This means
1491 that a horizontal thick line of width 1 straddles two scan lines.
1492 Aliasing requires one of these scan lines be chosen; the following
1493 nudges the point so that the right choice is made. */
1500 point_for_line (Drawable d, GC gc, int x, int y)
1502 CGPoint result = map_point (d, x, y);
1503 adjust_point_for_line (gc, &result);
1509 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1511 // when drawing a zero-length line, obey line-width and cap-style.
1512 if (x1 == x2 && y1 == y2) {
1513 int w = gc->gcv.line_width;
1516 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1517 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1520 w = 1; // Actually show zero-length lines.
1521 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1525 CGPoint p = point_for_line (d, gc, x1, y1);
1527 push_fg_gc (dpy, d, gc, NO);
1529 CGContextRef cgc = d->cgc;
1530 set_line_mode (cgc, &gc->gcv);
1531 CGContextBeginPath (cgc);
1532 CGContextMoveToPoint (cgc, p.x, p.y);
1533 p = point_for_line(d, gc, x2, y2);
1534 CGContextAddLineToPoint (cgc, p.x, p.y);
1535 CGContextStrokePath (cgc);
1537 invalidate_drawable_cache (d);
1542 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1547 push_fg_gc (dpy, d, gc, NO);
1549 CGContextRef cgc = d->cgc;
1551 set_line_mode (cgc, &gc->gcv);
1553 // if the first and last points coincide, use closepath to get
1554 // the proper line-joining.
1555 BOOL closed_p = (points[0].x == points[count-1].x &&
1556 points[0].y == points[count-1].y);
1557 if (closed_p) count--;
1559 p = point_for_line(d, gc, points->x, points->y);
1561 CGContextBeginPath (cgc);
1562 CGContextMoveToPoint (cgc, p.x, p.y);
1563 for (i = 1; i < count; i++) {
1564 if (mode == CoordModePrevious) {
1568 p = point_for_line(d, gc, points->x, points->y);
1570 CGContextAddLineToPoint (cgc, p.x, p.y);
1573 if (closed_p) CGContextClosePath (cgc);
1574 CGContextStrokePath (cgc);
1576 invalidate_drawable_cache (d);
1582 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1586 CGContextRef cgc = d->cgc;
1588 push_fg_gc (dpy, d, gc, NO);
1589 set_line_mode (cgc, &gc->gcv);
1590 CGContextBeginPath (cgc);
1591 for (i = 0; i < count; i++) {
1592 CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
1593 CGContextMoveToPoint (cgc, p.x, p.y);
1594 p = point_for_line (d, gc, segments->x2, segments->y2);
1595 CGContextAddLineToPoint (cgc, p.x, p.y);
1598 CGContextStrokePath (cgc);
1600 invalidate_drawable_cache (d);
1606 XClearWindow (Display *dpy, Window win)
1608 Assert (win && win->type == WINDOW, "not a window");
1609 CGRect wr = win->frame;
1610 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1614 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1616 Assert (w && w->type == WINDOW, "not a window");
1617 validate_pixel (dpy, pixel, 32, NO);
1618 w->window.background = pixel;
1623 draw_rects (Display *dpy, Drawable d, GC gc,
1624 const XRectangle *rectangles, unsigned nrectangles,
1625 unsigned long pixel, BOOL fill_p)
1627 Assert (!gc || gc->depth == drawable_depth (d), "depth mismatch");
1629 CGContextRef cgc = d->cgc;
1633 bitmap_context_p (d) &&
1634 (!gc || (gc->gcv.function == GXcopy &&
1635 !gc->gcv.alpha_allowed_p &&
1636 !gc->gcv.clip_mask));
1640 push_color_gc (dpy, d, gc, pixel, gc->gcv.antialias_p, fill_p);
1642 set_line_mode (cgc, &gc->gcv);
1644 set_color (dpy, d->cgc, pixel, drawable_depth (d), NO, fill_p);
1648 for (unsigned i = 0; i != nrectangles; ++i) {
1650 int x = rectangles[i].x;
1651 int y = rectangles[i].y;
1652 int width = rectangles[i].width;
1653 int height = rectangles[i].height;
1657 dw = CGBitmapContextGetWidth (cgc),
1658 dh = CGBitmapContextGetHeight (cgc);
1660 if (x >= dw || y >= dh)
1673 if (width <= 0 || height <= 0)
1676 int max_width = dw - x;
1677 if (width > max_width)
1679 int max_height = dh - y;
1680 if (height > max_height)
1681 height = max_height;
1683 if (drawable_depth (d) == 1)
1684 pixel = pixel ? WhitePixel(dpy, 0) : BlackPixel(dpy, 0);
1686 size_t dst_bytes_per_row = CGBitmapContextGetBytesPerRow (d->cgc);
1687 void *dst = seek_xy (CGBitmapContextGetData (d->cgc),
1688 dst_bytes_per_row, x, y);
1690 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
1692 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
1693 wmemset (dst, pixel, width);
1695 dst = (char *) dst + dst_bytes_per_row;
1700 r.origin = map_point (d, x, y);
1701 r.origin.y -= height;
1702 r.size.width = width;
1703 r.size.height = height;
1705 CGContextFillRect (cgc, r);
1707 adjust_point_for_line (gc, &r.origin);
1708 CGContextStrokeRect (cgc, r);
1713 if (!fast_fill_p && gc)
1715 invalidate_drawable_cache (d);
1719 draw_rect (Display *dpy, Drawable d, GC gc,
1720 int x, int y, unsigned int width, unsigned int height,
1721 unsigned long pixel, BOOL fill_p)
1723 XRectangle r = {x, y, width, height};
1724 draw_rects (dpy, d, gc, &r, 1, pixel, fill_p);
1728 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1729 unsigned int width, unsigned int height)
1731 draw_rect (dpy, d, gc, x, y, width, height, gc->gcv.foreground, YES);
1736 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1737 unsigned int width, unsigned int height)
1739 draw_rect (dpy, d, gc, x, y, width, height, gc->gcv.foreground, NO);
1744 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1746 draw_rects (dpy, d, gc, rects, n, gc->gcv.foreground, YES);
1752 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1754 Assert (win && win->type == WINDOW, "not a window");
1755 draw_rect (dpy, win, 0, x, y, w, h, win->window.background, YES);
1761 XFillPolygon (Display *dpy, Drawable d, GC gc,
1762 XPoint *points, int npoints, int shape, int mode)
1764 CGRect wr = d->frame;
1766 push_fg_gc (dpy, d, gc, YES);
1767 CGContextRef cgc = d->cgc;
1768 CGContextBeginPath (cgc);
1770 for (i = 0; i < npoints; i++) {
1771 if (i > 0 && mode == CoordModePrevious) {
1775 x = wr.origin.x + points[i].x;
1776 y = wr.origin.y + wr.size.height - points[i].y;
1780 CGContextMoveToPoint (cgc, x, y);
1782 CGContextAddLineToPoint (cgc, x, y);
1784 CGContextClosePath (cgc);
1785 if (gc->gcv.fill_rule == EvenOddRule)
1786 CGContextEOFillPath (cgc);
1788 CGContextFillPath (cgc);
1790 invalidate_drawable_cache (d);
1794 #define radians(DEG) ((DEG) * M_PI / 180.0)
1795 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1798 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1799 unsigned int width, unsigned int height, int angle1, int angle2,
1802 CGRect wr = d->frame;
1804 bound.origin.x = wr.origin.x + x;
1805 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1806 bound.size.width = width;
1807 bound.size.height = height;
1810 ctr.x = bound.origin.x + bound.size.width /2;
1811 ctr.y = bound.origin.y + bound.size.height/2;
1813 float r1 = radians (angle1/64.0);
1814 float r2 = radians (angle2/64.0) + r1;
1815 BOOL clockwise = angle2 < 0;
1816 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1818 push_fg_gc (dpy, d, gc, fill_p);
1820 CGContextRef cgc = d->cgc;
1821 CGContextBeginPath (cgc);
1823 CGContextSaveGState(cgc);
1824 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1825 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1827 CGContextMoveToPoint (cgc, 0, 0);
1829 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1830 CGContextRestoreGState (cgc); // restore before stroke, for line width
1833 CGContextClosePath (cgc); // for proper line joining
1836 CGContextFillPath (cgc);
1838 set_line_mode (cgc, &gc->gcv);
1839 CGContextStrokePath (cgc);
1843 invalidate_drawable_cache (d);
1848 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1849 unsigned int width, unsigned int height, int angle1, int angle2)
1851 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1855 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1856 unsigned int width, unsigned int height, int angle1, int angle2)
1858 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1862 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1865 for (i = 0; i < narcs; i++)
1866 draw_arc (dpy, d, gc,
1867 arcs[i].x, arcs[i].y,
1868 arcs[i].width, arcs[i].height,
1869 arcs[i].angle1, arcs[i].angle2,
1875 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1878 for (i = 0; i < narcs; i++)
1879 draw_arc (dpy, d, gc,
1880 arcs[i].x, arcs[i].y,
1881 arcs[i].width, arcs[i].height,
1882 arcs[i].angle1, arcs[i].angle2,
1889 gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
1891 memset (gcv, 0, sizeof(*gcv));
1892 gcv->function = GXcopy;
1893 gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
1894 gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
1895 gcv->line_width = 1;
1896 gcv->cap_style = CapNotLast;
1897 gcv->join_style = JoinMiter;
1898 gcv->fill_rule = EvenOddRule;
1900 gcv->alpha_allowed_p = NO;
1901 gcv->antialias_p = YES;
1905 set_gcv (Display *dpy, GC gc, XGCValues *from, unsigned long mask)
1908 Assert (gc && from, "no gc");
1909 if (!gc || !from) return;
1911 if (mask & GCFunction) gc->gcv.function = from->function;
1912 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1913 if (mask & GCBackground) gc->gcv.background = from->background;
1914 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1915 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1916 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1917 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1918 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1919 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1920 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1922 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1923 if (mask & GCFont) XSetFont (0, gc, from->font);
1925 if (mask & GCForeground) validate_pixel (dpy, from->foreground, gc->depth,
1926 gc->gcv.alpha_allowed_p);
1927 if (mask & GCBackground) validate_pixel (dpy, from->background, gc->depth,
1928 gc->gcv.alpha_allowed_p);
1930 Assert ((! (mask & (GCLineStyle |
1937 GCGraphicsExposures |
1941 "unimplemented gcvalues mask");
1946 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1948 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1949 gc->depth = drawable_depth (d);
1951 gcv_defaults (dpy, &gc->gcv, gc->depth);
1952 set_gcv (dpy, gc, xgcv, mask);
1957 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1959 set_gcv (dpy, gc, gcv, mask);
1965 XFreeGC (Display *dpy, GC gc)
1968 XUnloadFont (dpy, gc->gcv.font);
1970 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1972 if (gc->gcv.clip_mask) {
1973 XFreePixmap (dpy, gc->gcv.clip_mask);
1974 CGImageRelease (gc->clip_mask);
1982 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1984 Assert (w && w->type == WINDOW, "not a window");
1985 memset (xgwa, 0, sizeof(*xgwa));
1986 xgwa->x = w->frame.origin.x;
1987 xgwa->y = w->frame.origin.y;
1988 xgwa->width = w->frame.size.width;
1989 xgwa->height = w->frame.size.height;
1991 xgwa->screen = dpy->screen;
1992 xgwa->visual = dpy->screen->visual;
1997 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1998 int *x_ret, int *y_ret,
1999 unsigned int *w_ret, unsigned int *h_ret,
2000 unsigned int *bw_ret, unsigned int *d_ret)
2002 *x_ret = d->frame.origin.x;
2003 *y_ret = d->frame.origin.y;
2004 *w_ret = d->frame.size.width;
2005 *h_ret = d->frame.size.height;
2006 *d_ret = drawable_depth (d);
2007 *root_ret = RootWindow (dpy, 0);
2014 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
2016 color->pixel = alloc_color (dpy,
2017 color->red, color->green, color->blue, 0xFFFF);
2022 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
2023 unsigned long *pmret, unsigned int npl,
2024 unsigned long *pxret, unsigned int npx)
2030 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
2032 Assert(0, "XStoreColors called");
2037 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
2039 Assert(0, "XStoreColor called");
2044 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
2045 unsigned long planes)
2051 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
2053 unsigned char r=0, g=0, b=0;
2054 if (*spec == '#' && strlen(spec) == 7) {
2055 static unsigned const char hex[] = { // yeah yeah, shoot me.
2056 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,
2057 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,
2058 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,
2059 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,
2060 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,
2061 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,
2062 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,
2063 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};
2064 r = (hex[spec[1]] << 4) | hex[spec[2]];
2065 g = (hex[spec[3]] << 4) | hex[spec[4]];
2066 b = (hex[spec[5]] << 4) | hex[spec[6]];
2067 } else if (!strcasecmp(spec,"black")) {
2069 } else if (!strcasecmp(spec,"white")) {
2071 } else if (!strcasecmp(spec,"red")) {
2073 } else if (!strcasecmp(spec,"green")) {
2075 } else if (!strcasecmp(spec,"blue")) {
2077 } else if (!strcasecmp(spec,"cyan")) {
2079 } else if (!strcasecmp(spec,"magenta")) {
2081 } else if (!strcasecmp(spec,"yellow")) {
2087 ret->red = (r << 8) | r;
2088 ret->green = (g << 8) | g;
2089 ret->blue = (b << 8) | b;
2090 ret->flags = DoRed|DoGreen|DoBlue;
2095 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
2096 XColor *screen_ret, XColor *exact_ret)
2098 if (! XParseColor (dpy, cmap, name, screen_ret))
2100 *exact_ret = *screen_ret;
2101 return XAllocColor (dpy, cmap, screen_ret);
2105 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
2107 validate_pixel (dpy, color->pixel, 32, NO);
2109 query_color(dpy, color->pixel, rgba);
2110 color->red = (rgba[0] << 8) | rgba[0];
2111 color->green = (rgba[1] << 8) | rgba[1];
2112 color->blue = (rgba[2] << 8) | rgba[2];
2113 color->flags = DoRed|DoGreen|DoBlue;
2118 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
2121 for (i = 0; i < n; i++)
2122 XQueryColor (dpy, cmap, &c[i]);
2127 static unsigned long
2128 ximage_getpixel_1 (XImage *ximage, int x, int y)
2130 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
2134 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
2137 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
2139 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
2144 static unsigned long
2145 ximage_getpixel_32 (XImage *ximage, int x, int y)
2147 return ((unsigned long)
2148 *((uint32_t *) ximage->data +
2149 (y * (ximage->bytes_per_line >> 2)) +
2154 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
2156 *((uint32_t *) ximage->data +
2157 (y * (ximage->bytes_per_line >> 2)) +
2158 x) = (uint32_t) pixel;
2164 XInitImage (XImage *ximage)
2166 if (!ximage->bytes_per_line)
2167 ximage->bytes_per_line = (ximage->depth == 1
2168 ? (ximage->width + 7) / 8
2169 : ximage->width * 4);
2171 if (ximage->depth == 1) {
2172 ximage->f.put_pixel = ximage_putpixel_1;
2173 ximage->f.get_pixel = ximage_getpixel_1;
2174 } else if (ximage->depth == 32 || ximage->depth == 24) {
2175 ximage->f.put_pixel = ximage_putpixel_32;
2176 ximage->f.get_pixel = ximage_getpixel_32;
2178 Assert (0, "unknown depth");
2185 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
2186 int format, int offset, char *data,
2187 unsigned int width, unsigned int height,
2188 int bitmap_pad, int bytes_per_line)
2190 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
2191 ximage->width = width;
2192 ximage->height = height;
2193 ximage->format = format;
2194 ximage->data = data;
2195 ximage->bitmap_unit = 8;
2196 ximage->byte_order = LSBFirst;
2197 ximage->bitmap_bit_order = ximage->byte_order;
2198 ximage->bitmap_pad = bitmap_pad;
2199 ximage->depth = depth;
2200 ximage->red_mask = (depth == 1 ? 0 : dpy->screen->visual->red_mask);
2201 ximage->green_mask = (depth == 1 ? 0 : dpy->screen->visual->green_mask);
2202 ximage->blue_mask = (depth == 1 ? 0 : dpy->screen->visual->blue_mask);
2203 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
2204 ximage->bytes_per_line = bytes_per_line;
2206 XInitImage (ximage);
2211 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
2213 XImage *to = (XImage *) malloc (sizeof(*to));
2214 memcpy (to, from, sizeof(*from));
2217 to->bytes_per_line = 0;
2220 to->data = (char *) malloc (h * to->bytes_per_line);
2222 if (x >= from->width)
2224 else if (x+w > from->width)
2225 w = from->width - x;
2227 if (y >= from->height)
2229 else if (y+h > from->height)
2230 h = from->height - y;
2233 for (ty = 0; ty < h; ty++)
2234 for (tx = 0; tx < w; tx++)
2235 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
2240 XPixmapFormatValues *
2241 XListPixmapFormats (Display *dpy, int *n_ret)
2243 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
2245 ret[0].bits_per_pixel = 32;
2246 ret[0].scanline_pad = 8;
2248 ret[1].bits_per_pixel = 1;
2249 ret[1].scanline_pad = 8;
2256 XGetPixel (XImage *ximage, int x, int y)
2258 return ximage->f.get_pixel (ximage, x, y);
2263 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
2265 return ximage->f.put_pixel (ximage, x, y, pixel);
2269 XDestroyImage (XImage *ximage)
2271 if (ximage->data) free (ximage->data);
2278 flipbits (unsigned const char *in, unsigned char *out, int length)
2280 static const unsigned char table[256] = {
2281 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
2282 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
2283 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
2284 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
2285 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
2286 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
2287 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
2288 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
2289 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
2290 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
2291 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
2292 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
2293 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
2294 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
2295 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
2296 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
2297 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
2298 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
2299 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
2300 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
2301 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
2302 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
2303 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
2304 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
2305 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
2306 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
2307 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
2308 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
2309 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
2310 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
2311 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
2312 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
2314 while (length-- > 0)
2315 *out++ = table[*in++];
2320 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
2321 int src_x, int src_y, int dest_x, int dest_y,
2322 unsigned int w, unsigned int h)
2324 CGRect wr = d->frame;
2326 Assert (gc, "no GC");
2327 Assert ((w < 65535), "improbably large width");
2328 Assert ((h < 65535), "improbably large height");
2329 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
2330 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
2331 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
2332 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
2334 // Clip width and height to the bounds of the Drawable
2336 if (dest_x + w > wr.size.width) {
2337 if (dest_x > wr.size.width)
2339 w = wr.size.width - dest_x;
2341 if (dest_y + h > wr.size.height) {
2342 if (dest_y > wr.size.height)
2344 h = wr.size.height - dest_y;
2346 if (w <= 0 || h <= 0)
2349 // Clip width and height to the bounds of the XImage
2351 if (src_x + w > ximage->width) {
2352 if (src_x > ximage->width)
2354 w = ximage->width - src_x;
2356 if (src_y + h > ximage->height) {
2357 if (src_y > ximage->height)
2359 h = ximage->height - src_y;
2361 if (w <= 0 || h <= 0)
2364 CGContextRef cgc = d->cgc;
2366 if (gc->gcv.function == GXset ||
2367 gc->gcv.function == GXclear) {
2368 // "set" and "clear" are dumb drawing modes that ignore the source
2369 // bits and just draw solid rectangles.
2370 draw_rect (dpy, d, 0, dest_x, dest_y, w, h,
2371 (gc->gcv.function == GXset
2372 ? (gc->depth == 1 ? 1 : WhitePixel(dpy,0))
2373 : (gc->depth == 1 ? 0 : BlackPixel(dpy,0))), YES);
2377 int bpl = ximage->bytes_per_line;
2378 int bpp = ximage->bits_per_pixel;
2379 int bsize = bpl * h;
2380 char *data = ximage->data;
2383 r.origin.x = wr.origin.x + dest_x;
2384 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
2390 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
2391 to create a CGImage from a sub-rectagle of the XImage.
2393 data += (src_y * bpl) + (src_x * 4);
2394 CGDataProviderRef prov =
2395 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
2397 CGImageRef cgi = CGImageCreate (w, h,
2400 dpy->screen->bitmap_info,
2402 NULL, /* decode[] */
2403 NO, /* interpolate */
2404 kCGRenderingIntentDefault);
2405 CGDataProviderRelease (prov);
2406 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2407 CGContextDrawImage (cgc, r, cgi);
2408 CGImageRelease (cgi);
2410 } else { // (bpp == 1)
2412 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
2414 #### However, the bit order within a byte in a 1bpp XImage is
2415 the wrong way around from what Quartz expects, so first we
2416 have to copy the data to reverse it. Shit! Maybe it
2417 would be worthwhile to go through the hacks and #ifdef
2418 each one that diddles 1bpp XImage->data directly...
2420 Assert ((src_x % 8) == 0,
2421 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
2423 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
2424 unsigned char *flipped = (unsigned char *) malloc (bsize);
2426 flipbits ((unsigned char *) data, flipped, bsize);
2428 CGDataProviderRef prov =
2429 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
2430 CGImageRef mask = CGImageMaskCreate (w, h,
2433 NULL, /* decode[] */
2434 NO); /* interpolate */
2435 push_fg_gc (dpy, d, gc, YES);
2437 CGContextFillRect (cgc, r); // foreground color
2438 CGContextClipToMask (cgc, r, mask);
2439 set_color (dpy, cgc, gc->gcv.background, gc->depth, NO, YES);
2440 CGContextFillRect (cgc, r); // background color
2444 CGDataProviderRelease (prov);
2445 CGImageRelease (mask);
2448 invalidate_drawable_cache (d);
2455 XGetImage (Display *dpy, Drawable d, int x, int y,
2456 unsigned int width, unsigned int height,
2457 unsigned long plane_mask, int format)
2459 const unsigned char *data = 0;
2460 size_t depth, ibpp, ibpl;
2461 convert_mode_t mode;
2462 # ifndef USE_BACKBUFFER
2463 NSBitmapImageRep *bm = 0;
2466 Assert ((width < 65535), "improbably large width");
2467 Assert ((height < 65535), "improbably large height");
2468 Assert ((x < 65535 && x > -65535), "improbably large x");
2469 Assert ((y < 65535 && y > -65535), "improbably large y");
2471 CGContextRef cgc = d->cgc;
2473 #ifndef USE_BACKBUFFER
2474 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2475 if (d->type == PIXMAP)
2478 depth = drawable_depth (d);
2479 mode = convert_mode_to_rgba (dpy->screen->bitmap_info);
2480 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2481 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2482 data = CGBitmapContextGetData (cgc);
2483 Assert (data, "CGBitmapContextGetData failed");
2485 # ifndef USE_BACKBUFFER
2486 } else { /* (d->type == WINDOW) */
2488 // get the bits (desired sub-rectangle) out of the NSView
2490 nsfrom.origin.x = x;
2491 // nsfrom.origin.y = y;
2492 nsfrom.origin.y = d->frame.size.height - height - y;
2493 nsfrom.size.width = width;
2494 nsfrom.size.height = height;
2495 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2497 mode = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? 3 : 0;
2498 ibpp = [bm bitsPerPixel];
2499 ibpl = [bm bytesPerRow];
2500 data = [bm bitmapData];
2501 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2502 # endif // !USE_BACKBUFFER
2505 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2506 data += (y * ibpl) + (x * (ibpp/8));
2508 format = (depth == 1 ? XYPixmap : ZPixmap);
2509 XImage *image = XCreateImage (dpy, 0, (unsigned int) depth,
2510 format, 0, 0, width, height, 0, 0);
2511 image->data = (char *) malloc (height * image->bytes_per_line);
2513 int obpl = image->bytes_per_line;
2515 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2516 means that on Intel it is BGRA when viewed by bytes (And BGR
2517 when using 24bpp packing).
2519 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2520 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2521 indicator of this latest kink.
2525 const unsigned char *iline = data;
2526 for (yy = 0; yy < height; yy++) {
2528 const unsigned char *iline2 = iline;
2529 for (xx = 0; xx < width; xx++) {
2531 iline2++; // ignore R or A or A or B
2532 iline2++; // ignore G or B or R or G
2533 unsigned char r = *iline2++; // use B or G or G or R
2534 if (ibpp == 32) iline2++; // ignore A or R or B or A
2536 XPutPixel (image, xx, yy, (r ? 1 : 0));
2541 const unsigned char *iline = data;
2542 unsigned char *oline = (unsigned char *) image->data;
2544 mode = convert_mode_merge (mode,
2545 convert_mode_invert (
2546 convert_mode_to_rgba (dpy->screen->bitmap_info)));
2548 for (yy = 0; yy < height; yy++) {
2550 convert_row ((uint32_t *)oline, iline, width, mode, ibpp);
2557 # ifndef USE_BACKBUFFER
2558 if (bm) [bm release];
2566 /* Returns a transformation matrix to do rotation as per the provided
2567 EXIF "Orientation" value.
2569 static CGAffineTransform
2570 exif_rotate (int rot, CGSize rect)
2572 CGAffineTransform trans = CGAffineTransformIdentity;
2574 case 2: // flip horizontal
2575 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2576 trans = CGAffineTransformScale (trans, -1, 1);
2579 case 3: // rotate 180
2580 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2581 trans = CGAffineTransformRotate (trans, M_PI);
2584 case 4: // flip vertical
2585 trans = CGAffineTransformMakeTranslation (0, rect.height);
2586 trans = CGAffineTransformScale (trans, 1, -1);
2589 case 5: // transpose (UL-to-LR axis)
2590 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2591 trans = CGAffineTransformScale (trans, -1, 1);
2592 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2595 case 6: // rotate 90
2596 trans = CGAffineTransformMakeTranslation (0, rect.width);
2597 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2600 case 7: // transverse (UR-to-LL axis)
2601 trans = CGAffineTransformMakeScale (-1, 1);
2602 trans = CGAffineTransformRotate (trans, M_PI / 2);
2605 case 8: // rotate 270
2606 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2607 trans = CGAffineTransformRotate (trans, M_PI / 2);
2619 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2620 Bool nsimg_p, void *img_arg,
2621 XRectangle *geom_ret, int exif_rotation)
2625 CGImageSourceRef cgsrc;
2626 # endif // USE_IPHONE
2629 CGContextRef cgc = d->cgc;
2633 NSImage *nsimg = (NSImage *) img_arg;
2634 imgr = [nsimg size];
2637 // convert the NSImage to a CGImage via the toll-free-bridging
2638 // of NSData and CFData...
2640 NSData *nsdata = [NSBitmapImageRep
2641 TIFFRepresentationOfImageRepsInArray:
2642 [nsimg representations]];
2643 CFDataRef cfdata = (CFDataRef) nsdata;
2644 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2645 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2646 # else // USE_IPHONE
2647 cgi = nsimg.CGImage;
2648 # endif // USE_IPHONE
2651 cgi = (CGImageRef) img_arg;
2652 imgr.width = CGImageGetWidth (cgi);
2653 imgr.height = CGImageGetHeight (cgi);
2656 Bool rot_p = (exif_rotation >= 5);
2659 imgr = NSMakeSize (imgr.height, imgr.width);
2661 CGRect winr = d->frame;
2662 float rw = winr.size.width / imgr.width;
2663 float rh = winr.size.height / imgr.height;
2664 float r = (rw < rh ? rw : rh);
2667 dst.size.width = imgr.width * r;
2668 dst.size.height = imgr.height * r;
2669 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2670 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2672 dst2.origin.x = dst2.origin.y = 0;
2674 dst2.size.width = dst.size.height;
2675 dst2.size.height = dst.size.width;
2677 dst2.size = dst.size;
2680 // Clear the part not covered by the image to background or black.
2682 if (d->type == WINDOW)
2683 XClearWindow (dpy, d);
2685 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height,
2686 drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0), YES);
2689 CGAffineTransform trans =
2690 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2692 CGContextSaveGState (cgc);
2693 CGContextConcatCTM (cgc,
2694 CGAffineTransformMakeTranslation (dst.origin.x,
2696 CGContextConcatCTM (cgc, trans);
2697 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2698 CGContextDrawImage (cgc, dst2, cgi);
2699 CGContextRestoreGState (cgc);
2704 CGImageRelease (cgi);
2706 # endif // USE_IPHONE
2709 geom_ret->x = dst.origin.x;
2710 geom_ret->y = dst.origin.y;
2711 geom_ret->width = dst.size.width;
2712 geom_ret->height = dst.size.height;
2715 invalidate_drawable_cache (d);
2721 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2723 unsigned int w, unsigned int h,
2724 unsigned long fg, unsigned int bg,
2727 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2728 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2729 (char *) data, w, h, 0, 0);
2731 gcv.foreground = fg;
2732 gcv.background = bg;
2733 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2734 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2737 XDestroyImage (image);
2742 XCreatePixmap (Display *dpy, Drawable d,
2743 unsigned int width, unsigned int height, unsigned int depth)
2745 char *data = (char *) malloc (width * height * 4);
2746 if (! data) return 0;
2748 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2750 p->frame.size.width = width;
2751 p->frame.size.height = height;
2752 p->pixmap.depth = depth;
2753 p->pixmap.cgc_buffer = data;
2755 /* Quartz doesn't have a 1bpp image type.
2756 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2757 don't support that! So we always use 32bpp, regardless of depth. */
2759 p->cgc = CGBitmapContextCreate (data, width, height,
2760 8, /* bits per component */
2761 width * 4, /* bpl */
2763 dpy->screen->bitmap_info);
2764 Assert (p->cgc, "could not create CGBitmapContext");
2770 XFreePixmap (Display *d, Pixmap p)
2772 Assert (p && p->type == PIXMAP, "not a pixmap");
2773 invalidate_drawable_cache (p);
2774 CGContextRelease (p->cgc);
2775 if (p->pixmap.cgc_buffer)
2776 free (p->pixmap.cgc_buffer);
2783 copy_pixmap (Display *dpy, Pixmap p)
2786 Assert (p->type == PIXMAP, "not a pixmap");
2792 unsigned int width, height, border_width, depth;
2793 if (XGetGeometry (dpy, p, &root,
2794 &x, &y, &width, &height, &border_width, &depth)) {
2796 gcv.function = GXcopy;
2797 GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
2799 p2 = XCreatePixmap (dpy, p, width, height, depth);
2801 XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
2806 Assert (p2, "could not copy pixmap");
2813 XGetAtomName (Display *dpy, Atom atom)
2815 if (atom == XA_FONT)
2816 return strdup ("FONT");
2818 // Note that atoms (that aren't predefined) are just char *.
2819 return strdup ((char *) atom);
2823 /* Font metric terminology, as used by X11:
2825 "lbearing" is the distance from the logical origin to the leftmost pixel.
2826 If a character's ink extends to the left of the origin, it is negative.
2828 "rbearing" is the distance from the logical origin to the rightmost pixel.
2830 "descent" is the distance from the logical origin to the bottommost pixel.
2831 For characters with descenders, it is positive. For superscripts, it
2834 "ascent" is the distance from the logical origin to the topmost pixel.
2835 It is the number of pixels above the baseline.
2837 "width" is the distance from the logical origin to the position where
2838 the logical origin of the next character should be placed.
2840 If "rbearing" is greater than "width", then this character overlaps the
2841 following character. If smaller, then there is trailing blank space.
2844 utf8_metrics (Font fid, NSString *nsstr, XCharStruct *cs)
2846 // Returns the metrics of the multi-character, single-line UTF8 string.
2848 NSFont *nsfont = fid->nsfont;
2849 Drawable d = XRootWindow (fid->dpy, 0);
2851 CGContextRef cgc = d->cgc;
2852 NSDictionary *attr =
2853 [NSDictionary dictionaryWithObjectsAndKeys:
2854 nsfont, NSFontAttributeName,
2856 NSAttributedString *astr = [[NSAttributedString alloc]
2857 initWithString:nsstr
2859 CTLineRef ctline = CTLineCreateWithAttributedString (
2860 (__bridge CFAttributedStringRef) astr);
2861 CGContextSetTextPosition (cgc, 0, 0);
2862 CGContextSetShouldAntialias (cgc, True); // #### Guess?
2864 memset (cs, 0, sizeof(*cs));
2866 // "CTRun represents set of consecutive glyphs sharing the same
2867 // attributes and direction".
2869 // We also get multiple runs any time font subsitution happens:
2870 // E.g., if the current font is Verdana-Bold, a ← character
2871 // in the NSString will actually be rendered in LucidaGrande-Bold.
2874 for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
2875 CTRunRef run = (CTRunRef) runid;
2877 CGRect bbox = CTRunGetImageBounds (run, cgc, r);
2878 CGFloat ascent, descent, leading;
2879 CGFloat advancement =
2880 CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
2883 // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
2884 bbox.origin.x -= 2.0/3.0;
2885 bbox.size.width += 4.0/3.0;
2886 bbox.size.height += 1.0/2.0;
2889 // Create the metrics for this run:
2891 cc.ascent = ceil (bbox.origin.y + bbox.size.height);
2892 cc.descent = ceil (-bbox.origin.y);
2893 cc.lbearing = floor (bbox.origin.x);
2894 cc.rbearing = ceil (bbox.origin.x + bbox.size.width);
2895 cc.width = floor (advancement + 0.5);
2897 // Add those metrics into the cumulative metrics:
2902 cs->ascent = MAX (cs->ascent, cc.ascent);
2903 cs->descent = MAX (cs->descent, cc.descent);
2904 cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing);
2905 cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing);
2906 cs->width = MAX (cs->width, cs->width + cc.width);
2909 // Why no y? What about vertical text?
2910 // XCharStruct doesn't encapsulate that but XGlyphInfo does.
2920 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2923 query_font (Font fid)
2925 if (!fid || !fid->nsfont) {
2926 Assert (0, "no NSFont in fid");
2929 if (![fid->nsfont fontName]) {
2930 Assert(0, @"broken NSFont in fid");
2937 XFontStruct *f = &fid->metrics;
2938 XCharStruct *min = &f->min_bounds;
2939 XCharStruct *max = &f->max_bounds;
2942 f->min_char_or_byte2 = first;
2943 f->max_char_or_byte2 = last;
2944 f->default_char = 'M';
2945 f->ascent = ceil ([fid->nsfont ascender]);
2946 f->descent = -floor ([fid->nsfont descender]);
2948 min->width = 32767; // set to smaller values in the loop
2949 min->ascent = 32767;
2950 min->descent = 32767;
2951 min->lbearing = 32767;
2952 min->rbearing = 32767;
2954 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2956 for (int i = first; i <= last; i++) {
2957 XCharStruct *cs = &f->per_char[i-first];
2962 NSString *nsstr = [NSString stringWithCString:s2
2963 encoding:NSISOLatin1StringEncoding];
2964 utf8_metrics (fid, nsstr, cs);
2966 max->width = MAX (max->width, cs->width);
2967 max->ascent = MAX (max->ascent, cs->ascent);
2968 max->descent = MAX (max->descent, cs->descent);
2969 max->lbearing = MAX (max->lbearing, cs->lbearing);
2970 max->rbearing = MAX (max->rbearing, cs->rbearing);
2972 min->width = MIN (min->width, cs->width);
2973 min->ascent = MIN (min->ascent, cs->ascent);
2974 min->descent = MIN (min->descent, cs->descent);
2975 min->lbearing = MIN (min->lbearing, cs->lbearing);
2976 min->rbearing = MIN (min->rbearing, cs->rbearing);
2979 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2980 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2981 i, i, cs->width, cs->lbearing, cs->rbearing,
2982 cs->ascent, cs->descent,
2983 bbox.size.width, bbox.size.height,
2984 bbox.origin.x, bbox.origin.y,
2985 advancement.width, advancement.height);
2991 // Since 'Font' includes the metrics, this just makes a copy of that.
2994 XQueryFont (Display *dpy, Font fid)
2997 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
3001 f->n_properties = 1;
3002 f->properties = malloc (sizeof(*f->properties) * f->n_properties);
3003 f->properties[0].name = XA_FONT;
3004 Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
3005 "atoms probably needs a real implementation");
3006 // If XInternAtom is ever implemented, use it here.
3007 f->properties[0].card32 = (char *)fid->xa_font;
3009 // copy XCharStruct array
3010 int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
3011 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
3012 memcpy (f->per_char, fid->metrics.per_char,
3013 size * sizeof (XCharStruct));
3020 copy_font (Font fid)
3022 // copy 'Font' struct
3023 Font fid2 = (Font) malloc (sizeof(*fid2));
3026 // copy XCharStruct array
3027 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
3028 fid2->metrics.per_char = (XCharStruct *)
3029 malloc ((size + 2) * sizeof (XCharStruct));
3030 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
3031 size * sizeof (XCharStruct));
3033 // copy the other pointers
3034 fid2->ps_name = strdup (fid->ps_name);
3035 fid2->xa_font = strdup (fid->xa_font);
3036 // [fid2->nsfont retain];
3037 fid2->metrics.fid = fid2;
3044 font_family_members (NSString *family_name)
3047 return [[NSFontManager sharedFontManager]
3048 availableMembersOfFontFamily:family_name];
3050 return [UIFont fontNamesForFamilyName:family_name];
3056 default_font_family (NSFontTraitMask require)
3058 return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
3063 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
3064 NSString *family_name, float size,
3067 Assert (size > 0, "zero font size");
3069 NSArray *family_members = font_family_members (family_name);
3070 if (!family_members.count)
3071 family_members = font_family_members (default_font_family (traits));
3074 for (unsigned k = 0; k != family_members.count; ++k) {
3076 NSArray *member = [family_members objectAtIndex:k];
3077 NSFontTraitMask font_mask =
3078 [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
3080 if ((font_mask & mask) == traits) {
3082 NSString *name = [member objectAtIndex:0];
3083 NSFont *f = [NSFont fontWithName:name size:size];
3087 /* Don't use this font if it (probably) doesn't include ASCII characters.
3089 NSStringEncoding enc = [f mostCompatibleStringEncoding];
3090 if (! (enc == NSUTF8StringEncoding ||
3091 enc == NSISOLatin1StringEncoding ||
3092 enc == NSNonLossyASCIIStringEncoding ||
3093 enc == NSISOLatin2StringEncoding ||
3094 enc == NSUnicodeStringEncoding ||
3095 enc == NSWindowsCP1250StringEncoding ||
3096 enc == NSWindowsCP1252StringEncoding ||
3097 enc == NSMacOSRomanStringEncoding)) {
3098 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
3101 // NSLog(@"using \"%@\": %d", name, enc);
3103 // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
3104 *name_ret = strdup (name.UTF8String);
3108 # else // USE_IPHONE
3110 for (NSString *fn in family_members) {
3112 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
3115 // The magic invocation for getting font names is
3116 // [[UIFontDescriptor
3117 // fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
3119 // ...but this only works on iOS 7 and later.
3120 NSFontTraitMask font_mask = 0;
3122 font_mask |= NSBoldFontMask;
3123 if (MATCH(@"Italic") || MATCH(@"Oblique"))
3124 font_mask |= NSItalicFontMask;
3126 if ((font_mask & mask) == traits) {
3128 /* Check if it can do ASCII. No good way to accomplish this!
3129 These are fonts present in iPhone Simulator as of June 2012
3130 that don't include ASCII.
3132 if (MATCH(@"AppleGothic") || // Korean
3133 MATCH(@"Dingbats") || // Dingbats
3134 MATCH(@"Emoji") || // Emoticons
3135 MATCH(@"Geeza") || // Arabic
3136 MATCH(@"Hebrew") || // Hebrew
3137 MATCH(@"HiraKaku") || // Japanese
3138 MATCH(@"HiraMin") || // Japanese
3139 MATCH(@"Kailasa") || // Tibetan
3140 MATCH(@"Ornaments") || // Dingbats
3141 MATCH(@"STHeiti") // Chinese
3145 *name_ret = strdup (fn.UTF8String);
3146 return [UIFont fontWithName:fn size:size];
3157 /* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
3158 of XLFD strings; also they can be comma-separated strings with multiple
3159 font names. First one that exists wins.
3162 try_native_font (const char *name, float scale,
3163 char **name_ret, float *size_ret, char **xa_font)
3165 if (!name) return 0;
3166 const char *spc = strrchr (name, ' ');
3170 char *token = strdup (name);
3173 while ((name2 = strtok (token, ","))) {
3176 while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
3179 spc = strrchr (name2, ' ');
3183 if (1 != sscanf (spc, " %d ", &dsize))
3187 if (size <= 4) continue;
3191 name2[strlen(name2) - strlen(spc)] = 0;
3193 NSString *nsname = [NSString stringWithCString:name2
3194 encoding:NSUTF8StringEncoding];
3195 f = [NSFont fontWithName:nsname size:size];
3199 *xa_font = strdup (name); // Maybe this should be an XLFD?
3202 NSLog(@"No native font: \"%@\" %.0f", nsname, size);
3211 /* Returns a random font in the given size and face.
3214 random_font (NSFontTraitMask traits, NSFontTraitMask mask,
3215 float size, NSString **family_ret, char **name_ret)
3219 // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
3220 // returns an empty list, at least on a system with default fonts only.
3221 NSArray *families = [[NSFontManager sharedFontManager]
3222 availableFontFamilies];
3223 if (!families) return 0;
3225 NSArray *families = [UIFont familyNames];
3227 // There are many dups in the families array -- uniquify it.
3229 NSArray *sorted_families =
3230 [families sortedArrayUsingSelector:@selector(compare:)];
3231 NSMutableArray *new_families =
3232 [NSMutableArray arrayWithCapacity:sorted_families.count];
3234 NSString *prev_family = nil;
3235 for (NSString *family in sorted_families) {
3236 if ([family compare:prev_family])
3237 [new_families addObject:family];
3240 families = new_families;
3242 # endif // USE_IPHONE
3244 long n = [families count];
3245 if (n <= 0) return 0;
3248 for (j = 0; j < n; j++) {
3249 int i = random() % n;
3250 NSString *family_name = [families objectAtIndex:i];
3252 NSFont *result = try_font (traits, mask, family_name, size, name_ret);
3254 [*family_ret release];
3255 *family_ret = family_name;
3256 [*family_ret retain];
3261 // None of the fonts support ASCII?
3266 // Fonts need this. XDisplayHeightMM and friends should probably be consistent
3267 // with this as well if they're ever implemented.
3268 static const unsigned dpi = 75;
3272 xlfd_field_end (const char *s)
3274 const char *s2 = strchr(s, '-');
3282 xlfd_next (const char **s, const char **s2)
3287 Assert (**s2 == '-', "xlfd parse error");
3289 *s2 = xlfd_field_end (*s);
3297 try_xlfd_font (const char *name, float scale,
3298 char **name_ret, float *size_ret, char **xa_font)
3301 NSString *family_name = nil;
3302 NSFontTraitMask require = 0, forbid = 0;
3307 const char *s = (name ? name : "");
3309 size_t L = strlen (s);
3310 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
3311 # define UNSPEC (L == 0 || L == 1 && *s == '*')
3312 if (CMP ("6x10")) size = 8, require |= NSFixedPitchFontMask;
3313 else if (CMP ("6x10bold")) size = 8, require |= NSFixedPitchFontMask | NSBoldFontMask;
3314 else if (CMP ("fixed")) size = 12, require |= NSFixedPitchFontMask;
3315 else if (CMP ("9x15")) size = 12, require |= NSFixedPitchFontMask;
3316 else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
3317 else if (CMP ("vga")) size = 12, require |= NSFixedPitchFontMask;
3318 else if (CMP ("console")) size = 12, require |= NSFixedPitchFontMask;
3319 else if (CMP ("gallant")) size = 12, require |= NSFixedPitchFontMask;
3322 // Incorrect fields are ignored.
3326 const char *s2 = xlfd_field_end(s);
3330 L = xlfd_next (&s, &s2); // Family name
3331 // This used to substitute Georgia for Times. Now it doesn't.
3332 if (CMP ("random")) {
3334 } else if (CMP ("fixed")) {
3335 require |= NSFixedPitchFontMask;
3336 family_name = @"Courier";
3337 } else if (!UNSPEC) {
3338 family_name = [[[NSString alloc] initWithBytes:s
3340 encoding:NSUTF8StringEncoding]
3344 L = xlfd_next (&s, &s2); // Weight name
3345 if (CMP ("bold") || CMP ("demibold"))
3346 require |= NSBoldFontMask;
3347 else if (CMP ("medium") || CMP ("regular"))
3348 forbid |= NSBoldFontMask;
3350 L = xlfd_next (&s, &s2); // Slant
3351 if (CMP ("i") || CMP ("o"))
3352 require |= NSItalicFontMask;
3354 forbid |= NSItalicFontMask;
3356 xlfd_next (&s, &s2); // Set width name (ignore)
3357 xlfd_next (&s, &s2); // Add style name (ignore)
3359 xlfd_next (&s, &s2); // Pixel size (ignore)
3361 xlfd_next (&s, &s2); // Point size
3363 uintmax_t n = strtoumax(s, &s3, 10);
3367 xlfd_next (&s, &s2); // Resolution X (ignore)
3368 xlfd_next (&s, &s2); // Resolution Y (ignore)
3370 xlfd_next (&s, &s2); // Spacing
3372 forbid |= NSFixedPitchFontMask;
3373 else if (CMP ("m") || CMP ("c"))
3374 require |= NSFixedPitchFontMask;
3376 // Don't care about average_width or charset registry.
3381 if (!family_name && !rand)
3382 family_name = default_font_family (require);
3384 if (size < 6 || size > 1000)
3389 NSFontTraitMask mask = require | forbid;
3392 nsfont = random_font (require, mask, size, &family_name, &ps_name);
3393 [family_name autorelease];
3397 nsfont = try_font (require, mask, family_name, size, &ps_name);
3399 // if that didn't work, turn off attibutes until it does
3400 // (e.g., there is no "Monaco-Bold".)
3402 if (!nsfont && (mask & NSItalicFontMask)) {
3403 require &= ~NSItalicFontMask;
3404 mask &= ~NSItalicFontMask;
3405 nsfont = try_font (require, mask, family_name, size, &ps_name);
3407 if (!nsfont && (mask & NSBoldFontMask)) {
3408 require &= ~NSBoldFontMask;
3409 mask &= ~NSBoldFontMask;
3410 nsfont = try_font (require, mask, family_name, size, &ps_name);
3412 if (!nsfont && (mask & NSFixedPitchFontMask)) {
3413 require &= ~NSFixedPitchFontMask;
3414 mask &= ~NSFixedPitchFontMask;
3415 nsfont = try_font (require, mask, family_name, size, &ps_name);
3419 *name_ret = ps_name;
3421 float actual_size = size / scale;
3422 asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
3423 family_name.UTF8String,
3424 (require & NSBoldFontMask) ? "bold" : "medium",
3425 (require & NSItalicFontMask) ? 'o' : 'r',
3426 (unsigned)(dpi * actual_size / 72.27 + 0.5),
3427 (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
3428 (require & NSFixedPitchFontMask) ? 'm' : 'p');
3437 XLoadFont (Display *dpy, const char *name)
3439 Font fid = (Font) calloc (1, sizeof(*fid));
3444 /* Since iOS screens are physically smaller than desktop screens, scale up
3445 the fonts to make them more readable.
3447 Note that X11 apps on iOS also have the backbuffer sized in points
3448 instead of pixels, resulting in an effective X11 screen size of 768x1024
3449 or so, even if the display has significantly higher resolution. That is
3450 unrelated to this hack, which is really about DPI.
3456 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size,
3459 if (!fid->nsfont && name &&
3460 strchr (name, ' ') &&
3461 !strchr (name, '*')) {
3462 // If name contains a space but no stars, it is a native font spec --
3463 // return NULL so that we know it really didn't exist. Else, it is an
3464 // XLFD font, so keep trying.
3465 XUnloadFont (dpy, fid);
3470 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size,
3473 // We should never return NULL for XLFD fonts.
3475 Assert (0, "no font");
3478 CFRetain (fid->nsfont); // needed for garbage collection?
3480 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
3489 XLoadQueryFont (Display *dpy, const char *name)
3491 Font fid = XLoadFont (dpy, name);
3493 return XQueryFont (dpy, fid);
3497 XUnloadFont (Display *dpy, Font fid)
3500 free (fid->ps_name);
3501 if (fid->metrics.per_char)
3502 free (fid->metrics.per_char);
3504 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3505 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3506 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3507 // They're probably not very big...
3509 // [fid->nsfont release];
3510 // CFRelease (fid->nsfont);
3517 XFreeFontInfo (char **names, XFontStruct *info, int n)
3521 for (i = 0; i < n; i++)
3522 if (names[i]) free (names[i]);
3526 for (i = 0; i < n; i++)
3527 if (info[i].per_char) {
3528 free (info[i].per_char);
3529 free (info[i].properties);
3537 XFreeFont (Display *dpy, XFontStruct *f)
3540 XFreeFontInfo (0, f, 1);
3541 XUnloadFont (dpy, fid);
3547 XSetFont (Display *dpy, GC gc, Font fid)
3550 XUnloadFont (dpy, gc->gcv.font);
3551 gc->gcv.font = copy_font (fid);
3552 [gc->gcv.font->nsfont retain];
3553 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3559 XCreateFontSet (Display *dpy, char *name,
3560 char ***missing_charset_list_return,
3561 int *missing_charset_count_return,
3562 char **def_string_return)
3564 char *name2 = strdup (name);
3565 char *s = strchr (name, ",");
3568 XFontStruct *f = XLoadQueryFont (dpy, name2);
3571 set = (XFontSet) calloc (1, sizeof(*set));
3575 if (missing_charset_list_return) *missing_charset_list_return = 0;
3576 if (missing_charset_count_return) *missing_charset_count_return = 0;
3577 if (def_string_return) *def_string_return = 0;
3583 XFreeFontSet (Display *dpy, XFontSet set)
3585 XFreeFont (dpy, set->font);
3591 jwxyz_nativeFontName (Font f, float *size)
3593 if (size) *size = f->size;
3599 XFreeStringList (char **list)
3603 for (i = 0; list[i]; i++)
3609 // Returns the verbose Unicode name of this character, like "agrave" or
3610 // "daggerdouble". Used by fontglide debugMetrics.
3613 jwxyz_unicode_character_name (Font fid, unsigned long uc)
3617 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
3618 [fid->nsfont pointSize],
3620 Assert (ctfont, @"no CTFontRef for UIFont");
3623 if (CTFontGetGlyphsForCharacters (ctfont, (UniChar *) &uc, &cgglyph, 1)) {
3624 NSString *name = (NSString *)
3625 CGFontCopyGlyphNameForGlyph (CTFontCopyGraphicsFont (ctfont, 0),
3627 ret = (name ? strdup ([name UTF8String]) : 0);
3635 // Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored.
3636 // We have to do this because stringWithCString returns NULL if there are
3637 // any invalid characters at all.
3640 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
3642 int out_len = in_len * 4; // length of string might increase
3643 char *s2 = (char *) malloc (out_len);
3645 const char *in_end = in + in_len;
3646 const char *out_end = out + out_len;
3647 Bool latin1_p = True;
3652 long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
3653 long L2 = utf8_encode (uc, out, out_end - out);
3656 if (uc > 255) latin1_p = False;
3660 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
3662 if (latin1_pP) *latin1_pP = latin1_p;
3663 return (nsstr ? nsstr : @"");
3668 XTextExtents (XFontStruct *f, const char *s, int length,
3669 int *dir_ret, int *ascent_ret, int *descent_ret,
3672 // Unfortunately, adding XCharStructs together to get the extents for a
3673 // string doesn't work: Cocoa uses non-integral character advancements, but
3674 // XCharStruct.width is an integer. Plus that doesn't take into account
3675 // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
3678 NSString *nsstr = [[[NSString alloc] initWithBytes:s
3680 encoding:NSISOLatin1StringEncoding]
3682 utf8_metrics (f->fid, nsstr, cs);
3684 *ascent_ret = f->ascent;
3685 *descent_ret = f->descent;
3690 XTextWidth (XFontStruct *f, const char *s, int length)
3692 int ascent, descent, dir;
3694 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3700 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
3701 int *dir_ret, int *ascent_ret, int *descent_ret,
3704 Bool latin1_p = True;
3705 int i, utf8_len = 0;
3706 char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized
3708 for (i = 0; i < length; i++)
3709 if (s[i].byte1 > 0) {
3715 NSString *nsstr = [NSString stringWithCString:utf8
3716 encoding:NSUTF8StringEncoding];
3717 utf8_metrics (f->fid, nsstr, cs);
3721 *ascent_ret = f->ascent;
3722 *descent_ret = f->descent;
3728 /* "Returns the distance in pixels in the primary draw direction from
3729 the drawing origin to the origin of the next character to be drawn."
3731 "overall_ink_return is set to the bbox of the string's character ink."
3733 "The overall_ink_return for a nondescending, horizontally drawn Latin
3734 character is conventionally entirely above the baseline; that is,
3735 overall_ink_return.height <= -overall_ink_return.y."
3737 [So this means that y is the top of the ink, and height grows down:
3738 For above-the-baseline characters, y is negative.]
3740 "The overall_ink_return for a nonkerned character is entirely at, and to
3741 the right of, the origin; that is, overall_ink_return.x >= 0."
3743 [So this means that x is the left of the ink, and width grows right.
3744 For left-of-the-origin characters, x is negative.]
3746 "A character consisting of a single pixel at the origin would set
3747 overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
3750 Xutf8TextExtents (XFontSet set, const char *str, int len,
3751 XRectangle *overall_ink_return,
3752 XRectangle *overall_logical_return)
3755 NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
3758 utf8_metrics (set->font->fid, nsstr, &cs);
3760 /* "The overall_logical_return is the bounding box that provides minimum
3761 spacing to other graphical features for the string. Other graphical
3762 features, for example, a border surrounding the text, should not
3763 intersect this rectangle."
3765 So I think that means they're the same? Or maybe "ink" is the bounding
3766 box, and "logical" is the advancement? But then why is the return value
3769 if (overall_ink_return)
3770 XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
3771 if (overall_logical_return)
3772 XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
3779 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3782 if (! nsstr) return 1;
3784 CGRect wr = d->frame;
3785 CGContextRef cgc = d->cgc;
3787 unsigned long argb = gc->gcv.foreground;
3788 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3790 query_color_float (dpy, argb, rgba);
3791 NSColor *fg = [NSColor colorWithDeviceRed:rgba[0]
3796 if (!gc->gcv.font) {
3797 Assert (0, "no font");
3801 /* This crashes on iOS 5.1 because NSForegroundColorAttributeName,
3802 NSFontAttributeName, and NSAttributedString are only present on iOS 6
3803 and later. We could resurrect the Quartz code from v5.29 and do a
3804 runtime conditional on that, but that would be a pain in the ass.
3805 Probably time to just make iOS 6 a requirement.
3808 NSDictionary *attr =
3809 [NSDictionary dictionaryWithObjectsAndKeys:
3810 gc->gcv.font->nsfont, NSFontAttributeName,
3811 fg, NSForegroundColorAttributeName,
3814 // Don't understand why we have to do both set_color and
3815 // NSForegroundColorAttributeName, but we do.
3817 set_color (dpy, cgc, argb, 32, NO, YES);
3819 NSAttributedString *astr = [[NSAttributedString alloc]
3820 initWithString:nsstr
3822 CTLineRef dl = CTLineCreateWithAttributedString (
3823 (__bridge CFAttributedStringRef) astr);
3825 // Not sure why this is necessary, but xoff is positive when the first
3826 // character on the line has a negative lbearing. Without this, the
3827 // string is rendered with the first ink at 0 instead of at lbearing.
3828 // I have not seen xoff be negative, so I'm not sure if that can happen.
3830 // Test case: "Combining Double Tilde" U+0360 (\315\240) followed by
3833 CGFloat xoff = CTLineGetOffsetForStringIndex (dl, 0, NULL);
3834 Assert (xoff >= 0, "unexpected CTLineOffset");
3837 CGContextSetTextPosition (cgc,
3839 wr.origin.y + wr.size.height - y);
3840 CGContextSetShouldAntialias (cgc, gc->gcv.antialias_p);
3842 CTLineDraw (dl, cgc);
3845 invalidate_drawable_cache (d);
3851 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3852 const char *str, int len)
3854 char *s2 = (char *) malloc (len + 1);
3855 strncpy (s2, str, len);
3857 NSString *nsstr = [NSString stringWithCString:s2
3858 encoding:NSISOLatin1StringEncoding];
3859 int ret = draw_string (dpy, d, gc, x, y, nsstr);
3866 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
3867 const XChar2b *str, int len)
3869 char *s2 = XChar2b_to_utf8 (str, 0); // already sanitized
3871 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
3872 int ret = draw_string (dpy, d, gc, x, y, nsstr);
3879 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
3880 int x, int y, const char *str, int len)
3882 char *s2 = (char *) malloc (len + 1);
3883 strncpy (s2, str, len);
3885 NSString *nsstr = sanitize_utf8 (str, len, 0);
3886 draw_string (dpy, d, gc, x, y, nsstr);
3892 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3893 const char *str, int len)
3895 int ascent, descent, dir;
3897 XTextExtents (&gc->gcv.font->metrics, str, len,
3898 &dir, &ascent, &descent, &cs);
3899 draw_rect (dpy, d, gc,
3900 x + MIN (0, cs.lbearing),
3901 y - MAX (0, ascent),
3902 MAX (MAX (0, cs.rbearing) -
3903 MIN (0, cs.lbearing),
3905 MAX (0, ascent) + MAX (0, descent),
3906 gc->gcv.background, YES);
3907 return XDrawString (dpy, d, gc, x, y, str, len);
3912 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3914 validate_pixel (dpy, fg, gc->depth, gc->gcv.alpha_allowed_p);
3915 gc->gcv.foreground = fg;
3921 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3923 validate_pixel (dpy, bg, gc->depth, gc->gcv.alpha_allowed_p);
3924 gc->gcv.background = bg;
3929 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3931 gc->gcv.alpha_allowed_p = allowed;
3936 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3938 gc->gcv.antialias_p = antialias_p;
3944 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3945 int line_style, int cap_style, int join_style)
3947 gc->gcv.line_width = line_width;
3948 Assert (line_style == LineSolid, "only LineSolid implemented");
3949 // gc->gcv.line_style = line_style;
3950 gc->gcv.cap_style = cap_style;
3951 gc->gcv.join_style = join_style;
3956 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3962 XSetFunction (Display *dpy, GC gc, int which)
3964 gc->gcv.function = which;
3969 XSetSubwindowMode (Display *dpy, GC gc, int which)
3971 gc->gcv.subwindow_mode = which;
3976 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3978 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3980 if (gc->gcv.clip_mask) {
3981 XFreePixmap (dpy, gc->gcv.clip_mask);
3982 CGImageRelease (gc->clip_mask);
3985 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3986 if (gc->gcv.clip_mask)
3988 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3996 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3998 gc->gcv.clip_x_origin = x;
3999 gc->gcv.clip_y_origin = y;
4005 get_pos (Window w, NSPoint *vpos, NSPoint *p)
4013 p->x = w->window.last_mouse_x;
4014 p->y = w->window.last_mouse_y;
4017 # else // !USE_IPHONE
4019 NSWindow *nsw = [w->window.view window];
4021 // get bottom left of window on screen, from bottom left
4022 wpos.x = wpos.y = 0;
4023 wpos = [nsw convertBaseToScreen:wpos];
4025 // get bottom left of view on window, from bottom left
4026 vpos->x = vpos->y = 0;
4027 *vpos = [w->window.view convertPoint:*vpos toView:[nsw contentView]];
4029 // get bottom left of view on screen, from bottom left
4033 // get top left of view on screen, from bottom left
4034 vpos->y += w->frame.size.height;
4036 // get top left of view on screen, from top left
4037 NSArray *screens = [NSScreen screens];
4038 NSScreen *screen = (screens && [screens count] > 0
4039 ? [screens objectAtIndex:0]
4040 : [NSScreen mainScreen]);
4041 NSRect srect = [screen frame];
4042 vpos->y = srect.size.height - vpos->y;
4045 // get the mouse position on window, from bottom left
4046 NSEvent *e = [NSApp currentEvent];
4047 *p = [e locationInWindow];
4049 // get mouse position on screen, from bottom left
4053 // get mouse position on screen, from top left
4054 p->y = srect.size.height - p->y;
4057 # endif // !USE_IPHONE
4061 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
4062 int *root_x_ret, int *root_y_ret,
4063 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
4065 Assert (w && w->type == WINDOW, "not a window");
4068 get_pos (w, &vpos, &p);
4070 if (root_x_ret) *root_x_ret = (int) p.x;
4071 if (root_y_ret) *root_y_ret = (int) p.y;
4072 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
4073 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
4074 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
4075 if (root_ret) *root_ret = 0;
4076 if (child_ret) *child_ret = 0;
4081 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
4082 int src_x, int src_y,
4083 int *dest_x_ret, int *dest_y_ret,
4086 Assert (w && w->type == WINDOW, "not a window");
4089 get_pos (w, &vpos, NULL);
4091 // point starts out relative to top left of view
4095 // get point relative to top left of screen
4108 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
4114 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
4117 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
4119 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
4120 if ((unsigned int) ks <= 255)
4123 // Put control characters in the string. Not meta.
4124 if (e->state & ControlMask) {
4125 if (c >= 'a' && c <= 'z') // Upcase control.
4127 if (c >= '@' && c <= '_') // Shift to control page.
4129 if (c == ' ') // C-SPC is NULL.
4133 if (k_ret) *k_ret = ks;
4134 if (size > 0) buf[0] = c;
4135 if (size > 1) buf[1] = 0;
4136 return (size > 0 ? 1 : 0);
4141 XFlush (Display *dpy)
4143 // Just let the event loop take care of this on its own schedule.
4148 XSync (Display *dpy, Bool flush)
4150 return XFlush (dpy);
4154 // declared in utils/visual.h
4156 has_writable_cells (Screen *s, Visual *v)
4162 visual_depth (Screen *s, Visual *v)
4168 visual_cells (Screen *s, Visual *v)
4170 return (int)(v->red_mask | v->green_mask | v->blue_mask);
4174 visual_class (Screen *s, Visual *v)
4180 get_bits_per_pixel (Display *dpy, int depth)
4182 Assert (depth == 32 || depth == 1, "unexpected depth");
4187 screen_number (Screen *screen)
4189 Display *dpy = DisplayOfScreen (screen);
4191 for (i = 0; i < ScreenCount (dpy); i++)
4192 if (ScreenOfDisplay (dpy, i) == screen)
4198 // declared in utils/grabclient.h
4200 use_subwindow_mode_p (Screen *screen, Window window)