Blob


1 /*
2 * Cocoa's event loop must be in main thread.
3 *
4 * Unless otherwise stated, all coordinate systems
5 * are bottom-left-based.
6 */
8 #define Cursor OSXCursor
9 #define Point OSXPoint
10 #define Rect OSXRect
12 #import <Cocoa/Cocoa.h>
14 #undef Cursor
15 #undef Point
16 #undef Rect
18 #include <u.h>
19 #include <libc.h>
20 #include "cocoa-thread.h"
21 #include <draw.h>
22 #include <memdraw.h>
23 #include <keyboard.h>
24 #include <cursor.h>
25 #include "cocoa-screen.h"
26 #include "osx-keycodes.h"
27 #include "devdraw.h"
28 #include "bigarrow.h"
29 #include "glendapng.h"
31 // Use non-deprecated names.
32 #define NSKeyDown NSEventTypeKeyDown
33 #define NSShiftKeyMask NSEventModifierFlagShift
34 #define NSAlternateKeyMask NSEventModifierFlagOption
35 #define NSCommandKeyMask NSEventModifierFlagCommand
36 #define NSResizableWindowMask NSWindowStyleMaskResizable
37 #define NSLeftMouseDown NSEventTypeLeftMouseDown
38 #define NSLeftMouseUp NSEventTypeLeftMouseUp
39 #define NSRightMouseDown NSEventTypeRightMouseDown
40 #define NSRightMouseUp NSEventTypeRightMouseUp
41 #define NSOtherMouseDown NSEventTypeOtherMouseDown
42 #define NSOtherMouseUp NSEventTypeOtherMouseUp
43 #define NSScrollWheel NSEventTypeScrollWheel
44 #define NSMouseMoved NSEventTypeMouseMoved
45 #define NSLeftMouseDragged NSEventTypeLeftMouseDragged
46 #define NSRightMouseDragged NSEventTypeRightMouseDragged
47 #define NSOtherMouseDragged NSEventTypeOtherMouseDragged
48 #define NSCompositeCopy NSCompositingOperationCopy
49 #define NSCompositeSourceIn NSCompositingOperationSourceIn
50 #define NSFlagsChanged NSEventTypeFlagsChanged
51 #define NSTitledWindowMask NSWindowStyleMaskTitled
52 #define NSClosableWindowMask NSWindowStyleMaskClosable
53 #define NSMiniaturizableWindowMask NSWindowStyleMaskMiniaturizable
54 #define NSBorderlessWindowMask NSWindowStyleMaskBorderless
56 AUTOFRAMEWORK(Cocoa)
58 #define LOG if(0)NSLog
59 #define panic sysfatal
61 int usegestures = 0;
62 int useliveresizing = 0;
63 int useoldfullscreen = 0;
64 int usebigarrow = 0;
66 static void setprocname(const char*);
68 /*
69 * By default, devdraw uses retina displays.
70 * Set devdrawretina=0 in the environment to override.
71 */
72 int devdrawretina = 1;
74 void
75 usage(void)
76 {
77 fprint(2, "usage: devdraw (don't run directly)\n");
78 threadexitsall("usage");
79 }
81 @interface appdelegate : NSObject<NSApplicationDelegate,NSWindowDelegate> @end
83 NSObject<NSApplicationDelegate,NSWindowDelegate> *myApp;
85 void
86 threadmain(int argc, char **argv)
87 {
88 char *envvar;
90 /*
91 * Move the protocol off stdin/stdout so that
92 * any inadvertent prints don't screw things up.
93 */
94 dup(0,3);
95 dup(1,4);
96 close(0);
97 close(1);
98 open("/dev/null", OREAD);
99 open("/dev/null", OWRITE);
101 ARGBEGIN{
102 case 'D': /* for good ps -a listings */
103 break;
104 case 'f':
105 useoldfullscreen = 1;
106 break;
107 case 'g':
108 usegestures = 1;
109 break;
110 case 'b':
111 usebigarrow = 1;
112 break;
113 default:
114 usage();
115 }ARGEND
117 setprocname(argv0);
119 if (envvar = getenv("devdrawretina"))
120 devdrawretina = atoi(envvar) > 0;
122 if(OSX_VERSION < 100700)
123 [NSAutoreleasePool new];
125 [NSApplication sharedApplication];
126 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
127 myApp = [appdelegate new];
128 [NSApp setDelegate:myApp];
129 [NSApp run];
132 #define WIN win.ofs[win.isofs]
134 struct
136 NSWindow *ofs[2]; /* ofs[1] for old fullscreen; ofs[0] else */
137 int isofs;
138 int isnfs;
139 NSView *content;
140 NSBitmapImageRep *img;
141 int needimg;
142 int deferflush;
143 NSCursor *cursor;
144 CGFloat topointscale;
145 CGFloat topixelscale;
146 } win;
148 struct
150 NSCursor *bigarrow;
151 int kbuttons;
152 int mbuttons;
153 NSPoint mpos;
154 int mscroll;
155 int willactivate;
156 } in;
158 static void hidebars(int);
159 static void flushimg(NSRect);
160 static void autoflushwin(int);
161 static void flushwin(void);
162 static void followzoombutton(NSRect);
163 static void getmousepos(void);
164 static void makeicon(void);
165 static void makemenu(void);
166 static void makewin(char*);
167 static void sendmouse(void);
168 static void kicklabel0(char*);
169 static void setcursor0(Cursor*);
170 static void togglefs(void);
171 static void acceptresizing(int);
173 static NSCursor* makecursor(Cursor*);
175 static NSSize winsizepixels();
176 static NSSize winsizepoints();
177 static NSRect scalerect(NSRect, CGFloat);
178 static NSPoint scalepoint(NSPoint, CGFloat);
179 static NSRect dilate(NSRect);
181 @implementation appdelegate
182 - (void)applicationDidFinishLaunching:(id)arg
184 in.bigarrow = makecursor(&bigarrow);
185 makeicon();
186 makemenu();
187 [NSApplication
188 detachDrawingThread:@selector(callservep9p:)
189 toTarget:[self class] withObject:nil];
192 - (void)windowDidBecomeKey:(id)arg
194 getmousepos();
195 sendmouse();
197 - (void)windowDidResize:(id)arg
199 getmousepos();
200 sendmouse();
202 - (void)windowWillStartLiveResize:(id)arg
204 if(useliveresizing == 0)
205 [win.content setHidden:YES];
207 - (void)windowDidEndLiveResize:(id)arg
209 if(useliveresizing == 0)
210 [win.content setHidden:NO];
212 - (void)windowDidChangeScreen:(id)arg
214 if(win.isnfs || win.isofs)
215 hidebars(1);
216 [win.ofs[1] setFrame:[[WIN screen] frame] display:YES];
218 - (BOOL)windowShouldZoom:(id)arg toFrame:(NSRect)r
220 followzoombutton(r);
221 return YES;
223 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(id)arg
225 return YES;
227 - (void)applicationDidBecomeActive:(id)arg{ in.willactivate = 0;}
228 - (void)windowWillEnterFullScreen:(id)arg{ acceptresizing(1);}
229 - (void)windowDidEnterFullScreen:(id)arg{ win.isnfs = 1; hidebars(1);}
230 - (void)windowWillExitFullScreen:(id)arg{ win.isnfs = 0; hidebars(0);}
231 - (void)windowDidExitFullScreen:(id)arg
233 NSButton *b;
235 b = [WIN standardWindowButton:NSWindowMiniaturizeButton];
237 if([b isEnabled] == 0){
238 [b setEnabled:YES];
239 hidebars(0);
242 - (void)windowWillClose:(id)arg
244 autoflushwin(0); /* can crash otherwise */
247 + (void)callservep9p:(id)arg
249 servep9p();
250 [NSApp terminate:self];
252 - (void)plumbmanual:(id)arg
254 if(fork() != 0)
255 return;
256 execl("plumb", "plumb", "devdraw(1)", nil);
258 + (void)callflushwin:(id)arg{ flushwin();}
259 - (void)calltogglefs:(id)arg{ togglefs();}
261 + (void)callflushimg:(NSValue*)v{ flushimg([v rectValue]);}
262 + (void)callmakewin:(NSValue*)v{ makewin([v pointerValue]);}
263 + (void)callsetcursor0:(NSValue*)v{ setcursor0([v pointerValue]);}
264 + (void)callkicklabel0:(NSValue*)v{ kicklabel0([v pointerValue]);}
265 @end
267 static Memimage* initimg(void);
269 Memimage*
270 attachscreen(char *label, char *winsize)
272 static int first = 1;
274 if(first)
275 first = 0;
276 else
277 panic("attachscreen called twice");
279 if(label == nil)
280 label = "gnot a label";
281 if(strcmp(label, "page") == 0)
282 useliveresizing = 1;
284 /*
285 * Create window in main thread, else no cursor
286 * change while resizing.
287 */
288 [appdelegate
289 performSelectorOnMainThread:@selector(callmakewin:)
290 withObject:[NSValue valueWithPointer:winsize]
291 waitUntilDone:YES];
292 // makewin(winsize);
294 kicklabel(label);
295 return initimg();
298 @interface appwin : NSWindow @end
299 @interface contentview : NSView @end
301 @implementation appwin
302 - (NSTimeInterval)animationResizeTime:(NSRect)r
304 return 0;
306 - (BOOL)canBecomeKeyWindow
308 return YES; /* else no keyboard for old fullscreen */
310 - (void)makeKeyAndOrderFront:(id)arg
312 LOG(@"makeKeyAndOrderFront");
314 autoflushwin(1);
315 [win.content setHidden:NO];
316 [super makeKeyAndOrderFront:arg];
318 - (void)miniaturize:(id)arg
320 [super miniaturize:arg];
321 [NSApp hide:nil];
323 [win.content setHidden:YES];
324 autoflushwin(0);
326 - (void)deminiaturize:(id)arg
328 autoflushwin(1);
329 [win.content setHidden:NO];
330 [super deminiaturize:arg];
333 - (NSDragOperation)draggingEntered:(id)arg
335 NSPasteboard *b;
336 NSDragOperation op;
338 op = [arg draggingSourceOperationMask];
339 b = [arg draggingPasteboard];
341 if([[b types] containsObject:NSFilenamesPboardType])
342 if(op&NSDragOperationLink)
343 return NSDragOperationLink;
345 return NSDragOperationNone;
348 - (BOOL)performDragOperation:(id)arg
350 NSPasteboard *b;
351 NSArray *files;
352 int i, n;
354 b = [arg draggingPasteboard];
355 if(![[b types] containsObject:NSFilenamesPboardType])
356 return NO;
358 files = [b propertyListForType:NSFilenamesPboardType];
359 n = [files count];
360 for(i=0; i<n; i++)
361 if(fork() == 0)
362 execl("macedit", "macedit", [[files objectAtIndex:i] UTF8String], nil);
364 return YES;
367 @end
369 double
370 min(double a, double b)
372 return a<b? a : b;
375 enum
377 Winstyle = NSTitledWindowMask
378 | NSClosableWindowMask
379 | NSMiniaturizableWindowMask
380 | NSResizableWindowMask
381 };
383 static void
384 makewin(char *s)
386 NSRect r, sr;
387 NSWindow *w;
388 Rectangle wr;
389 int i, set;
391 sr = [[NSScreen mainScreen] frame];
392 r = [[NSScreen mainScreen] visibleFrame];
394 if(s && *s){
395 if(parsewinsize(s, &wr, &set) < 0)
396 sysfatal("%r");
397 }else{
398 wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3);
399 set = 0;
402 r.origin.x = wr.min.x;
403 r.origin.y = sr.size.height-wr.max.y; /* winsize is top-left-based */
404 r.size.width = min(Dx(wr), r.size.width);
405 r.size.height = min(Dy(wr), r.size.height);
406 r = [NSWindow contentRectForFrameRect:r
407 styleMask:Winstyle];
409 w = [[appwin alloc]
410 initWithContentRect:r
411 styleMask:Winstyle
412 backing:NSBackingStoreBuffered defer:NO];
413 [w setTitle:@"devdraw"];
415 if(!set)
416 [w center];
417 #if OSX_VERSION >= 100700
418 [w setCollectionBehavior:
419 NSWindowCollectionBehaviorFullScreenPrimary];
420 #endif
421 [w setContentMinSize:NSMakeSize(128,128)];
423 [w registerForDraggedTypes:[NSArray arrayWithObjects:
424 NSFilenamesPboardType, nil]];
426 win.ofs[0] = w;
427 win.ofs[1] = [[appwin alloc]
428 initWithContentRect:sr
429 styleMask:NSBorderlessWindowMask
430 backing:NSBackingStoreBuffered defer:YES];
431 for(i=0; i<2; i++){
432 [win.ofs[i] setAcceptsMouseMovedEvents:YES];
433 [win.ofs[i] setDelegate:myApp];
434 [win.ofs[i] setDisplaysWhenScreenProfileChanges:NO];
436 win.isofs = 0;
437 win.content = [contentview new];
438 [WIN setContentView:win.content];
440 topwin();
443 static Memimage*
444 initimg(void)
446 Memimage *i;
447 NSSize size, ptsize;
448 Rectangle r;
450 size = winsizepixels();
451 LOG(@"initimg %.0f %.0f", size.width, size.height);
453 r = Rect(0, 0, size.width, size.height);
454 i = allocmemimage(r, XBGR32);
455 if(i == nil)
456 panic("allocmemimage: %r");
457 if(i->data == nil)
458 panic("i->data == nil");
460 win.img = [[NSBitmapImageRep alloc]
461 initWithBitmapDataPlanes:&i->data->bdata
462 pixelsWide:Dx(r)
463 pixelsHigh:Dy(r)
464 bitsPerSample:8
465 samplesPerPixel:3
466 hasAlpha:NO
467 isPlanar:NO
468 colorSpaceName:NSDeviceRGBColorSpace
469 bytesPerRow:bytesperline(r, 32)
470 bitsPerPixel:32];
471 ptsize = winsizepoints();
472 [win.img setSize: ptsize];
473 win.topixelscale = size.width / ptsize.width;
474 win.topointscale = 1.0f / win.topixelscale;
476 // NOTE: This is not really the display DPI.
477 // On retina, topixelscale is 2; otherwise it is 1.
478 // This formula gives us 220 for retina, 110 otherwise.
479 // That's not quite right but it's close to correct.
480 // http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density#Apple
481 displaydpi = win.topixelscale * 110;
483 return i;
486 void
487 resizeimg(void)
489 [win.img release];
490 _drawreplacescreenimage(initimg());
492 mouseresized = 1;
493 sendmouse();
496 static void
497 waitimg(int msec)
499 NSDate *limit;
500 int n;
502 win.needimg = 1;
503 win.deferflush = 0;
505 n = 0;
506 limit = [NSDate dateWithTimeIntervalSinceNow:msec/1000.0];
507 do{
508 [[NSRunLoop currentRunLoop]
509 runMode:@"waiting image"
510 beforeDate:limit];
511 n++;
512 }while(win.needimg && [(NSDate*)[NSDate date] compare:limit]<0);
514 win.deferflush = win.needimg;
516 LOG(@"waitimg %s (%d loop)", win.needimg?"defer":"ok", n);
519 void
520 _flushmemscreen(Rectangle r)
522 static int n;
523 NSRect rect;
525 LOG(@"_flushmemscreen");
527 if(n==0){
528 n++;
529 return; /* to skip useless white init rect */
530 }else
531 if(n==1){
532 [WIN performSelectorOnMainThread:
533 @selector(makeKeyAndOrderFront:)
534 withObject:nil
535 waitUntilDone:NO];
536 n++;
537 }else
538 if([win.content canDraw] == 0)
539 return;
541 rect = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r));
542 [appdelegate
543 performSelectorOnMainThread:@selector(callflushimg:)
544 withObject:[NSValue valueWithRect:rect]
545 waitUntilDone:YES
546 modes:[NSArray arrayWithObjects:
547 NSRunLoopCommonModes,
548 @"waiting image", nil]];
551 static void drawimg(NSRect, uint);
552 static void drawresizehandle(void);
554 enum
556 Pixel = 1,
557 Barsize = 4*Pixel,
558 Cornersize = 3*Pixel,
559 Handlesize = 3*Barsize + 1*Pixel,
560 };
562 /*
563 * |rect| is in pixel coordinates.
564 */
565 static void
566 flushimg(NSRect rect)
568 NSRect dr, r;
570 if([win.content lockFocusIfCanDraw] == 0)
571 return;
573 if(win.needimg){
574 if(!NSEqualSizes(scalerect(rect, win.topointscale).size, [win.img size])){
575 LOG(@"flushimg reject %.0f %.0f",
576 rect.size.width, rect.size.height);
577 [win.content unlockFocus];
578 return;
580 win.needimg = 0;
581 }else
582 win.deferflush = 1;
584 LOG(@"flushimg ok %.0f %.0f", rect.size.width, rect.size.height);
586 /*
587 * Unless we are inside "drawRect", we have to round
588 * the corners ourselves, if this is the custom.
589 * "NSCompositeSourceIn" can do that, but we don't
590 * apply it to the whole rectangle, because this
591 * slows down trackpad scrolling considerably in
592 * Acme.
593 */
594 r = [win.content bounds];
595 rect = dilate(scalerect(rect, win.topointscale));
596 r.size.height -= Cornersize;
597 dr = NSIntersectionRect(r, rect);
598 LOG(@"r %.0f %.0f %.0f %.0f", r.origin.x, r.origin.y, rect.size.width, rect.size.height);
599 LOG(@"rect in points %f %f %.0f %.0f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
600 LOG(@"dr in points %f %f %.0f %.0f", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height);
601 drawimg(dr, NSCompositeCopy);
603 r.origin.y = r.size.height;
604 r.size = NSMakeSize(Cornersize, Cornersize);
605 dr = NSIntersectionRect(r, rect);
606 drawimg(dr, NSCompositeSourceIn);
608 r.origin.x = [win.img size].width - Cornersize;
609 dr = NSIntersectionRect(r, rect);
610 drawimg(dr, NSCompositeSourceIn);
612 r.size.width = r.origin.x - Cornersize;
613 r.origin.x -= r.size.width;
614 dr = NSIntersectionRect(r, rect);
615 drawimg(dr, NSCompositeCopy);
617 if(OSX_VERSION<100700 && win.isofs==0){
618 r.origin.x = [win.img size].width - Handlesize;
619 r.origin.y = [win.img size].height - Handlesize;
620 r.size = NSMakeSize(Handlesize, Handlesize);
621 if(NSIntersectsRect(r, rect))
622 drawresizehandle();
624 [win.content unlockFocus];
627 static void
628 autoflushwin(int set)
630 static NSTimer *t;
632 if(set){
633 if(t)
634 return;
635 /*
636 * We need "NSRunLoopCommonModes", otherwise the
637 * timer will not fire during live resizing.
638 */
639 t = [NSTimer
640 timerWithTimeInterval:0.033
641 target:[appdelegate class]
642 selector:@selector(callflushwin:) userInfo:nil
643 repeats:YES];
644 [[NSRunLoop currentRunLoop] addTimer:t
645 forMode:NSRunLoopCommonModes];
646 }else{
647 [t invalidate];
648 t = nil;
649 win.deferflush = 0;
653 static void
654 flushwin(void)
656 if(win.deferflush && win.needimg==0){
657 [WIN flushWindow];
658 win.deferflush = 0;
662 /*
663 * |dr| is sized in points. What if I make it pixels?
664 */
665 static void
666 drawimg(NSRect dr, uint op)
668 CGContextRef c;
669 CGImageRef i;
670 NSRect sr;
672 if(NSIsEmptyRect(dr))
673 return;
675 sr = [win.content convertRect:dr fromView:nil];
676 LOG(@"before dr: %f %f %f %f\n", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height);
677 LOG(@"before sr: %f %f %f %f\n", sr.origin.x, sr.origin.y, sr.size.width, sr.size.height);
679 dr = scalerect(dr, win.topixelscale);
680 sr = scalerect(sr, win.topixelscale);
682 LOG(@"dr: %f %f %f %f\n", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height);
683 LOG(@"sr: %f %f %f %f\n", sr.origin.x, sr.origin.y, sr.size.width, sr.size.height);
684 if(OSX_VERSION >= 100800){
685 i = CGImageCreateWithImageInRect([win.img CGImage], NSRectToCGRect(dr));
686 c = [[WIN graphicsContext] graphicsPort];
688 CGContextSaveGState(c);
689 if(op == NSCompositeSourceIn)
690 CGContextSetBlendMode(c, kCGBlendModeSourceIn);
691 LOG(@"wim.img size %f %f\n", [win.img size].width, [win.img size].height);
692 CGContextTranslateCTM(c, 0, [win.img size].height);
693 CGContextScaleCTM(c, win.topointscale, -win.topointscale);
694 CGContextDrawImage(c, NSRectToCGRect(sr), i);
695 CGContextRestoreGState(c);
697 CGImageRelease(i);
698 }else{
699 [win.img drawInRect:dr fromRect:sr
700 operation:op fraction:1
701 respectFlipped:YES hints:nil];
703 // NSFrameRect(dr);
706 static void
707 drawresizehandle(void)
709 NSColor *color[Barsize];
710 NSPoint a,b;
711 Point c;
712 int i,j;
714 c = Pt([win.img size].width, [win.img size].height);
716 [[WIN graphicsContext] setShouldAntialias:NO];
718 color[0] = [NSColor clearColor];
719 color[1] = [NSColor darkGrayColor];
720 color[2] = [NSColor lightGrayColor];
721 color[3] = [NSColor whiteColor];
723 for(i=1; i+Barsize <= Handlesize; )
724 for(j=0; j<Barsize; j++){
725 [color[j] setStroke];
726 i++;
727 a = NSMakePoint(c.x-i, c.y-1);
728 b = NSMakePoint(c.x-2, c.y+1-i);
729 [NSBezierPath strokeLineFromPoint:a toPoint:b];
733 static void getgesture(NSEvent*);
734 static void getkeyboard(NSEvent*);
735 static void getmouse(NSEvent*);
736 static void gettouch(NSEvent*, int);
737 static void updatecursor(void);
739 @implementation contentview
740 /*
741 * "drawRect" is called each time Cocoa needs an
742 * image, and each time we call "display". It is
743 * preceded by background painting, and followed by
744 * "flushWindow".
745 */
746 - (void)drawRect:(NSRect)r
748 static int first = 1;
750 LOG(@"drawrect %.0f %.0f %.0f %.0f",
751 r.origin.x, r.origin.y, r.size.width, r.size.height);
753 if(first)
754 first = 0;
755 else
756 resizeimg();
758 if([WIN inLiveResize])
759 waitimg(100);
760 else
761 waitimg(500);
763 - (BOOL)isFlipped
765 return YES; /* to make the content's origin top left */
767 - (BOOL)acceptsFirstResponder
769 return YES; /* else no keyboard */
771 - (id)initWithFrame:(NSRect)r
773 [super initWithFrame:r];
774 [self setAcceptsTouchEvents:YES];
775 [self setHidden:YES]; /* to avoid early "drawRect" call */
776 return self;
778 - (void)setHidden:(BOOL)set
780 if(!set)
781 [WIN makeFirstResponder:self]; /* for keyboard focus */
782 [super setHidden:set];
784 - (void)cursorUpdate:(NSEvent*)e{ updatecursor();}
786 - (void)mouseMoved:(NSEvent*)e{ getmouse(e);}
787 - (void)mouseDown:(NSEvent*)e{ getmouse(e);}
788 - (void)mouseDragged:(NSEvent*)e{ getmouse(e);}
789 - (void)mouseUp:(NSEvent*)e{ getmouse(e);}
790 - (void)otherMouseDown:(NSEvent*)e{ getmouse(e);}
791 - (void)otherMouseDragged:(NSEvent*)e{ getmouse(e);}
792 - (void)otherMouseUp:(NSEvent*)e{ getmouse(e);}
793 - (void)rightMouseDown:(NSEvent*)e{ getmouse(e);}
794 - (void)rightMouseDragged:(NSEvent*)e{ getmouse(e);}
795 - (void)rightMouseUp:(NSEvent*)e{ getmouse(e);}
796 - (void)scrollWheel:(NSEvent*)e{ getmouse(e);}
798 - (void)keyDown:(NSEvent*)e{ getkeyboard(e);}
799 - (void)flagsChanged:(NSEvent*)e{ getkeyboard(e);}
801 - (void)magnifyWithEvent:(NSEvent*)e{ getgesture(e);}
803 - (void)touchesBeganWithEvent:(NSEvent*)e
805 gettouch(e, NSTouchPhaseBegan);
807 - (void)touchesMovedWithEvent:(NSEvent*)e
809 gettouch(e, NSTouchPhaseMoved);
811 - (void)touchesEndedWithEvent:(NSEvent*)e
813 gettouch(e, NSTouchPhaseEnded);
815 - (void)touchesCancelledWithEvent:(NSEvent*)e
817 gettouch(e, NSTouchPhaseCancelled);
819 @end
821 static int keycvt[] =
823 [QZ_IBOOK_ENTER]= '\n',
824 [QZ_RETURN]= '\n',
825 [QZ_ESCAPE]= 27,
826 [QZ_BACKSPACE]= '\b',
827 [QZ_LALT]= Kalt,
828 [QZ_LCTRL]= Kctl,
829 [QZ_LSHIFT]= Kshift,
830 [QZ_F1]= KF+1,
831 [QZ_F2]= KF+2,
832 [QZ_F3]= KF+3,
833 [QZ_F4]= KF+4,
834 [QZ_F5]= KF+5,
835 [QZ_F6]= KF+6,
836 [QZ_F7]= KF+7,
837 [QZ_F8]= KF+8,
838 [QZ_F9]= KF+9,
839 [QZ_F10]= KF+10,
840 [QZ_F11]= KF+11,
841 [QZ_F12]= KF+12,
842 [QZ_INSERT]= Kins,
843 [QZ_DELETE]= 0x7F,
844 [QZ_HOME]= Khome,
845 [QZ_END]= Kend,
846 [QZ_KP_PLUS]= '+',
847 [QZ_KP_MINUS]= '-',
848 [QZ_TAB]= '\t',
849 [QZ_PAGEUP]= Kpgup,
850 [QZ_PAGEDOWN]= Kpgdown,
851 [QZ_UP]= Kup,
852 [QZ_DOWN]= Kdown,
853 [QZ_LEFT]= Kleft,
854 [QZ_RIGHT]= Kright,
855 [QZ_KP_MULTIPLY]= '*',
856 [QZ_KP_DIVIDE]= '/',
857 [QZ_KP_ENTER]= '\n',
858 [QZ_KP_PERIOD]= '.',
859 [QZ_KP0]= '0',
860 [QZ_KP1]= '1',
861 [QZ_KP2]= '2',
862 [QZ_KP3]= '3',
863 [QZ_KP4]= '4',
864 [QZ_KP5]= '5',
865 [QZ_KP6]= '6',
866 [QZ_KP7]= '7',
867 [QZ_KP8]= '8',
868 [QZ_KP9]= '9',
869 };
871 @interface apptext : NSTextView @end
873 @implementation apptext
874 - (void)doCommandBySelector:(SEL)s{} /* Esc key beeps otherwise */
875 - (void)insertText:(id)arg{} /* to avoid a latency after some time */
876 @end
878 static void
879 interpretdeadkey(NSEvent *e)
881 static apptext *t;
883 if(t == nil)
884 t = [apptext new];
885 [t interpretKeyEvents:[NSArray arrayWithObject:e]];
888 static void
889 getkeyboard(NSEvent *e)
891 static int omod;
892 NSString *s;
893 char c;
894 int k, m;
895 uint code;
897 m = [e modifierFlags];
899 switch([e type]){
900 case NSKeyDown:
901 s = [e characters];
902 c = [s UTF8String][0];
904 interpretdeadkey(e);
906 if(m & NSCommandKeyMask){
907 if((m & NSShiftKeyMask) && 'a' <= c && c <= 'z')
908 c += 'A' - 'a';
909 if(' '<=c && c<='~')
910 keystroke(Kcmd+c);
911 break;
913 k = c;
914 code = [e keyCode];
915 if(code<nelem(keycvt) && keycvt[code])
916 k = keycvt[code];
917 if(k==0)
918 break;
919 if(k>0)
920 keystroke(k);
921 else
922 keystroke([s characterAtIndex:0]);
923 break;
925 case NSFlagsChanged:
926 if(in.mbuttons || in.kbuttons){
927 in.kbuttons = 0;
928 if(m & NSAlternateKeyMask)
929 in.kbuttons |= 2;
930 if(m & NSCommandKeyMask)
931 in.kbuttons |= 4;
932 sendmouse();
933 }else
934 if(m&NSAlternateKeyMask && (omod&NSAlternateKeyMask)==0)
935 keystroke(Kalt);
936 break;
938 default:
939 panic("getkey: unexpected event type");
941 omod = m;
944 /*
945 * Devdraw does not use NSTrackingArea, that often
946 * forgets to update the cursor on entering and on
947 * leaving the area, and that sometimes stops sending
948 * us MouseMove events, at least on OS X Lion.
949 */
950 static void
951 updatecursor(void)
953 NSCursor *c;
954 int isdown, isinside;
956 isinside = NSPointInRect(in.mpos, [win.content bounds]);
957 isdown = (in.mbuttons || in.kbuttons);
959 if(win.cursor && (isinside || isdown))
960 c = win.cursor;
961 else if(isinside && usebigarrow)
962 c = in.bigarrow;
963 else
964 c = [NSCursor arrowCursor];
965 [c set];
967 /*
968 * Without this trick, we can come back from the dock
969 * with a resize cursor.
970 */
971 if(OSX_VERSION >= 100700)
972 [NSCursor unhide];
975 static void
976 acceptresizing(int set)
978 uint old, style;
980 old = [WIN styleMask];
982 if((old | NSResizableWindowMask) != Winstyle)
983 return; /* when entering new fullscreen */
985 if(set)
986 style = Winstyle;
987 else
988 style = Winstyle & ~NSResizableWindowMask;
990 if(style != old)
991 [WIN setStyleMask:style];
994 static void
995 getmousepos(void)
997 NSPoint p, q;
999 p = [WIN mouseLocationOutsideOfEventStream];
1000 q = [win.content convertPoint:p fromView:nil];
1002 /* q is in point coordinates. in.mpos is in pixels. */
1003 q = scalepoint(q, win.topixelscale);
1005 in.mpos.x = round(q.x);
1006 in.mpos.y = round(q.y);
1008 updatecursor();
1010 if(win.isnfs || win.isofs)
1011 hidebars(1);
1012 else if(OSX_VERSION>=100700 && [WIN inLiveResize]==0){
1013 if(p.x<12 && p.y<12 && p.x>2 && p.y>2)
1014 acceptresizing(0);
1015 else
1016 acceptresizing(1);
1020 static void
1021 getmouse(NSEvent *e)
1023 float d;
1024 int b, m;
1026 if([WIN isKeyWindow] == 0)
1027 return;
1029 getmousepos();
1031 switch([e type]){
1032 case NSLeftMouseDown:
1033 case NSLeftMouseUp:
1034 case NSOtherMouseDown:
1035 case NSOtherMouseUp:
1036 case NSRightMouseDown:
1037 case NSRightMouseUp:
1038 b = [NSEvent pressedMouseButtons];
1039 b = b&~6 | (b&4)>>1 | (b&2)<<1;
1040 b = mouseswap(b);
1042 if(b == 1){
1043 m = [e modifierFlags];
1044 if(m & NSAlternateKeyMask){
1045 abortcompose();
1046 b = 2;
1047 }else
1048 if(m & NSCommandKeyMask)
1049 b = 4;
1051 in.mbuttons = b;
1052 break;
1054 case NSScrollWheel:
1055 #if OSX_VERSION >= 100700
1056 d = [e scrollingDeltaY];
1057 #else
1058 d = [e deltaY];
1059 #endif
1060 if(d>0)
1061 in.mscroll = 8;
1062 else
1063 if(d<0)
1064 in.mscroll = 16;
1065 break;
1067 case NSMouseMoved:
1068 case NSLeftMouseDragged:
1069 case NSRightMouseDragged:
1070 case NSOtherMouseDragged:
1071 break;
1073 default:
1074 panic("getmouse: unexpected event type");
1076 sendmouse();
1079 #define Minpinch 0.02
1081 static void
1082 getgesture(NSEvent *e)
1084 switch([e type]){
1085 case NSEventTypeMagnify:
1086 if(fabs([e magnification]) > Minpinch)
1087 togglefs();
1088 break;
1092 static void sendclick(int);
1094 static uint
1095 msec(void)
1097 return nsec()/1000000;
1100 static void
1101 gettouch(NSEvent *e, int type)
1103 static int tapping;
1104 static uint taptime;
1105 NSSet *set;
1106 int p;
1108 switch(type){
1109 case NSTouchPhaseBegan:
1110 p = NSTouchPhaseTouching;
1111 set = [e touchesMatchingPhase:p inView:nil];
1112 if(set.count == 3){
1113 tapping = 1;
1114 taptime = msec();
1115 }else
1116 if(set.count > 3)
1117 tapping = 0;
1118 break;
1120 case NSTouchPhaseMoved:
1121 tapping = 0;
1122 break;
1124 case NSTouchPhaseEnded:
1125 p = NSTouchPhaseTouching;
1126 set = [e touchesMatchingPhase:p inView:nil];
1127 if(set.count == 0){
1128 if(tapping && msec()-taptime<400)
1129 sendclick(2);
1130 tapping = 0;
1132 break;
1134 case NSTouchPhaseCancelled:
1135 break;
1137 default:
1138 panic("gettouch: unexpected event type");
1142 static void
1143 sendclick(int b)
1145 in.mbuttons = b;
1146 sendmouse();
1147 in.mbuttons = 0;
1148 sendmouse();
1151 static void
1152 sendmouse(void)
1154 NSSize size;
1155 int b;
1157 size = winsizepixels();
1158 mouserect = Rect(0, 0, size.width, size.height);
1160 b = in.kbuttons | in.mbuttons | in.mscroll;
1161 mousetrack(in.mpos.x, in.mpos.y, b, msec());
1162 in.mscroll = 0;
1166 * |p| is in pixels.
1168 void
1169 setmouse(Point p)
1171 NSPoint q;
1172 NSRect r;
1174 if([NSApp isActive]==0 && in.willactivate==0)
1175 return;
1177 if([WIN inLiveResize])
1178 return;
1180 in.mpos = scalepoint(NSMakePoint(p.x, p.y), win.topointscale); // race condition
1182 q = [win.content convertPoint:in.mpos toView:nil];
1183 q = [WIN convertRectToScreen:NSMakeRect(q.x, q.y, 0, 0)].origin;
1185 r = [[[NSScreen screens] objectAtIndex:0] frame];
1186 q.y = r.size.height - q.y; /* Quartz is top-left-based here */
1188 CGWarpMouseCursorPosition(NSPointToCGPoint(q));
1189 CGAssociateMouseAndMouseCursorPosition(true);
1193 * |r| is in points.
1195 static void
1196 followzoombutton(NSRect r)
1198 NSRect wr;
1199 Point p;
1200 NSPoint pt;
1202 wr = [WIN frame];
1203 wr.origin.y += wr.size.height;
1204 r.origin.y += r.size.height;
1206 getmousepos();
1207 pt.x = in.mpos.x;
1208 pt.y = in.mpos.y;
1209 pt = scalepoint(pt, win.topointscale);
1210 pt.x = (r.origin.x - wr.origin.x) + pt.x;
1211 pt.y = -(r.origin.y - wr.origin.y) + pt.y;
1212 pt = scalepoint(pt, win.topixelscale);
1214 p.x = pt.x;
1215 p.y = pt.y;
1217 setmouse(p);
1220 static void
1221 togglefs(void)
1223 uint opt, tmp;
1225 #if OSX_VERSION >= 100700
1226 NSScreen *s, *s0;
1228 s = [WIN screen];
1229 s0 = [[NSScreen screens] objectAtIndex:0];
1231 if((s==s0 && useoldfullscreen==0) || win.isnfs) {
1232 [WIN toggleFullScreen:nil];
1233 return;
1235 #endif
1236 [win.content retain];
1237 [WIN orderOut:nil];
1238 [WIN setContentView:nil];
1240 win.isofs = ! win.isofs;
1241 hidebars(win.isofs);
1244 * If we move the window from one space to another,
1245 * ofs[0] and ofs[1] can be on different spaces.
1246 * This "setCollectionBehavior" trick moves the
1247 * window to the active space.
1249 opt = [WIN collectionBehavior];
1250 tmp = opt | NSWindowCollectionBehaviorCanJoinAllSpaces;
1251 [WIN setContentView:win.content];
1252 [WIN setCollectionBehavior:tmp];
1253 [WIN makeKeyAndOrderFront:nil];
1254 [WIN setCollectionBehavior:opt];
1255 [win.content release];
1258 enum
1260 Autohiddenbars = NSApplicationPresentationAutoHideDock
1261 | NSApplicationPresentationAutoHideMenuBar,
1263 Hiddenbars = NSApplicationPresentationHideDock
1264 | NSApplicationPresentationHideMenuBar,
1267 static void
1268 hidebars(int set)
1270 NSScreen *s,*s0;
1271 uint old, opt;
1273 s = [WIN screen];
1274 s0 = [[NSScreen screens] objectAtIndex:0];
1275 old = [NSApp presentationOptions];
1277 #if OSX_VERSION >= 100700
1278 /* This bit can get lost, resulting in dreadful bugs. */
1279 if(win.isnfs)
1280 old |= NSApplicationPresentationFullScreen;
1281 #endif
1283 if(set && s==s0)
1284 opt = (old & ~Autohiddenbars) | Hiddenbars;
1285 else
1286 opt = old & ~(Autohiddenbars | Hiddenbars);
1288 if(opt != old)
1289 [NSApp setPresentationOptions:opt];
1292 static void
1293 makemenu(void)
1295 NSMenu *m;
1296 NSMenuItem *i0,*i1;
1298 m = [NSMenu new];
1299 i0 = [m addItemWithTitle:@"app" action:NULL keyEquivalent:@""];
1300 i1 = [m addItemWithTitle:@"help" action:NULL keyEquivalent:@""];
1301 [NSApp setMainMenu:m];
1302 [m release];
1304 m = [[NSMenu alloc] initWithTitle:@"app"];
1305 [m addItemWithTitle:@"Full Screen"
1306 action:@selector(calltogglefs:)
1307 keyEquivalent:@"f"];
1308 [m addItemWithTitle:@"Hide"
1309 action:@selector(hide:)
1310 keyEquivalent:@"h"];
1311 [m addItemWithTitle:@"Quit"
1312 action:@selector(terminate:)
1313 keyEquivalent:@"q"];
1314 [i0 setSubmenu:m];
1315 [m release];
1317 m = [[NSMenu alloc] initWithTitle:@"help"];
1318 [m addItemWithTitle:@"Plumb devdraw(1)"
1319 action:@selector(plumbmanual:)
1320 keyEquivalent:@""];
1321 [i1 setSubmenu:m];
1322 [m release];
1325 // FIXME: Introduce a high-resolution Glenda image.
1326 static void
1327 makeicon(void)
1329 NSData *d;
1330 NSImage *i;
1332 d = [[NSData alloc]
1333 initWithBytes:glenda_png
1334 length:(sizeof glenda_png)];
1336 i = [[NSImage alloc] initWithData:d];
1337 [NSApp setApplicationIconImage:i];
1338 [[NSApp dockTile] display];
1339 [i release];
1340 [d release];
1343 QLock snarfl;
1345 char*
1346 getsnarf(void)
1348 NSPasteboard *pb;
1349 NSString *s;
1351 pb = [NSPasteboard generalPasteboard];
1353 qlock(&snarfl);
1354 s = [pb stringForType:NSPasteboardTypeString];
1355 qunlock(&snarfl);
1357 if(s)
1358 return strdup((char*)[s UTF8String]);
1359 else
1360 return nil;
1363 void
1364 putsnarf(char *s)
1366 NSArray *t;
1367 NSPasteboard *pb;
1368 NSString *str;
1370 if(strlen(s) >= SnarfSize)
1371 return;
1373 t = [NSArray arrayWithObject:NSPasteboardTypeString];
1374 pb = [NSPasteboard generalPasteboard];
1375 str = [[NSString alloc] initWithUTF8String:s];
1377 qlock(&snarfl);
1378 [pb declareTypes:t owner:nil];
1379 [pb setString:str forType:NSPasteboardTypeString];
1380 qunlock(&snarfl);
1382 [str release];
1385 void
1386 kicklabel(char *label)
1388 if(label == nil)
1389 return;
1391 [appdelegate
1392 performSelectorOnMainThread:@selector(callkicklabel0:)
1393 withObject:[NSValue valueWithPointer:label]
1394 waitUntilDone:YES];
1397 static void
1398 kicklabel0(char *label) {
1399 NSString *s;
1401 s = [[NSString alloc] initWithUTF8String:label];
1402 [win.ofs[0] setTitle:s];
1403 [win.ofs[1] setTitle:s];
1404 [[NSApp dockTile] setBadgeLabel:s];
1405 [s release];
1408 void
1409 setcursor(Cursor *c)
1412 * No cursor change unless in main thread.
1414 [appdelegate
1415 performSelectorOnMainThread:@selector(callsetcursor0:)
1416 withObject:[NSValue valueWithPointer:c]
1417 waitUntilDone:YES];
1420 static void
1421 setcursor0(Cursor *c)
1423 NSCursor *d;
1425 d = win.cursor;
1427 if(c)
1428 win.cursor = makecursor(c);
1429 else
1430 win.cursor = nil;
1432 updatecursor();
1434 if(d)
1435 [d release];
1439 * Cursors will be scaled on retina display.
1441 static NSCursor*
1442 makecursor(Cursor *c)
1444 NSBitmapImageRep *r;
1445 NSCursor *d;
1446 NSImage *i;
1447 NSPoint p;
1448 int b;
1449 uchar *plane[5];
1451 r = [[NSBitmapImageRep alloc]
1452 initWithBitmapDataPlanes:nil
1453 pixelsWide:16
1454 pixelsHigh:16
1455 bitsPerSample:1
1456 samplesPerPixel:2
1457 hasAlpha:YES
1458 isPlanar:YES
1459 colorSpaceName:NSDeviceWhiteColorSpace
1460 bytesPerRow:2
1461 bitsPerPixel:1];
1463 [r getBitmapDataPlanes:plane];
1465 for(b=0; b<2*16; b++){
1466 plane[0][b] = ~c->set[b];
1467 plane[1][b] = c->clr[b];
1469 p = NSMakePoint(-c->offset.x, -c->offset.y);
1470 i = [NSImage new];
1471 [i addRepresentation:r];
1472 [r release];
1474 d = [[NSCursor alloc] initWithImage:i hotSpot:p];
1475 [i release];
1476 return d;
1479 void
1480 topwin(void)
1482 [WIN performSelectorOnMainThread:
1483 @selector(makeKeyAndOrderFront:)
1484 withObject:nil
1485 waitUntilDone:NO];
1487 in.willactivate = 1;
1488 [NSApp activateIgnoringOtherApps:YES];
1491 static NSSize
1492 winsizepoints()
1494 return [win.content bounds].size;
1497 static NSSize
1498 winsizepixels()
1500 #if OSX_VERSION >= 100700
1501 if (OSX_VERSION >= 100700 && devdrawretina)
1502 return [win.content convertSizeToBacking: winsizepoints()];
1503 else
1504 #endif
1505 return winsizepoints();
1508 static NSRect
1509 scalerect(NSRect r, CGFloat scale)
1511 r.origin.x *= scale;
1512 r.origin.y *= scale;
1513 r.size.width *= scale;
1514 r.size.height *= scale;
1515 return r;
1519 * Expands rectangle |r|'s bounds to more inclusive integer bounds to
1520 * eliminate 1 pixel gaps.
1522 static NSRect
1523 dilate(NSRect r)
1525 if(win.topixelscale > 1.0f){
1526 r.origin.x = floorf(r.origin.x);
1527 r.origin.y = floorf(r.origin.y);
1528 r.size.width = ceilf(r.size.width + 0.5);
1529 r.size.height = ceilf(r.size.height + 0.5);
1531 return r;
1534 static NSPoint
1535 scalepoint(NSPoint pt, CGFloat scale)
1537 pt.x *= scale;
1538 pt.y *= scale;
1539 return pt;
1542 static void
1543 setprocname(const char *s)
1545 CFStringRef process_name;
1547 process_name = CFStringCreateWithBytes(nil, (uchar*)s, strlen(s), kCFStringEncodingUTF8, false);
1549 // Adapted from Chrome's mac_util.mm.
1550 // http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm
1552 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
1554 // Redistribution and use in source and binary forms, with or without
1555 // modification, are permitted provided that the following conditions are
1556 // met:
1558 // * Redistributions of source code must retain the above copyright
1559 // notice, this list of conditions and the following disclaimer.
1560 // * Redistributions in binary form must reproduce the above
1561 // copyright notice, this list of conditions and the following disclaimer
1562 // in the documentation and/or other materials provided with the
1563 // distribution.
1564 // * Neither the name of Google Inc. nor the names of its
1565 // contributors may be used to endorse or promote products derived from
1566 // this software without specific prior written permission.
1568 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1569 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1570 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1571 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1572 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1573 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1574 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1575 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1576 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1577 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1578 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1579 // Warning: here be dragons! This is SPI reverse-engineered from WebKit's
1580 // plugin host, and could break at any time (although realistically it's only
1581 // likely to break in a new major release).
1582 // When 10.7 is available, check that this still works, and update this
1583 // comment for 10.8.
1585 // Private CFType used in these LaunchServices calls.
1586 typedef CFTypeRef PrivateLSASN;
1587 typedef PrivateLSASN (*LSGetCurrentApplicationASNType)();
1588 typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN,
1589 CFStringRef,
1590 CFStringRef,
1591 CFDictionaryRef*);
1593 static LSGetCurrentApplicationASNType ls_get_current_application_asn_func =
1594 NULL;
1595 static LSSetApplicationInformationItemType
1596 ls_set_application_information_item_func = NULL;
1597 static CFStringRef ls_display_name_key = NULL;
1599 static bool did_symbol_lookup = false;
1600 if (!did_symbol_lookup) {
1601 did_symbol_lookup = true;
1602 CFBundleRef launch_services_bundle =
1603 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices"));
1604 if (!launch_services_bundle) {
1605 fprint(2, "Failed to look up LaunchServices bundle\n");
1606 return;
1609 ls_get_current_application_asn_func =
1610 (LSGetCurrentApplicationASNType)(
1611 CFBundleGetFunctionPointerForName(
1612 launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN")));
1613 if (!ls_get_current_application_asn_func)
1614 fprint(2, "Could not find _LSGetCurrentApplicationASN\n");
1616 ls_set_application_information_item_func =
1617 (LSSetApplicationInformationItemType)(
1618 CFBundleGetFunctionPointerForName(
1619 launch_services_bundle,
1620 CFSTR("_LSSetApplicationInformationItem")));
1621 if (!ls_set_application_information_item_func)
1622 fprint(2, "Could not find _LSSetApplicationInformationItem\n");
1624 CFStringRef* key_pointer = (CFStringRef*)(
1625 CFBundleGetDataPointerForName(launch_services_bundle,
1626 CFSTR("_kLSDisplayNameKey")));
1627 ls_display_name_key = key_pointer ? *key_pointer : NULL;
1628 if (!ls_display_name_key)
1629 fprint(2, "Could not find _kLSDisplayNameKey\n");
1631 // Internally, this call relies on the Mach ports that are started up by the
1632 // Carbon Process Manager. In debug builds this usually happens due to how
1633 // the logging layers are started up; but in release, it isn't started in as
1634 // much of a defined order. So if the symbols had to be loaded, go ahead
1635 // and force a call to make sure the manager has been initialized and hence
1636 // the ports are opened.
1637 ProcessSerialNumber psn;
1638 GetCurrentProcess(&psn);
1640 if (!ls_get_current_application_asn_func ||
1641 !ls_set_application_information_item_func ||
1642 !ls_display_name_key) {
1643 return;
1646 PrivateLSASN asn = ls_get_current_application_asn_func();
1647 // Constant used by WebKit; what exactly it means is unknown.
1648 const int magic_session_constant = -2;
1649 OSErr err =
1650 ls_set_application_information_item_func(magic_session_constant, asn,
1651 ls_display_name_key,
1652 process_name,
1653 NULL /* optional out param */);
1654 if(err != noErr)
1655 fprint(2, "Call to set process name failed\n");