Blob


1 #define Point OSXPoint
2 #define Rect OSXRect
3 #define Cursor OSXCursor
4 #import <Cocoa/Cocoa.h>
5 #import <AppKit/AppKit.h>
6 #undef Rect
7 #undef Point
8 #undef Cursor
9 #undef offsetof
10 #undef nil
12 #include <u.h>
13 #include <libc.h>
14 #include <draw.h>
15 #include <memdraw.h>
16 #include <keyboard.h>
17 #include <mouse.h>
18 #include <cursor.h>
19 #include "osx-screen.h"
20 #include "osx-keycodes.h"
21 #include "devdraw.h"
22 #include "glendapng.h"
24 AUTOFRAMEWORK(Cocoa)
26 #define panic sysfatal
28 extern Rectangle mouserect;
30 struct {
31 char *label;
32 char *winsize;
33 QLock labellock;
35 Rectangle fullscreenr;
36 Rectangle screenr;
37 Memimage *screenimage;
38 int isfullscreen;
39 ulong fullscreentime;
41 Point xy;
42 int buttons;
43 int kbuttons;
45 CGDataProviderRef provider;
46 NSWindow *window;
47 CGImageRef image;
48 CGContextRef windowctx;
50 int needflush;
51 QLock flushlock;
52 int active;
53 int infullscreen;
54 int kalting; // last keystroke was Kalt
55 } osx;
57 enum
58 {
59 WindowAttrs = NSClosableWindowMask |
60 NSTitledWindowMask |
61 NSMiniaturizableWindowMask |
62 NSResizableWindowMask
63 };
65 static void screenproc(void*);
66 void eresized(int);
67 void fullscreen(int);
68 void seticon(void);
69 static void activated(int);
71 enum
72 {
73 CmdFullScreen = 1,
74 };
76 @interface P9View : NSView
77 {}
78 @end
80 @implementation P9View
81 - (BOOL)acceptsFirstResponder
82 {
83 return YES;
84 }
85 @end
87 void screeninit(void);
88 void _flushmemscreen(Rectangle r);
90 Memimage*
91 attachscreen(char *label, char *winsize)
92 {
93 if(label == nil)
94 label = "gnot a label";
95 osx.label = strdup(label);
96 osx.winsize = winsize;
97 if(osx.screenimage == nil){
98 screeninit();
99 if(osx.screenimage == nil)
100 panic("cannot create OS X screen");
102 return osx.screenimage;
105 void
106 _screeninit(void)
108 CGRect cgr;
109 NSRect or;
110 Rectangle r;
111 int havemin;
113 memimageinit();
115 cgr = CGDisplayBounds(CGMainDisplayID());
116 osx.fullscreenr = Rect(0, 0, cgr.size.width, cgr.size.height);
118 // Create the window.
119 r = Rect(0, 0, Dx(osx.fullscreenr)*2/3, Dy(osx.fullscreenr)*2/3);
120 havemin = 0;
121 if(osx.winsize && osx.winsize[0]){
122 if(parsewinsize(osx.winsize, &r, &havemin) < 0)
123 sysfatal("%r");
125 if(!havemin)
126 r = rectaddpt(r, Pt((Dx(osx.fullscreenr)-Dx(r))/2, (Dy(osx.fullscreenr)-Dy(r))/2));
127 or = NSMakeRect(r.min.x, r.min.y, r.max.x, r.max.y);
128 osx.window = [[NSWindow alloc] initWithContentRect:or styleMask:WindowAttrs
129 backing:NSBackingStoreBuffered defer:NO screen:[NSScreen mainScreen]];
130 [osx.window setDelegate:[NSApp delegate]];
131 [osx.window setAcceptsMouseMovedEvents:YES];
133 P9View *view = [[P9View alloc] initWithFrame:or];
134 [osx.window setContentView:view];
135 [view release];
137 setlabel(osx.label);
138 seticon();
140 // Finally, put the window on the screen.
141 eresized(0);
142 [osx.window makeKeyAndOrderFront:nil];
144 [NSCursor unhide];
147 static Rendez scr;
148 static QLock slock;
150 void
151 screeninit(void)
153 scr.l = &slock;
154 qlock(scr.l);
155 // proccreate(screenproc, nil, 256*1024);
156 screenproc(NULL);
157 while(osx.window == nil)
158 rsleep(&scr);
159 qunlock(scr.l);
162 static void
163 screenproc(void *v)
165 qlock(scr.l);
166 _screeninit();
167 rwakeup(&scr);
168 qunlock(scr.l);
171 static ulong
172 msec(void)
174 return nsec()/1000000;
177 //static void
178 void
179 mouseevent(NSEvent *event)
181 int wheel;
182 NSPoint op;
184 op = [event locationInWindow];
186 osx.xy = subpt(Pt(op.x, op.y), osx.screenr.min);
187 wheel = 0;
189 switch([event type]){
190 case NSScrollWheel:;
191 CGFloat delta = [event deltaY];
192 if(delta > 0)
193 wheel = 8;
194 else
195 wheel = 16;
196 break;
198 case NSLeftMouseDown:
199 case NSRightMouseDown:
200 case NSOtherMouseDown:
201 case NSLeftMouseUp:
202 case NSRightMouseUp:
203 case NSOtherMouseUp:;
204 NSInteger but;
205 NSUInteger mod;
206 but = [event buttonNumber];
207 mod = [event modifierFlags];
209 // OS X swaps button 2 and 3
210 but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
211 but = mouseswap(but);
213 // Apply keyboard modifiers and pretend it was a real mouse button.
214 // (Modifiers typed while holding the button go into kbuttons,
215 // but this one does not.)
216 if(but == 1){
217 if(mod & NSAlternateKeyMask) {
218 // Take the ALT away from the keyboard handler.
219 if(osx.kalting) {
220 osx.kalting = 0;
221 keystroke(Kalt);
223 but = 2;
225 else if(mod & NSCommandKeyMask)
226 but = 4;
228 osx.buttons = but;
229 break;
231 case NSMouseMoved:
232 case NSLeftMouseDragged:
233 case NSRightMouseDragged:
234 case NSOtherMouseDragged:
235 break;
237 default:
238 return;
241 mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons|wheel, msec());
244 static int keycvt[] =
246 [QZ_IBOOK_ENTER] '\n',
247 [QZ_RETURN] '\n',
248 [QZ_ESCAPE] 27,
249 [QZ_BACKSPACE] '\b',
250 [QZ_LALT] Kalt,
251 [QZ_LCTRL] Kctl,
252 [QZ_LSHIFT] Kshift,
253 [QZ_F1] KF+1,
254 [QZ_F2] KF+2,
255 [QZ_F3] KF+3,
256 [QZ_F4] KF+4,
257 [QZ_F5] KF+5,
258 [QZ_F6] KF+6,
259 [QZ_F7] KF+7,
260 [QZ_F8] KF+8,
261 [QZ_F9] KF+9,
262 [QZ_F10] KF+10,
263 [QZ_F11] KF+11,
264 [QZ_F12] KF+12,
265 [QZ_INSERT] Kins,
266 [QZ_DELETE] 0x7F,
267 [QZ_HOME] Khome,
268 [QZ_END] Kend,
269 [QZ_KP_PLUS] '+',
270 [QZ_KP_MINUS] '-',
271 [QZ_TAB] '\t',
272 [QZ_PAGEUP] Kpgup,
273 [QZ_PAGEDOWN] Kpgdown,
274 [QZ_UP] Kup,
275 [QZ_DOWN] Kdown,
276 [QZ_LEFT] Kleft,
277 [QZ_RIGHT] Kright,
278 [QZ_KP_MULTIPLY] '*',
279 [QZ_KP_DIVIDE] '/',
280 [QZ_KP_ENTER] '\n',
281 [QZ_KP_PERIOD] '.',
282 [QZ_KP0] '0',
283 [QZ_KP1] '1',
284 [QZ_KP2] '2',
285 [QZ_KP3] '3',
286 [QZ_KP4] '4',
287 [QZ_KP5] '5',
288 [QZ_KP6] '6',
289 [QZ_KP7] '7',
290 [QZ_KP8] '8',
291 [QZ_KP9] '9',
292 };
294 //static void
295 void
296 kbdevent(NSEvent *event)
298 char ch;
299 UInt32 code;
300 UInt32 mod;
301 int k;
303 ch = [[event characters] characterAtIndex:0];
304 code = [event keyCode];
305 mod = [event modifierFlags];
307 switch([event type]){
308 case NSKeyDown:
309 osx.kalting = 0;
310 if(mod == NSCommandKeyMask){
311 if(ch == 'F' || ch == 'f'){
312 if(osx.isfullscreen && msec() - osx.fullscreentime > 500)
313 fullscreen(0);
314 return;
317 // Pass most Cmd keys through as Kcmd + ch.
318 // OS X interprets a few no matter what we do,
319 // so it is useless to pass them through as keystrokes too.
320 switch(ch) {
321 case 'm': // minimize window
322 case 'h': // hide window
323 case 'H': // hide others
324 case 'q': // quit
325 return;
327 if(' ' <= ch && ch <= '~') {
328 keystroke(Kcmd + ch);
329 return;
331 return;
333 k = ch;
334 if(code < nelem(keycvt) && keycvt[code])
335 k = keycvt[code];
336 if(k >= 0)
337 keystroke(k);
338 else{
339 keystroke(ch);
341 break;
343 case NSFlagsChanged:
344 if(!osx.buttons && !osx.kbuttons){
345 if(mod == NSAlternateKeyMask) {
346 osx.kalting = 1;
347 keystroke(Kalt);
349 break;
352 // If the mouse button is being held down, treat
353 // changes in the keyboard modifiers as changes
354 // in the mouse buttons.
355 osx.kbuttons = 0;
356 if(mod & NSAlternateKeyMask)
357 osx.kbuttons |= 2;
358 if(mod & NSCommandKeyMask)
359 osx.kbuttons |= 4;
360 mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
361 break;
363 return;
366 //static void
367 void
368 eresized(int new)
370 Memimage *m;
371 NSRect or;
372 ulong chan;
373 Rectangle r;
374 int bpl;
375 CGDataProviderRef provider;
376 CGImageRef image;
377 CGColorSpaceRef cspace;
379 or = [[osx.window contentView] bounds];
380 r = Rect(or.origin.x, or.origin.y, or.size.width, or.size.height);
381 if(Dx(r) == Dx(osx.screenr) && Dy(r) == Dy(osx.screenr)){
382 // No need to make new image.
383 osx.screenr = r;
384 return;
387 chan = XBGR32;
388 m = allocmemimage(Rect(0, 0, Dx(r), Dy(r)), chan);
389 if(m == nil)
390 panic("allocmemimage: %r");
391 if(m->data == nil)
392 panic("m->data == nil");
393 bpl = bytesperline(r, 32);
394 provider = CGDataProviderCreateWithData(0,
395 m->data->bdata, Dy(r)*bpl, 0);
396 //cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
397 cspace = CGColorSpaceCreateDeviceRGB();
398 image = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl,
399 cspace,
400 kCGImageAlphaNoneSkipLast,
401 provider, 0, 0, kCGRenderingIntentDefault);
402 CGColorSpaceRelease(cspace);
403 CGDataProviderRelease(provider); // CGImageCreate did incref
405 mouserect = m->r;
406 if(new){
407 mouseresized = 1;
408 mousetrack(osx.xy.x, osx.xy.y, osx.buttons|osx.kbuttons, msec());
410 // termreplacescreenimage(m);
411 _drawreplacescreenimage(m); // frees old osx.screenimage if any
412 if(osx.image)
413 CGImageRelease(osx.image);
414 osx.image = image;
415 osx.screenimage = m;
416 osx.screenr = r;
419 void
420 flushproc(void *v)
422 for(;;){
423 if(osx.needflush && osx.windowctx && canqlock(&osx.flushlock)){
424 if(osx.windowctx){
425 CGContextFlush(osx.windowctx);
426 osx.needflush = 0;
428 qunlock(&osx.flushlock);
430 usleep(33333);
434 void
435 _flushmemscreen(Rectangle r)
437 CGRect cgr;
438 CGImageRef subimg;
440 qlock(&osx.flushlock);
441 if(osx.windowctx == nil){
442 osx.windowctx = [[osx.window graphicsContext] graphicsPort];
443 // [osx.window flushWindow];
444 // proccreate(flushproc, nil, 256*1024);
447 cgr.origin.x = r.min.x;
448 cgr.origin.y = r.min.y;
449 cgr.size.width = Dx(r);
450 cgr.size.height = Dy(r);
451 subimg = CGImageCreateWithImageInRect(osx.image, cgr);
452 cgr.origin.y = Dy(osx.screenr) - r.max.y; // XXX how does this make any sense?
453 CGContextDrawImage(osx.windowctx, cgr, subimg);
454 osx.needflush = 1;
455 qunlock(&osx.flushlock);
456 CGImageRelease(subimg);
459 void
460 activated(int active)
462 osx.active = active;
465 void
466 fullscreen(int wascmd)
468 NSView *view = [osx.window contentView];
470 if(osx.isfullscreen){
471 [view exitFullScreenModeWithOptions:nil];
472 osx.isfullscreen = 0;
473 }else{
474 [view enterFullScreenMode:[osx.window screen] withOptions:nil];
475 osx.isfullscreen = 1;
476 osx.fullscreentime = msec();
478 eresized(1);
481 void
482 setmouse(Point p)
484 CGPoint cgp;
486 cgp.x = p.x + osx.screenr.min.x;
487 cgp.y = p.y + osx.screenr.min.y;
488 CGWarpMouseCursorPosition(cgp);
491 void
492 setcursor(Cursor *c)
494 NSImage *image;
495 NSBitmapImageRep *bitmap;
496 NSCursor *nsc;
497 unsigned char *planes[5];
498 int i;
500 if(c == nil){
501 [NSCursor pop];
502 return;
505 image = [[NSImage alloc] initWithSize:NSMakeSize(16.0, 16.0)];
506 bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
507 pixelsWide:16
508 pixelsHigh:16
509 bitsPerSample:1
510 samplesPerPixel:2
511 hasAlpha:YES
512 isPlanar:YES
513 colorSpaceName:NSCalibratedWhiteColorSpace
514 bytesPerRow:2
515 bitsPerPixel:1];
517 [bitmap getBitmapDataPlanes:planes];
519 for(i=0; i<16; i++){
520 planes[0][i] = ((ushort*)c->set)[i];
521 planes[1][i] = planes[0][i] | ((ushort*)c->clr)[i];
524 [image addRepresentation:bitmap];
526 nsc = [[NSCursor alloc] initWithImage:image
527 hotSpot:NSMakePoint(c->offset.x, c->offset.y)];
528 [nsc push];
530 [image release];
531 [bitmap release];
532 [nsc release];
535 void
536 getcolor(ulong i, ulong *r, ulong *g, ulong *b)
538 ulong v;
540 v = 0;
541 *r = (v>>16)&0xFF;
542 *g = (v>>8)&0xFF;
543 *b = v&0xFF;
546 int
547 setcolor(ulong i, ulong r, ulong g, ulong b)
549 /* no-op */
550 return 0;
554 int
555 hwdraw(Memdrawparam *p)
557 return 0;
560 struct {
561 QLock lk;
562 char buf[SnarfSize];
563 Rune rbuf[SnarfSize];
564 NSPasteboard *apple;
565 } clip;
567 char*
568 getsnarf(void)
570 char *s, *t;
571 NSArray *types;
572 NSString *string;
573 NSData * data;
574 NSUInteger ndata;
576 /* fprint(2, "applegetsnarf\n"); */
577 qlock(&clip.lk);
579 clip.apple = [NSPasteboard generalPasteboard];
580 types = [clip.apple types];
582 string = [clip.apple stringForType:NSStringPboardType];
583 if(string == nil){
584 fprint(2, "apple pasteboard get item type failed\n");
585 qunlock(&clip.lk);
586 return nil;
589 data = [string dataUsingEncoding:NSUnicodeStringEncoding];
590 if(data != nil){
591 ndata = [data length];
592 qunlock(&clip.lk);
593 s = smprint("%.*S", ndata/2, (Rune*)[data bytes]);
594 for(t=s; *t; t++)
595 if(*t == '\r')
596 *t = '\n';
597 return s;
600 qunlock(&clip.lk);
601 return nil;
604 void
605 putsnarf(char *s)
607 NSArray *pboardTypes;
608 NSString *string;
610 /* fprint(2, "appleputsnarf\n"); */
612 if(strlen(s) >= SnarfSize)
613 return;
614 qlock(&clip.lk);
615 strcpy(clip.buf, s);
616 runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
618 pboardTypes = [NSArray arrayWithObject:NSStringPboardType];
620 clip.apple = [NSPasteboard generalPasteboard];
621 [clip.apple declareTypes:pboardTypes owner:nil];
623 assert(sizeof(clip.rbuf[0]) == 2);
624 string = [NSString stringWithCharacters:clip.rbuf length:runestrlen(clip.rbuf)*2];
625 if(string == nil){
626 fprint(2, "apple pasteboard data create failed\n");
627 qunlock(&clip.lk);
628 return;
630 if(![clip.apple setString:string forType:NSStringPboardType]){
631 fprint(2, "apple pasteboard putitem failed\n");
632 qunlock(&clip.lk);
633 return;
635 qunlock(&clip.lk);
638 void
639 setlabel(char *label)
641 CFStringRef cs;
642 cs = CFStringCreateWithBytes(nil, (uchar*)label, strlen(osx.label), kCFStringEncodingUTF8, false);
643 [osx.window setTitle:(NSString*)cs];
644 CFRelease(cs);
647 void
648 kicklabel(char *label)
650 char *p;
652 p = strdup(label);
653 if(p == nil)
654 return;
655 qlock(&osx.labellock);
656 free(osx.label);
657 osx.label = p;
658 qunlock(&osx.labellock);
660 setlabel(label);
663 // static void
664 void
665 seticon(void)
667 NSImage *im;
668 NSData *d;
670 d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)];
671 im = [[NSImage alloc] initWithData:d];
672 if(im){
673 NSLog(@"here");
674 [NSApp setApplicationIconImage:im];
675 [[NSApp dockTile] setShowsApplicationBadge:YES];
676 [[NSApp dockTile] display];
678 [d release];
679 [im release];