3 #define Cursor OSXCursor
4 #include <Carbon/Carbon.h>
5 #import <Foundation/Foundation.h>
7 #include <IOKit/IOKitLib.h>
8 #include <IOKit/hidsystem/IOHIDShared.h>
24 #include "osx-screen.h"
25 #include "osx-keycodes.h"
27 #include "glendapng.h"
33 AUTOFRAMEWORK(MultitouchSupport)
37 #define panic sysfatal
39 extern Rectangle mouserect;
46 Rectangle fullscreenr;
48 Memimage *screenimage;
56 CGDataProviderRef provider;
61 CGContextRef windowctx;
67 int kalting; // last keystroke was Kalt
68 int touched; // last mouse event was touchCallback
69 int collapsed; // parked in dock
70 int flushing; // flushproc has started
71 NSMutableArray* devicelist;
75 These structs are required, in order to handle some parameters returned from the
89 Some reversed engineered informations from MultiTouchSupport.framework
93 int frame; //the current frame
94 double timestamp; //event timestamp
95 int identifier; //identifier guaranteed unique for life of touch per device
96 int state; //the current state (not sure what the values mean)
97 int unknown1; //no idea what this does
98 int unknown2; //no idea what this does either
99 mtReadout normalized; //the normalized position and vector of the touch (0,0 to 1,1)
100 float size; //the size of the touch (the area of your finger being tracked)
101 int unknown3; //no idea what this does
102 float angle; //the angle of the touch -|
103 float majorAxis; //the major axis of the touch -|-- an ellipsoid. you can track the angle of each finger!
104 float minorAxis; //the minor axis of the touch -|
105 mtReadout unknown4; //not sure what this is for
106 int unknown5[2]; //no clue
107 float unknown6; //no clue
110 //a reference pointer for the multitouch device
111 typedef void *MTDeviceRef;
113 //the prototype for the callback function
114 typedef int (*MTContactCallbackFunction)(int,Touch*,int,double,int);
116 //returns a pointer to the default device (the trackpad?)
117 MTDeviceRef MTDeviceCreateDefault(void);
119 //returns a CFMutableArrayRef array of all multitouch devices
120 CFMutableArrayRef MTDeviceCreateList(void);
122 //registers a device's frame callback to your callback function
123 void MTRegisterContactFrameCallback(MTDeviceRef, MTContactCallbackFunction);
124 void MTUnregisterContactFrameCallback(MTDeviceRef, MTContactCallbackFunction);
126 //start sending events
127 void MTDeviceStart(MTDeviceRef, int);
128 void MTDeviceStop(MTDeviceRef);
130 MTDeviceRef MTDeviceCreateFromService(io_service_t);
131 io_service_t MTDeviceGetService(MTDeviceRef);
136 float firstThreshTime;
140 #define kSizeSensitivity 1.25f
141 #define kTimeSensitivity 0.03f /* seconds */
142 #define kButtonLimit 0.6f /* percentage from base of pad */
148 for(i = 0; i < kNTracks; ++i)
149 if(tracks[i].id == id)
154 #define kMoveSensitivity 0.05f
157 moved(mtPoint a, mtPoint b)
159 if(fabs(a.x - b.x) > kMoveSensitivity)
161 if(fabs(a.y - b.y) > kMoveSensitivity)
167 classifyTouch(Touch *t)
172 p = t->normalized.position;
174 i = findTrack(t->identifier);
178 return 0; // No empty tracks.
179 tracks[i].id = t->identifier;
180 tracks[i].firstThreshTime = t->timestamp;
182 // we don't have a touch yet - we wait kTimeSensitivity before reporting it.
186 if(t->size == 0) { // lost touch
190 if(t->size < kSizeSensitivity) {
191 tracks[i].firstThreshTime = t->timestamp;
193 if((t->timestamp - tracks[i].firstThreshTime) < kTimeSensitivity) {
196 if(p.y > kButtonLimit && t->size > kSizeSensitivity) {
201 if(p.x > 0.35 && p.x < 0.65)
207 static ulong msec(void);
210 touchCallback(int device, Touch *data, int nFingers, double timestamp, int frame)
213 int buttons, delta, i;
218 p.x = osx.xy.x+osx.screenr.min.x;
219 p.y = osx.xy.y+osx.screenr.min.y;
220 if(!ptinrect(Pt(p.x, p.y), osx.screenr))
224 for(i = 0; i < nFingers; ++i)
225 buttons |= classifyTouch(data+i);
226 delta = buttons ^ obuttons;
229 e = CGEventCreateMouseEvent(NULL,
230 (buttons & 1) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp,
233 CGEventPost(kCGSessionEventTap, e);
237 e = CGEventCreateMouseEvent(NULL,
238 (buttons & 2) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp,
241 CGEventPost(kCGSessionEventTap, e);
245 e = CGEventCreateMouseEvent(NULL,
246 (buttons & 4) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp,
249 CGEventPost(kCGSessionEventTap, e);
258 extern int multitouch;
263 kWindowCloseBoxAttribute |
264 kWindowCollapseBoxAttribute |
265 kWindowResizableAttribute |
266 kWindowStandardHandlerAttribute |
267 kWindowFullZoomAttribute
272 P9PEventLabelUpdate = 1
275 static void screenproc(void*);
276 static void eresized(int);
277 static void fullscreen(int);
278 static void seticon(void);
279 static void activated(int);
281 static OSStatus quithandler(EventHandlerCallRef, EventRef, void*);
282 static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*);
283 static OSStatus cmdhandler(EventHandlerCallRef, EventRef, void*);
290 void screeninit(void);
291 void _flushmemscreen(Rectangle r);
295 RegisterMultitouch(void *ctx, io_iterator_t iter)
300 while((io = IOIteratorNext(iter)) != 0){
301 dev = MTDeviceCreateFromService(io);
303 MTRegisterContactFrameCallback(dev, touchCallback);
304 [osx.devicelist addObject:dev];
306 MTDeviceStart(dev, 0);
314 UnregisterMultitouch(void *ctx, io_iterator_t iter)
320 while((io = IOIteratorNext(iter)) != 0){
321 for(i = 0; i < [osx.devicelist count]; i++){
322 dev = [osx.devicelist objectAtIndex:i];
323 if(IOObjectIsEqualTo(MTDeviceGetService(dev), io)){
326 MTUnregisterContactFrameCallback(dev, touchCallback);
327 [osx.devicelist removeObjectAtIndex:i];
336 #endif /*MULTITOUCH*/
342 IONotificationPortRef port;
343 CFRunLoopSourceRef source;
352 osx.devicelist = [[NSMutableArray alloc] init];
354 for(i = 0; i < kNTracks; ++i)
357 port = IONotificationPortCreate(kIOMasterPortDefault);
359 fprint(2, "failed to get an IO notification port\n");
363 source = IONotificationPortGetRunLoopSource(port);
365 fprint(2, "failed to get loop source for port");
370 (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop()),
372 kCFRunLoopDefaultMode);
374 kr = IOServiceAddMatchingNotification(
375 port, kIOTerminatedNotification,
376 IOServiceMatching("AppleMultitouchDevice"),
377 &UnregisterMultitouch,
380 if(kr != KERN_SUCCESS){
381 fprint(2, "failed to add termination notification\n");
385 /* Arm the notification */
386 while((obj = IOIteratorNext(iter)) != 0)
387 IOObjectRelease(obj);
389 kr = IOServiceAddMatchingNotification(
390 port, kIOMatchedNotification,
391 IOServiceMatching("AppleMultitouchDevice"),
395 if(kr != KERN_SUCCESS){
396 fprint(2, "failed to add matching notification\n");
400 RegisterMultitouch(nil, iter);
405 attachscreen(char *label, char *winsize)
408 label = "gnot a label";
409 osx.label = strdup(label);
410 osx.winsize = winsize;
411 if(osx.screenimage == nil){
413 if(osx.screenimage == nil)
414 panic("cannot create OS X screen");
416 return osx.screenimage;
419 extern int multitouch;
431 ProcessSerialNumber psn = { 0, kCurrentProcess };
432 TransformProcessType(&psn, kProcessTransformToForegroundApplication);
433 SetFrontProcess(&psn);
435 cgr = CGDisplayBounds(CGMainDisplayID());
436 osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height);
440 // Create minimal menu with full-screen option.
442 CreateStandardWindowMenu(0, &osx.wmenu);
443 InsertMenu(osx.wmenu, 0);
445 CreateNewMenu(1004, 0, &osx.vmenu); // XXX 1004?
446 SetMenuTitleWithCFString(osx.vmenu, CFSTR("View"));
447 AppendMenuItemTextWithCFString(osx.vmenu,
448 CFSTR("Full Screen"), 0, CmdFullScreen, &ix);
449 SetMenuItemCommandKey(osx.vmenu, ix, 0, 'F');
450 AppendMenuItemTextWithCFString(osx.vmenu,
451 CFSTR("Cmd-F exits full screen"),
452 kMenuItemAttrDisabled, CmdFullScreen, &ix);
453 InsertMenu(osx.vmenu, GetMenuID(osx.wmenu));
456 // Create the window.
457 r = Rect(0, 0, Dx(osx.fullscreenr)*2/3, Dy(osx.fullscreenr)*2/3);
459 if(osx.winsize && osx.winsize[0]){
460 if(parsewinsize(osx.winsize, &r, &havemin) < 0)
464 r = rectaddpt(r, Pt((Dx(osx.fullscreenr)-Dx(r))/2, (Dy(osx.fullscreenr)-Dy(r))/2));
469 CreateNewWindow(kDocumentWindowClass, WindowAttrs, &or, &osx.window);
473 // Set up the clip board.
474 if(PasteboardCreate(kPasteboardClipboard, &osx.snarf) != noErr)
475 panic("pasteboard create");
477 // Explain in great detail which events we want to handle.
478 // Why can't we just have one handler?
479 const EventTypeSpec quits[] = {
480 { kEventClassApplication, kEventAppQuit }
482 const EventTypeSpec cmds[] = {
483 { kEventClassWindow, kEventWindowClosed },
484 { kEventClassWindow, kEventWindowBoundsChanged },
485 { kEventClassWindow, kEventWindowDrawContent },
486 { kEventClassCommand, kEventCommandProcess },
487 { kEventClassWindow, kEventWindowActivated },
488 { kEventClassWindow, kEventWindowDeactivated },
489 { kEventClassWindow, kEventWindowCollapsed },
490 { kEventClassWindow, kEventWindowExpanded },
492 const EventTypeSpec events[] = {
493 { kEventClassApplication, kEventAppShown },
494 { kEventClassKeyboard, kEventRawKeyDown },
495 { kEventClassKeyboard, kEventRawKeyModifiersChanged },
496 { kEventClassKeyboard, kEventRawKeyRepeat },
497 { kEventClassMouse, kEventMouseDown },
498 { kEventClassMouse, kEventMouseUp },
499 { kEventClassMouse, kEventMouseMoved },
500 { kEventClassMouse, kEventMouseDragged },
501 { kEventClassMouse, kEventMouseWheelMoved },
502 { 'P9PE', P9PEventLabelUpdate}
505 InstallApplicationEventHandler(
506 NewEventHandlerUPP(quithandler),
507 nelem(quits), quits, nil, nil);
509 InstallApplicationEventHandler(
510 NewEventHandlerUPP(eventhandler),
511 nelem(events), events, nil, nil);
513 InstallWindowEventHandler(osx.window,
514 NewEventHandlerUPP(cmdhandler),
515 nelem(cmds), cmds, osx.window, nil);
517 // Finally, put the window on the screen.
518 ShowWindow(osx.window);
521 SelectWindow(osx.window);
526 // CoreGraphics pins mouse events to the destination point of a
527 // CGWarpMouseCursorPosition (see setmouse) for an interval of time
528 // following the move. Disable this by setting the interval to zero
530 CGSetLocalEventsSuppressionInterval(0.0);
543 proccreate(screenproc, nil, 256*1024);
544 while(osx.window == nil)
556 RunApplicationEventLoop();
559 static OSStatus kbdevent(EventRef);
560 static OSStatus mouseevent(EventRef);
563 cmdhandler(EventHandlerCallRef next, EventRef event, void *arg)
565 return eventhandler(next, event, arg);
569 quithandler(EventHandlerCallRef next, EventRef event, void *arg)
576 eventhandler(EventHandlerCallRef next, EventRef event, void *arg)
580 result = CallNextEventHandler(next, event);
582 switch(GetEventClass(event)){
585 if(GetEventKind(event) == P9PEventLabelUpdate) {
586 qlock(&osx.labellock);
588 qunlock(&osx.labellock);
591 return eventNotHandledErr;
593 case kEventClassApplication:;
594 Rectangle r = Rect(0, 0, Dx(osx.screenr), Dy(osx.screenr));
596 return eventNotHandledErr;
598 case kEventClassKeyboard:
599 return kbdevent(event);
601 case kEventClassMouse:
602 return mouseevent(event);
604 case kEventClassCommand:;
606 GetEventParameter(event, kEventParamDirectObject,
607 typeHICommand, nil, sizeof cmd, nil, &cmd);
608 switch(cmd.commandID){
617 return eventNotHandledErr;
621 case kEventClassWindow:
622 switch(GetEventKind(event)){
623 case kEventWindowClosed:
626 case kEventWindowBoundsChanged:;
627 // We see kEventWindowDrawContent
628 // if we grow a window but not if we shrink it.
630 GetEventParameter(event, kEventParamAttributes,
631 typeUInt32, 0, sizeof flags, 0, &flags);
632 int new = (flags & kWindowBoundsChangeSizeChanged) != 0;
636 case kEventWindowDrawContent:
637 // Tried using just flushmemimage here, but
638 // it causes an odd artifact in which making a window
639 // bigger in both width and height can then only draw
640 // on the new border: it's like the old window is stuck
641 // floating on top. Doing a full "get a new window"
642 // seems to solve the problem.
646 case kEventWindowActivated:
649 return eventNotHandledErr;
651 case kEventWindowDeactivated:
653 return eventNotHandledErr;
655 case kEventWindowCollapsed:
658 return eventNotHandledErr;
660 case kEventWindowExpanded:
663 return eventNotHandledErr;
666 return eventNotHandledErr;
677 return nsec()/1000000;
681 mouseevent(EventRef event)
686 GetEventParameter(event, kEventParamMouseLocation,
687 typeQDPoint, 0, sizeof op, 0, &op);
689 osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min);
692 switch(GetEventKind(event)){
693 case kEventMouseWheelMoved:;
695 GetEventParameter(event, kEventParamMouseWheelDelta,
696 typeSInt32, 0, sizeof delta, 0, &delta);
698 // if I have any active touches in my region, I need to ignore the wheel motion.
700 //for(i = 0; i < kNTracks; ++i) {
701 // if(tracks[i].id != -1 && tracks[i].pos.y > kButtonLimit) break;
703 //if(i == kNTracks) { // No active touches, go ahead and scroll.
711 case kEventMouseDown:
714 GetEventParameter(event, kEventParamMouseChord,
715 typeUInt32, 0, sizeof but, 0, &but);
716 GetEventParameter(event, kEventParamKeyModifiers,
717 typeUInt32, 0, sizeof mod, 0, &mod);
719 // OS X swaps button 2 and 3
720 but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
721 but = (but & ~((1<<10)-1)) | mouseswap(but & ((1<<10)-1));
723 // in multitouch we use the clicks down to enable our
733 // Apply keyboard modifiers and pretend it was a real mouse button.
734 // (Modifiers typed while holding the button go into kbuttons,
735 // but this one does not.)
737 if(mod & optionKey) {
738 // Take the ALT away from the keyboard handler.
745 else if(mod & cmdKey)
751 case kEventMouseMoved:
752 case kEventMouseDragged:
756 return eventNotHandledErr;
759 mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec());
763 static int keycvt[] =
765 [QZ_IBOOK_ENTER] '\n',
792 [QZ_PAGEDOWN] Kpgdown,
797 [QZ_KP_MULTIPLY] '*',
814 kbdevent(EventRef event)
821 GetEventParameter(event, kEventParamKeyMacCharCodes,
822 typeChar, nil, sizeof ch, nil, &ch);
823 GetEventParameter(event, kEventParamKeyCode,
824 typeUInt32, nil, sizeof code, nil, &code);
825 GetEventParameter(event, kEventParamKeyModifiers,
826 typeUInt32, nil, sizeof mod, nil, &mod);
828 switch(GetEventKind(event)){
829 case kEventRawKeyDown:
830 case kEventRawKeyRepeat:
833 if(ch == 'F' || ch == 'f'){
834 if(osx.isfullscreen && msec() - osx.fullscreentime > 500)
839 // Pass most Cmd keys through as Kcmd + ch.
840 // OS X interprets a few no matter what we do,
841 // so it is useless to pass them through as keystrokes too.
843 case 'm': // minimize window
844 case 'h': // hide window
845 case 'H': // hide others
847 return eventNotHandledErr;
849 if(' ' <= ch && ch <= '~') {
850 keystroke(Kcmd + ch);
853 return eventNotHandledErr;
856 if(code < nelem(keycvt) && keycvt[code])
866 s = GetEventParameter(event, kEventParamKeyUnicodes,
867 typeUnicodeText, nil, sizeof uc, nil, &uc);
873 case kEventRawKeyModifiersChanged:
874 if(!osx.buttons && !osx.kbuttons){
875 if(mod == optionKey) {
882 // If the mouse button is being held down, treat
883 // changes in the keyboard modifiers as changes
884 // in the mouse buttons.
890 mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
904 CGDataProviderRef provider;
906 CGColorSpaceRef cspace;
908 GetWindowBounds(osx.window, kWindowContentRgn, &or);
909 r = Rect(or.left, or.top, or.right, or.bottom);
910 if(Dx(r) == Dx(osx.screenr) && Dy(r) == Dy(osx.screenr) && !new){
911 // No need to make new image.
917 m = allocmemimage(Rect(0, 0, Dx(r), Dy(r)), chan);
919 panic("allocmemimage: %r");
921 panic("m->data == nil");
922 bpl = bytesperline(r, 32);
923 provider = CGDataProviderCreateWithData(0,
924 m->data->bdata, Dy(r)*bpl, 0);
925 //cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
926 cspace = CGColorSpaceCreateDeviceRGB();
927 image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl,
929 kCGImageAlphaNoneSkipLast,
930 provider, 0, 0, kCGRenderingIntentDefault);
931 CGColorSpaceRelease(cspace);
932 CGDataProviderRelease(provider); // CGImageCreate did incref
937 mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
939 // termreplacescreenimage(m);
940 _drawreplacescreenimage(m); // frees old osx.screenimage if any
942 CGImageRelease(osx.image);
948 qlock(&osx.flushlock);
949 QDEndCGContext(GetWindowPort(osx.window), &osx.windowctx);
951 qunlock(&osx.flushlock);
959 if(osx.needflush && osx.windowctx && canqlock(&osx.flushlock)){
961 CGContextFlush(osx.windowctx);
964 qunlock(&osx.flushlock);
971 _flushmemscreen(Rectangle r)
976 qlock(&osx.flushlock);
977 if(osx.windowctx == nil){
978 QDBeginCGContext(GetWindowPort(osx.window), &osx.windowctx);
980 proccreate(flushproc, nil, 256*1024);
985 cgr.origin.x = r.min.x;
986 cgr.origin.y = r.min.y;
987 cgr.size.width = Dx(r);
988 cgr.size.height = Dy(r);
989 subimg = CGImageCreateWithImageInRect(osx.image, cgr);
990 cgr.origin.y = Dy(osx.screenr) - r.max.y; // XXX how does this make any sense?
991 CGContextDrawImage(osx.windowctx, cgr, subimg);
993 qunlock(&osx.flushlock);
994 CGImageRelease(subimg);
998 activated(int active)
1003 for(i = 0; i<[osx.devicelist count]; i++) { //iterate available devices
1004 MTDeviceStart([osx.devicelist objectAtIndex:i], 0); //start sending events
1008 for(i = 0; i<[osx.devicelist count]; i++) { //iterate available devices
1009 MTDeviceStop([osx.devicelist objectAtIndex:i]); //stop sending events
1011 for(i = 0; i<kNTracks; ++i) {
1016 osx.active = active;
1020 fullscreen(int wascmd)
1022 static OSXRect oldrect;
1029 if(!osx.isfullscreen){
1030 GetWindowGreatestAreaDevice(osx.window,
1031 kWindowTitleBarRgn, &device, nil);
1032 dr = (*device)->gdRect;
1033 if(dr.top == 0 && dr.left == 0)
1035 GetWindowBounds(osx.window, kWindowContentRgn, &oldrect);
1036 ChangeWindowAttributes(osx.window,
1037 kWindowNoTitleBarAttribute,
1038 kWindowResizableAttribute);
1039 MoveWindow(osx.window, 0, 0, 1);
1040 MoveWindow(osx.window, dr.left, dr.top, 0);
1041 SizeWindow(osx.window,
1043 dr.bottom - dr.top, 0);
1044 osx.isfullscreen = 1;
1047 ChangeWindowAttributes(osx.window,
1048 kWindowResizableAttribute,
1049 kWindowNoTitleBarAttribute);
1050 SizeWindow(osx.window,
1051 oldrect.right - oldrect.left,
1052 oldrect.bottom - oldrect.top, 0);
1053 MoveWindow(osx.window, oldrect.left, oldrect.top, 0);
1054 osx.isfullscreen = 0;
1064 cgp.x = p.x + osx.screenr.min.x;
1065 cgp.y = p.y + osx.screenr.min.y;
1066 CGWarpMouseCursorPosition(cgp);
1071 setcursor(Cursor *c)
1081 // SetCursor is deprecated, but what replaces it?
1082 for(i=0; i<16; i++){
1083 oc.data[i] = ((ushort*)c->set)[i];
1084 oc.mask[i] = oc.data[i] | ((ushort*)c->clr)[i];
1086 oc.hotSpot.h = - c->offset.x;
1087 oc.hotSpot.v = - c->offset.y;
1092 getcolor(ulong i, ulong *r, ulong *g, ulong *b)
1103 setcolor(ulong i, ulong r, ulong g, ulong b)
1111 hwdraw(Memdrawparam *p)
1118 char buf[SnarfSize];
1119 Rune rbuf[SnarfSize];
1120 PasteboardRef apple;
1129 CFIndex nflavor, ndata, j;
1132 PasteboardItemID id;
1133 PasteboardSyncFlags flags;
1139 /* fprint(2, "applegetsnarf\n"); */
1141 clip.apple = osx.snarf;
1142 if(clip.apple == nil){
1143 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
1144 fprint(2, "apple pasteboard create failed\n");
1149 flags = PasteboardSynchronize(clip.apple);
1150 if(flags&kPasteboardClientIsOwner){
1151 s = strdup(clip.buf);
1155 if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){
1156 fprint(2, "apple pasteboard get item count failed\n");
1160 for(i=1; i<=nitem; i++){
1161 if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr)
1163 if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr)
1165 nflavor = CFArrayGetCount(flavors);
1166 for(j=0; j<nflavor; j++){
1167 type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j);
1168 if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text")))
1170 if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr)
1173 ndata = CFDataGetLength(data)/2;
1174 u = (u16int*)CFDataGetBytePtr(data);
1176 // decode utf-16. what was apple thinking?
1177 for(i=0; i<ndata; i++) {
1179 if(0xd800 <= r && r < 0xdc00 && i+1 < ndata && 0xdc00 <= u[i+1] && u[i+1] < 0xe000) {
1180 r = (((r - 0xd800)<<10) | (u[i+1] - 0xdc00)) + 0x10000;
1183 else if(0xd800 <= r && r < 0xe000)
1191 return fmtstrflush(&fmt);
1203 PasteboardSyncFlags flags;
1208 /* fprint(2, "appleputsnarf\n"); */
1210 if(strlen(s) >= SnarfSize)
1213 strcpy(clip.buf, s);
1214 runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
1215 clip.apple = osx.snarf;
1216 if(PasteboardClear(clip.apple) != noErr){
1217 fprint(2, "apple pasteboard clear failed\n");
1221 flags = PasteboardSynchronize(clip.apple);
1222 if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
1223 fprint(2, "apple pasteboard cannot assert ownership\n");
1227 u = malloc(runestrlen(clip.rbuf)*4);
1229 for(i=0; clip.rbuf[i]; i++) {
1231 // convert to utf-16
1232 if(0xd800 <= r && r < 0xe000)
1236 *p++ = 0xd800 + (r>>10);
1237 *p++ = 0xdc00 + (r & ((1<<10)-1));
1241 cfdata = CFDataCreate(kCFAllocatorDefault,
1242 (uchar*)u, (p-u)*2);
1245 fprint(2, "apple pasteboard cfdatacreate failed\n");
1249 if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1,
1250 CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
1251 fprint(2, "apple pasteboard putitem failed\n");
1261 setlabel(char *label)
1265 cs = CFStringCreateWithBytes(nil, (uchar*)label, strlen(label), kCFStringEncodingUTF8, false);
1266 SetWindowTitleWithCFString(osx.window, cs);
1271 kicklabel(char *label)
1279 qlock(&osx.labellock);
1282 qunlock(&osx.labellock);
1284 CreateEvent(nil, 'P9PE', P9PEventLabelUpdate, 0, kEventAttributeUserEvent, &e);
1285 PostEventToQueue(GetMainEventQueue(), e, kEventPriorityStandard);
1293 CGDataProviderRef d;
1295 d = CGDataProviderCreateWithData(nil, glenda_png, sizeof glenda_png, nil);
1296 im = CGImageCreateWithPNGDataProvider(d, nil, true, kCGRenderingIntentDefault);
1298 SetApplicationDockTileImage(im);
1300 CGDataProviderRelease(d);