git: 9front

Download patch

ref: 61a7e15cb1ddb7b08f9bcbdb035e39af3d6a5e8d
parent: ff14120af134f3b2159062cef6eae87991939e92
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Sun Apr 14 12:28:54 EDT 2013

reduce software cursor flickering

the software cursor starts flickering and reacts bumby if a process
spends most of its time with drawlock acquired because the timer interrupt
thats supposed to redraw the cursor fails to acquire the lock at the time
the timer fires.

instead of trying to draw the cursor on the screen from a timer interrupt
30 times per second, devmouse now creates a process calling cursoron() and
cursoroff() when the cursor needs to be redrawn. this allows the swcursor
to schedule a redraw while holding the drawlock in swcursoravoid() and
cursoron()/cursoroff() are now able to wait for a qlock (drawlock) because
they get called from process context.

the overall responsiveness is also improved with this change as the cursor
redraw rate isnt limited to 30 times a second anymore.

--- a/sys/src/9/bcm/fns.h
+++ b/sys/src/9/bcm/fns.h
@@ -64,7 +64,6 @@
 extern void setr13(int, u32int*);
 extern int splfhi(void);
 extern int splflo(void);
-extern void swcursorinit(void);
 extern int tas(void *);
 extern void touser(uintptr);
 extern void trapinit(void);
--- a/sys/src/9/bcm/main.c
+++ b/sys/src/9/bcm/main.c
@@ -249,8 +249,6 @@
 	clockinit();
 	printinit();
 	timersinit();
-	if(conf.monitor)
-		swcursorinit();
 	cpuidprint();
 	archreset();
 
--- a/sys/src/9/bcm/picpuf
+++ b/sys/src/9/bcm/picpuf
@@ -14,7 +14,7 @@
 	cap
 	fs
 	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
-	draw	screen
+	draw	screen swcursor
 	mouse	mouse
 	uart
 
--- a/sys/src/9/bcm/pif
+++ b/sys/src/9/bcm/pif
@@ -14,7 +14,7 @@
 	cap
 	fs
 	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
-	draw	screen
+	draw	screen swcursor
 	mouse	mouse
 	uart
 
--- a/sys/src/9/bcm/screen.c
+++ b/sys/src/9/bcm/screen.c
@@ -69,205 +69,34 @@
 static void screenputc(char *buf);
 static void screenwin(void);
 
-/*
- * Software cursor. 
- */
-static int	swvisible;	/* is the cursor visible? */
-static int	swenabled;	/* is the cursor supposed to be on the screen? */
-static Memimage *swback;	/* screen under cursor */
-static Memimage *swimg;		/* cursor image */
-static Memimage *swmask;	/* cursor mask */
-static Memimage *swimg1;
-static Memimage *swmask1;
-
-static Point	swoffset;
-static Rectangle swrect;	/* screen rectangle in swback */
-static Point	swpt;		/* desired cursor location */
-static Point	swvispt;	/* actual cursor location */
-static int	swvers;		/* incremented each time cursor image changes */
-static int	swvisvers;	/* the version on the screen */
-
-/*
- * called with drawlock locked for us, most of the time.
- * kernel prints at inopportune times might mean we don't
- * hold the lock, but memimagedraw is now reentrant so
- * that should be okay: worst case we get cursor droppings.
- */
-static void
-swcursorhide(void)
+void
+cursoron(void)
 {
-	if(swvisible == 0)
-		return;
-	if(swback == nil)
-		return;
-	swvisible = 0;
-	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
-	flushmemscreen(swrect);
+	qlock(&drawlock);
+	lock(&cursor);
+	swcursorhide();
+	swcursordraw(mousexy());
+	unlock(&cursor);
+	qunlock(&drawlock);
 }
 
-static void
-swcursoravoid(Rectangle r)
-{
-	if(swvisible && rectXrect(r, swrect))
-		swcursorhide();
-}
-
-static void
-swcursordraw(void)
-{
-	int dounlock;
-
-	if(swvisible)
-		return;
-	if(swenabled == 0)
-		return;
-	if(swback == nil || swimg1 == nil || swmask1 == nil)
-		return;
-	dounlock = canqlock(&drawlock);
-	swvispt = swpt;
-	swvisvers = swvers;
-	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
-	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
-	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
-	flushmemscreen(swrect);
-	swvisible = 1;
-	if(dounlock)
-		qunlock(&drawlock);
-}
-
-int
-cursoron(int dolock)
-{
-	int retry;
-
-	if (dolock)
-		lock(&cursor);
-	if (canqlock(&drawlock)) {
-		retry = 0;
-		swcursorhide();
-		swcursordraw();
-		qunlock(&drawlock);
-	} else
-		retry = 1;
-	if (dolock)
-		unlock(&cursor);
-	return retry;
-}
-
 void
-cursoroff(int dolock)
+cursoroff(void)
 {
-	if (dolock)
-		lock(&cursor);
+	qlock(&drawlock);
+	lock(&cursor);
 	swcursorhide();
-	if (dolock)
-		unlock(&cursor);
+	unlock(&cursor);
+	qunlock(&drawlock);
 }
 
-static void
-swload(Cursor *curs)
-{
-	uchar *ip, *mp;
-	int i, j, set, clr;
-
-	if(!swimg || !swmask || !swimg1 || !swmask1)
-		return;
-	/*
-	 * Build cursor image and mask.
-	 * Image is just the usual cursor image
-	 * but mask is a transparent alpha mask.
-	 * 
-	 * The 16x16x8 memimages do not have
-	 * padding at the end of their scan lines.
-	 */
-	ip = byteaddr(swimg, ZP);
-	mp = byteaddr(swmask, ZP);
-	for(i=0; i<32; i++){
-		set = curs->set[i];
-		clr = curs->clr[i];
-		for(j=0x80; j; j>>=1){
-			*ip++ = set&j ? 0x00 : 0xFF;
-			*mp++ = (clr|set)&j ? 0xFF : 0x00;
-		}
-	}
-	swoffset = curs->offset;
-	swvers++;
-	memimagedraw(swimg1,  swimg1->r,  swimg,  ZP, memopaque, ZP, S);
-	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
-}
-
 /* called from devmouse */
 void
 setcursor(Cursor* curs)
 {
-	cursoroff(0);
-	swload(curs);
-	cursoron(0);
+	swcursorload(curs);
 }
 
-static int
-swmove(Point p)
-{
-	swpt = addpt(p, swoffset);
-	return 0;
-}
-
-static void
-swcursorclock(void)
-{
-	int x;
-
-	if(!swenabled)
-		return;
-	swmove(mousexy());
-	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
-		return;
-
-	x = splhi();
-	if(swenabled)
-	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
-	if(canqlock(&drawlock)){
-		swcursorhide();
-		swcursordraw();
-		qunlock(&drawlock);
-	}
-	splx(x);
-}
-
-void
-swcursorinit(void)
-{
-	static int init;
-
-	if(!init){
-		init = 1;
-		addclock0link(swcursorclock, 10);
-		swenabled = 1;
-	}
-	if(swback){
-		freememimage(swback);
-		freememimage(swmask);
-		freememimage(swmask1);
-		freememimage(swimg);
-		freememimage(swimg1);
-	}
-
-	swback  = allocmemimage(Rect(0,0,32,32), gscreen->chan);
-	swmask  = allocmemimage(Rect(0,0,16,16), GREY8);
-	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
-	swimg   = allocmemimage(Rect(0,0,16,16), GREY8);
-	swimg1  = allocmemimage(Rect(0,0,16,16), GREY1);
-	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
-		print("software cursor: allocmemimage fails\n");
-		return;
-	}
-
-	memfillcolor(swmask, DOpaque);
-	memfillcolor(swmask1, DOpaque);
-	memfillcolor(swimg, DBlack);
-	memfillcolor(swimg1, DBlack);
-}
-
 int
 hwdraw(Memdrawparam *par)
 {
@@ -348,6 +177,7 @@
 	memdefont = getmemdefont();
 	screenwin();
 	screenputs = myscreenputs;
+	swcursorinit();
 }
 
 void
--- a/sys/src/9/bcm/screen.h
+++ b/sys/src/9/bcm/screen.h
@@ -21,13 +21,14 @@
 /* mouse.c */
 extern void mousectl(Cmdbuf*);
 extern void mouseresize(void);
+extern void mouseredraw(void);
 
 /* screen.c */
 extern void	blankscreen(int);
 extern void	flushmemscreen(Rectangle);
 extern uchar*	attachscreen(Rectangle*, ulong*, int*, int*, int*);
-extern int	cursoron(int);
-extern void	cursoroff(int);
+extern void	cursoron(void);
+extern void	cursoroff(void);
 extern void	setcursor(Cursor*);
 
 /* devdraw.c */
@@ -34,3 +35,10 @@
 extern QLock	drawlock;
 
 #define ishwimage(i)	1		/* for ../port/devdraw.c */
+
+/* swcursor.c */
+void		swcursorhide(void);
+void		swcursoravoid(Rectangle);
+void		swcursordraw(Point);
+void		swcursorload(Cursor *);
+void		swcursorinit(void);
--- a/sys/src/9/omap/beagle
+++ b/sys/src/9/omap/beagle
@@ -24,7 +24,7 @@
 	ether		netif
 	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
 
-	draw		screen
+	draw		screen swcursor
 	dss
 	mouse
 
--- a/sys/src/9/omap/screen.c
+++ b/sys/src/9/omap/screen.c
@@ -346,174 +346,33 @@
 	blankscreen(on == 0);
 }
 
-/*
- * called with drawlock locked for us, most of the time.
- * kernel prints at inopportune times might mean we don't
- * hold the lock, but memimagedraw is now reentrant so
- * that should be okay: worst case we get cursor droppings.
- */
 void
-swcursorhide(void)
+cursoron(void)
 {
-	if(swvisible == 0)
-		return;
-	if(swback == nil)
-		return;
-	swvisible = 0;
-	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
-	flushmemscreen(swrect);
+	qlock(&drawlock);
+	lock(&cursor);
+	swcursorhide();
+	swcursordraw(mousexy());
+	unlock(&cursor);
+	qunlock(&drawlock);
 }
 
 void
-swcursoravoid(Rectangle r)
+cursoroff(void)
 {
-	if(swvisible && rectXrect(r, swrect))
-		swcursorhide();
-}
-
-void
-swcursordraw(void)
-{
-	if(swvisible)
-		return;
-	if(swenabled == 0)
-		return;
-	if(swback == nil || swimg1 == nil || swmask1 == nil)
-		return;
-//	assert(!canqlock(&drawlock));		// assertion fails on omap
-	swvispt = swpt;
-	swvisvers = swvers;
-	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
-	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
-	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
-	flushmemscreen(swrect);
-	swvisible = 1;
-}
-
-int
-cursoron(int dolock)
-{
-	if (dolock)
-		lock(&oscreen);
-	cursoroff(0);
-	swcursordraw();
-	if (dolock)
-		unlock(&oscreen);
-	return 0;
-}
-
-void
-cursoroff(int dolock)
-{
-	if (dolock)
-		lock(&oscreen);
+	qlock(&drawlock);
+	lock(&cursor);
 	swcursorhide();
-	if (dolock)
-		unlock(&oscreen);
+	unlock(&cursor);
+	qunlock(&drawlock);
 }
 
-void
-swload(Cursor *curs)
-{
-	uchar *ip, *mp;
-	int i, j, set, clr;
-
-	if(!swimg || !swmask || !swimg1 || !swmask1)
-		return;
-	/*
-	 * Build cursor image and mask.
-	 * Image is just the usual cursor image
-	 * but mask is a transparent alpha mask.
-	 * 
-	 * The 16x16x8 memimages do not have
-	 * padding at the end of their scan lines.
-	 */
-	ip = byteaddr(swimg, ZP);
-	mp = byteaddr(swmask, ZP);
-	for(i=0; i<32; i++){
-		set = curs->set[i];
-		clr = curs->clr[i];
-		for(j=0x80; j; j>>=1){
-			*ip++ = set&j ? 0x00 : 0xFF;
-			*mp++ = (clr|set)&j ? 0xFF : 0x00;
-		}
-	}
-	swoffset = curs->offset;
-	swvers++;
-	memimagedraw(swimg1,  swimg1->r,  swimg,  ZP, memopaque, ZP, S);
-	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
-}
-
 /* called from devmouse */
 void
 setcursor(Cursor* curs)
 {
-	cursoroff(1);
 	oscreen.Cursor = *curs;
-	swload(curs);
-	cursoron(1);
-}
-
-int
-swmove(Point p)
-{
-	swpt = addpt(p, swoffset);
-	return 0;
-}
-
-void
-swcursorclock(void)
-{
-	int x;
-
-	if(!swenabled)
-		return;
-	swmove(mousexy());
-	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
-		return;
-
-	x = splhi();
-	if(swenabled)
-	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
-	if(canqlock(&drawlock)){
-		swcursorhide();
-		swcursordraw();
-		qunlock(&drawlock);
-	}
-	splx(x);
-}
-
-void
-swcursorinit(void)
-{
-	static int init;
-
-	if(!init){
-		init = 1;
-		addclock0link(swcursorclock, 10);
-	}
-	if(swback){
-		freememimage(swback);
-		freememimage(swmask);
-		freememimage(swmask1);
-		freememimage(swimg);
-		freememimage(swimg1);
-	}
-
-	swback  = allocmemimage(Rect(0,0,32,32), gscreen->chan);
-	swmask  = allocmemimage(Rect(0,0,16,16), GREY8);
-	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
-	swimg   = allocmemimage(Rect(0,0,16,16), GREY8);
-	swimg1  = allocmemimage(Rect(0,0,16,16), GREY1);
-	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
-		print("software cursor: allocmemimage fails\n");
-		return;
-	}
-
-	memfillcolor(swmask, DOpaque);
-	memfillcolor(swmask1, DOpaque);
-	memfillcolor(swimg, DBlack);
-	memfillcolor(swimg1, DBlack);
+	swcursorload(curs);
 }
 
 /* called from main and possibly later from devdss to change resolution */
--- a/sys/src/9/omap/screen.h
+++ b/sys/src/9/omap/screen.h
@@ -20,12 +20,13 @@
 
 extern void	mouseaccelerate(int);
 extern void	mouseresize(void);
+extern void	mouseredraw(void);
 
 /* screen.c */
 extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*);
 extern void	flushmemscreen(Rectangle);
-extern int	cursoron(int);
-extern void	cursoroff(int);
+extern void	cursoron(void);
+extern void	cursoroff(void);
 extern void	setcursor(Cursor*);
 extern int	screensize(int, int, int, ulong);
 extern int	screenaperture(int, int);
@@ -49,6 +50,13 @@
 
 #define ishwimage(i)	0		/* for ../port/devdraw.c */
 
+/* swcursor.c */
+void		swcursorhide(void);
+void		swcursoravoid(Rectangle);
+void		swcursordraw(Point);
+void		swcursorload(Cursor *);
+void		swcursorinit(void);
+
 /* for communication between devdss.c and screen.c */
 
 enum {
@@ -93,7 +101,6 @@
 };
 
 struct OScreen {
-	Lock;
 	Cursor;
 	Settings *settings;
 	int	open;
--- a/sys/src/9/pc/devvga.c
+++ b/sys/src/9/pc/devvga.c
@@ -349,7 +349,7 @@
 		if(chantodepth(chan) != z)
 			error("depth, channel do not match");
 
-		cursoroff(1);
+		cursoroff();
 		deletescreenimage();
 		if(screensize(x, y, z, chan))
 			error(Egreg);
@@ -397,7 +397,7 @@
 		y = scr->gscreen->r.max.y;
 		z = scr->gscreen->depth;
 		chan = scr->gscreen->chan;
-		cursoroff(1);
+		cursoroff();
 		deletescreenimage();
 		if(screensize(x, y, z, chan))
 			error(Egreg);
@@ -411,7 +411,7 @@
 		hwaccel = !scr->softscreen && (scr->scroll || scr->fill);
 		vgascreenwin(scr);
 		resetscreenimage();
-		cursoron(1);
+		cursoron();
 		return;
 
 	case CMlinear:
--- a/sys/src/9/pc/pccpuf
+++ b/sys/src/9/pc/pccpuf
@@ -23,7 +23,7 @@
 	ether		netif
 	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum386 inferno
 
-	draw		screen vga vgax
+	draw		screen vga vgax swcursor
 	mouse		mouse
 	kbd
 	vga
--- a/sys/src/9/pc/pcf
+++ b/sys/src/9/pc/pcf
@@ -21,7 +21,7 @@
 	ether		netif
 	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum386 inferno
 
-	draw		screen vga vgax
+	draw		screen vga vgax swcursor
 	mouse		mouse
 	kbd
 	vga
--- a/sys/src/9/pc/screen.c
+++ b/sys/src/9/pc/screen.c
@@ -23,7 +23,7 @@
 
 VGAscr vgascreen[1];
 
-Cursor	arrow = {
+Cursor arrow = {
 	{ -1, -1 },
 	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
 	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
@@ -37,8 +37,6 @@
 	},
 };
 
-int didswcursorinit;
-
 int
 screensize(int x, int y, int, ulong chan)
 {
@@ -101,8 +99,7 @@
 	poperror();
 
 	drawcmap();
-	if(didswcursorinit)
-		swcursorinit();
+	swcursorinit();
 
 	qunlock(&drawlock);
 	poperror();
@@ -327,27 +324,63 @@
 	return setpalette(p, r, g, b);
 }
 
+void
+swenable(VGAscr*)
+{
+	swcursorload(&arrow);
+}
+
+void
+swdisable(VGAscr*)
+{
+}
+
+void
+swload(VGAscr*, Cursor *curs)
+{
+	swcursorload(curs);
+}
+
 int
-cursoron(int dolock)
+swmove(VGAscr*, Point p)
 {
+	swcursorhide();
+	swcursordraw(p);
+	return 0;
+}
+
+VGAcur swcursor =
+{
+	"soft",
+	swenable,
+	swdisable,
+	swload,
+	swmove,
+};
+
+
+void
+cursoron(void)
+{
 	VGAscr *scr;
-	int v;
+	VGAcur *cur;
 
 	scr = &vgascreen[0];
-	if(scr->cur == nil || scr->cur->move == nil)
-		return 0;
+	cur = scr->cur;
+	if(cur == nil || cur->move == nil)
+		return;
 
-	if(dolock)
-		lock(&cursor);
-	v = scr->cur->move(scr, mousexy());
-	if(dolock)
-		unlock(&cursor);
-
-	return v;
+	if(cur == &swcursor)
+		qlock(&drawlock);
+	lock(&cursor);
+	cur->move(scr, mousexy());
+	unlock(&cursor);
+	if(cur == &swcursor)
+		qunlock(&drawlock);
 }
 
 void
-cursoroff(int)
+cursoroff(void)
 {
 }
 
@@ -532,196 +565,3 @@
 		poperror();
 	}
 }
-
-
-/*
- * Software cursor. 
- */
-int	swvisible;	/* is the cursor visible? */
-int	swenabled;	/* is the cursor supposed to be on the screen? */
-Memimage*	swback;	/* screen under cursor */
-Memimage*	swimg;	/* cursor image */
-Memimage*	swmask;	/* cursor mask */
-Memimage*	swimg1;
-Memimage*	swmask1;
-
-Point	swoffset;
-Rectangle	swrect;	/* screen rectangle in swback */
-Point	swpt;	/* desired cursor location */
-Point	swvispt;	/* actual cursor location */
-int	swvers;	/* incremented each time cursor image changes */
-int	swvisvers;	/* the version on the screen */
-
-/*
- * called with drawlock locked for us, most of the time.
- * kernel prints at inopportune times might mean we don't
- * hold the lock, but memimagedraw is now reentrant so
- * that should be okay: worst case we get cursor droppings.
- */
-void
-swcursorhide(void)
-{
-	if(swvisible == 0)
-		return;
-	if(swback == nil)
-		return;
-	swvisible = 0;
-	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
-	flushmemscreen(swrect);
-}
-
-void
-swcursoravoid(Rectangle r)
-{
-	if(swvisible && rectXrect(r, swrect))
-		swcursorhide();
-}
-
-void
-swcursordraw(void)
-{
-	if(swvisible)
-		return;
-	if(swenabled == 0)
-		return;
-	if(swback == nil || swimg1 == nil || swmask1 == nil)
-		return;
-	assert(!canqlock(&drawlock));
-	swvispt = swpt;
-	swvisvers = swvers;
-	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
-	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
-	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
-	flushmemscreen(swrect);
-	swvisible = 1;
-}
-
-void
-swload(VGAscr*, Cursor *curs)
-{
-	uchar *ip, *mp;
-	int i, j, set, clr;
-
-	if(!swimg || !swmask || !swimg1 || !swmask1)
-		return;
-	/*
-	 * Build cursor image and mask.
-	 * Image is just the usual cursor image
-	 * but mask is a transparent alpha mask.
-	 * 
-	 * The 16x16x8 memimages do not have
-	 * padding at the end of their scan lines.
-	 */
-	ip = byteaddr(swimg, ZP);
-	mp = byteaddr(swmask, ZP);
-	for(i=0; i<32; i++){
-		set = curs->set[i];
-		clr = curs->clr[i];
-		for(j=0x80; j; j>>=1){
-			*ip++ = set&j ? 0x00 : 0xFF;
-			*mp++ = (clr|set)&j ? 0xFF : 0x00;
-		}
-	}
-	swoffset = curs->offset;
-	swvers++;
-	memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
-	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
-}
-
-int
-swmove(VGAscr*, Point p)
-{
-	swpt = addpt(p, swoffset);
-	return 0;
-}
-
-void
-swcursorclock(void)
-{
-	int x;
-
-	if(!swenabled)
-		return;
-	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
-		return;
-
-	x = splhi();
-	if(swenabled)
-	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
-	if(canqlock(&drawlock)){
-		swcursorhide();
-		swcursordraw();
-		qunlock(&drawlock);
-	}
-	splx(x);
-}
-
-void
-swcursorinit(void)
-{
-	static int init;
-	VGAscr *scr;
-
-	didswcursorinit = 1;
-	if(!init){
-		init = 1;
-		addclock0link(swcursorclock, 10);
-	}
-
-	scr = &vgascreen[0];
-	if(scr->gscreen==nil)
-		return;
-
-	if(swback){
-		freememimage(swback);
-		freememimage(swmask);
-		freememimage(swmask1);
-		freememimage(swimg);
-		freememimage(swimg1); 
-	}
-	swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
-	swmask = allocmemimage(Rect(0,0,16,16), GREY8);
-	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
-	swimg = allocmemimage(Rect(0,0,16,16), GREY8);
-	swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
-	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil)
-		print("software cursor: allocmemimage fails");
-	memfillcolor(swback, DTransparent);
-	memfillcolor(swmask, DOpaque);
-	memfillcolor(swmask1, DOpaque);
-	memfillcolor(swimg, DBlack);
-	memfillcolor(swimg1, DBlack);
-}
-
-/*
- * Need to lock drawlock for ourselves.
- */
-void
-swenable(VGAscr *scr)
-{
-	swenabled = 1;
-	if(canqlock(&drawlock)){
-		swload(scr, &arrow);
-		swcursordraw();
-		qunlock(&drawlock);
-	}
-}
-
-void
-swdisable(VGAscr*)
-{
-	swenabled = 0;
-	if(canqlock(&drawlock)){
-		swcursorhide();
-		qunlock(&drawlock);
-	}
-}
-
-VGAcur swcursor =
-{
-	"soft",
-	swenable,
-	swdisable,
-	swload,
-	swmove,
-};
--- a/sys/src/9/pc/screen.h
+++ b/sys/src/9/pc/screen.h
@@ -132,6 +132,7 @@
 /* mouse.c */
 extern void mousectl(Cmdbuf*);
 extern void mouseresize(void);
+extern void mouseredraw(void);
 
 /* screen.c */
 extern int		hwaccel;	/* use hw acceleration; default on */
@@ -140,8 +141,8 @@
 extern void addvgaseg(char*, ulong, ulong);
 extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*);
 extern void	flushmemscreen(Rectangle);
-extern int	cursoron(int);
-extern void	cursoroff(int);
+extern void	cursoron(void);
+extern void	cursoroff(void);
 extern void	setcursor(Cursor*);
 extern int	screensize(int, int, int, ulong);
 extern int	screenaperture(int, int);
@@ -176,3 +177,10 @@
 extern Lock	vgascreenlock;
 
 #define ishwimage(i)	(vgascreen[0].gscreendata && (i)->data->bdata == vgascreen[0].gscreendata->bdata)
+
+/* swcursor.c */
+void		swcursorhide(void);
+void		swcursoravoid(Rectangle);
+void		swcursordraw(Point);
+void		swcursorload(Cursor *);
+void		swcursorinit(void);
--- a/sys/src/9/port/devmouse.c
+++ b/sys/src/9/port/devmouse.c
@@ -33,16 +33,13 @@
 {
 	Lock;
 	Mousestate;
-	int	dx;
-	int	dy;
-	int	track;		/* dx & dy updated */
 	int	redraw;		/* update cursor on screen */
+	Rendez	redrawr;	/* wait for cursor screen updates */
 	ulong	lastcounter;	/* value when /dev/mouse read */
 	ulong	lastresize;
 	ulong	resize;
 	Rendez	r;
 	Ref;
-	QLock;
 	int	open;
 	int	acceleration;
 	int	maxacc;
@@ -77,9 +74,8 @@
 
 void	Cursortocursor(Cursor*);
 int	mousechanged(void*);
+void	mouseredraw(void);
 
-static void mouseclock(void);
-
 enum{
 	Qdir,
 	Qcursor,
@@ -114,8 +110,6 @@
 
 	curs = arrow;
 	Cursortocursor(&arrow);
-	/* redraw cursor about 30 times per second */
-	addclock0link(mouseclock, 33);
 }
 
 static int
@@ -129,6 +123,8 @@
 	return rc;
 }
 
+static void mouseproc(void*);
+
 static void
 mouseinit(void)
 {
@@ -137,8 +133,10 @@
 
 	curs = arrow;
 	Cursortocursor(&arrow);
-	cursoron(1);
+	cursoron();
 	mousetime = seconds();
+
+	kproc("mouse", mouseproc, 0);
 }
 
 static Chan*
@@ -222,13 +220,15 @@
 			unlock(&mouse);
 			return;
 		}
-		if(--mouse.ref == 0){
-			cursoroff(1);
-			curs = arrow;
-			Cursortocursor(&arrow);
-			cursoron(1);
+		if(--mouse.ref != 0){
+			unlock(&mouse);
+			return;
 		}
 		unlock(&mouse);
+		cursoroff();
+		curs = arrow;
+		Cursortocursor(&arrow);
+		cursoron();
 	}
 }
 
@@ -373,7 +373,7 @@
 		error(Eisdir);
 
 	case Qcursor:
-		cursoroff(1);
+		cursoroff();
 		if(n < 2*4+2*2*16){
 			curs = arrow;
 			Cursortocursor(&arrow);
@@ -385,11 +385,7 @@
 			memmove(curs.set, p+40, 2*16);
 			Cursortocursor(&curs);
 		}
-		qlock(&mouse);
-		mouse.redraw = 1;
-		mouseclock();
-		qunlock(&mouse);
-		cursoron(1);
+		cursoron();
 		return n;
 
 	case Qmousectl:
@@ -466,14 +462,10 @@
 		if(p == 0)
 			error(Eshort);
 		pt.y = strtoul(p, 0, 0);
-		qlock(&mouse);
-		if(ptinrect(pt, gscreen->r)){
+		if(gscreen != nil && ptinrect(pt, gscreen->r)){
 			mouse.xy = pt;
-			mouse.redraw = 1;
-			mouse.track = 1;
-			mouseclock();
+			mousetrack(0, 0, mouse.buttons, TK2MS(MACHP(0)->ticks));
 		}
-		qunlock(&mouse);
 		return n;
 	}
 
@@ -505,32 +497,40 @@
 void
 Cursortocursor(Cursor *c)
 {
+	qlock(&drawlock);
 	lock(&cursor);
 	memmove(&cursor.Cursor, c, sizeof(Cursor));
 	setcursor(c);
 	unlock(&cursor);
+	qunlock(&drawlock);
 }
 
 
+static int
+shouldredraw(void*)
+{
+	return mouse.redraw != 0;
+}
+
 /*
- *  called by the clock routine to redraw the cursor
+ * process that redraws the cursor
  */
 static void
-mouseclock(void)
+mouseproc(void*)
 {
-	if(mouse.track){
-		mousetrack(mouse.dx, mouse.dy, mouse.buttons, TK2MS(MACHP(0)->ticks));
-		mouse.track = 0;
-		mouse.dx = 0;
-		mouse.dy = 0;
+	while(waserror())
+		;
+	for(;;){
+		if(mouse.redraw){
+			mouse.redraw = 0;
+			cursoroff();
+			cursoron();
+			drawactive(1);
+		} else {
+			drawactive(0);
+		}
+		tsleep(&mouse.redrawr, shouldredraw, 0, 20*1000);
 	}
-	if(mouse.redraw && canlock(&cursor)){
-		mouse.redraw = 0;
-		cursoroff(0);
-		mouse.redraw = cursoron(0);
-		unlock(&cursor);
-	}
-	drawactive(0);
 }
 
 static int
@@ -595,7 +595,6 @@
 	lastb = mouse.buttons;
 	mouse.xy = Pt(x, y);
 	mouse.buttons = b;
-	mouse.redraw = 1;
 	mouse.counter++;
 	mouse.msec = msec;
 
@@ -611,7 +610,8 @@
 			mouse.qfull = 1;
 	}
 	wakeup(&mouse.r);
-	drawactive(1);
+
+	mouseredraw();
 }
 
 /*
@@ -782,3 +782,9 @@
 	wakeup(&mouse.r);
 }
 
+void
+mouseredraw(void)
+{
+	mouse.redraw = 1;
+	wakeup(&mouse.redrawr);
+}
--- a/sys/src/9/port/portmkfile
+++ b/sys/src/9/port/portmkfile
@@ -81,6 +81,7 @@
 devmouse.$O:	screen.h /sys/include/memdraw.h
 devdraw.$O:	screen.h /sys/include/memdraw.h
 screen.$O:	screen.h /sys/include/memdraw.h
+swcursor.$O:	screen.h /sys/include/memdraw.h
 thwack.$O:	../port/thwack.h
 unthwack.$O:	../port/thwack.h
 devsdp.$O:	../port/thwack.h
--- /dev/null
+++ b/sys/src/9/port/swcursor.c
@@ -1,0 +1,133 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#define	Image	IMAGE
+#include	<draw.h>
+#include	<memdraw.h>
+#include	<cursor.h>
+#include	"screen.h"
+
+extern Memimage* gscreen;
+
+/*
+ * Software cursor. 
+ */
+static Memimage*	swback;		/* screen under cursor */
+static Memimage*	swimg;		/* cursor image */
+static Memimage*	swmask;		/* cursor mask */
+static Memimage*	swimg1;
+static Memimage*	swmask1;
+
+static Point		swoffset;
+static Rectangle	swrect;		/* screen rectangle in swback */
+static Point		swvispt;	/* actual cursor location */
+static int		swvisible;	/* is the cursor visible? */
+
+/*
+ * called with drawlock locked for us, most of the time.
+ * kernel prints at inopportune times might mean we don't
+ * hold the lock, but memimagedraw is now reentrant so
+ * that should be okay: worst case we get cursor droppings.
+ */
+void
+swcursorhide(void)
+{
+	if(swvisible == 0)
+		return;
+	if(swback == nil || gscreen == nil)
+		return;
+	swvisible = 0;
+	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
+	flushmemscreen(swrect);
+}
+
+void
+swcursoravoid(Rectangle r)
+{
+	if(swvisible && rectXrect(r, swrect)){
+		swcursorhide();
+		mouseredraw();	/* schedule cursor redraw after we release drawlock */
+	}
+}
+
+void
+swcursordraw(Point p)
+{
+	if(swvisible)
+		return;
+	if(swback == nil || swimg1 == nil || swmask1 == nil || gscreen == nil)
+		return;
+	assert(!canqlock(&drawlock));
+	swvispt = addpt(swoffset, p);
+	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
+	memimagedraw(swback, swback->r, gscreen, swvispt, memopaque, ZP, S);
+	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
+	flushmemscreen(swrect);
+	swvisible = 1;
+}
+
+void
+swcursorload(Cursor *curs)
+{
+	uchar *ip, *mp;
+	int i, j, set, clr;
+
+	if(swimg == nil || swmask == nil || swimg1 == nil || swmask1 == nil)
+		return;
+	/*
+	 * Build cursor image and mask.
+	 * Image is just the usual cursor image
+	 * but mask is a transparent alpha mask.
+	 * 
+	 * The 16x16x8 memimages do not have
+	 * padding at the end of their scan lines.
+	 */
+	ip = byteaddr(swimg, ZP);
+	mp = byteaddr(swmask, ZP);
+	for(i=0; i<32; i++){
+		set = curs->set[i];
+		clr = curs->clr[i];
+		for(j=0x80; j; j>>=1){
+			*ip++ = set&j ? 0x00 : 0xFF;
+			*mp++ = (clr|set)&j ? 0xFF : 0x00;
+		}
+	}
+	swoffset = curs->offset;
+	memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
+	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
+
+	mouseredraw();
+}
+
+void
+swcursorinit(void)
+{
+	if(gscreen == nil)
+		return;
+
+	if(swback){
+		freememimage(swback);
+		freememimage(swmask);
+		freememimage(swmask1);
+		freememimage(swimg);
+		freememimage(swimg1); 
+	}
+	swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
+	swmask = allocmemimage(Rect(0,0,16,16), GREY8);
+	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
+	swimg = allocmemimage(Rect(0,0,16,16), GREY8);
+	swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
+	if(swback == nil || swmask == nil || swmask1 == nil || swimg == nil || swimg1 == nil){
+		print("software cursor: allocmemimage fails\n");
+		return;
+	}
+	memfillcolor(swback, DTransparent);
+	memfillcolor(swmask, DOpaque);
+	memfillcolor(swmask1, DOpaque);
+	memfillcolor(swimg, DBlack);
+	memfillcolor(swimg1, DBlack);
+}
--