| ---
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 |