devdraw: draft cocoa support

R=rsc
CC=plan9port.codebot
http://codereview.appspot.com/4974060
This commit is contained in:
David Jeannot 2011-09-06 10:10:43 -04:00 committed by Russ Cox
parent f0a4e8bd6c
commit a287dbab23
8 changed files with 1340 additions and 0 deletions

View file

@ -10,6 +10,7 @@ Anthony Sorace <a@9srv.net>
Arvindh Rajesh Tamilmani <art@a-30.net>
Benjamin Huntsman <BHuntsman@mail2.cu-portland.edu>
David du Colombier <0intro@gmail.com>
David Jeannot <djeannot24@gmail.com>
David Swasey <david.swasey@gmail.com>
Enrique Soriano <enrique.soriano@gmail.com>
Eoghan Sherry <ejsherry@gmail.com>

View file

@ -751,6 +751,10 @@ texttype(Text *t, Rune r)
typecommit(t);
cut(t, t, nil, TRUE, FALSE, nil, 0);
return;
case Kcmd+'z': /* %Z: undo */
typecommit(t);
undo(t, nil, nil, TRUE, 0, nil, 0);
return;
Tagdown:
/* expand tag to show all text */

View file

@ -0,0 +1,19 @@
#define setcursor dsetcursor
Memimage *attachscreen(char*, char*);
void setmouse(Point);
void setcursor(Cursor*);
void setlabel(char*);
char* getsnarf(void);
void putsnarf(char*);
void mousetrack(int, int, int, int);
void keystroke(int);
void kicklabel(char*);
void servep9p(void);
void zlock(void);
void zunlock(void);
Rectangle mouserect;
int mouseresized;

View file

@ -0,0 +1,835 @@
/*
* Cocoa's event loop must be in the main thread.
*/
#define Point OSXPoint
#define Rect OSXRect
#define Cursor OSXCursor
#import <Cocoa/Cocoa.h>
#undef Rect
#undef Point
#undef Cursor
#include <u.h>
#include <libc.h>
#include "cocoa-thread.h"
#include <draw.h>
#include <memdraw.h>
#include <keyboard.h>
#include <cursor.h>
#include "cocoa-screen.h"
#include "osx-keycodes.h"
#include "devdraw.h"
#include "glendapng.h"
#define DEBUG if(0)NSLog
AUTOFRAMEWORK(Cocoa)
#define panic sysfatal
struct {
NSWindow *obj;
NSString *label;
char *winsize;
int ispositioned;
NSImage *img;
Memimage *imgbuf;
NSSize imgsize;
QLock lock;
Rendez meeting;
NSRect flushr;
int osxdrawing;
int p9pflushing;
int isresizing;
} win;
@interface appdelegate : NSObject
+(void)callmakewin:(id)arg; @end
@interface appthreads : NSObject
+(void)callservep9p:(id)arg; @end
@interface appview : NSView @end
int chatty;
int multitouch = 1;
void
usage(void)
{
fprint(2, "usage: devdraw (don't run directly)\n");
exits("usage");
}
void
main(int argc, char **argv)
{
/*
* Move the protocol off stdin/stdout so that
* any inadvertent prints don't screw things up.
*/
dup(0,3);
dup(1,4);
close(0);
close(1);
open("/dev/null", OREAD);
open("/dev/null", OWRITE);
ARGBEGIN{
case 'D':
chatty++;
break;
case 'M':
multitouch = 0;
break;
default:
usage();
}ARGEND
/*
* Ignore arguments. They're only for good ps -a listings.
*/
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp setDelegate:[appdelegate new]];
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
}
static void eresized(int);
static void getmousepos(void);
static void makemenu(NSString*);
static void makewin();
static void seticon(NSString*);
@implementation appdelegate
- (void)applicationDidFinishLaunching:(id)arg
{
[NSApplication detachDrawingThread:@selector(callservep9p:)
toTarget:[appthreads class] withObject:nil];
}
+ (void)callmakewin:(id)arg
{
makewin();
}
- (void)windowDidResize:(id)arg
{
eresized(1);
}
- (void)windowDidBecomeKey:(id)arg
{
getmousepos();
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(id)arg
{
return YES;
}
@end
@implementation appthreads
+(void)callservep9p:(id)arg
{
servep9p();
[NSApp terminate:self];
}
@end
Memimage*
attachscreen(char *label, char *winsize)
{
static int first = 1;
if(! first--)
panic("attachscreen called twice");
if(label == nil)
label = "gnot a label";
win.label = [[NSString alloc] initWithUTF8String:label];
win.meeting.l = &win.lock;
win.winsize = strdup(winsize);
makemenu(win.label);
// make NSWindow in the main thread,
// else no resize cursor when resizing.
[appdelegate
performSelectorOnMainThread:@selector(callmakewin:)
withObject:nil
waitUntilDone:YES];
// makewin();
seticon(win.label);
eresized(0);
return win.imgbuf;
}
void
makewin(id winsize)
{
char *s;
int style;
NSWindow *w;
NSRect r, sr;
Rectangle wr;
s = win.winsize;
if(s && *s){
if(parsewinsize(s, &wr, &win.ispositioned) < 0)
sysfatal("%r");
}else{
sr = [[NSScreen mainScreen] frame];
wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3);
}
// The origin is the left-bottom corner with Cocoa.
// Does the following work with any rectangles?
r = NSMakeRect(wr.min.x, r.size.height-wr.min.y, Dx(wr), Dy(wr));
style = NSTitledWindowMask
| NSClosableWindowMask
| NSResizableWindowMask
| NSMiniaturizableWindowMask;
w = [[NSWindow alloc]
initWithContentRect:r
styleMask:style
backing:NSBackingStoreBuffered
defer:NO];
[w setAcceptsMouseMovedEvents:YES];
#if OSX_VERSION >= 100700
[w setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
#endif
[w setContentView:[appview new]];
[w setDelegate:[NSApp delegate]];
[w setMinSize:NSMakeSize(128,128)];
[[w contentView] setAcceptsTouchEvents:YES];
if(win.ispositioned == 0)
[w center];
[w setTitle:win.label];
[w makeKeyAndOrderFront:nil];
win.obj = w;
}
static void sendmouse(int);
static void
eresized(int new)
{
static int first = 1;
uint ch;
NSSize size;
Rectangle r;
Memimage *m;
int bpl;
if(first--)
memimageinit();
size = [[win.obj contentView] bounds].size;
DEBUG(@"eresized called new=%d, [%.0f %.0f] -> [%.0f %.0f]", new,
win.imgsize.width, win.imgsize.height, size.width, size.height);
r = Rect(0, 0, size.width, size.height);
ch = XBGR32;
m = allocmemimage(r, ch);
if(m == nil)
panic("allocmemimage: %r");
if(m->data == nil)
panic("m->data == nil");
bpl = bytesperline(r, 32);
CGDataProviderRef dp;
CGImageRef i;
CGColorSpaceRef cs;
dp = CGDataProviderCreateWithData(0, m->data->bdata, Dy(r)*bpl, 0);
cs = CGColorSpaceCreateDeviceRGB();
i = CGImageCreate(Dx(r), Dy(r), 8, 32, bpl,
cs, kCGImageAlphaNone, dp, 0, 0, kCGRenderingIntentDefault);
_drawreplacescreenimage(m);
if(win.img)
[win.img release];
win.img = [[NSImage alloc] initWithCGImage:i size:size];
win.imgbuf = m;
win.imgsize = size;
CGColorSpaceRelease(cs);
CGDataProviderRelease(dp);
CGImageRelease(i);
if(new){
win.isresizing = 1; // to call before mousetrack
sendmouse(1);
}
DEBUG(@"eresized exit");
}
static void getgesture(NSEvent*);
static void getkeyboard(NSEvent*);
static void getmouse(NSEvent*);
@implementation appview
- (void)mouseMoved:(NSEvent*)e{ getmouse(e);}
- (void)mouseDown:(NSEvent*)e{ getmouse(e);}
- (void)mouseDragged:(NSEvent*)e{ getmouse(e);}
- (void)mouseUp:(NSEvent*)e{ getmouse(e);}
- (void)otherMouseDown:(NSEvent*)e{ getmouse(e);}
- (void)otherMouseDragged:(NSEvent*)e{ getmouse(e);}
- (void)otherMouseUp:(NSEvent*)e{ getmouse(e);}
- (void)rightMouseDown:(NSEvent*)e{ getmouse(e);}
- (void)rightMouseDragged:(NSEvent*)e{ getmouse(e);}
- (void)rightMouseUp:(NSEvent*)e{ getmouse(e);}
- (void)scrollWheel:(NSEvent*)e{ getmouse(e);}
- (void)keyDown:(NSEvent*)e{ getkeyboard(e);}
- (void)flagsChanged:(NSEvent*)e{ getkeyboard(e);}
- (void)magnifyWithEvent:(NSEvent*)e{ DEBUG(@"magnifyWithEvent"); getgesture(e);}
- (void)swipeWithEvent:(NSEvent*)e{ DEBUG(@"swipeWithEvent"); getgesture(e);}
- (void)touchesEndedWithEvent:(NSEvent*)e{ DEBUG(@"touchesEndedWithEvent"); getgesture(e);}
- (BOOL)acceptsFirstResponder{ return YES; } // to receive mouseMoved events
- (BOOL)isFlipped{ return YES; }
- (BOOL)isOpaque{ return YES; } // to disable background painting before drawRect calls
- (void)drawRect:(NSRect)r
{
NSRect sr;
NSView *v;
v = [win.obj contentView];
DEBUG(@"drawRect called [%.0f %.0f] [%.0f %.0f]",
r.origin.x, r.origin.y, r.size.width, r.size.height);
if(! NSEqualSizes([v bounds].size, win.imgsize)){
DEBUG(@"drawRect: contentview & img don't correspond: [%.0f %.0f] [%.0f %.0f]",
[v bounds].size.width, [v bounds].size.height,
win.imgsize.width, win.imgsize.height);
return;
}
qlock(win.meeting.l);
if(win.isresizing){
if(! NSEqualRects(r, [v bounds])){
DEBUG(@"drawRect reject osx");
goto Return;
}
win.isresizing = 0;
DEBUG(@"drawRect serve osx");
}else{
if(! NSEqualRects(r, win.flushr)){
DEBUG(@"drawRect reject p9p");
goto Return;
}
DEBUG(@"drawRect serve p9p");
}
win.flushr = r;
win.osxdrawing = 1;
rwakeup(&win.meeting);
DEBUG(@"drawRect rsleep for p9pflushing=1");
while(win.p9pflushing == 0)
rsleep(&win.meeting);
DEBUG(@"drawRect drawInRect [%.0f %.0f] [%.0f %.0f]",
r.origin.x, r.origin.y, r.size.width, r.size.height);
sr = [v convertRect:r fromView:nil];
[win.img drawInRect:r fromRect:sr
operation:NSCompositeCopy fraction:1
respectFlipped:YES hints:nil];
[win.obj flushWindow];
win.osxdrawing = 0;
rwakeup(&win.meeting);
Return:
DEBUG(@"drawRect exit");
qunlock(win.meeting.l);
}
@end
void
_flushmemscreen(Rectangle r)
{
NSRect rect;
NSView *v;
v = [win.obj contentView];
rect = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r));
DEBUG(@"_flushmemscreen called [%.0f %.0f] [%.0f %.0f]",
rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
qlock(win.meeting.l);
if(win.osxdrawing == 0){
DEBUG(@"_flushmemscreen setNeedsDisplayInRect");
[v setNeedsDisplayInRect:rect];
win.flushr = rect;
DEBUG(@"_flushmemscreen rsleep for osxdrawing=1");
while(win.osxdrawing == 0)
rsleep(&win.meeting);
}
if(! NSEqualRects(rect, win.flushr)){
qunlock(win.meeting.l);
DEBUG(@"_flushmemscreen bad rectangle");
return;
}
win.flushr = NSMakeRect(0,0,0,0);
win.p9pflushing = 1;
rwakeup(&win.meeting);
DEBUG(@"_flushmemscreen rsleep for osxdrawing=0");
while(win.osxdrawing)
rsleep(&win.meeting);
win.p9pflushing = 0;
DEBUG(@"_flushmemscreen exit");
qunlock(win.meeting.l);
}
static int keycvt[] =
{
[QZ_IBOOK_ENTER] '\n',
[QZ_RETURN] '\n',
[QZ_ESCAPE] 27,
[QZ_BACKSPACE] '\b',
[QZ_LALT] Kalt,
[QZ_LCTRL] Kctl,
[QZ_LSHIFT] Kshift,
[QZ_F1] KF+1,
[QZ_F2] KF+2,
[QZ_F3] KF+3,
[QZ_F4] KF+4,
[QZ_F5] KF+5,
[QZ_F6] KF+6,
[QZ_F7] KF+7,
[QZ_F8] KF+8,
[QZ_F9] KF+9,
[QZ_F10] KF+10,
[QZ_F11] KF+11,
[QZ_F12] KF+12,
[QZ_INSERT] Kins,
[QZ_DELETE] 0x7F,
[QZ_HOME] Khome,
[QZ_END] Kend,
[QZ_KP_PLUS] '+',
[QZ_KP_MINUS] '-',
[QZ_TAB] '\t',
[QZ_PAGEUP] Kpgup,
[QZ_PAGEDOWN] Kpgdown,
[QZ_UP] Kup,
[QZ_DOWN] Kdown,
[QZ_LEFT] Kleft,
[QZ_RIGHT] Kright,
[QZ_KP_MULTIPLY] '*',
[QZ_KP_DIVIDE] '/',
[QZ_KP_ENTER] '\n',
[QZ_KP_PERIOD] '.',
[QZ_KP0] '0',
[QZ_KP1] '1',
[QZ_KP2] '2',
[QZ_KP3] '3',
[QZ_KP4] '4',
[QZ_KP5] '5',
[QZ_KP6] '6',
[QZ_KP7] '7',
[QZ_KP8] '8',
[QZ_KP9] '9',
};
int kalting;
int kbuttons;
int mbuttons;
Point mpos;
int scroll;
static void
getkeyboard(NSEvent *e)
{
uint code;
int k, m;
char c;
m = [e modifierFlags];
switch([e type]){
case NSKeyDown:
kalting = 0;
c = [[e characters] characterAtIndex:0];
if(m & NSCommandKeyMask){
// If I add cmd+h in the menu, does the combination
// appear here? If it doesn't, remove the following
//
// // OS X interprets a few no matter what we do,
// switch(c) {
// case 'm': // minimize window
// case 'h': // hide window
// case 'H': // hide others
// case 'q': // quit
// return;
// }
if(' '<=c && c<='~') {
keystroke(Kcmd+c);
return;
}
return;
}
// to undersand
k = c;
code = [e keyCode];
if(code < nelem(keycvt) && keycvt[code])
k = keycvt[code];
if(k == 0)
return;
if(k > 0)
keystroke(k);
else
keystroke(c);
break;
case NSFlagsChanged:
if(mbuttons || kbuttons){
kbuttons = 0;
if(m & NSAlternateKeyMask)
kbuttons |= 2;
if(m & NSCommandKeyMask)
kbuttons |= 4;
sendmouse(0);
}else
if(m & NSAlternateKeyMask) {
kalting = 1;
keystroke(Kalt);
}
break;
default:
panic("getkey: unexpected event type");
}
}
static void
getmousepos(void)
{
NSPoint p;
p = [win.obj mouseLocationOutsideOfEventStream];
p = [[win.obj contentView] convertPoint:p fromView:nil];
// DEBUG(@"getmousepos: %0.f %0.f", p.x, p.y);
mpos = Pt(p.x, p.y);
}
static void
getmouse(NSEvent *e)
{
int b, m;
float d;
getmousepos();
switch([e type]){
case NSLeftMouseDown:
case NSLeftMouseUp:
case NSOtherMouseDown:
case NSOtherMouseUp:
case NSRightMouseDown:
case NSRightMouseUp:
b = [NSEvent pressedMouseButtons];
b = b&~6 | (b&4)>>1 | (b&2)<<1;
b = mouseswap(b);
if(b == 1){
m = [e modifierFlags];
if(m & NSAlternateKeyMask) {
b = 2;
// Take the ALT away from the keyboard handler.
if(kalting) {
kalting = 0;
keystroke(Kalt);
}
}else
if(m & NSCommandKeyMask)
b = 4;
}
mbuttons = b;
break;
case NSScrollWheel:
#if OSX_VERSION >= 100700
d = [e scrollingDeltaY];
#else
d = [e deltaY];
#endif
if(d>0)
scroll = 8;
else if(d<0)
scroll = 16;
break;
case NSMouseMoved:
case NSLeftMouseDragged:
case NSRightMouseDragged:
case NSOtherMouseDragged:
break;
default:
panic("getmouse: unexpected event type");
}
sendmouse(0);
}
static void sendexec(int);
static void sendcmd(int, int*);
static void
getgesture(NSEvent *e)
{
static int undo;
int dx, dy;
switch([e type]){
case NSEventTypeMagnify:
#if OSX_VERSION >= 100700
[win.obj toggleFullScreen:nil];
#endif
break;
case NSEventTypeSwipe:
dx = - [e deltaX];
dy = - [e deltaY];
if(dx == -1)
sendcmd('x', &undo);
else
if(dx == +1)
sendcmd('v', &undo);
else
if(dy == -1)
sendexec(0);
else
if(dy == +1)
sendexec(1);
else // fingers lifted
undo = 0;
break;
// When I lift the fingers from the trackpad, I
// receive 1, 2, or 3 events "touchesEndedWithEvent".
// Their type is either generic (NSEventTypeGesture)
// or specific (NSEventTypeSwipe for example). I
// always receive at least 1 event of specific type.
// I sometimes receive NSEventTypeEndGesture
// apparently, even without implementing
// "endGestureWithEvent"
// I even received a NSEventTypeBeginGesture once.
case NSEventTypeBeginGesture:
break;
case NSEventTypeGesture:
case NSEventTypeEndGesture:
// do a undo here? because 2 times I had the impression undo was still 1
// after having lifted my fingers
undo = 0;
break;
default:
DEBUG(@"getgesture: unexpected event type: %d", [e type]);
}
}
static void
sendcmd(int c, int *undo)
{
if(*undo)
c = 'z';
*undo = ! *undo;
keystroke(Kcmd+c);
}
static void
sendexec(int giveargs)
{
mbuttons = 2;
sendmouse(0);
if(giveargs){
mbuttons |= 1;
sendmouse(0);
}
mbuttons = 0;
sendmouse(0);
}
static uint
msec(void)
{
return nsec()/1000000;
}
static void
sendmouse(int resized)
{
if(resized)
mouseresized = 1;
mouserect = win.imgbuf->r;
mousetrack(mpos.x, mpos.y, kbuttons|mbuttons|scroll, msec());
scroll = 0;
}
void
setmouse(Point p)
{
NSPoint q;
NSRect r;
r = [[NSScreen mainScreen] frame];
q = NSMakePoint(p.x,p.y);
q = [[win.obj contentView] convertPoint:q toView:nil];
q = [win.obj convertBaseToScreen:q];
q.y = r.size.height - q.y;
CGWarpMouseCursorPosition(q);
// race condition
mpos = p;
}
// setBadgeLabel don't have to be in this function.
// Remove seticon's argument too.
static void
seticon(NSString *s)
{
NSData *d;
NSImage *i;
d = [[NSData alloc]
initWithBytes:glenda_png
length:(sizeof glenda_png)];
i = [[NSImage alloc] initWithData:d];
if(i){
[NSApp setApplicationIconImage:i];
[[NSApp dockTile] display];
[[NSApp dockTile] setBadgeLabel:s];
}
[d release];
[i release];
}
// Menu should be called during app creation, not window creation.
// See ./osx-delegate.m implementation.
// If an application supports fullscreen, it should
// add an "Enter Full Screen" menu item to the View
// menu. The menu item is now available through
// Xcode 4. You can also add the item
// programmatically, with toggleFullScreen: as the
// action, nil as the target, and cmd-ctrl-f as the
// key equivalent. AppKit will automatically update
// the menu item title as part of its menu item
// validation.
static void
makemenu(NSString *s)
{
NSString *title;
NSMenu *menu;
NSMenuItem *appmenu, *item;
menu = [NSMenu new];
appmenu = [NSMenuItem new];
[menu addItem:appmenu];
[NSApp setMenu:menu];
[menu release];
title = [@"Quit " stringByAppendingString:win.label];
item = [[NSMenuItem alloc]
initWithTitle:title
action:@selector(terminate:) keyEquivalent:@"q"];
menu = [NSMenu new];
[menu addItem:item];
[item release];
[appmenu setSubmenu:menu];
[appmenu release];
[menu release];
}
QLock snarfl;
char*
getsnarf(void)
{
NSString *s;
NSPasteboard *pb;
pb = [NSPasteboard generalPasteboard];
// use NSPasteboardTypeString instead of NSStringPboardType
qlock(&snarfl);
s = [pb stringForType:NSStringPboardType];
qunlock(&snarfl);
// change the pastebuffer here to see if s is
// altered. Move the lock accordingly.
if(s)
return strdup((char*)[s UTF8String]);
else
return nil;
// should I call autorelease here for example?
}
void
putsnarf(char *s)
{
NSArray *t;
NSString *str;
NSPasteboard *pb;
int r;
if(strlen(s) >= SnarfSize)
return;
t = [NSArray arrayWithObject:NSPasteboardTypeString];
pb = [NSPasteboard generalPasteboard];
str = [[NSString alloc] initWithUTF8String:s];
qlock(&snarfl);
[pb declareTypes:t owner:nil];
r = [pb setString:str forType:NSPasteboardTypeString];
qunlock(&snarfl);
if(!r)
DEBUG(@"putsnarf: setString failed");
}
void
kicklabel(char *c)
{
}
void
setcursor(Cursor *c)
{
}

397
src/cmd/devdraw/cocoa-srv.c Normal file
View file

@ -0,0 +1,397 @@
/*
* Window system protocol server.
*/
#include <u.h>
#include <libc.h>
#include "cocoa-thread.h"
#include <draw.h>
#include <memdraw.h>
#include <keyboard.h>
#include <mouse.h>
#include <cursor.h>
#include <drawfcall.h>
#include "cocoa-screen.h"
#include "devdraw.h"
typedef struct Kbdbuf Kbdbuf;
typedef struct Mousebuf Mousebuf;
typedef struct Fdbuf Fdbuf;
typedef struct Tagbuf Tagbuf;
struct Kbdbuf
{
Rune r[32];
int ri;
int wi;
int stall;
};
struct Mousebuf
{
Mouse m[32];
Mouse last;
int ri;
int wi;
int stall;
};
struct Tagbuf
{
int t[32];
int ri;
int wi;
};
Kbdbuf kbd;
Mousebuf mouse;
Tagbuf kbdtags;
Tagbuf mousetags;
void runmsg(Wsysmsg*);
void replymsg(Wsysmsg*);
void matchkbd(void);
void matchmouse(void);
QLock lk;
void
zlock(void)
{
qlock(&lk);
}
void
zunlock(void)
{
qunlock(&lk);
}
int trace = 0;
void
servep9p(void)
{
uchar buf[4], *mbuf;
int nmbuf, n, nn;
Wsysmsg m;
fmtinstall('W', drawfcallfmt);
// notify(bell);
mbuf = nil;
nmbuf = 0;
while((n = read(3, buf, 4)) == 4){
GET(buf, n);
if(n > nmbuf){
free(mbuf);
mbuf = malloc(4+n);
if(mbuf == nil)
sysfatal("malloc: %r");
nmbuf = n;
}
memmove(mbuf, buf, 4);
nn = readn(3, mbuf+4, n-4);
if(nn != n-4)
sysfatal("eof during message");
/* pick off messages one by one */
if(convM2W(mbuf, nn+4, &m) <= 0)
sysfatal("cannot convert message");
if(trace) fprint(2, "<- %W\n", &m);
runmsg(&m);
}
}
void
replyerror(Wsysmsg *m)
{
char err[256];
rerrstr(err, sizeof err);
m->type = Rerror;
m->error = err;
replymsg(m);
}
/*
* Handle a single wsysmsg.
* Might queue for later (kbd, mouse read)
*/
void
runmsg(Wsysmsg *m)
{
static uchar buf[65536];
int n;
Memimage *i;
switch(m->type){
case Tinit:
memimageinit();
i = attachscreen(m->label, m->winsize);
_initdisplaymemimage(i);
replymsg(m);
break;
case Trdmouse:
zlock();
mousetags.t[mousetags.wi++] = m->tag;
if(mousetags.wi == nelem(mousetags.t))
mousetags.wi = 0;
if(mousetags.wi == mousetags.ri)
sysfatal("too many queued mouse reads");
mouse.stall = 0;
matchmouse();
zunlock();
break;
case Trdkbd:
zlock();
kbdtags.t[kbdtags.wi++] = m->tag;
if(kbdtags.wi == nelem(kbdtags.t))
kbdtags.wi = 0;
if(kbdtags.wi == kbdtags.ri)
sysfatal("too many queued keyboard reads");
kbd.stall = 0;
matchkbd();
zunlock();
break;
case Tmoveto:
setmouse(m->mouse.xy);
replymsg(m);
break;
case Tcursor:
if(m->arrowcursor)
setcursor(nil);
else
setcursor(&m->cursor);
replymsg(m);
break;
case Tbouncemouse:
// _xbouncemouse(&m->mouse);
replymsg(m);
break;
case Tlabel:
kicklabel(m->label);
replymsg(m);
break;
case Trdsnarf:
m->snarf = getsnarf();
replymsg(m);
free(m->snarf);
break;
case Twrsnarf:
putsnarf(m->snarf);
replymsg(m);
break;
case Trddraw:
n = m->count;
if(n > sizeof buf)
n = sizeof buf;
n = _drawmsgread(buf, n);
if(n < 0)
replyerror(m);
else{
m->count = n;
m->data = buf;
replymsg(m);
}
break;
case Twrdraw:
if(_drawmsgwrite(m->data, m->count) < 0)
replyerror(m);
else
replymsg(m);
break;
case Ttop:
// _xtopwindow();
replymsg(m);
break;
case Tresize:
// _xresizewindow(m->rect);
replymsg(m);
break;
}
}
/*
* Reply to m.
*/
QLock replylock;
void
replymsg(Wsysmsg *m)
{
int n;
static uchar *mbuf;
static int nmbuf;
/* T -> R msg */
if(m->type%2 == 0)
m->type++;
if(trace) fprint(2, "-> %W\n", m);
/* copy to output buffer */
n = sizeW2M(m);
qlock(&replylock);
if(n > nmbuf){
free(mbuf);
mbuf = malloc(n);
if(mbuf == nil)
sysfatal("out of memory");
nmbuf = n;
}
convW2M(m, mbuf, n);
if(write(4, mbuf, n) != n)
sysfatal("write: %r");
qunlock(&replylock);
}
/*
* Match queued kbd reads with queued kbd characters.
*/
void
matchkbd(void)
{
Wsysmsg m;
if(kbd.stall)
return;
while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){
m.type = Rrdkbd;
m.tag = kbdtags.t[kbdtags.ri++];
if(kbdtags.ri == nelem(kbdtags.t))
kbdtags.ri = 0;
m.rune = kbd.r[kbd.ri++];
if(kbd.ri == nelem(kbd.r))
kbd.ri = 0;
replymsg(&m);
}
}
/*
* Match queued mouse reads with queued mouse events.
*/
void
matchmouse(void)
{
Wsysmsg m;
while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){
m.type = Rrdmouse;
m.tag = mousetags.t[mousetags.ri++];
if(mousetags.ri == nelem(mousetags.t))
mousetags.ri = 0;
m.mouse = mouse.m[mouse.ri];
m.resized = mouseresized;
/*
if(m.resized)
fprint(2, "sending resize\n");
*/
mouseresized = 0;
mouse.ri++;
if(mouse.ri == nelem(mouse.m))
mouse.ri = 0;
replymsg(&m);
}
}
void
mousetrack(int x, int y, int b, int ms)
{
Mouse *m;
if(x < mouserect.min.x)
x = mouserect.min.x;
if(x > mouserect.max.x)
x = mouserect.max.x;
if(y < mouserect.min.y)
y = mouserect.min.y;
if(y > mouserect.max.y)
y = mouserect.max.y;
zlock();
// If reader has stopped reading, don't bother.
// If reader is completely caught up, definitely queue.
// Otherwise, queue only button change events.
if(!mouse.stall)
if(mouse.wi == mouse.ri || mouse.last.buttons != b){
m = &mouse.last;
m->xy.x = x;
m->xy.y = y;
m->buttons = b;
m->msec = ms;
mouse.m[mouse.wi] = *m;
if(++mouse.wi == nelem(mouse.m))
mouse.wi = 0;
if(mouse.wi == mouse.ri){
mouse.stall = 1;
mouse.ri = 0;
mouse.wi = 1;
mouse.m[0] = *m;
}
matchmouse();
}
zunlock();
}
void
kputc(int c)
{
zlock();
kbd.r[kbd.wi++] = c;
if(kbd.wi == nelem(kbd.r))
kbd.wi = 0;
if(kbd.ri == kbd.wi)
kbd.stall = 1;
matchkbd();
zunlock();
}
void
keystroke(int c)
{
static Rune k[10];
static int alting, nk;
int i;
if(c == Kalt){
alting = !alting;
return;
}
if(!alting){
kputc(c);
return;
}
if(nk >= nelem(k)) // should not happen
nk = 0;
k[nk++] = c;
c = _latin1(k, nk);
if(c > 0){
alting = 0;
kputc(c);
nk = 0;
return;
}
if(c == -1){
alting = 0;
for(i=0; i<nk; i++)
kputc(k[i]);
nk = 0;
return;
}
// need more input
return;
}

View file

@ -0,0 +1,54 @@
#include <u.h>
#include <libc.h>
#include "cocoa-thread.h"
static pthread_mutex_t initlock = PTHREAD_MUTEX_INITIALIZER;
void
qlock(QLock *q)
{
if(q->init == 0){
pthread_mutex_lock(&initlock);
if(q->init == 0){
pthread_mutex_init(&q->m, nil);
q->init = 1;
}
pthread_mutex_unlock(&initlock);
}
pthread_mutex_lock(&q->m);
}
void
qunlock(QLock *q)
{
pthread_mutex_unlock(&q->m);
}
static void
rinit(Rendez *r)
{
pthread_mutex_lock(&initlock);
if(r->init == 0){
pthread_cond_init(&r->c, nil);
r->init = 1;
}
pthread_mutex_unlock(&initlock);
}
void
rsleep(Rendez *r)
{
if(r->init == 0)
rinit(r);
pthread_cond_wait(&r->c, &r->l->m);
}
int
rwakeup(Rendez *r)
{
if(r->init == 0)
rinit(r);
pthread_cond_signal(&r->c);
return 0;
}

View file

@ -0,0 +1,27 @@
#define QLock DQLock
#define qlock dqlock
#define qunlock dqunlock
#define Rendez DRendez
#define rsleep drsleep
#define rwakeup drwakeup
typedef struct QLock QLock;
typedef struct Rendez Rendez;
struct QLock
{
pthread_mutex_t m;
int init;
};
struct Rendez
{
QLock *l;
pthread_cond_t c;
int init;
};
void qlock(QLock*);
void qunlock(QLock*);
void rsleep(Rendez*);
int rwakeup(Rendez*); /* BUG: always returns 0 */

View file

@ -38,6 +38,9 @@ CLEANFILES=latin1.h $O.mklatinkbd
%-objc.$O: %.m
$CC $CFLAGS -o $target $stem.m
cocoa: devdraw.o latin1.o mouseswap.o winsize.o osx-draw.o cocoa-screen-objc.o cocoa-srv.o cocoa-thread.o
$LD -o $target $prereq
devdraw-cocoa: devdraw.o latin1.o mouseswap.o winsize.o osx-screen-objc.o osx-draw.o osx-srv-objc.o osx-delegate-objc.o
$LD -o $target $prereq