tmac-screen.m - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tmac-screen.m (35695B) | |
--- | |
1 #define Cursor OSXCursor | |
2 #define Point OSXPoint | |
3 #define Rect OSXRect | |
4 | |
5 #import <Cocoa/Cocoa.h> | |
6 #import <Metal/Metal.h> | |
7 #import <QuartzCore/CAMetalLayer.h> | |
8 | |
9 #undef Cursor | |
10 #undef Point | |
11 #undef Rect | |
12 | |
13 #include <u.h> | |
14 #include <libc.h> | |
15 #include <thread.h> | |
16 #include <draw.h> | |
17 #include <memdraw.h> | |
18 #include <memlayer.h> | |
19 #include <mouse.h> | |
20 #include <cursor.h> | |
21 #include <keyboard.h> | |
22 #include <drawfcall.h> | |
23 #include "devdraw.h" | |
24 #include "bigarrow.h" | |
25 #include "glendapng.h" | |
26 | |
27 AUTOFRAMEWORK(Cocoa) | |
28 AUTOFRAMEWORK(Metal) | |
29 AUTOFRAMEWORK(QuartzCore) | |
30 AUTOFRAMEWORK(CoreFoundation) | |
31 | |
32 #define LOG if(0)NSLog | |
33 | |
34 // TODO: Maintain list of views for dock menu. | |
35 | |
36 static void setprocname(const char*); | |
37 static uint keycvt(uint); | |
38 static uint msec(void); | |
39 | |
40 static void rpc_resizeimg(Client*); | |
41 static void rpc_resizewindow(Client*, Rectangle); | |
42 static void rpc_setcursor(Client*, Cursor*, Cursor2*); | |
43 static void rpc_setlabel(Client*, char*); | |
44 static void rpc_setmouse(Client*, Point); | |
45 static void rpc_topwin(Client*); | |
46 static void rpc_bouncemouse(Client*, Mouse); | |
47 static void rpc_flush(Client*, Rectangle); | |
48 | |
49 static ClientImpl macimpl = { | |
50 rpc_resizeimg, | |
51 rpc_resizewindow, | |
52 rpc_setcursor, | |
53 rpc_setlabel, | |
54 rpc_setmouse, | |
55 rpc_topwin, | |
56 rpc_bouncemouse, | |
57 rpc_flush | |
58 }; | |
59 | |
60 @class DrawView; | |
61 @class DrawLayer; | |
62 | |
63 @interface AppDelegate : NSObject<NSApplicationDelegate> | |
64 @end | |
65 | |
66 static AppDelegate *myApp = NULL; | |
67 | |
68 void | |
69 gfx_main(void) | |
70 { | |
71 if(client0) | |
72 setprocname(argv0); | |
73 | |
74 @autoreleasepool{ | |
75 [NSApplication sharedApplication]; | |
76 [NSApp setActivationPolicy:NSApplicationActivationPolicy… | |
77 myApp = [AppDelegate new]; | |
78 [NSApp setDelegate:myApp]; | |
79 [NSApp run]; | |
80 } | |
81 } | |
82 | |
83 | |
84 void | |
85 rpc_shutdown(void) | |
86 { | |
87 [NSApp terminate:myApp]; | |
88 } | |
89 | |
90 @implementation AppDelegate | |
91 - (void)applicationDidFinishLaunching:(id)arg | |
92 { | |
93 NSMenu *m, *sm; | |
94 NSData *d; | |
95 NSImage *i; | |
96 | |
97 LOG(@"applicationDidFinishLaunching"); | |
98 | |
99 sm = [NSMenu new]; | |
100 [sm addItemWithTitle:@"Toggle Full Screen" action:@selector(togg… | |
101 [sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivale… | |
102 [sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEqu… | |
103 m = [NSMenu new]; | |
104 [m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""]; | |
105 [m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]]; | |
106 [NSApp setMainMenu:m]; | |
107 | |
108 d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glen… | |
109 i = [[NSImage alloc] initWithData:d]; | |
110 [NSApp setApplicationIconImage:i]; | |
111 [[NSApp dockTile] display]; | |
112 | |
113 gfx_started(); | |
114 } | |
115 | |
116 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *… | |
117 return client0 != nil; | |
118 } | |
119 @end | |
120 | |
121 @interface DrawLayer : CAMetalLayer | |
122 @property (nonatomic, retain) id<MTLCommandQueue> cmd; | |
123 @property (nonatomic, retain) id<MTLTexture> texture; | |
124 @end | |
125 | |
126 @implementation DrawLayer | |
127 - (void)display | |
128 { | |
129 LOG(@"display"); | |
130 LOG(@"display query drawable"); | |
131 | |
132 @autoreleasepool{ | |
133 id<CAMetalDrawable> drawable = [self nextDrawable]; | |
134 if(!drawable){ | |
135 LOG(@"display couldn't get drawable"); | |
136 [self setNeedsDisplay]; | |
137 return; | |
138 } | |
139 | |
140 LOG(@"display got drawable"); | |
141 | |
142 id<MTLCommandBuffer> cbuf = [self.cmd commandBuffer]; | |
143 id<MTLBlitCommandEncoder> blit = [cbuf blitCommandEncode… | |
144 [blit copyFromTexture:self.texture | |
145 sourceSlice:0 | |
146 sourceLevel:0 | |
147 sourceOrigin:MTLOriginMake(0, 0, 0) | |
148 sourceSize:MTLSizeMake(self.texture.width, self.… | |
149 toTexture:drawable.texture | |
150 destinationSlice:0 | |
151 destinationLevel:0 | |
152 destinationOrigin:MTLOriginMake(0, 0, 0)]; | |
153 [blit endEncoding]; | |
154 | |
155 [cbuf presentDrawable:drawable]; | |
156 drawable = nil; | |
157 [cbuf addCompletedHandler:^(id<MTLCommandBuffer> cmdBuff… | |
158 if(cmdBuff.error){ | |
159 NSLog(@"command buffer finished with err… | |
160 cmdBuff.error.localizedDescripti… | |
161 }else | |
162 LOG(@"command buffer finishes present dr… | |
163 }]; | |
164 [cbuf commit]; | |
165 } | |
166 LOG(@"display commit"); | |
167 } | |
168 @end | |
169 | |
170 @interface DrawView : NSView<NSTextInputClient,NSWindowDelegate> | |
171 @property (nonatomic, assign) Client *client; | |
172 @property (nonatomic, retain) DrawLayer *dlayer; | |
173 @property (nonatomic, retain) NSWindow *win; | |
174 @property (nonatomic, retain) NSCursor *currentCursor; | |
175 @property (nonatomic, assign) Memimage *img; | |
176 | |
177 - (id)attach:(Client*)client winsize:(char*)winsize label:(char*)label; | |
178 - (void)topwin; | |
179 - (void)setlabel:(char*)label; | |
180 - (void)setcursor:(Cursor*)c cursor2:(Cursor2*)c2; | |
181 - (void)setmouse:(Point)p; | |
182 - (void)clearInput; | |
183 - (void)getmouse:(NSEvent*)e; | |
184 - (void)sendmouse:(NSUInteger)b; | |
185 - (void)resetLastInputRect; | |
186 - (void)enlargeLastInputRect:(NSRect)r; | |
187 @end | |
188 | |
189 @implementation DrawView | |
190 { | |
191 NSMutableString *_tmpText; | |
192 NSRange _markedRange; | |
193 NSRange _selectedRange; | |
194 NSRect _lastInputRect; // The view is flipped, this is no… | |
195 BOOL _tapping; | |
196 NSUInteger _tapFingers; | |
197 NSUInteger _tapTime; | |
198 } | |
199 | |
200 - (id)init | |
201 { | |
202 LOG(@"View init"); | |
203 self = [super init]; | |
204 [self setAllowedTouchTypes:NSTouchTypeMaskDirect|NSTouchTypeMask… | |
205 _tmpText = [[NSMutableString alloc] initWithCapacity:2]; | |
206 _markedRange = NSMakeRange(NSNotFound, 0); | |
207 _selectedRange = NSMakeRange(0, 0); | |
208 return self; | |
209 } | |
210 | |
211 - (CALayer*)makeBackingLayer { return [DrawLayer layer]; } | |
212 - (BOOL)wantsUpdateLayer { return YES; } | |
213 - (BOOL)isOpaque { return YES; } | |
214 - (BOOL)isFlipped { return YES; } | |
215 - (BOOL)acceptsFirstResponder { return YES; } | |
216 | |
217 // rpc_attach allocates a new screen window with the given label and size | |
218 // and attaches it to client c (by setting c->view). | |
219 Memimage* | |
220 rpc_attach(Client *c, char *label, char *winsize) | |
221 { | |
222 LOG(@"attachscreen(%s, %s)", label, winsize); | |
223 | |
224 c->impl = &macimpl; | |
225 dispatch_sync(dispatch_get_main_queue(), ^(void) { | |
226 @autoreleasepool { | |
227 DrawView *view = [[DrawView new] attach:c winsiz… | |
228 [view initimg]; | |
229 } | |
230 }); | |
231 return ((__bridge DrawView*)c->view).img; | |
232 } | |
233 | |
234 - (id)attach:(Client*)client winsize:(char*)winsize label:(char*)label { | |
235 NSRect r, sr; | |
236 Rectangle wr; | |
237 int set; | |
238 char *s; | |
239 NSArray *allDevices; | |
240 | |
241 NSWindowStyleMask Winstyle = NSWindowStyleMaskTitled | |
242 | NSWindowStyleMaskClosable | |
243 | NSWindowStyleMaskMiniaturizable | |
244 | NSWindowStyleMaskResizable; | |
245 | |
246 if(label == nil || *label == '\0') | |
247 Winstyle &= ~NSWindowStyleMaskTitled; | |
248 | |
249 s = winsize; | |
250 sr = [[NSScreen mainScreen] frame]; | |
251 r = [[NSScreen mainScreen] visibleFrame]; | |
252 | |
253 LOG(@"makewin(%s)", s); | |
254 if(s == nil || *s == '\0' || parsewinsize(s, &wr, &set) < 0) { | |
255 wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3); | |
256 set = 0; | |
257 } | |
258 | |
259 r.origin.x = wr.min.x; | |
260 r.origin.y = sr.size.height-wr.max.y; /* winsize is top-l… | |
261 r.size.width = fmin(Dx(wr), r.size.width); | |
262 r.size.height = fmin(Dy(wr), r.size.height); | |
263 r = [NSWindow contentRectForFrameRect:r styleMask:Winstyle]; | |
264 | |
265 NSWindow *win = [[NSWindow alloc] | |
266 initWithContentRect:r | |
267 styleMask:Winstyle | |
268 backing:NSBackingStoreBuffered defer:NO]; | |
269 [win setTitle:@"devdraw"]; | |
270 | |
271 if(!set) | |
272 [win center]; | |
273 [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenP… | |
274 [win setContentMinSize:NSMakeSize(64,64)]; | |
275 [win setOpaque:YES]; | |
276 [win setRestorable:NO]; | |
277 [win setAcceptsMouseMovedEvents:YES]; | |
278 | |
279 client->view = CFBridgingRetain(self); | |
280 self.client = client; | |
281 self.win = win; | |
282 self.currentCursor = nil; | |
283 [win setContentView:self]; | |
284 [win setDelegate:self]; | |
285 [self setWantsLayer:YES]; | |
286 [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSe… | |
287 | |
288 id<MTLDevice> device = nil; | |
289 allDevices = MTLCopyAllDevices(); | |
290 for(id mtlDevice in allDevices) { | |
291 if ([mtlDevice isLowPower] && ![mtlDevice isRemovable]) { | |
292 device = mtlDevice; | |
293 break; | |
294 } | |
295 } | |
296 if(!device) | |
297 device = MTLCreateSystemDefaultDevice(); | |
298 | |
299 DrawLayer *layer = (DrawLayer*)[self layer]; | |
300 self.dlayer = layer; | |
301 layer.device = device; | |
302 layer.cmd = [device newCommandQueue]; | |
303 layer.pixelFormat = MTLPixelFormatBGRA8Unorm; | |
304 layer.framebufferOnly = YES; | |
305 layer.opaque = YES; | |
306 | |
307 // We use a default transparent layer on top of the CAMetalLayer. | |
308 // This seems to make fullscreen applications behave. | |
309 // Specifically, without this code if you enter full screen with… | |
310 // the screen goes black until the first mouse click. | |
311 if(1) { | |
312 CALayer *stub = [CALayer layer]; | |
313 stub.frame = CGRectMake(0, 0, 1, 1); | |
314 [stub setNeedsDisplay]; | |
315 [layer addSublayer:stub]; | |
316 } | |
317 | |
318 [NSEvent setMouseCoalescingEnabled:NO]; | |
319 | |
320 [self topwin]; | |
321 [self setlabel:label]; | |
322 [self setcursor:nil cursor2:nil]; | |
323 | |
324 return self; | |
325 } | |
326 | |
327 // rpc_topwin moves the window to the top of the desktop. | |
328 // Called from an RPC thread with no client lock held. | |
329 static void | |
330 rpc_topwin(Client *c) | |
331 { | |
332 DrawView *view = (__bridge DrawView*)c->view; | |
333 dispatch_sync(dispatch_get_main_queue(), ^(void) { | |
334 [view topwin]; | |
335 }); | |
336 } | |
337 | |
338 - (void)topwin { | |
339 [self.win makeKeyAndOrderFront:nil]; | |
340 [NSApp activateIgnoringOtherApps:YES]; | |
341 } | |
342 | |
343 // rpc_setlabel updates the client window's label. | |
344 // If label == nil, the call is a no-op. | |
345 // Called from an RPC thread with no client lock held. | |
346 static void | |
347 rpc_setlabel(Client *client, char *label) | |
348 { | |
349 DrawView *view = (__bridge DrawView*)client->view; | |
350 dispatch_sync(dispatch_get_main_queue(), ^(void){ | |
351 [view setlabel:label]; | |
352 }); | |
353 } | |
354 | |
355 - (void)setlabel:(char*)label { | |
356 LOG(@"setlabel(%s)", label); | |
357 if(label == nil) | |
358 return; | |
359 | |
360 @autoreleasepool{ | |
361 NSString *s = [[NSString alloc] initWithUTF8String:label… | |
362 [self.win setTitle:s]; | |
363 if(client0) | |
364 [[NSApp dockTile] setBadgeLabel:s]; | |
365 } | |
366 } | |
367 | |
368 // rpc_setcursor updates the client window's cursor image. | |
369 // Either c and c2 are both non-nil, or they are both nil to use the def… | |
370 // Called from an RPC thread with no client lock held. | |
371 static void | |
372 rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2) | |
373 { | |
374 DrawView *view = (__bridge DrawView*)client->view; | |
375 dispatch_sync(dispatch_get_main_queue(), ^(void){ | |
376 [view setcursor:c cursor2:c2]; | |
377 }); | |
378 } | |
379 | |
380 - (void)setcursor:(Cursor*)c cursor2:(Cursor2*)c2 { | |
381 if(!c) { | |
382 c = &bigarrow; | |
383 c2 = &bigarrow2; | |
384 } | |
385 | |
386 NSBitmapImageRep *r, *r2; | |
387 NSImage *i; | |
388 NSPoint p; | |
389 uchar *plane[5], *plane2[5]; | |
390 uint b; | |
391 | |
392 r = [[NSBitmapImageRep alloc] | |
393 initWithBitmapDataPlanes:nil | |
394 pixelsWide:16 | |
395 pixelsHigh:16 | |
396 bitsPerSample:1 | |
397 samplesPerPixel:2 | |
398 hasAlpha:YES | |
399 isPlanar:YES | |
400 colorSpaceName:NSDeviceWhiteColorSpace | |
401 bytesPerRow:2 | |
402 bitsPerPixel:0]; | |
403 [r getBitmapDataPlanes:plane]; | |
404 for(b=0; b<nelem(c->set); b++){ | |
405 plane[0][b] = ~c->set[b] & c->clr[b]; | |
406 plane[1][b] = c->set[b] | c->clr[b]; | |
407 } | |
408 | |
409 r2 = [[NSBitmapImageRep alloc] | |
410 initWithBitmapDataPlanes:nil | |
411 pixelsWide:32 | |
412 pixelsHigh:32 | |
413 bitsPerSample:1 | |
414 samplesPerPixel:2 | |
415 hasAlpha:YES | |
416 isPlanar:YES | |
417 colorSpaceName:NSDeviceWhiteColorSpace | |
418 bytesPerRow:4 | |
419 bitsPerPixel:0]; | |
420 [r2 getBitmapDataPlanes:plane2]; | |
421 for(b=0; b<nelem(c2->set); b++){ | |
422 plane2[0][b] = ~c2->set[b] & c2->clr[b]; | |
423 plane2[1][b] = c2->set[b] | c2->clr[b]; | |
424 } | |
425 | |
426 static BOOL debug = NO; | |
427 if(debug){ | |
428 NSData *data = [r representationUsingType: NSBitmapImage… | |
429 [data writeToFile: @"/tmp/r.bmp" atomically: NO]; | |
430 data = [r2 representationUsingType: NSBitmapImageFileTyp… | |
431 [data writeToFile: @"/tmp/r2.bmp" atomically: NO]; | |
432 debug = NO; | |
433 } | |
434 | |
435 i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; | |
436 [i addRepresentation:r2]; | |
437 [i addRepresentation:r]; | |
438 | |
439 p = NSMakePoint(-c->offset.x, -c->offset.y); | |
440 self.currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p… | |
441 [self.win invalidateCursorRectsForView:self]; | |
442 } | |
443 | |
444 - (void)initimg { | |
445 @autoreleasepool { | |
446 CGFloat scale; | |
447 NSSize size; | |
448 MTLTextureDescriptor *textureDesc; | |
449 | |
450 size = [self convertSizeToBacking:[self bounds].size]; | |
451 self.client->mouserect = Rect(0, 0, size.width, size.hei… | |
452 | |
453 LOG(@"initimg %.0f %.0f", size.width, size.height); | |
454 | |
455 self.img = allocmemimage(self.client->mouserect, XRGB32); | |
456 if(self.img == nil) | |
457 panic("allocmemimage: %r"); | |
458 if(self.img->data == nil) | |
459 panic("img->data == nil"); | |
460 | |
461 textureDesc = [MTLTextureDescriptor | |
462 texture2DDescriptorWithPixelFormat:MTLPixelForma… | |
463 width:size.width | |
464 height:size.height | |
465 mipmapped:NO]; | |
466 textureDesc.allowGPUOptimizedContents = YES; | |
467 textureDesc.usage = MTLTextureUsageShaderRead; | |
468 textureDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; | |
469 self.dlayer.texture = [self.dlayer.device newTextureWith… | |
470 | |
471 scale = [self.win backingScaleFactor]; | |
472 [self.dlayer setDrawableSize:size]; | |
473 [self.dlayer setContentsScale:scale]; | |
474 | |
475 // NOTE: This is not really the display DPI. | |
476 // On retina, scale is 2; otherwise it is 1. | |
477 // This formula gives us 220 for retina, 110 otherwise. | |
478 // That's not quite right but it's close to correct. | |
479 // https://en.wikipedia.org/wiki/Retina_display#Models | |
480 self.client->displaydpi = scale * 110; | |
481 } | |
482 } | |
483 | |
484 // rpc_flush flushes changes to view.img's rectangle r | |
485 // to the on-screen window, making them visible. | |
486 // Called from an RPC thread with no client lock held. | |
487 static void | |
488 rpc_flush(Client *client, Rectangle r) | |
489 { | |
490 DrawView *view = (__bridge DrawView*)client->view; | |
491 dispatch_async(dispatch_get_main_queue(), ^(void){ | |
492 [view flush:r]; | |
493 }); | |
494 } | |
495 | |
496 - (void)flush:(Rectangle)r { | |
497 @autoreleasepool{ | |
498 if(!rectclip(&r, Rect(0, 0, self.dlayer.texture.width, s… | |
499 return; | |
500 | |
501 // drawlk protects the pixel data in self.img. | |
502 // In addition to avoiding a technical data race, | |
503 // the lock avoids drawing partial updates, which makes | |
504 // animations like sweeping windows much less flickery. | |
505 qlock(&drawlk); | |
506 [self.dlayer.texture | |
507 replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, … | |
508 mipmapLevel:0 | |
509 withBytes:byteaddr(self.img, Pt(r.min.x, r.min.y… | |
510 bytesPerRow:self.img->width*sizeof(u32int)]; | |
511 qunlock(&drawlk); | |
512 | |
513 NSRect nr = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r)); | |
514 dispatch_time_t time; | |
515 | |
516 LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", nr.ori… | |
517 nr = [self.win convertRectFromBacking:nr]; | |
518 LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", nr.origin.… | |
519 [self.dlayer setNeedsDisplayInRect:nr]; | |
520 | |
521 time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MS… | |
522 dispatch_after(time, dispatch_get_main_queue(), ^(void){ | |
523 [self.dlayer setNeedsDisplayInRect:nr]; | |
524 }); | |
525 | |
526 [self enlargeLastInputRect:nr]; | |
527 } | |
528 } | |
529 | |
530 // rpc_resizeimg forces the client window to discard its current window … | |
531 // It is called when the user types Cmd-R to toggle whether retina mode … | |
532 // Called from an RPC thread with no client lock held. | |
533 static void | |
534 rpc_resizeimg(Client *c) | |
535 { | |
536 DrawView *view = (__bridge DrawView*)c->view; | |
537 dispatch_async(dispatch_get_main_queue(), ^(void){ | |
538 [view resizeimg]; | |
539 }); | |
540 } | |
541 | |
542 - (void)resizeimg { | |
543 [self initimg]; | |
544 gfx_replacescreenimage(self.client, self.img); | |
545 } | |
546 | |
547 - (void)windowDidResize:(NSNotification *)notification { | |
548 if(![self inLiveResize] && self.img) { | |
549 [self resizeimg]; | |
550 } | |
551 } | |
552 - (void)viewDidEndLiveResize | |
553 { | |
554 [super viewDidEndLiveResize]; | |
555 if(self.img) | |
556 [self resizeimg]; | |
557 } | |
558 | |
559 - (void)viewDidChangeBackingProperties | |
560 { | |
561 [super viewDidChangeBackingProperties]; | |
562 if(self.img) | |
563 [self resizeimg]; | |
564 } | |
565 | |
566 // rpc_resizewindow asks for the client window to be resized to size r. | |
567 // Called from an RPC thread with no client lock held. | |
568 static void | |
569 rpc_resizewindow(Client *c, Rectangle r) | |
570 { | |
571 DrawView *view = (__bridge DrawView*)c->view; | |
572 | |
573 LOG(@"resizewindow %d %d %d %d", r.min.x, r.min.y, Dx(r), Dy(r)); | |
574 dispatch_async(dispatch_get_main_queue(), ^(void){ | |
575 NSSize s; | |
576 | |
577 s = [view convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r)… | |
578 [view.win setContentSize:s]; | |
579 }); | |
580 } | |
581 | |
582 | |
583 - (void)windowDidBecomeKey:(id)arg { | |
584 [self sendmouse:0]; | |
585 } | |
586 | |
587 - (void)windowDidResignKey:(id)arg { | |
588 gfx_abortcompose(self.client); | |
589 } | |
590 | |
591 - (void)mouseMoved:(NSEvent*)e{ [self getmouse:e];} | |
592 - (void)mouseDown:(NSEvent*)e{ [self getmouse:e];} | |
593 - (void)mouseDragged:(NSEvent*)e{ [self getmouse:e];} | |
594 - (void)mouseUp:(NSEvent*)e{ [self getmouse:e];} | |
595 - (void)otherMouseDown:(NSEvent*)e{ [self getmouse:e];} | |
596 - (void)otherMouseDragged:(NSEvent*)e{ [self getmouse:e];} | |
597 - (void)otherMouseUp:(NSEvent*)e{ [self getmouse:e];} | |
598 - (void)rightMouseDown:(NSEvent*)e{ [self getmouse:e];} | |
599 - (void)rightMouseDragged:(NSEvent*)e{ [self getmouse:e];} | |
600 - (void)rightMouseUp:(NSEvent*)e{ [self getmouse:e];} | |
601 | |
602 - (void)scrollWheel:(NSEvent*)e | |
603 { | |
604 NSInteger s; | |
605 | |
606 s = [e scrollingDeltaY]; | |
607 if(s > 0) | |
608 [self sendmouse:8]; | |
609 else if (s < 0) | |
610 [self sendmouse:16]; | |
611 } | |
612 | |
613 - (void)keyDown:(NSEvent*)e | |
614 { | |
615 LOG(@"keyDown to interpret"); | |
616 | |
617 [self interpretKeyEvents:[NSArray arrayWithObject:e]]; | |
618 | |
619 [self resetLastInputRect]; | |
620 } | |
621 | |
622 - (void)flagsChanged:(NSEvent*)e | |
623 { | |
624 static NSEventModifierFlags omod; | |
625 NSEventModifierFlags m; | |
626 uint b; | |
627 | |
628 LOG(@"flagsChanged"); | |
629 m = [e modifierFlags]; | |
630 | |
631 b = [NSEvent pressedMouseButtons]; | |
632 b = (b&~6) | (b&4)>>1 | (b&2)<<1; | |
633 if(b){ | |
634 if(m & ~omod & NSEventModifierFlagControl) | |
635 b |= 1; | |
636 if(m & ~omod & NSEventModifierFlagOption) | |
637 b |= 2; | |
638 if(m & ~omod & NSEventModifierFlagCommand) | |
639 b |= 4; | |
640 [self sendmouse:b]; | |
641 }else if(m & ~omod & NSEventModifierFlagOption) | |
642 gfx_keystroke(self.client, Kalt); | |
643 | |
644 omod = m; | |
645 } | |
646 | |
647 - (void)magnifyWithEvent:(NSEvent*)e | |
648 { | |
649 if(fabs([e magnification]) > 0.02) | |
650 [[self window] toggleFullScreen:nil]; | |
651 } | |
652 | |
653 - (void)touchesBeganWithEvent:(NSEvent*)e | |
654 { | |
655 _tapping = YES; | |
656 _tapFingers = [e touchesMatchingPhase:NSTouchPhaseTouching inVie… | |
657 _tapTime = msec(); | |
658 } | |
659 - (void)touchesMovedWithEvent:(NSEvent*)e | |
660 { | |
661 _tapping = NO; | |
662 } | |
663 - (void)touchesEndedWithEvent:(NSEvent*)e | |
664 { | |
665 if(_tapping | |
666 && [e touchesMatchingPhase:NSTouchPhaseTouching inView:n… | |
667 && msec() - _tapTime < 250){ | |
668 switch(_tapFingers){ | |
669 case 3: | |
670 [self sendmouse:2]; | |
671 [self sendmouse:0]; | |
672 break; | |
673 case 4: | |
674 [self sendmouse:2]; | |
675 [self sendmouse:1]; | |
676 [self sendmouse:0]; | |
677 break; | |
678 } | |
679 _tapping = NO; | |
680 } | |
681 } | |
682 - (void)touchesCancelledWithEvent:(NSEvent*)e | |
683 { | |
684 _tapping = NO; | |
685 } | |
686 | |
687 - (void)getmouse:(NSEvent *)e | |
688 { | |
689 NSUInteger b; | |
690 NSEventModifierFlags m; | |
691 | |
692 b = [NSEvent pressedMouseButtons]; | |
693 b = b&~6 | (b&4)>>1 | (b&2)<<1; | |
694 b = mouseswap(b); | |
695 | |
696 if(b == 1){ | |
697 m = [e modifierFlags]; | |
698 if(m & NSEventModifierFlagOption){ | |
699 gfx_abortcompose(self.client); | |
700 b = 2; | |
701 }else | |
702 if(m & NSEventModifierFlagCommand) | |
703 b = 4; | |
704 } | |
705 [self sendmouse:b]; | |
706 } | |
707 | |
708 - (void)sendmouse:(NSUInteger)b | |
709 { | |
710 NSPoint p; | |
711 | |
712 p = [self.window convertPointToBacking: | |
713 [self.window mouseLocationOutsideOfEventStream]]; | |
714 p.y = Dy(self.client->mouserect) - p.y; | |
715 // LOG(@"(%g, %g) <- sendmouse(%d)", p.x, p.y, (uint)b); | |
716 gfx_mousetrack(self.client, p.x, p.y, b, msec()); | |
717 if(b && _lastInputRect.size.width && _lastInputRect.size.height) | |
718 [self resetLastInputRect]; | |
719 } | |
720 | |
721 // rpc_setmouse moves the mouse cursor. | |
722 // Called from an RPC thread with no client lock held. | |
723 static void | |
724 rpc_setmouse(Client *c, Point p) | |
725 { | |
726 DrawView *view = (__bridge DrawView*)c->view; | |
727 dispatch_async(dispatch_get_main_queue(), ^(void){ | |
728 [view setmouse:p]; | |
729 }); | |
730 } | |
731 | |
732 - (void)setmouse:(Point)p { | |
733 @autoreleasepool{ | |
734 NSPoint q; | |
735 | |
736 LOG(@"setmouse(%d,%d)", p.x, p.y); | |
737 q = [self.win convertPointFromBacking:NSMakePoint(p.x, p… | |
738 LOG(@"(%g, %g) <- fromBacking", q.x, q.y); | |
739 q = [self convertPoint:q toView:nil]; | |
740 LOG(@"(%g, %g) <- toWindow", q.x, q.y); | |
741 q = [self.win convertPointToScreen:q]; | |
742 LOG(@"(%g, %g) <- toScreen", q.x, q.y); | |
743 // Quartz has the origin of the "global display | |
744 // coordinate space" at the top left of the primary | |
745 // screen with y increasing downward, while Cocoa has | |
746 // the origin at the bottom left of the primary screen | |
747 // with y increasing upward. We flip the coordinate | |
748 // with a negative sign and shift upward by the height | |
749 // of the primary screen. | |
750 q.y = NSScreen.screens[0].frame.size.height - q.y; | |
751 LOG(@"(%g, %g) <- setmouse", q.x, q.y); | |
752 CGWarpMouseCursorPosition(NSPointToCGPoint(q)); | |
753 CGAssociateMouseAndMouseCursorPosition(true); | |
754 } | |
755 } | |
756 | |
757 | |
758 - (void)resetCursorRects { | |
759 [super resetCursorRects]; | |
760 [self addCursorRect:self.bounds cursor:self.currentCursor]; | |
761 } | |
762 | |
763 // conforms to protocol NSTextInputClient | |
764 - (BOOL)hasMarkedText { return _markedRange.location != NSNotFound; } | |
765 - (NSRange)markedRange { return _markedRange; } | |
766 - (NSRange)selectedRange { return _selectedRange; } | |
767 | |
768 - (void)setMarkedText:(id)string | |
769 selectedRange:(NSRange)sRange | |
770 replacementRange:(NSRange)rRange | |
771 { | |
772 NSString *str; | |
773 | |
774 LOG(@"setMarkedText: %@ (%ld, %ld) (%ld, %ld)", string, | |
775 sRange.location, sRange.length, | |
776 rRange.location, rRange.length); | |
777 | |
778 [self clearInput]; | |
779 | |
780 if([string isKindOfClass:[NSAttributedString class]]) | |
781 str = [string string]; | |
782 else | |
783 str = string; | |
784 | |
785 if(rRange.location == NSNotFound){ | |
786 if(_markedRange.location != NSNotFound){ | |
787 rRange = _markedRange; | |
788 }else{ | |
789 rRange = _selectedRange; | |
790 } | |
791 } | |
792 | |
793 if(str.length == 0){ | |
794 [_tmpText deleteCharactersInRange:rRange]; | |
795 [self unmarkText]; | |
796 }else{ | |
797 _markedRange = NSMakeRange(rRange.location, str.length); | |
798 [_tmpText replaceCharactersInRange:rRange withString:str… | |
799 } | |
800 _selectedRange.location = rRange.location + sRange.location; | |
801 _selectedRange.length = sRange.length; | |
802 | |
803 if(_tmpText.length){ | |
804 uint i; | |
805 LOG(@"text length %ld", _tmpText.length); | |
806 for(i = 0; i <= _tmpText.length; ++i){ | |
807 if(i == _markedRange.location) | |
808 gfx_keystroke(self.client, '['); | |
809 if(_selectedRange.length){ | |
810 if(i == _selectedRange.location) | |
811 gfx_keystroke(self.client, '{'); | |
812 if(i == NSMaxRange(_selectedRange)) | |
813 gfx_keystroke(self.client, '}'); | |
814 } | |
815 if(i == NSMaxRange(_markedRange)) | |
816 gfx_keystroke(self.client, ']'); | |
817 if(i < _tmpText.length) | |
818 gfx_keystroke(self.client, [_tmpText cha… | |
819 } | |
820 int l; | |
821 l = 1 + _tmpText.length - NSMaxRange(_selectedRange) | |
822 + (_selectedRange.length > 0); | |
823 LOG(@"move left %d", l); | |
824 for(i = 0; i < l; ++i) | |
825 gfx_keystroke(self.client, Kleft); | |
826 } | |
827 | |
828 LOG(@"text: \"%@\" (%ld,%ld) (%ld,%ld)", _tmpText, | |
829 _markedRange.location, _markedRange.length, | |
830 _selectedRange.location, _selectedRange.length); | |
831 } | |
832 | |
833 - (void)unmarkText { | |
834 //NSUInteger i; | |
835 NSUInteger len; | |
836 | |
837 LOG(@"unmarkText"); | |
838 len = [_tmpText length]; | |
839 //for(i = 0; i < len; ++i) | |
840 // gfx_keystroke(self.client, [_tmpText characterAtIndex:… | |
841 [_tmpText deleteCharactersInRange:NSMakeRange(0, len)]; | |
842 _markedRange = NSMakeRange(NSNotFound, 0); | |
843 _selectedRange = NSMakeRange(0, 0); | |
844 } | |
845 | |
846 - (NSArray<NSAttributedStringKey>*)validAttributesForMarkedText { | |
847 LOG(@"validAttributesForMarkedText"); | |
848 return @[]; | |
849 } | |
850 | |
851 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)r | |
852 actualRange:(NSRangePointer)actualRange | |
853 { | |
854 NSRange sr; | |
855 NSAttributedString *s; | |
856 | |
857 LOG(@"attributedSubstringForProposedRange: (%ld, %ld) (%ld, %ld)… | |
858 r.location, r.length, actualRange->location, actualRange… | |
859 sr = NSMakeRange(0, [_tmpText length]); | |
860 sr = NSIntersectionRange(sr, r); | |
861 if(actualRange) | |
862 *actualRange = sr; | |
863 LOG(@"use range: %ld, %ld", sr.location, sr.length); | |
864 s = nil; | |
865 if(sr.length) | |
866 s = [[NSAttributedString alloc] | |
867 initWithString:[_tmpText substringWithRange:sr]]; | |
868 LOG(@" return %@", s); | |
869 return s; | |
870 } | |
871 | |
872 - (void)insertText:(id)s replacementRange:(NSRange)r { | |
873 NSUInteger i; | |
874 NSUInteger len; | |
875 | |
876 LOG(@"insertText: %@ replacementRange: %ld, %ld", s, r.location,… | |
877 | |
878 [self clearInput]; | |
879 | |
880 len = [s length]; | |
881 for(i = 0; i < len; ++i) | |
882 gfx_keystroke(self.client, [s characterAtIndex:i]); | |
883 [_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length… | |
884 _markedRange = NSMakeRange(NSNotFound, 0); | |
885 _selectedRange = NSMakeRange(0, 0); | |
886 } | |
887 | |
888 - (NSUInteger)characterIndexForPoint:(NSPoint)point | |
889 { | |
890 LOG(@"characterIndexForPoint: %g, %g", point.x, point.y); | |
891 return 0; | |
892 } | |
893 | |
894 - (NSRect)firstRectForCharacterRange:(NSRange)r actualRange:(NSRangePoin… | |
895 LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)", | |
896 r.location, r.length, actualRange->location, actualRange… | |
897 if(actualRange) | |
898 *actualRange = r; | |
899 return [[self window] convertRectToScreen:_lastInputRect]; | |
900 } | |
901 | |
902 - (void)doCommandBySelector:(SEL)s { | |
903 NSEvent *e; | |
904 NSEventModifierFlags m; | |
905 uint c, k; | |
906 | |
907 LOG(@"doCommandBySelector (%@)", NSStringFromSelector(s)); | |
908 | |
909 e = [NSApp currentEvent]; | |
910 c = [[e characters] characterAtIndex:0]; | |
911 k = keycvt(c); | |
912 LOG(@"keyDown: character0: 0x%x -> 0x%x", c, k); | |
913 m = [e modifierFlags]; | |
914 | |
915 if(m & NSEventModifierFlagCommand){ | |
916 if((m & NSEventModifierFlagShift) && 'a' <= k && k <= 'z… | |
917 k += 'A' - 'a'; | |
918 if(' '<=k && k<='~') | |
919 k += Kcmd; | |
920 } | |
921 if(k>0) | |
922 gfx_keystroke(self.client, k); | |
923 } | |
924 | |
925 // Helper for managing input rect approximately | |
926 - (void)resetLastInputRect { | |
927 LOG(@"resetLastInputRect"); | |
928 _lastInputRect.origin.x = 0.0; | |
929 _lastInputRect.origin.y = 0.0; | |
930 _lastInputRect.size.width = 0.0; | |
931 _lastInputRect.size.height = 0.0; | |
932 } | |
933 | |
934 - (void)enlargeLastInputRect:(NSRect)r { | |
935 r.origin.y = [self bounds].size.height - r.origin.y - r.size.hei… | |
936 _lastInputRect = NSUnionRect(_lastInputRect, r); | |
937 LOG(@"update last input rect (%g, %g, %g, %g)", | |
938 _lastInputRect.origin.x, _lastInputRect.origin.y, | |
939 _lastInputRect.size.width, _lastInputRect.size.height); | |
940 } | |
941 | |
942 - (void)clearInput { | |
943 if(_tmpText.length){ | |
944 uint i; | |
945 int l; | |
946 l = 1 + _tmpText.length - NSMaxRange(_selectedRange) | |
947 + (_selectedRange.length > 0); | |
948 LOG(@"move right %d", l); | |
949 for(i = 0; i < l; ++i) | |
950 gfx_keystroke(self.client, Kright); | |
951 l = _tmpText.length+2+2*(_selectedRange.length > 0); | |
952 LOG(@"backspace %d", l); | |
953 for(uint i = 0; i < l; ++i) | |
954 gfx_keystroke(self.client, Kbs); | |
955 } | |
956 } | |
957 | |
958 - (NSApplicationPresentationOptions)window:(id)arg | |
959 willUseFullScreenPresentationOptions:(NSApplicationPrese… | |
960 // The default for full-screen is to auto-hide the dock and menu… | |
961 // but the menu bar in particular comes back when the cursor is … | |
962 // near the top of the screen, which makes acme's top tag line v… | |
963 // Disable the menu bar entirely. | |
964 // In theory this code disables the dock entirely too, but if yo… | |
965 // down far enough off the bottom of the screen the dock still u… | |
966 // That's OK. | |
967 NSApplicationPresentationOptions o; | |
968 o = proposedOptions; | |
969 o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPres… | |
970 o |= NSApplicationPresentationHideDock | NSApplicationPresentati… | |
971 return o; | |
972 } | |
973 | |
974 - (void)windowWillEnterFullScreen:(NSNotification*)notification { | |
975 // This is a heavier-weight way to make sure the menu bar and do… | |
976 // but this affects all screens even though the app is running o… | |
977 // on only one screen, so it's not great. The behavior from the | |
978 // willUseFullScreenPresentationOptions seems to be enough for n… | |
979 /* | |
980 [[NSApplication sharedApplication] | |
981 setPresentationOptions:NSApplicationPresentationHideMenu… | |
982 */ | |
983 } | |
984 | |
985 - (void)windowDidExitFullScreen:(NSNotification*)notification { | |
986 /* | |
987 [[NSApplication sharedApplication] | |
988 setPresentationOptions:NSApplicationPresentationDefault]; | |
989 */ | |
990 } | |
991 @end | |
992 | |
993 static uint | |
994 msec(void) | |
995 { | |
996 return nsec()/1000000; | |
997 } | |
998 | |
999 static uint | |
1000 keycvt(uint code) | |
1001 { | |
1002 switch(code){ | |
1003 case '\r': return '\n'; | |
1004 case 127: return '\b'; | |
1005 case NSUpArrowFunctionKey: return Kup; | |
1006 case NSDownArrowFunctionKey: return Kdown; | |
1007 case NSLeftArrowFunctionKey: return Kleft; | |
1008 case NSRightArrowFunctionKey: return Kright; | |
1009 case NSInsertFunctionKey: return Kins; | |
1010 case NSDeleteFunctionKey: return Kdel; | |
1011 case NSHomeFunctionKey: return Khome; | |
1012 case NSEndFunctionKey: return Kend; | |
1013 case NSPageUpFunctionKey: return Kpgup; | |
1014 case NSPageDownFunctionKey: return Kpgdown; | |
1015 case NSF1FunctionKey: return KF|1; | |
1016 case NSF2FunctionKey: return KF|2; | |
1017 case NSF3FunctionKey: return KF|3; | |
1018 case NSF4FunctionKey: return KF|4; | |
1019 case NSF5FunctionKey: return KF|5; | |
1020 case NSF6FunctionKey: return KF|6; | |
1021 case NSF7FunctionKey: return KF|7; | |
1022 case NSF8FunctionKey: return KF|8; | |
1023 case NSF9FunctionKey: return KF|9; | |
1024 case NSF10FunctionKey: return KF|10; | |
1025 case NSF11FunctionKey: return KF|11; | |
1026 case NSF12FunctionKey: return KF|12; | |
1027 case NSBeginFunctionKey: | |
1028 case NSPrintScreenFunctionKey: | |
1029 case NSScrollLockFunctionKey: | |
1030 case NSF13FunctionKey: | |
1031 case NSF14FunctionKey: | |
1032 case NSF15FunctionKey: | |
1033 case NSF16FunctionKey: | |
1034 case NSF17FunctionKey: | |
1035 case NSF18FunctionKey: | |
1036 case NSF19FunctionKey: | |
1037 case NSF20FunctionKey: | |
1038 case NSF21FunctionKey: | |
1039 case NSF22FunctionKey: | |
1040 case NSF23FunctionKey: | |
1041 case NSF24FunctionKey: | |
1042 case NSF25FunctionKey: | |
1043 case NSF26FunctionKey: | |
1044 case NSF27FunctionKey: | |
1045 case NSF28FunctionKey: | |
1046 case NSF29FunctionKey: | |
1047 case NSF30FunctionKey: | |
1048 case NSF31FunctionKey: | |
1049 case NSF32FunctionKey: | |
1050 case NSF33FunctionKey: | |
1051 case NSF34FunctionKey: | |
1052 case NSF35FunctionKey: | |
1053 case NSPauseFunctionKey: | |
1054 case NSSysReqFunctionKey: | |
1055 case NSBreakFunctionKey: | |
1056 case NSResetFunctionKey: | |
1057 case NSStopFunctionKey: | |
1058 case NSMenuFunctionKey: | |
1059 case NSUserFunctionKey: | |
1060 case NSSystemFunctionKey: | |
1061 case NSPrintFunctionKey: | |
1062 case NSClearLineFunctionKey: | |
1063 case NSClearDisplayFunctionKey: | |
1064 case NSInsertLineFunctionKey: | |
1065 case NSDeleteLineFunctionKey: | |
1066 case NSInsertCharFunctionKey: | |
1067 case NSDeleteCharFunctionKey: | |
1068 case NSPrevFunctionKey: | |
1069 case NSNextFunctionKey: | |
1070 case NSSelectFunctionKey: | |
1071 case NSExecuteFunctionKey: | |
1072 case NSUndoFunctionKey: | |
1073 case NSRedoFunctionKey: | |
1074 case NSFindFunctionKey: | |
1075 case NSHelpFunctionKey: | |
1076 case NSModeSwitchFunctionKey: return 0; | |
1077 default: return code; | |
1078 } | |
1079 } | |
1080 | |
1081 // rpc_getsnarf reads the current pasteboard as a plain text string. | |
1082 // Called from an RPC thread with no client lock held. | |
1083 char* | |
1084 rpc_getsnarf(void) | |
1085 { | |
1086 char __block *ret; | |
1087 | |
1088 ret = nil; | |
1089 dispatch_sync(dispatch_get_main_queue(), ^(void) { | |
1090 @autoreleasepool { | |
1091 NSPasteboard *pb = [NSPasteboard generalPasteboa… | |
1092 NSString *s = [pb stringForType:NSPasteboardType… | |
1093 if(s) | |
1094 ret = strdup((char*)[s UTF8String]); | |
1095 } | |
1096 }); | |
1097 return ret; | |
1098 } | |
1099 | |
1100 // rpc_putsnarf writes the given text to the pasteboard. | |
1101 // Called from an RPC thread with no client lock held. | |
1102 void | |
1103 rpc_putsnarf(char *s) | |
1104 { | |
1105 if(s == nil || strlen(s) >= SnarfSize) | |
1106 return; | |
1107 | |
1108 dispatch_sync(dispatch_get_main_queue(), ^(void) { | |
1109 @autoreleasepool{ | |
1110 NSArray *t = [NSArray arrayWithObject:NSPasteboa… | |
1111 NSPasteboard *pb = [NSPasteboard generalPasteboa… | |
1112 NSString *str = [[NSString alloc] initWithUTF8St… | |
1113 [pb declareTypes:t owner:nil]; | |
1114 [pb setString:str forType:NSPasteboardTypeString… | |
1115 } | |
1116 }); | |
1117 } | |
1118 | |
1119 // rpc_bouncemouse is for sending a mouse event | |
1120 // back to the X11 window manager rio(1). | |
1121 // Does not apply here. | |
1122 static void | |
1123 rpc_bouncemouse(Client *c, Mouse m) | |
1124 { | |
1125 } | |
1126 | |
1127 // We don't use the graphics thread state during memimagedraw, | |
1128 // so rpc_gfxdrawlock and rpc_gfxdrawunlock are no-ops. | |
1129 void | |
1130 rpc_gfxdrawlock(void) | |
1131 { | |
1132 } | |
1133 | |
1134 void | |
1135 rpc_gfxdrawunlock(void) | |
1136 { | |
1137 } | |
1138 | |
1139 static void | |
1140 setprocname(const char *s) | |
1141 { | |
1142 CFStringRef process_name; | |
1143 | |
1144 process_name = CFStringCreateWithBytes(nil, (uchar*)s, strlen(s), kCFS… | |
1145 | |
1146 // Adapted from Chrome's mac_util.mm. | |
1147 // http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm | |
1148 // | |
1149 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
1150 // | |
1151 // Redistribution and use in source and binary forms, with or without | |
1152 // modification, are permitted provided that the following conditions … | |
1153 // met: | |
1154 // | |
1155 // * Redistributions of source code must retain the above copyright | |
1156 // notice, this list of conditions and the following disclaimer. | |
1157 // * Redistributions in binary form must reproduce the above | |
1158 // copyright notice, this list of conditions and the following disclai… | |
1159 // in the documentation and/or other materials provided with the | |
1160 // distribution. | |
1161 // * Neither the name of Google Inc. nor the names of its | |
1162 // contributors may be used to endorse or promote products derived from | |
1163 // this software without specific prior written permission. | |
1164 // | |
1165 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
1166 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
1167 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS F… | |
1168 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
1169 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTA… | |
1170 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
1171 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF US… | |
1172 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON A… | |
1173 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
1174 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE U… | |
1175 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
1176 // Warning: here be dragons! This is SPI reverse-engineered from WebKi… | |
1177 // plugin host, and could break at any time (although realistically it… | |
1178 // likely to break in a new major release). | |
1179 // When 10.7 is available, check that this still works, and update this | |
1180 // comment for 10.8. | |
1181 | |
1182 // Private CFType used in these LaunchServices calls. | |
1183 typedef CFTypeRef PrivateLSASN; | |
1184 typedef PrivateLSASN (*LSGetCurrentApplicationASNType)(); | |
1185 typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLS… | |
1186 CFStringRef, | |
1187 CFStringRef, | |
1188 CFDictionaryRe… | |
1189 | |
1190 static LSGetCurrentApplicationASNType ls_get_current_application_asn_f… | |
1191 NULL; | |
1192 static LSSetApplicationInformationItemType | |
1193 ls_set_application_information_item_func = NULL; | |
1194 static CFStringRef ls_display_name_key = NULL; | |
1195 | |
1196 static bool did_symbol_lookup = false; | |
1197 if (!did_symbol_lookup) { | |
1198 did_symbol_lookup = true; | |
1199 CFBundleRef launch_services_bundle = | |
1200 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices"… | |
1201 if (!launch_services_bundle) { | |
1202 fprint(2, "Failed to look up LaunchServices bundle\n"); | |
1203 return; | |
1204 } | |
1205 | |
1206 ls_get_current_application_asn_func = | |
1207 (LSGetCurrentApplicationASNType)( | |
1208 CFBundleGetFunctionPointerForName( | |
1209 launch_services_bundle, CFSTR("_LSGetCurrentApplicationA… | |
1210 if (!ls_get_current_application_asn_func) | |
1211 fprint(2, "Could not find _LSGetCurrentApplicationASN\n"); | |
1212 | |
1213 ls_set_application_information_item_func = | |
1214 (LSSetApplicationInformationItemType)( | |
1215 CFBundleGetFunctionPointerForName( | |
1216 launch_services_bundle, | |
1217 CFSTR("_LSSetApplicationInformationItem"))); | |
1218 if (!ls_set_application_information_item_func) | |
1219 fprint(2, "Could not find _LSSetApplicationInformationItem\n"); | |
1220 | |
1221 CFStringRef* key_pointer = (CFStringRef*)( | |
1222 CFBundleGetDataPointerForName(launch_services_bundle, | |
1223 CFSTR("_kLSDisplayNameKey"))); | |
1224 ls_display_name_key = key_pointer ? *key_pointer : NULL; | |
1225 if (!ls_display_name_key) | |
1226 fprint(2, "Could not find _kLSDisplayNameKey\n"); | |
1227 | |
1228 // Internally, this call relies on the Mach ports that are started u… | |
1229 // Carbon Process Manager. In debug builds this usually happens due… | |
1230 // the logging layers are started up; but in release, it isn't start… | |
1231 // much of a defined order. So if the symbols had to be loaded, go … | |
1232 // and force a call to make sure the manager has been initialized an… | |
1233 // the ports are opened. | |
1234 ProcessSerialNumber psn; | |
1235 GetCurrentProcess(&psn); | |
1236 } | |
1237 if (!ls_get_current_application_asn_func || | |
1238 !ls_set_application_information_item_func || | |
1239 !ls_display_name_key) { | |
1240 return; | |
1241 } | |
1242 | |
1243 PrivateLSASN asn = ls_get_current_application_asn_func(); | |
1244 // Constant used by WebKit; what exactly it means is unknown. | |
1245 const int magic_session_constant = -2; | |
1246 OSErr err = | |
1247 ls_set_application_information_item_func(magic_session_constant, a… | |
1248 ls_display_name_key, | |
1249 process_name, | |
1250 NULL /* optional out para… | |
1251 if(err != noErr) | |
1252 fprint(2, "Call to set process name failed\n"); | |
1253 } |