blob: 197fd512a1aadbd300bf9e6074afb80dd522f9d3 [file] [log] [blame]
/*
* 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);
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:
topwin();
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, 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 resizeimg(void);
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;
}