screen.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       screen.c (15169B)
       ---
            1 #define Point OSXPoint
            2 #define Rect OSXRect
            3 #define Cursor OSXCursor
            4 #include <Carbon/Carbon.h>
            5 #include <QuickTime/QuickTime.h> // for full screen
            6 #undef Rect
            7 #undef Point
            8 #undef Cursor
            9 #undef offsetof
           10 #undef nil
           11 
           12 #include "u.h"
           13 #include "lib.h"
           14 #include "mem.h"
           15 #include "dat.h"
           16 #include "fns.h"
           17 #include "error.h"
           18 #define Image IMAGE        /* kernel has its own Image */
           19 #include <draw.h>
           20 #include <memdraw.h>
           21 #include <keyboard.h>
           22 #include <cursor.h>
           23 #include "screen.h"
           24 #include "mouse.h"
           25 #include "keycodes.h"
           26 #include "nineball.h"
           27 
           28 struct {
           29         Rectangle fullscreenr;
           30         Rectangle screenr;
           31         Memimage *screenimage;
           32         int isfullscreen;
           33         ulong fullscreentime;
           34         
           35         Point xy;
           36         int buttons;
           37         int kbuttons;
           38 
           39         CGDataProviderRef provider;
           40         MenuRef wmenu;
           41         MenuRef vmenu;
           42         WindowRef window;
           43         CGImageRef image;
           44         PasteboardRef snarf;
           45 } osx;
           46 
           47 enum
           48 {
           49         WindowAttrs =
           50                 kWindowCloseBoxAttribute |
           51                 kWindowCollapseBoxAttribute |
           52                 kWindowResizableAttribute |
           53                 kWindowStandardHandlerAttribute |
           54                 kWindowFullZoomAttribute
           55 };
           56 
           57 static void screenproc(void*);
           58 static void eresized(int force);
           59 static void fullscreen(void);
           60 static void seticon(void);
           61 
           62 static OSStatus quithandler(EventHandlerCallRef, EventRef, void*);
           63 static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*);
           64 static OSStatus cmdhandler(EventHandlerCallRef, EventRef, void*);
           65 
           66 enum
           67 {
           68         CmdFullScreen = 1,
           69 };
           70 
           71 uchar*
           72 attachscreen(Rectangle *r, ulong *chan, int *depth,
           73         int *width, int *softscreen, void **X)
           74 {
           75         Memimage *m;
           76 
           77         if(osx.screenimage == nil){
           78                 screeninit();
           79                 if(osx.screenimage == nil)
           80                         panic("cannot create OS X screen");
           81         }
           82         m = osx.screenimage;
           83         *r = m->r;
           84         *chan = m->chan;
           85         *depth = m->depth;
           86         *width = m->width;
           87         *X = nil;
           88         *softscreen = 1;
           89         return m->data->bdata;
           90 }
           91 
           92 void
           93 _screeninit(void)
           94 {
           95         CGRect cgr;
           96         OSXRect or;
           97 
           98         _memimageinit();
           99 
          100         ProcessSerialNumber psn = { 0, kCurrentProcess };
          101         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
          102         SetFrontProcess(&psn);
          103 
          104         cgr = CGDisplayBounds(CGMainDisplayID());
          105         osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height);
          106         
          107         InitCursor();
          108         
          109         // Create minimal menu with full-screen option.
          110         ClearMenuBar();
          111         CreateStandardWindowMenu(0, &osx.wmenu);
          112         InsertMenu(osx.wmenu, 0);
          113         MenuItemIndex ix;
          114         CreateNewMenu(1004, 0, &osx.vmenu);        // XXX 1004?
          115         SetMenuTitleWithCFString(osx.vmenu, CFSTR("View"));
          116         AppendMenuItemTextWithCFString(osx.vmenu,
          117                 CFSTR("Full Screen"), 0, CmdFullScreen, &ix);
          118         SetMenuItemCommandKey(osx.vmenu, ix, 0, 'F');
          119         InsertMenu(osx.vmenu, GetMenuID(osx.wmenu));
          120         DrawMenuBar();
          121 
          122         // Create the window.
          123         or.left = 0;
          124         or.top = 50;
          125         or.bottom = Dy(osx.fullscreenr) - 200;
          126         or.right = Dx(osx.fullscreenr);
          127         CreateNewWindow(kDocumentWindowClass, WindowAttrs, &or, &osx.window);
          128         SetWindowTitleWithCFString(osx.window, CFSTR("Plan 9 VX"));
          129         seticon();
          130 
          131         // Set up the clip board.
          132         if(PasteboardCreate(kPasteboardClipboard, &osx.snarf) != noErr)
          133                 panic("pasteboard create");
          134 
          135         // Explain in great detail which events we want to handle.
          136         // Why can't we just have one handler?
          137         const EventTypeSpec quits[] = {
          138                 { kEventClassApplication, kEventAppQuit }
          139         };
          140         const EventTypeSpec cmds[] = {
          141                 { kEventClassWindow, kEventWindowClosed },
          142                 { kEventClassWindow, kEventWindowBoundsChanged },
          143                 { kEventClassCommand, kEventCommandProcess }
          144         };
          145         const EventTypeSpec events[] = {
          146                 { kEventClassKeyboard, kEventRawKeyDown },
          147                 { kEventClassKeyboard, kEventRawKeyModifiersChanged },
          148                 { kEventClassKeyboard, kEventRawKeyRepeat },
          149                 { kEventClassMouse, kEventMouseDown },
          150                 { kEventClassMouse, kEventMouseUp },
          151                 { kEventClassMouse, kEventMouseMoved },
          152                 { kEventClassMouse, kEventMouseDragged },
          153                 { kEventClassMouse, kEventMouseWheelMoved },
          154         };
          155 
          156         InstallApplicationEventHandler(
          157                 NewEventHandlerUPP(quithandler),
          158                 nelem(quits), quits, nil, nil);
          159 
          160          InstallApplicationEventHandler(
          161                  NewEventHandlerUPP(eventhandler),
          162                 nelem(events), events, nil, nil);
          163 
          164         InstallWindowEventHandler(osx.window,
          165                 NewEventHandlerUPP(cmdhandler),
          166                 nelem(cmds), cmds, osx.window, nil);
          167 
          168         // Finally, put the window on the screen.
          169         ShowWindow(osx.window);
          170         ShowMenuBar();
          171         eresized(1);
          172         SelectWindow(osx.window);
          173         
          174         InitCursor();
          175 }
          176 
          177 static Psleep scr;
          178 
          179 void
          180 screeninit(void)
          181 {
          182         plock(&scr);
          183         kproc("*screen*", screenproc, nil);
          184         while(osx.window == nil)
          185                 psleep(&scr);
          186         punlock(&scr);
          187 }
          188 
          189 static void
          190 screenproc(void *v)
          191 {
          192         plock(&scr);
          193         _screeninit();
          194         pwakeup(&scr);
          195         punlock(&scr);
          196         RunApplicationEventLoop();
          197         iprint("screenproc exited!\n");
          198 }
          199 
          200 static OSStatus kbdevent(EventRef);
          201 static OSStatus mouseevent(EventRef);
          202 
          203 static OSStatus
          204 cmdhandler(EventHandlerCallRef next, EventRef event, void *arg)
          205 {
          206         return eventhandler(next, event, arg);
          207 }
          208 
          209 static OSStatus
          210 quithandler(EventHandlerCallRef next, EventRef event, void *arg)
          211 {
          212         restoretty();        // XXX: should we?
          213         exit(0);
          214         return 0;
          215 }
          216 
          217 static OSStatus
          218 eventhandler(EventHandlerCallRef next, EventRef event, void *arg)
          219 {
          220         OSStatus result;
          221 
          222         result = CallNextEventHandler(next, event);
          223 
          224         switch(GetEventClass(event)){
          225         case kEventClassKeyboard:
          226                 return kbdevent(event);
          227         
          228         case kEventClassMouse:
          229                 return mouseevent(event);
          230         
          231         case kEventClassCommand:;
          232                 HICommand cmd;
          233                 GetEventParameter(event, kEventParamDirectObject,
          234                         typeHICommand, nil, sizeof cmd, nil, &cmd);
          235                 switch(cmd.commandID){
          236                 case kHICommandQuit:
          237                         restoretty();        // XXX: should we?
          238                         exit(0);
          239                 
          240                 case CmdFullScreen:
          241                         fullscreen();
          242                         break;
          243                 
          244                 default:
          245                         return eventNotHandledErr;
          246                 }
          247                 break;
          248         
          249         case kEventClassWindow:;
          250                 switch(GetEventKind(event)){
          251                 case kEventWindowClosed:
          252                         restoretty();        // XXX: should we?
          253                         exit(0);
          254                 
          255                 case kEventWindowBoundsChanged:
          256                         eresized(0);
          257                         break;
          258                 
          259                 default:
          260                         return eventNotHandledErr;
          261                 }
          262                 break;
          263         }
          264         
          265         return result;
          266 }
          267 
          268 static OSStatus
          269 mouseevent(EventRef event)
          270 {
          271         int wheel;
          272         OSXPoint op;
          273         
          274         GetEventParameter(event, kEventParamMouseLocation,
          275                 typeQDPoint, 0, sizeof op, 0, &op);
          276 
          277         osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min);
          278         wheel = 0;
          279 
          280         switch(GetEventKind(event)){
          281         case kEventMouseWheelMoved:;
          282                 SInt32 delta;
          283                 GetEventParameter(event, kEventParamMouseWheelDelta,
          284                         typeSInt32, 0, sizeof delta, 0, &delta);
          285                 if(delta > 0)
          286                         wheel = 8;
          287                 else
          288                         wheel = 16;
          289                 break;
          290         
          291         case kEventMouseDown:
          292         case kEventMouseUp:;
          293                 UInt32 but, mod;
          294                 GetEventParameter(event, kEventParamMouseChord,
          295                         typeUInt32, 0, sizeof but, 0, &but);
          296                 GetEventParameter(event, kEventParamKeyModifiers,
          297                         typeUInt32, 0, sizeof mod, 0, &mod);
          298                 
          299                 // OS X swaps button 2 and 3
          300                 but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
          301                 
          302                 // Apply keyboard modifiers and pretend it was a real mouse button.
          303                 // (Modifiers typed while holding the button go into kbuttons,
          304                 // but this one does not.)
          305                 if(but == 1){
          306                         if(mod & optionKey)
          307                                 but = 2;
          308                         else if(mod & cmdKey)
          309                                 but = 4;
          310                 }
          311                 osx.buttons = but;
          312                 break;
          313 
          314         case kEventMouseMoved:
          315         case kEventMouseDragged:
          316                 break;
          317         
          318         default:
          319                 return eventNotHandledErr;
          320         }
          321 
          322         mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec());
          323         return noErr;        
          324 }
          325 
          326 static int keycvt[] =
          327 {
          328         [QZ_IBOOK_ENTER] '\n',
          329         [QZ_RETURN] '\n',
          330         [QZ_ESCAPE] 27,
          331         [QZ_BACKSPACE] '\b',
          332         [QZ_LALT] Kalt,
          333         [QZ_LCTRL] Kctl,
          334         [QZ_LSHIFT] Kshift,
          335         [QZ_F1] KF+1,
          336         [QZ_F2] KF+2,
          337         [QZ_F3] KF+3,
          338         [QZ_F4] KF+4,
          339         [QZ_F5] KF+5,
          340         [QZ_F6] KF+6,
          341         [QZ_F7] KF+7,
          342         [QZ_F8] KF+8,
          343         [QZ_F9] KF+9,
          344         [QZ_F10] KF+10,
          345         [QZ_F11] KF+11,
          346         [QZ_F12] KF+12,
          347         [QZ_INSERT] Kins,
          348         [QZ_DELETE] 0x7F,
          349         [QZ_HOME] Khome,
          350         [QZ_END] Kend,
          351         [QZ_KP_PLUS] '+',
          352         [QZ_KP_MINUS] '-',
          353         [QZ_TAB] '\t',
          354         [QZ_PAGEUP] Kpgup,
          355         [QZ_PAGEDOWN] Kpgdown,
          356         [QZ_UP] Kup,
          357         [QZ_DOWN] Kdown,
          358         [QZ_LEFT] Kleft,
          359         [QZ_RIGHT] Kright,
          360         [QZ_KP_MULTIPLY] '*',
          361         [QZ_KP_DIVIDE] '/',
          362         [QZ_KP_ENTER] '\n',
          363         [QZ_KP_PERIOD] '.',
          364         [QZ_KP0] '0',
          365         [QZ_KP1] '1',
          366         [QZ_KP2] '2',
          367         [QZ_KP3] '3',
          368         [QZ_KP4] '4',
          369         [QZ_KP5] '5',
          370         [QZ_KP6] '6',
          371         [QZ_KP7] '7',
          372         [QZ_KP8] '8',
          373         [QZ_KP9] '9',
          374 };
          375 
          376 static void
          377 kputc(int c)
          378 {
          379         kbdputc(kbdq, c);
          380 }
          381 
          382 static OSStatus
          383 kbdevent(EventRef event)
          384 {
          385         char ch;
          386         UInt32 code;
          387         UInt32 mod;
          388         int k;
          389 
          390         GetEventParameter(event, kEventParamKeyMacCharCodes,
          391                 typeChar, nil, sizeof ch, nil, &ch);
          392         GetEventParameter(event, kEventParamKeyCode,
          393                 typeUInt32, nil, sizeof code, nil, &code);
          394         GetEventParameter(event, kEventParamKeyModifiers,
          395                 typeUInt32, nil, sizeof mod, nil, &mod);
          396 
          397         switch(GetEventKind(event)){
          398         case kEventRawKeyDown:
          399         case kEventRawKeyRepeat:
          400                 if(mod == cmdKey){
          401                         if(ch == 'F' || ch == 'f'){
          402                                 if(osx.isfullscreen && msec() - osx.fullscreentime > 500)
          403                                         fullscreen();
          404                                 return noErr;
          405                         }
          406                         return eventNotHandledErr;
          407                 }
          408                 k = ch;
          409                 if(code < nelem(keycvt) && keycvt[code])
          410                         k = keycvt[code];
          411                 if(k >= 0)
          412                         latin1putc(k, kputc);
          413                 else{
          414                         UniChar uc;
          415                         OSStatus s;
          416                         
          417                         s = GetEventParameter(event, kEventParamKeyUnicodes,
          418                                 typeUnicodeText, nil, sizeof uc, nil, &uc);
          419                         if(s == noErr)
          420                                 kputc(uc);
          421                 }
          422                 break;
          423 
          424         case kEventRawKeyModifiersChanged:
          425                 if(!osx.buttons && !osx.kbuttons){
          426                         if(mod == optionKey)
          427                                 latin1putc(Kalt, kputc);
          428                         break;
          429                 }
          430                 
          431                 // If the mouse button is being held down, treat 
          432                 // changes in the keyboard modifiers as changes
          433                 // in the mouse buttons.
          434                 osx.kbuttons = 0;
          435                 if(mod & optionKey)
          436                         osx.kbuttons |= 2;
          437                 if(mod & cmdKey)
          438                         osx.kbuttons |= 4;
          439                 mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
          440                 break;
          441         }
          442         return noErr;
          443 }
          444 
          445 static void
          446 eresized(int force)
          447 {
          448         Memimage *m;
          449         OSXRect or;
          450         ulong chan;
          451         Rectangle r;
          452         int bpl;
          453         CGDataProviderRef provider;
          454         CGImageRef image;
          455         
          456         GetWindowBounds(osx.window, kWindowContentRgn, &or);
          457         r = Rect(or.left, or.top, or.right, or.bottom);
          458         if(Dx(r) == Dx(osx.screenr) && Dy(r) == Dy(osx.screenr) && !force){
          459                 // No need to make new image.
          460                 osx.screenr = r;
          461                 return;
          462         }
          463 
          464         chan = XBGR32;
          465         m = allocmemimage(Rect(0, 0, Dx(r), Dy(r)), chan);
          466         if(m == nil)
          467                 panic("allocmemimage: %r");
          468         if(m->data == nil)
          469                 panic("m->data == nil");
          470         bpl = bytesperline(r, 32);
          471         provider = CGDataProviderCreateWithData(0,
          472                 m->data->bdata, Dy(r)*bpl, 0);
          473         image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl,
          474                 CGColorSpaceCreateDeviceRGB(),
          475                 kCGImageAlphaNoneSkipLast,
          476                 provider, 0, 0, kCGRenderingIntentDefault);
          477         CGDataProviderRelease(provider);        // CGImageCreate did incref
          478 
          479         mouserect = m->r;
          480         if(osx.image)
          481                 CGImageRelease(osx.image);
          482         osx.image = image;
          483         osx.screenr = r;
          484         osx.screenimage = m;
          485         termreplacescreenimage(m);
          486         drawreplacescreenimage(m);        // frees old osx.screenimage if any
          487 }
          488 
          489 void
          490 flushmemscreen(Rectangle r)
          491 {
          492         CGRect cgr;
          493         CGContextRef context;
          494         CGImageRef subimg;
          495 
          496         QDBeginCGContext(GetWindowPort(osx.window), &context);
          497         
          498         cgr.origin.x = r.min.x;
          499         cgr.origin.y = r.min.y;
          500         cgr.size.width = Dx(r);
          501         cgr.size.height = Dy(r);
          502         subimg = CGImageCreateWithImageInRect(osx.image, cgr);
          503         cgr.origin.y = Dy(osx.screenr) - r.max.y; // XXX how does this make any sense?
          504         CGContextDrawImage(context, cgr, subimg);
          505         CGContextFlush(context);
          506         CGImageRelease(subimg);
          507 
          508         QDEndCGContext(GetWindowPort(osx.window), &context);
          509 }
          510 
          511 void
          512 fullscreen(void)
          513 {
          514         static Ptr restore;
          515         static WindowRef oldwindow;
          516         GDHandle device;
          517 
          518         if(osx.isfullscreen){
          519                 EndFullScreen(restore, 0);
          520                 osx.window = oldwindow;
          521                 ShowWindow(osx.window);
          522                 osx.isfullscreen = 0;
          523         }else{
          524                 HideWindow(osx.window);
          525                 oldwindow = osx.window;
          526                 GetWindowGreatestAreaDevice(osx.window, kWindowTitleBarRgn, &device, nil);
          527                 BeginFullScreen(&restore, device, 0, 0, &osx.window, 0, 0);
          528                 osx.isfullscreen = 1;
          529                 osx.fullscreentime = msec();
          530         }
          531         eresized(1);
          532 }
          533 
          534 void
          535 setmouse(Point p)
          536 {
          537         CGPoint cgp;
          538         
          539         cgp.x = p.x + osx.screenr.min.x;
          540         cgp.y = p.y + osx.screenr.min.y;
          541         CGWarpMouseCursorPosition(cgp);
          542 }
          543 
          544 void
          545 setcursor(struct Cursor *c)
          546 {
          547         OSXCursor oc;
          548         int i;
          549 
          550         // SetCursor is deprecated, but what replaces it?
          551         for(i=0; i<16; i++){
          552                 oc.data[i] = ((ushort*)c->set)[i];
          553                 oc.mask[i] = oc.data[i] | ((ushort*)c->clr)[i];
          554         }
          555         oc.hotSpot.h = - c->offset.x;
          556         oc.hotSpot.v = - c->offset.y;
          557         SetCursor(&oc);
          558 }
          559 
          560 void
          561 getcolor(ulong i, ulong *r, ulong *g, ulong *b)
          562 {
          563         ulong v;
          564         
          565         v = 0;
          566         *r = (v>>16)&0xFF;
          567         *g = (v>>8)&0xFF;
          568         *b = v&0xFF;
          569 }
          570 
          571 int
          572 setcolor(ulong i, ulong r, ulong g, ulong b)
          573 {
          574         /* no-op */
          575         return 0;
          576 }
          577 
          578 
          579 int
          580 hwdraw(Memdrawparam *p)
          581 {
          582         return 0;
          583 }
          584 
          585 struct {
          586         QLock lk;
          587         char buf[SnarfSize];
          588         Rune rbuf[SnarfSize];
          589         PasteboardRef apple;
          590 } clip;
          591 
          592 char*
          593 getsnarf(void)
          594 {
          595         char *s, *t;
          596         CFArrayRef flavors;
          597         CFDataRef data;
          598         CFIndex nflavor, ndata, j;
          599         CFStringRef type;
          600         ItemCount nitem;
          601         PasteboardItemID id;
          602         PasteboardSyncFlags flags;
          603         UInt32 i;
          604 
          605 /*        fprint(2, "applegetsnarf\n"); */
          606         qlock(&clip.lk);
          607         clip.apple = osx.snarf;
          608         if(clip.apple == nil){
          609                 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
          610                         fprint(2, "apple pasteboard create failed\n");
          611                         qunlock(&clip.lk);
          612                         return nil;
          613                 }
          614         }
          615         flags = PasteboardSynchronize(clip.apple);
          616         if(flags&kPasteboardClientIsOwner){
          617                 s = strdup(clip.buf);
          618                 qunlock(&clip.lk);
          619                 return s;
          620         }
          621         if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){
          622                 fprint(2, "apple pasteboard get item count failed\n");
          623                 qunlock(&clip.lk);
          624                 return nil;
          625         }
          626         for(i=1; i<=nitem; i++){
          627                 if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr)
          628                         continue;
          629                 if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr)
          630                         continue;
          631                 nflavor = CFArrayGetCount(flavors);
          632                 for(j=0; j<nflavor; j++){
          633                         type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j);
          634                         if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text")))
          635                                 continue;
          636                         if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr)
          637                                 continue;
          638                         ndata = CFDataGetLength(data);
          639                         qunlock(&clip.lk);
          640                         s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data));
          641                         CFRelease(flavors);
          642                         CFRelease(data);
          643                         for(t=s; *t; t++)
          644                                 if(*t == '\r')
          645                                         *t = '\n';
          646                         return s;
          647                 }
          648                 CFRelease(flavors);
          649         }
          650         qunlock(&clip.lk);
          651         return nil;                
          652 }
          653 
          654 void
          655 putsnarf(char *s)
          656 {
          657         CFDataRef cfdata;
          658         PasteboardSyncFlags flags;
          659 
          660 /*        fprint(2, "appleputsnarf\n"); */
          661 
          662         if(strlen(s) >= SnarfSize)
          663                 return;
          664         qlock(&clip.lk);
          665         strcpy(clip.buf, s);
          666         runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
          667         clip.apple = osx.snarf;
          668         if(PasteboardClear(clip.apple) != noErr){
          669                 fprint(2, "apple pasteboard clear failed\n");
          670                 qunlock(&clip.lk);
          671                 return;
          672         }
          673         flags = PasteboardSynchronize(clip.apple);
          674         if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
          675                 fprint(2, "apple pasteboard cannot assert ownership\n");
          676                 qunlock(&clip.lk);
          677                 return;
          678         }
          679         cfdata = CFDataCreate(kCFAllocatorDefault, 
          680                 (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2);
          681         if(cfdata == nil){
          682                 fprint(2, "apple pasteboard cfdatacreate failed\n");
          683                 qunlock(&clip.lk);
          684                 return;
          685         }
          686         if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1,
          687                 CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
          688                 fprint(2, "apple pasteboard putitem failed\n");
          689                 CFRelease(cfdata);
          690                 qunlock(&clip.lk);
          691                 return;
          692         }
          693         /* CFRelease(cfdata); ??? */
          694         qunlock(&clip.lk);
          695 }
          696 
          697 static void
          698 seticon(void)
          699 {
          700         CGImageRef im;
          701         CGDataProviderRef d;
          702 
          703         d = CGDataProviderCreateWithData(nil, nineball_png, sizeof nineball_png, nil);
          704         im = CGImageCreateWithPNGDataProvider(d, nil, true, kCGRenderingIntentDefault);
          705         if(im)
          706                 SetApplicationDockTileImage(im);
          707         CGImageRelease(im);
          708         CGDataProviderRelease(d);
          709 }
          710