devdraw: more cleanup, clearer locking

This commit is contained in:
Russ Cox 2020-01-10 00:11:55 -05:00
parent b1a086dee9
commit 41547af3f6
8 changed files with 331 additions and 315 deletions

View file

@ -14,14 +14,12 @@
#include <drawfcall.h> #include <drawfcall.h>
#include "devdraw.h" #include "devdraw.h"
static Draw sdraw;
Client *client0;
static int drawuninstall(Client*, int); static int drawuninstall(Client*, int);
static Memimage* drawinstall(Client*, int, Memimage*, DScreen*); static Memimage* drawinstall(Client*, int, Memimage*, DScreen*);
static void drawfreedimage(Client*, DImage*); static void drawfreedimage(Client*, DImage*);
void void
_initdisplaymemimage(Client *c, Memimage *m) draw_initdisplaymemimage(Client *c, Memimage *m)
{ {
c->screenimage = m; c->screenimage = m;
m->screenref = 1; m->screenref = 1;
@ -30,10 +28,10 @@ _initdisplaymemimage(Client *c, Memimage *m)
c->op = SoverD; c->op = SoverD;
} }
// _drawreplacescreen replaces c's screen image with m. // gfx_replacescreenimage replaces c's screen image with m.
// It is called by the host driver on the main host thread. // It is called by the host driver on the main host thread.
void void
_drawreplacescreenimage(Client *c, Memimage *m) gfx_replacescreenimage(Client *c, Memimage *m)
{ {
/* /*
* Replace the screen image because the screen * Replace the screen image because the screen
@ -49,21 +47,21 @@ _drawreplacescreenimage(Client *c, Memimage *m)
*/ */
Memimage *om; Memimage *om;
qlock(&c->inputlk); qlock(&c->drawlk);
qlock(&sdraw.lk);
om = c->screenimage; om = c->screenimage;
c->screenimage = m; c->screenimage = m;
m->screenref = 1; m->screenref = 1;
c->mouse.resized = 1;
if(om && --om->screenref == 0){ if(om && --om->screenref == 0){
_freememimage(om); _freememimage(om);
} }
qunlock(&sdraw.lk); qunlock(&c->drawlk);
qunlock(&c->inputlk);
qlock(&c->eventlk);
c->mouse.resized = 1;
qunlock(&c->eventlk);
} }
static static void
void
drawrefreshscreen(DImage *l, Client *client) drawrefreshscreen(DImage *l, Client *client)
{ {
while(l != nil && l->dscreen == nil) while(l != nil && l->dscreen == nil)
@ -72,8 +70,7 @@ drawrefreshscreen(DImage *l, Client *client)
l->dscreen->owner->refreshme = 1; l->dscreen->owner->refreshme = 1;
} }
static static void
void
drawrefresh(Memimage *m, Rectangle r, void *v) drawrefresh(Memimage *m, Rectangle r, void *v)
{ {
Refx *x; Refx *x;
@ -106,7 +103,7 @@ static void
addflush(Client *c, Rectangle r) addflush(Client *c, Rectangle r)
{ {
int abb, ar, anbb; int abb, ar, anbb;
Rectangle nbb; Rectangle nbb, fr;
if(/*sdraw.softscreen==0 ||*/ !rectclip(&r, c->screenimage->r)) if(/*sdraw.softscreen==0 ||*/ !rectclip(&r, c->screenimage->r))
return; return;
@ -140,14 +137,20 @@ addflush(Client *c, Rectangle r)
return; return;
} }
/* emit current state */ /* emit current state */
if(c->flushrect.min.x < c->flushrect.max.x) fr = c->flushrect;
rpc_flushmemscreen(c, c->flushrect);
c->flushrect = r; c->flushrect = r;
c->waste = 0; c->waste = 0;
if(fr.min.x < fr.max.x) {
// Unlock drawlk because rpc_flush may want to run on gfx thread,
// and gfx thread might be blocked on drawlk trying to install a new screen
// during a resize.
qunlock(&c->drawlk);
rpc_flush(c, fr);
qlock(&c->drawlk);
}
} }
static static void
void
dstflush(Client *c, int dstid, Memimage *dst, Rectangle r) dstflush(Client *c, int dstid, Memimage *dst, Rectangle r)
{ {
Memlayer *l; Memlayer *l;
@ -173,17 +176,24 @@ dstflush(Client *c, int dstid, Memimage *dst, Rectangle r)
addflush(c, r); addflush(c, r);
} }
static static void
void
drawflush(Client *c) drawflush(Client *c)
{ {
if(c->flushrect.min.x < c->flushrect.max.x) Rectangle r;
rpc_flushmemscreen(c, c->flushrect);
r = c->flushrect;
c->flushrect = Rect(10000, 10000, -10000, -10000); c->flushrect = Rect(10000, 10000, -10000, -10000);
if(r.min.x < r.max.x) {
// Unlock drawlk because rpc_flush may want to run on gfx thread,
// and gfx thread might be blocked on drawlk trying to install a new screen
// during a resize.
qunlock(&c->drawlk);
rpc_flush(c, r);
qlock(&c->drawlk);
}
} }
static static int
int
drawcmp(char *a, char *b, int n) drawcmp(char *a, char *b, int n)
{ {
if(strlen(a) != n) if(strlen(a) != n)
@ -191,8 +201,7 @@ drawcmp(char *a, char *b, int n)
return memcmp(a, b, n); return memcmp(a, b, n);
} }
static static DName*
DName*
drawlookupname(Client *client, int n, char *str) drawlookupname(Client *client, int n, char *str)
{ {
DName *name, *ename; DName *name, *ename;
@ -205,8 +214,7 @@ drawlookupname(Client *client, int n, char *str)
return 0; return 0;
} }
static static int
int
drawgoodname(Client *client, DImage *d) drawgoodname(Client *client, DImage *d)
{ {
DName *n; DName *n;
@ -224,8 +232,7 @@ drawgoodname(Client *client, DImage *d)
return 1; return 1;
} }
static static DImage*
DImage*
drawlookup(Client *client, int id, int checkname) drawlookup(Client *client, int id, int checkname)
{ {
DImage *d; DImage *d;
@ -246,8 +253,7 @@ drawlookup(Client *client, int id, int checkname)
return 0; return 0;
} }
static static DScreen*
DScreen*
drawlookupdscreen(Client *c, int id) drawlookupdscreen(Client *c, int id)
{ {
DScreen *s; DScreen *s;
@ -261,8 +267,7 @@ drawlookupdscreen(Client *c, int id)
return 0; return 0;
} }
static static DScreen*
DScreen*
drawlookupscreen(Client *client, int id, CScreen **cs) drawlookupscreen(Client *client, int id, CScreen **cs)
{ {
CScreen *s; CScreen *s;
@ -279,8 +284,7 @@ drawlookupscreen(Client *client, int id, CScreen **cs)
return 0; return 0;
} }
static static Memimage*
Memimage*
drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
{ {
DImage *d; DImage *d;
@ -304,8 +308,7 @@ drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
return i; return i;
} }
static static Memscreen*
Memscreen*
drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
{ {
Memscreen *s; Memscreen *s;
@ -358,8 +361,7 @@ drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *df
return d->screen; return d->screen;
} }
static static void
void
drawdelname(Client *client, DName *name) drawdelname(Client *client, DName *name)
{ {
int i; int i;
@ -369,8 +371,7 @@ drawdelname(Client *client, DName *name)
client->nname--; client->nname--;
} }
static static void
void
drawfreedscreen(Client *client, DScreen *this) drawfreedscreen(Client *client, DScreen *this)
{ {
DScreen *ds, *next; DScreen *ds, *next;
@ -406,8 +407,7 @@ drawfreedscreen(Client *client, DScreen *this)
free(this); free(this);
} }
static static void
void
drawfreedimage(Client *client, DImage *dimage) drawfreedimage(Client *client, DImage *dimage)
{ {
int i; int i;
@ -456,8 +456,7 @@ drawfreedimage(Client *client, DImage *dimage)
free(dimage); free(dimage);
} }
static static void
void
drawuninstallscreen(Client *client, CScreen *this) drawuninstallscreen(Client *client, CScreen *this)
{ {
CScreen *cs, *next; CScreen *cs, *next;
@ -480,8 +479,7 @@ drawuninstallscreen(Client *client, CScreen *this)
} }
} }
static static int
int
drawuninstall(Client *client, int id) drawuninstall(Client *client, int id)
{ {
DImage *d, **l; DImage *d, **l;
@ -496,8 +494,7 @@ drawuninstall(Client *client, int id)
return -1; return -1;
} }
static static int
int
drawaddname(Client *client, DImage *di, int n, char *str, char **err) drawaddname(Client *client, DImage *di, int n, char *str, char **err)
{ {
DName *name, *ename, *new, *t; DName *name, *ename, *new, *t;
@ -541,8 +538,7 @@ drawclientop(Client *cl)
return op; return op;
} }
static static Memimage*
Memimage*
drawimage(Client *client, uchar *a) drawimage(Client *client, uchar *a)
{ {
DImage *d; DImage *d;
@ -553,8 +549,7 @@ drawimage(Client *client, uchar *a)
return d->image; return d->image;
} }
static static void
void
drawrectangle(Rectangle *r, uchar *a) drawrectangle(Rectangle *r, uchar *a)
{ {
r->min.x = BGLONG(a+0*4); r->min.x = BGLONG(a+0*4);
@ -563,16 +558,14 @@ drawrectangle(Rectangle *r, uchar *a)
r->max.y = BGLONG(a+3*4); r->max.y = BGLONG(a+3*4);
} }
static static void
void
drawpoint(Point *p, uchar *a) drawpoint(Point *p, uchar *a)
{ {
p->x = BGLONG(a+0*4); p->x = BGLONG(a+0*4);
p->y = BGLONG(a+1*4); p->y = BGLONG(a+1*4);
} }
static static Point
Point
drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
{ {
FChar *fc; FChar *fc;
@ -592,8 +585,7 @@ drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int ind
return p; return p;
} }
static static uchar*
uchar*
drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
{ {
int b, x; int b, x;
@ -619,9 +611,9 @@ drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
} }
int int
_drawmsgread(Client *cl, void *a, int n) draw_dataread(Client *cl, void *a, int n)
{ {
qlock(&sdraw.lk); qlock(&cl->drawlk);
if(cl->readdata == nil){ if(cl->readdata == nil){
werrstr("no draw data"); werrstr("no draw data");
goto err; goto err;
@ -634,16 +626,16 @@ _drawmsgread(Client *cl, void *a, int n)
memmove(a, cl->readdata, cl->nreaddata); memmove(a, cl->readdata, cl->nreaddata);
free(cl->readdata); free(cl->readdata);
cl->readdata = nil; cl->readdata = nil;
qunlock(&sdraw.lk); qunlock(&cl->drawlk);
return n; return n;
err: err:
qunlock(&sdraw.lk); qunlock(&cl->drawlk);
return -1; return -1;
} }
int int
_drawmsgwrite(Client *client, void *v, int n) draw_datawrite(Client *client, void *v, int n)
{ {
char cbuf[40], *err, ibuf[12*12+1], *s; char cbuf[40], *err, ibuf[12*12+1], *s;
int c, ci, doflush, dstid, e0, e1, esize, j, m; int c, ci, doflush, dstid, e0, e1, esize, j, m;
@ -663,7 +655,7 @@ _drawmsgwrite(Client *client, void *v, int n)
Refreshfn reffn; Refreshfn reffn;
Refx *refx; Refx *refx;
qlock(&sdraw.lk); qlock(&client->drawlk);
a = v; a = v;
m = 0; m = 0;
oldn = n; oldn = n;
@ -1436,7 +1428,7 @@ _drawmsgwrite(Client *client, void *v, int n)
continue; continue;
} }
} }
qunlock(&sdraw.lk); qunlock(&client->drawlk);
return oldn - n; return oldn - n;
Enodrawimage: Enodrawimage:
@ -1506,6 +1498,6 @@ Ebadarg:
error: error:
werrstr("%s", err); werrstr("%s", err);
qunlock(&sdraw.lk); qunlock(&client->drawlk);
return -1; return -1;
} }

View file

@ -7,7 +7,6 @@ typedef struct Mousebuf Mousebuf;
typedef struct Tagbuf Tagbuf; typedef struct Tagbuf Tagbuf;
typedef struct Client Client; typedef struct Client Client;
typedef struct Draw Draw;
typedef struct DImage DImage; typedef struct DImage DImage;
typedef struct DScreen DScreen; typedef struct DScreen DScreen;
typedef struct CScreen CScreen; typedef struct CScreen CScreen;
@ -16,11 +15,6 @@ typedef struct Refresh Refresh;
typedef struct Refx Refx; typedef struct Refx Refx;
typedef struct DName DName; typedef struct DName DName;
struct Draw
{
QLock lk;
};
struct Kbdbuf struct Kbdbuf
{ {
Rune r[256]; Rune r[256];
@ -51,6 +45,19 @@ struct Tagbuf
struct Client struct Client
{ {
int rfd;
// wfdlk protects writes to wfd, which can be issued from either
// the RPC thread or the graphics thread.
QLock wfdlk;
int wfd;
uchar* mbuf;
int nmbuf;
// drawlk protects the draw data structures.
// It can be acquired by an RPC thread or a graphics thread
// but must not be held on one thread while waiting for the other.
QLock drawlk;
/*Ref r;*/ /*Ref r;*/
DImage* dimage[NHASH]; DImage* dimage[NHASH];
CScreen* cscreen; CScreen* cscreen;
@ -64,7 +71,6 @@ struct Client
int refreshme; int refreshme;
int infoid; int infoid;
int op; int op;
int displaydpi; int displaydpi;
int forcedpi; int forcedpi;
int waste; int waste;
@ -75,11 +81,11 @@ struct Client
DName* name; DName* name;
int namevers; int namevers;
int rfd; // Only accessed/modified by the graphics thread.
int wfd;
const void* view; const void* view;
QLock inputlk; // eventlk protects the keyboard and mouse events.
QLock eventlk;
Kbdbuf kbd; Kbdbuf kbd;
Mousebuf mouse; Mousebuf mouse;
Tagbuf kbdtags; Tagbuf kbdtags;
@ -157,30 +163,59 @@ struct DScreen
DScreen* next; DScreen* next;
}; };
int _drawmsgread(Client*, void*, int); // For the most part, the graphics driver-specific code in files
int _drawmsgwrite(Client*, void*, int); // like mac-screen.m runs in the graphics library's main thread,
void _initdisplaymemimage(Client*, Memimage*); // while the RPC service code in srv.c runs on the RPC service thread.
void _drawreplacescreenimage(Client*, Memimage*); // The exceptions in each file, which are called by the other,
// are marked with special prefixes: gfx_* indicates code that
int _latin1(Rune*, int); // is in srv.c but nonetheless runs on the main graphics thread,
int parsewinsize(char*, Rectangle*, int*); // while rpc_* indicates code that is in, say, mac-screen.m but
int mouseswap(int); // nonetheless runs on the RPC service thread.
//
// The gfx_* and rpc_* calls typically synchronize with the other
// code in the file by acquiring a lock (or running a callback on the
// target thread, which amounts to the same thing).
// To avoid deadlock, callers of those routines must not hold any locks.
// gfx_* routines are called on the graphics thread,
// invoked from graphics driver callbacks to do RPC work.
// No locks are held on entry.
void gfx_abortcompose(Client*); void gfx_abortcompose(Client*);
void gfx_keystroke(Client*, int); void gfx_keystroke(Client*, int);
void gfx_main(void);
void gfx_mousetrack(Client*, int, int, int, uint); void gfx_mousetrack(Client*, int, int, int, uint);
void gfx_replacescreenimage(Client*, Memimage*);
void gfx_started(void);
void rpc_setmouse(Client*, Point); // rpc_* routines are called on the RPC thread,
void rpc_setcursor(Client*, Cursor*, Cursor2*); // invoked by the RPC server code to do graphics work.
void rpc_setlabel(Client*, char*); // No locks are held on entry.
void rpc_resizeimg(Client*); Memimage *rpc_attach(Client*, char*, char*);
void rpc_resizewindow(Client*, Rectangle);
void rpc_topwin(Client*);
char* rpc_getsnarf(void); char* rpc_getsnarf(void);
void rpc_putsnarf(char*); void rpc_putsnarf(char*);
Memimage *rpc_attachscreen(Client*, char*, char*); void rpc_resizeimg(Client*);
void rpc_flushmemscreen(Client*, Rectangle); void rpc_resizewindow(Client*, Rectangle);
void rpc_serve(Client*);
void rpc_setcursor(Client*, Cursor*, Cursor2*);
void rpc_setlabel(Client*, char*);
void rpc_setmouse(Client*, Point);
void rpc_shutdown(void);
void rpc_topwin(Client*);
void rpc_main(void);
extern Client *client0; // TODO: rpc_flush is called from draw_datawrite,
// which holds c->drawlk. Is this OK?
void rpc_flush(Client*, Rectangle);
// draw* routines are called on the RPC thread,
// invoked by the RPC server to do pixel pushing.
// c->drawlk is held on entry.
int draw_dataread(Client*, void*, int);
int draw_datawrite(Client*, void*, int);
void draw_initdisplaymemimage(Client*, Memimage*);
// utility routines
int latin1(Rune*, int);
int mouseswap(int);
int parsewinsize(char*, Rectangle*, int*);
void servep9p(Client*);

View file

@ -46,7 +46,7 @@ unicode(Rune *k)
* is minus the required n. * is minus the required n.
*/ */
int int
_latin1(Rune *k, int n) latin1(Rune *k, int n)
{ {
struct cvlist *l; struct cvlist *l;
int c; int c;

View file

@ -34,13 +34,6 @@ static void setprocname(const char*);
static uint keycvt(uint); static uint keycvt(uint);
static uint msec(void); static uint msec(void);
void
usage(void)
{
fprint(2, "usage: devdraw (don't run directly)\n");
threadexitsall("usage");
}
@class DrawView; @class DrawView;
@class DrawLayer; @class DrawLayer;
@ -49,43 +42,9 @@ usage(void)
static AppDelegate *myApp = NULL; static AppDelegate *myApp = NULL;
static QLock snarfl;
void void
threadmain(int argc, char **argv) gfx_main(void)
{ {
/*
* 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': /* for good ps -a listings */
break;
case 'f': /* fall through for backward compatibility */
case 'g':
case 'b':
break;
default:
usage();
}ARGEND
client0 = mallocz(sizeof(Client), 1);
if(client0 == nil){
fprint(2, "initdraw: allocating client0: out of memory");
abort();
}
client0->displaydpi = 100;
client0->rfd = 3;
client0->wfd = 4;
setprocname(argv0); setprocname(argv0);
@autoreleasepool{ @autoreleasepool{
@ -97,12 +56,10 @@ threadmain(int argc, char **argv)
} }
} }
void
callservep9p(void *v)
{
USED(v);
servep9p(client0); void
rpc_shutdown(void)
{
[NSApp terminate:myApp]; [NSApp terminate:myApp];
} }
@ -129,7 +86,7 @@ callservep9p(void *v)
[NSApp setApplicationIconImage:i]; [NSApp setApplicationIconImage:i];
[[NSApp dockTile] display]; [[NSApp dockTile] display];
proccreate(callservep9p, nil, 0); gfx_started();
} }
- (NSApplicationPresentationOptions)window:(id)arg - (NSApplicationPresentationOptions)window:(id)arg
@ -242,10 +199,10 @@ callservep9p(void *v)
- (BOOL)isFlipped { return YES; } - (BOOL)isFlipped { return YES; }
- (BOOL)acceptsFirstResponder { return YES; } - (BOOL)acceptsFirstResponder { return YES; }
// rpc_attachscreen allocates a new screen window with the given label and size // rpc_attach allocates a new screen window with the given label and size
// and attaches it to client c (by setting c->view). // and attaches it to client c (by setting c->view).
Memimage* Memimage*
rpc_attachscreen(Client *c, char *label, char *winsize) rpc_attach(Client *c, char *label, char *winsize)
{ {
LOG(@"attachscreen(%s, %s)", label, winsize); LOG(@"attachscreen(%s, %s)", label, winsize);
@ -468,7 +425,7 @@ rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2)
} }
- (void)initimg { - (void)initimg {
@autoreleasepool{ @autoreleasepool {
CGFloat scale; CGFloat scale;
NSSize size; NSSize size;
MTLTextureDescriptor *textureDesc; MTLTextureDescriptor *textureDesc;
@ -504,35 +461,37 @@ rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2)
// That's not quite right but it's close to correct. // That's not quite right but it's close to correct.
// https://en.wikipedia.org/wiki/Retina_display#Models // https://en.wikipedia.org/wiki/Retina_display#Models
self.client->displaydpi = scale * 110; self.client->displaydpi = scale * 110;
} }
LOG(@"initimg return");
} }
// rpc_flushmemscreen flushes changes to view.img's rectangle r // rpc_flush flushes changes to view.img's rectangle r
// to the on-screen window, making them visible. // to the on-screen window, making them visible.
// Called from an RPC thread with no client lock held. // Called from an RPC thread with no client lock held.
void void
rpc_flushmemscreen(Client *client, Rectangle r) rpc_flush(Client *client, Rectangle r)
{ {
DrawView *view = (__bridge DrawView*)client->view; DrawView *view = (__bridge DrawView*)client->view;
dispatch_async(dispatch_get_main_queue(), ^(void){ dispatch_async(dispatch_get_main_queue(), ^(void){
[view flushmemscreen:r]; [view flush:r];
}); });
} }
- (void)flushmemscreen:(Rectangle)r { - (void)flush:(Rectangle)r {
LOG(@"flushmemscreen(%d,%d,%d,%d)", r.min.x, r.min.y, Dx(r), Dy(r));
if(!rectinrect(r, Rect(0, 0, self.dlayer.texture.width, self.dlayer.texture.height))){
LOG(@"Rectangle is out of bounds, return.");
return;
}
@autoreleasepool{ @autoreleasepool{
if(!rectclip(&r, Rect(0, 0, self.dlayer.texture.width, self.dlayer.texture.height)) || !rectclip(&r, self.img->r))
return;
// self.client->drawlk protects the pixel data in self.img.
// In addition to avoiding a technical data race,
// the lock avoids drawing partial updates, which makes
// animations like sweeping windows much less flickery.
qlock(&self.client->drawlk);
[self.dlayer.texture [self.dlayer.texture
replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, Dx(r), Dy(r)) replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, Dx(r), Dy(r))
mipmapLevel:0 mipmapLevel:0
withBytes:byteaddr(self.img, Pt(r.min.x, r.min.y)) withBytes:byteaddr(self.img, Pt(r.min.x, r.min.y))
bytesPerRow:self.img->width*sizeof(u32int)]; bytesPerRow:self.img->width*sizeof(u32int)];
qunlock(&self.client->drawlk);
NSRect nr = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r)); NSRect nr = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r));
dispatch_time_t time; dispatch_time_t time;
@ -565,7 +524,7 @@ rpc_resizeimg(Client *c)
- (void)resizeimg { - (void)resizeimg {
[self initimg]; [self initimg];
_drawreplacescreenimage(self.client, self.img); gfx_replacescreenimage(self.client, self.img);
[self sendmouse:0]; [self sendmouse:0];
} }
@ -750,7 +709,7 @@ rpc_setmouse(Client *c, Point p)
}); });
} }
-(void)setmouse:(Point)p { - (void)setmouse:(Point)p {
@autoreleasepool{ @autoreleasepool{
NSPoint q; NSPoint q;
@ -782,21 +741,10 @@ rpc_setmouse(Client *c, Point p)
} }
// conforms to protocol NSTextInputClient // conforms to protocol NSTextInputClient
- (BOOL)hasMarkedText - (BOOL)hasMarkedText { return _markedRange.location != NSNotFound; }
{ - (NSRange)markedRange { return _markedRange; }
LOG(@"hasMarkedText"); - (NSRange)selectedRange { return _selectedRange; }
return _markedRange.location != NSNotFound;
}
- (NSRange)markedRange
{
LOG(@"markedRange");
return _markedRange;
}
- (NSRange)selectedRange
{
LOG(@"selectedRange");
return _selectedRange;
}
- (void)setMarkedText:(id)string - (void)setMarkedText:(id)string
selectedRange:(NSRange)sRange selectedRange:(NSRange)sRange
replacementRange:(NSRange)rRange replacementRange:(NSRange)rRange
@ -861,8 +809,8 @@ rpc_setmouse(Client *c, Point p)
_markedRange.location, _markedRange.length, _markedRange.location, _markedRange.length,
_selectedRange.location, _selectedRange.length); _selectedRange.location, _selectedRange.length);
} }
- (void)unmarkText
{ - (void)unmarkText {
//NSUInteger i; //NSUInteger i;
NSUInteger len; NSUInteger len;
@ -874,12 +822,13 @@ rpc_setmouse(Client *c, Point p)
_markedRange = NSMakeRange(NSNotFound, 0); _markedRange = NSMakeRange(NSNotFound, 0);
_selectedRange = NSMakeRange(0, 0); _selectedRange = NSMakeRange(0, 0);
} }
- (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText
{ - (NSArray<NSAttributedStringKey>*)validAttributesForMarkedText {
LOG(@"validAttributesForMarkedText"); LOG(@"validAttributesForMarkedText");
return @[]; return @[];
} }
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)r
- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)r
actualRange:(NSRangePointer)actualRange actualRange:(NSRangePointer)actualRange
{ {
NSRange sr; NSRange sr;
@ -899,9 +848,8 @@ rpc_setmouse(Client *c, Point p)
LOG(@" return %@", s); LOG(@" return %@", s);
return s; return s;
} }
- (void)insertText:(id)s
replacementRange:(NSRange)r - (void)insertText:(id)s replacementRange:(NSRange)r {
{
NSUInteger i; NSUInteger i;
NSUInteger len; NSUInteger len;
@ -916,22 +864,22 @@ rpc_setmouse(Client *c, Point p)
_markedRange = NSMakeRange(NSNotFound, 0); _markedRange = NSMakeRange(NSNotFound, 0);
_selectedRange = NSMakeRange(0, 0); _selectedRange = NSMakeRange(0, 0);
} }
- (NSUInteger)characterIndexForPoint:(NSPoint)point - (NSUInteger)characterIndexForPoint:(NSPoint)point
{ {
LOG(@"characterIndexForPoint: %g, %g", point.x, point.y); LOG(@"characterIndexForPoint: %g, %g", point.x, point.y);
return 0; return 0;
} }
- (NSRect)firstRectForCharacterRange:(NSRange)r
actualRange:(NSRangePointer)actualRange - (NSRect)firstRectForCharacterRange:(NSRange)r actualRange:(NSRangePointer)actualRange {
{
LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)", LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)",
r.location, r.length, actualRange->location, actualRange->length); r.location, r.length, actualRange->location, actualRange->length);
if(actualRange) if(actualRange)
*actualRange = r; *actualRange = r;
return [[self window] convertRectToScreen:_lastInputRect]; return [[self window] convertRectToScreen:_lastInputRect];
} }
- (void)doCommandBySelector:(SEL)s
{ - (void)doCommandBySelector:(SEL)s {
NSEvent *e; NSEvent *e;
NSEventModifierFlags m; NSEventModifierFlags m;
uint c, k; uint c, k;
@ -955,8 +903,7 @@ rpc_setmouse(Client *c, Point p)
} }
// Helper for managing input rect approximately // Helper for managing input rect approximately
- (void)resetLastInputRect - (void)resetLastInputRect {
{
LOG(@"resetLastInputRect"); LOG(@"resetLastInputRect");
_lastInputRect.origin.x = 0.0; _lastInputRect.origin.x = 0.0;
_lastInputRect.origin.y = 0.0; _lastInputRect.origin.y = 0.0;
@ -964,8 +911,7 @@ rpc_setmouse(Client *c, Point p)
_lastInputRect.size.height = 0.0; _lastInputRect.size.height = 0.0;
} }
- (void)enlargeLastInputRect:(NSRect)r - (void)enlargeLastInputRect:(NSRect)r {
{
r.origin.y = [self bounds].size.height - r.origin.y - r.size.height; r.origin.y = [self bounds].size.height - r.origin.y - r.size.height;
_lastInputRect = NSUnionRect(_lastInputRect, r); _lastInputRect = NSUnionRect(_lastInputRect, r);
LOG(@"update last input rect (%g, %g, %g, %g)", LOG(@"update last input rect (%g, %g, %g, %g)",
@ -973,8 +919,7 @@ rpc_setmouse(Client *c, Point p)
_lastInputRect.size.width, _lastInputRect.size.height); _lastInputRect.size.width, _lastInputRect.size.height);
} }
- (void)clearInput - (void)clearInput {
{
if(_tmpText.length){ if(_tmpText.length){
uint i; uint i;
int l; int l;
@ -1079,48 +1024,42 @@ keycvt(uint code)
} }
} }
// TODO // rpc_getsnarf reads the current pasteboard as a plain text string.
// Called from an RPC thread with no client lock held.
char* char*
rpc_getsnarf(void) rpc_getsnarf(void)
{ {
NSPasteboard *pb; char __block *ret;
NSString *s;
@autoreleasepool{
pb = [NSPasteboard generalPasteboard];
qlock(&snarfl);
s = [pb stringForType:NSPasteboardTypeString];
qunlock(&snarfl);
ret = nil;
dispatch_sync(dispatch_get_main_queue(), ^(void) {
@autoreleasepool {
NSPasteboard *pb = [NSPasteboard generalPasteboard];
NSString *s = [pb stringForType:NSPasteboardTypeString];
if(s) if(s)
return strdup((char *)[s UTF8String]); ret = strdup((char*)[s UTF8String]);
else
return nil;
} }
});
return ret;
} }
// TODO // rpc_putsnarf writes the given text to the pasteboard.
// Called from an RPC thread with no client lock held.
void void
rpc_putsnarf(char *s) rpc_putsnarf(char *s)
{ {
NSArray *t; if(s == nil || strlen(s) >= SnarfSize)
NSPasteboard *pb;
NSString *str;
if(strlen(s) >= SnarfSize)
return; return;
dispatch_sync(dispatch_get_main_queue(), ^(void) {
@autoreleasepool{ @autoreleasepool{
t = [NSArray arrayWithObject:NSPasteboardTypeString]; NSArray *t = [NSArray arrayWithObject:NSPasteboardTypeString];
pb = [NSPasteboard generalPasteboard]; NSPasteboard *pb = [NSPasteboard generalPasteboard];
str = [[NSString alloc] initWithUTF8String:s]; NSString *str = [[NSString alloc] initWithUTF8String:s];
qlock(&snarfl);
[pb declareTypes:t owner:nil]; [pb declareTypes:t owner:nil];
[pb setString:str forType:NSPasteboardTypeString]; [pb setString:str forType:NSPasteboardTypeString];
qunlock(&snarfl);
} }
});
} }
static void static void

View file

@ -18,18 +18,72 @@ static void runmsg(Client*, Wsysmsg*);
static void replymsg(Client*, Wsysmsg*); static void replymsg(Client*, Wsysmsg*);
static void matchkbd(Client*); static void matchkbd(Client*);
static void matchmouse(Client*); static void matchmouse(Client*);
static void serve(void*);
static Client *client0;
int trace = 0; int trace = 0;
void static void
servep9p(Client *c) usage(void)
{ {
fprint(2, "usage: devdraw (don't run directly)\n");
threadexitsall("usage");
}
void
threadmain(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': /* for good ps -a listings */
break;
case 'f': /* fall through for backward compatibility */
case 'g':
case 'b':
break;
default:
usage();
}ARGEND
fmtinstall('W', drawfcallfmt);
client0 = mallocz(sizeof(Client), 1);
if(client0 == nil){
fprint(2, "initdraw: allocating client0: out of memory");
abort();
}
client0->displaydpi = 100;
client0->rfd = 3;
client0->wfd = 4;
gfx_main();
}
void
gfx_started(void)
{
proccreate(serve, client0, 0);
}
static void
serve(void *v)
{
Client *c;
uchar buf[4], *mbuf; uchar buf[4], *mbuf;
int nmbuf, n, nn; int nmbuf, n, nn;
Wsysmsg m; Wsysmsg m;
fmtinstall('W', drawfcallfmt); c = v;
mbuf = nil; mbuf = nil;
nmbuf = 0; nmbuf = 0;
while((n = read(c->rfd, buf, 4)) == 4){ while((n = read(c->rfd, buf, 4)) == 4){
@ -52,6 +106,9 @@ servep9p(Client *c)
if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m); if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m);
runmsg(c, &m); runmsg(c, &m);
} }
rpc_shutdown();
threadexitsall(nil);
} }
static void static void
@ -79,13 +136,13 @@ runmsg(Client *c, Wsysmsg *m)
switch(m->type){ switch(m->type){
case Tinit: case Tinit:
memimageinit(); memimageinit();
i = rpc_attachscreen(c, m->label, m->winsize); i = rpc_attach(c, m->label, m->winsize);
_initdisplaymemimage(c, i); draw_initdisplaymemimage(c, i);
replymsg(c, m); replymsg(c, m);
break; break;
case Trdmouse: case Trdmouse:
qlock(&c->inputlk); qlock(&c->eventlk);
c->mousetags.t[c->mousetags.wi++] = m->tag; c->mousetags.t[c->mousetags.wi++] = m->tag;
if(c->mousetags.wi == nelem(c->mousetags.t)) if(c->mousetags.wi == nelem(c->mousetags.t))
c->mousetags.wi = 0; c->mousetags.wi = 0;
@ -93,11 +150,11 @@ runmsg(Client *c, Wsysmsg *m)
sysfatal("too many queued mouse reads"); sysfatal("too many queued mouse reads");
c->mouse.stall = 0; c->mouse.stall = 0;
matchmouse(c); matchmouse(c);
qunlock(&c->inputlk); qunlock(&c->eventlk);
break; break;
case Trdkbd: case Trdkbd:
qlock(&c->inputlk); qlock(&c->eventlk);
c->kbdtags.t[c->kbdtags.wi++] = m->tag; c->kbdtags.t[c->kbdtags.wi++] = m->tag;
if(c->kbdtags.wi == nelem(c->kbdtags.t)) if(c->kbdtags.wi == nelem(c->kbdtags.t))
c->kbdtags.wi = 0; c->kbdtags.wi = 0;
@ -105,7 +162,7 @@ runmsg(Client *c, Wsysmsg *m)
sysfatal("too many queued keyboard reads"); sysfatal("too many queued keyboard reads");
c->kbd.stall = 0; c->kbd.stall = 0;
matchkbd(c); matchkbd(c);
qunlock(&c->inputlk); qunlock(&c->eventlk);
break; break;
case Tmoveto: case Tmoveto:
@ -148,16 +205,15 @@ runmsg(Client *c, Wsysmsg *m)
break; break;
case Twrsnarf: case Twrsnarf:
putsnarf(m->snarf); rpc_putsnarf(m->snarf);
replymsg(c, m); replymsg(c, m);
break; break;
case Trddraw: case Trddraw:
qlock(&c->inputlk);
n = m->count; n = m->count;
if(n > sizeof buf) if(n > sizeof buf)
n = sizeof buf; n = sizeof buf;
n = _drawmsgread(c, buf, n); n = draw_dataread(c, buf, n);
if(n < 0) if(n < 0)
replyerror(c, m); replyerror(c, m);
else{ else{
@ -165,16 +221,13 @@ runmsg(Client *c, Wsysmsg *m)
m->data = buf; m->data = buf;
replymsg(c, m); replymsg(c, m);
} }
qunlock(&c->inputlk);
break; break;
case Twrdraw: case Twrdraw:
qlock(&c->inputlk); if(draw_datawrite(c, m->data, m->count) < 0)
if(_drawmsgwrite(c, m->data, m->count) < 0)
replyerror(c, m); replyerror(c, m);
else else
replymsg(c, m); replymsg(c, m);
qunlock(&c->inputlk);
break; break;
case Ttop: case Ttop:
@ -192,13 +245,10 @@ runmsg(Client *c, Wsysmsg *m)
/* /*
* Reply to m. * Reply to m.
*/ */
QLock replylock;
static void static void
replymsg(Client *c, Wsysmsg *m) replymsg(Client *c, Wsysmsg *m)
{ {
int n; int n;
static uchar *mbuf;
static int nmbuf;
/* T -> R msg */ /* T -> R msg */
if(m->type%2 == 0) if(m->type%2 == 0)
@ -208,18 +258,18 @@ replymsg(Client *c, Wsysmsg *m)
/* copy to output buffer */ /* copy to output buffer */
n = sizeW2M(m); n = sizeW2M(m);
qlock(&replylock); qlock(&c->wfdlk);
if(n > nmbuf){ if(n > c->nmbuf){
free(mbuf); free(c->mbuf);
mbuf = malloc(n); c->mbuf = malloc(n);
if(mbuf == nil) if(c->mbuf == nil)
sysfatal("out of memory"); sysfatal("out of memory");
nmbuf = n; c->nmbuf = n;
} }
convW2M(m, mbuf, n); convW2M(m, c->mbuf, n);
if(write(c->wfd, mbuf, n) != n) if(write(c->wfd, c->mbuf, n) != n)
sysfatal("write: %r"); sysfatal("write: %r");
qunlock(&replylock); qunlock(&c->wfdlk);
} }
/* /*
@ -245,13 +295,13 @@ matchkbd(Client *c)
} }
// matchmouse matches queued mouse reads with queued mouse events. // matchmouse matches queued mouse reads with queued mouse events.
// It must be called with c->inputlk held. // It must be called with c->eventlk held.
static void static void
matchmouse(Client *c) matchmouse(Client *c)
{ {
Wsysmsg m; Wsysmsg m;
if(canqlock(&c->inputlk)) { if(canqlock(&c->eventlk)) {
fprint(2, "misuse of matchmouse\n"); fprint(2, "misuse of matchmouse\n");
abort(); abort();
} }
@ -280,7 +330,7 @@ gfx_mousetrack(Client *c, int x, int y, int b, uint ms)
{ {
Mouse *m; Mouse *m;
qlock(&c->inputlk); qlock(&c->eventlk);
if(x < c->mouserect.min.x) if(x < c->mouserect.min.x)
x = c->mouserect.min.x; x = c->mouserect.min.x;
if(x > c->mouserect.max.x) if(x > c->mouserect.max.x)
@ -312,15 +362,15 @@ gfx_mousetrack(Client *c, int x, int y, int b, uint ms)
} }
matchmouse(c); matchmouse(c);
} }
qunlock(&c->inputlk); qunlock(&c->eventlk);
} }
// kputc adds ch to the keyboard buffer. // kputc adds ch to the keyboard buffer.
// It must be called with c->inputlk held. // It must be called with c->eventlk held.
static void static void
kputc(Client *c, int ch) kputc(Client *c, int ch)
{ {
if(canqlock(&c->inputlk)) { if(canqlock(&c->eventlk)) {
fprint(2, "misuse of kputc\n"); fprint(2, "misuse of kputc\n");
abort(); abort();
} }
@ -339,12 +389,12 @@ kputc(Client *c, int ch)
void void
gfx_abortcompose(Client *c) gfx_abortcompose(Client *c)
{ {
qlock(&c->inputlk); qlock(&c->eventlk);
if(c->kbd.alting) { if(c->kbd.alting) {
c->kbd.alting = 0; c->kbd.alting = 0;
c->kbd.nk = 0; c->kbd.nk = 0;
} }
qunlock(&c->inputlk); qunlock(&c->eventlk);
} }
// gfx_keystroke records a single-rune keystroke. // gfx_keystroke records a single-rune keystroke.
@ -354,11 +404,11 @@ gfx_keystroke(Client *c, int ch)
{ {
int i; int i;
qlock(&c->inputlk); qlock(&c->eventlk);
if(ch == Kalt){ if(ch == Kalt){
c->kbd.alting = !c->kbd.alting; c->kbd.alting = !c->kbd.alting;
c->kbd.nk = 0; c->kbd.nk = 0;
qunlock(&c->inputlk); qunlock(&c->eventlk);
return; return;
} }
if(ch == Kcmd+'r') { if(ch == Kcmd+'r') {
@ -368,24 +418,24 @@ gfx_keystroke(Client *c, int ch)
c->forcedpi = 100; c->forcedpi = 100;
else else
c->forcedpi = 225; c->forcedpi = 225;
qunlock(&c->inputlk); qunlock(&c->eventlk);
rpc_resizeimg(c); rpc_resizeimg(c);
return; return;
} }
if(!c->kbd.alting){ if(!c->kbd.alting){
kputc(c, ch); kputc(c, ch);
qunlock(&c->inputlk); qunlock(&c->eventlk);
return; return;
} }
if(c->kbd.nk >= nelem(c->kbd.k)) // should not happen if(c->kbd.nk >= nelem(c->kbd.k)) // should not happen
c->kbd.nk = 0; c->kbd.nk = 0;
c->kbd.k[c->kbd.nk++] = ch; c->kbd.k[c->kbd.nk++] = ch;
ch = _latin1(c->kbd.k, c->kbd.nk); ch = latin1(c->kbd.k, c->kbd.nk);
if(ch > 0){ if(ch > 0){
c->kbd.alting = 0; c->kbd.alting = 0;
kputc(c, ch); kputc(c, ch);
c->kbd.nk = 0; c->kbd.nk = 0;
qunlock(&c->inputlk); qunlock(&c->eventlk);
return; return;
} }
if(ch == -1){ if(ch == -1){
@ -393,10 +443,10 @@ gfx_keystroke(Client *c, int ch)
for(i=0; i<c->kbd.nk; i++) for(i=0; i<c->kbd.nk; i++)
kputc(c, c->kbd.k[i]); kputc(c, c->kbd.k[i]);
c->kbd.nk = 0; c->kbd.nk = 0;
qunlock(&c->inputlk); qunlock(&c->eventlk);
return; return;
} }
// need more input // need more input
qunlock(&c->inputlk); qunlock(&c->eventlk);
return; return;
} }

View file

@ -733,6 +733,6 @@ _xreplacescreenimage(void)
XFreePixmap(_x.display, _x.nextscreenpm); XFreePixmap(_x.display, _x.nextscreenpm);
_x.nextscreenpm = pixmap; _x.nextscreenpm = pixmap;
_x.screenr = r; _x.screenr = r;
_drawreplacescreenimage(m); gfx_replacescreenimage(m);
return 1; return 1;
} }

View file

@ -146,7 +146,7 @@ abortcompose(void)
static Rune* sendrune(Rune); static Rune* sendrune(Rune);
extern int _latin1(Rune*, int); extern int latin1(Rune*, int);
static Rune* static Rune*
xtoplan9latin1(XEvent *e) xtoplan9latin1(XEvent *e)
{ {
@ -182,7 +182,7 @@ sendrune(Rune r)
return nil; return nil;
} }
k[nk++] = r; k[nk++] = r;
n = _latin1(k, nk); n = latin1(k, nk);
if(n > 0){ if(n > 0){
alting = 0; alting = 0;
k[0] = n; k[0] = n;

View file

@ -365,7 +365,7 @@ runmsg(Wsysmsg *m)
n = m->count; n = m->count;
if(n > sizeof buf) if(n > sizeof buf)
n = sizeof buf; n = sizeof buf;
n = _drawmsgread(buf, n); n = draw_dataread(buf, n);
if(n < 0) if(n < 0)
replyerror(m); replyerror(m);
else{ else{
@ -376,7 +376,7 @@ runmsg(Wsysmsg *m)
break; break;
case Twrdraw: case Twrdraw:
if(_drawmsgwrite(m->data, m->count) < 0) if(draw_datawrite(m->data, m->count) < 0)
replyerror(m); replyerror(m);
else else
replymsg(m); replymsg(m);