screen.c - vx32 - Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log
Files
Refs
---
screen.c (15169B)
---
     1 #define Point OSXPoint
     2 #define Rect OSXRect
     3 #define Cursor OSXCursor
     4 #include 
     5 #include  // 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 
    20 #include 
    21 #include 
    22 #include 
    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= 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