Files
plan9port/src/cmd/devdraw/cocoa-srv.c
Xiao-Yong Jin 9af9ceca26 devdraw: rewrite the Cocoa screen using Metal
Add a new macOS cocoa screen, cocoa-screen-metal.m.
Rewrite the macOS cocoa drawing code to use the builtin runloop,
and use Metal to push pixels with CAMetalLayer.

Remove all of the deprecated code, and simplify some of the logic.
Modify mkwsysrules.sh such that the new code is used only when
the system version is equal or higher than 10.14.

Allow touch events to simulate mouse clicks:
three finger tap for the middle mouse button;
four finger tap for the 2-1 chord.

Support Tresize.

Scale 16x16 Cursor up to 32x32 with an EPX algorithm.

Support macOS input sources including the basic dead keys and the
advanced CJK input methods.

Increase the communication buffers in cocoa-srv.c to allow more
input, especially for long sentences prepared by the macOS input
souces.
2018-11-15 20:39:35 -05:00

422 lines
6.3 KiB
C

/*
* 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[256];
int ri;
int wi;
int stall;
};
struct Mousebuf
{
Mouse m[256];
Mouse last;
int ri;
int wi;
int stall;
};
struct Tagbuf
{
int t[256];
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);
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, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &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:
zlock();
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);
}
zunlock();
break;
case Twrdraw:
zlock();
if(_drawmsgwrite(m->data, m->count) < 0)
replyerror(m);
else
replymsg(m);
zunlock();
break;
case Ttop:
topwin();
replymsg(m);
break;
case Tresize:
#if OSX_VERSION >= 101400
resizewindow(m->rect);
#endif
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, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), 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;
mouseresized = 0;
/*
if(m.resized)
fprint(2, "sending resize\n");
*/
mouse.ri++;
if(mouse.ri == nelem(mouse.m))
mouse.ri = 0;
replymsg(&m);
}
}
void
mousetrack(int x, int y, int b, uint 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();
}
static int alting;
void
abortcompose(void)
{
if(alting)
keystroke(Kalt);
}
void
keystroke(int c)
{
static Rune k[10];
static int nk;
int i;
if(c == Kalt){
alting = !alting;
nk = 0;
return;
}
if(c == Kcmd+'r') {
if(forcedpi)
forcedpi = 0;
else if(displaydpi >= 200)
forcedpi = 100;
else
forcedpi = 225;
resizeimg();
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;
}