vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

devmouse.c (9700B)


      1 /*
      2  * Mouse device.  Also provides /dev/snarf, because it was convenient.
      3  * In a perfect world, this would be autogenerated from a
      4  * Plan 9 devmouse.
      5  */
      6 #include	"u.h"
      7 #include	"lib.h"
      8 #include	"mem.h"
      9 #include	"dat.h"
     10 #include	"fns.h"
     11 #include	"error.h"
     12 
     13 #define	Image	IMAGE
     14 #include	<draw.h>
     15 #include	<memdraw.h>
     16 #include	<cursor.h>
     17 #include	"screen.h"
     18 
     19 typedef struct Cursor Cursor;
     20 
     21 Cursor	arrow = {
     22 	{ -1, -1 },
     23 	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
     24 	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
     25 	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 
     26 	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, 
     27 	},
     28 	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 
     29 	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 
     30 	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 
     31 	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, 
     32 	},
     33 };
     34 
     35 enum {
     36 	ScrollUp = 0x08,
     37 	ScrollDown = 0x10,
     38 	ScrollLeft = 0x20,
     39 	ScrollRight = 0x40,
     40 };
     41 
     42 enum
     43 {
     44 	CMbuttonmap,
     45 	CMscrollswap,
     46 	CMswap,
     47 };
     48 
     49 static Cmdtab mousectlmsg[] =
     50 {
     51 	CMbuttonmap,	"buttonmap",	0,
     52 	CMscrollswap,	"scrollswap",	0,
     53 	CMswap,		"swap",		1,
     54 };
     55 
     56 Mouseinfo	mouse;
     57 Cursorinfo	cursor;
     58 int		mouseshifted;
     59 Cursor		curs;
     60 
     61 void	Cursortocursor(Cursor*);
     62 int	mousechanged(void*);
     63 
     64 enum{
     65 	Qdir,
     66 	Qcursor,
     67 	Qmouse,
     68 	Qmousectl,
     69 	Qsnarf,
     70 };
     71 
     72 static Dirtab mousedir[]={
     73 	".",	{Qdir, 0, QTDIR},	0,			DMDIR|0555,
     74 	"cursor",	{Qcursor},	0,			0666,
     75 	"mouse",	{Qmouse},	0,			0666,
     76 	"mousectl",	{Qmousectl},	0,			0220,
     77 	"snarf",	{Qsnarf},	0,	0666,
     78 };
     79 
     80 static uchar buttonmap[8] = {
     81 	0, 1, 2, 3, 4, 5, 6, 7,
     82 };
     83 static int mouseswap;
     84 static int scrollswap;
     85 static ulong mousetime;
     86 
     87 Rectangle mouserect;	/* maintained by x11 for us */
     88 
     89 static void
     90 mousereset(void)
     91 {
     92 	if(!conf.monitor)
     93 		return;
     94 }
     95 
     96 static int
     97 mousedevgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
     98 {
     99 	int rc;
    100 
    101 	rc = devgen(c, name, tab, ntab, i, dp);
    102 	if(rc != -1)
    103 		dp->atime = mousetime;
    104 	return rc;
    105 }
    106 
    107 static void
    108 mouseinit(void)
    109 {
    110 	if(!conf.monitor)
    111 		return;
    112 	mousetime = seconds();
    113 }
    114 
    115 static Chan*
    116 mouseattach(char *spec)
    117 {
    118 	if(!conf.monitor)
    119 		error(Egreg);
    120 	curs = arrow;
    121 	Cursortocursor(&arrow);
    122 	return devattach('m', spec);
    123 }
    124 
    125 static Walkqid*
    126 mousewalk(Chan *c, Chan *nc, char **name, int nname)
    127 {
    128 	Walkqid *wq;
    129 
    130 	/*
    131 	 * We use devgen() and not mousedevgen() here
    132 	 * see "Ugly problem" in dev.c/devwalk()
    133 	 */
    134 	wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen);
    135 	if(wq != nil && wq->clone != c && wq->clone != nil && (wq->clone->qid.type&QTDIR)==0)
    136 		incref(&mouse.ref);
    137 	return wq;
    138 }
    139 
    140 static int
    141 mousestat(Chan *c, uchar *db, int n)
    142 {
    143 	return devstat(c, db, n, mousedir, nelem(mousedir), mousedevgen);
    144 }
    145 
    146 static Chan*
    147 mouseopen(Chan *c, int omode)
    148 {
    149 	switch((ulong)c->qid.path){
    150 	case Qdir:
    151 		if(omode != OREAD)
    152 			error(Eperm);
    153 		break;
    154 	case Qmouse:
    155 		lock(&mouse.ref.lk);
    156 		if(mouse.open){
    157 			unlock(&mouse.ref.lk);
    158 			error(Einuse);
    159 		}
    160 		mouse.open = 1;
    161 		mouse.ref.ref++;
    162 		mouse.lastresize = mouse.resize;
    163 		unlock(&mouse.ref.lk);
    164 		break;
    165 	case Qsnarf:
    166 		if(omode == ORDWR)
    167 			error(Eperm);	/* one at a time please */
    168 		c->aux = nil;
    169 		incref(&mouse.ref);
    170 		break;
    171 	default:
    172 		incref(&mouse.ref);
    173 	}
    174 	c->mode = openmode(omode);
    175 	c->flag |= COPEN;
    176 	c->offset = 0;
    177 	return c;
    178 }
    179 
    180 static void
    181 mousecreate(Chan *c, char *name, int perm, ulong mode)
    182 {
    183 	if(!conf.monitor)
    184 		error(Egreg);
    185 	error(Eperm);
    186 }
    187 
    188 static void
    189 mouseclose(Chan *c)
    190 {
    191 	if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){
    192 		if(c->qid.path == Qsnarf){
    193 			if(c->mode == OWRITE){
    194 				if(c->aux)
    195 					putsnarf(c->aux);
    196 				else
    197 					putsnarf("");
    198 			}
    199 			free(c->aux);
    200 		}
    201 		lock(&mouse.ref.lk);
    202 		if(c->qid.path == Qmouse)
    203 			mouse.open = 0;
    204 		if(--mouse.ref.ref == 0){
    205 			curs = arrow;
    206 			Cursortocursor(&arrow);
    207 		}
    208 		unlock(&mouse.ref.lk);
    209 		if(c->qid.path == Qmouse)
    210 			termredraw();
    211 	}
    212 }
    213 
    214 static long
    215 mouseread(Chan *c, void *va, long n, vlong off)
    216 {
    217 	char buf[1+4*12+1], *s;
    218 	uchar *p;
    219 	ulong offset = off;
    220 	Mousestate m;
    221 	int b;
    222 
    223 	p = va;
    224 	switch((ulong)c->qid.path){
    225 	case Qdir:
    226 		return devdirread(c, va, n, mousedir, nelem(mousedir), mousedevgen);
    227 
    228 	case Qcursor:
    229 		if(offset != 0)
    230 			return 0;
    231 		if(n < 2*4+2*2*16)
    232 			error(Eshort);
    233 		n = 2*4+2*2*16;
    234 		lock(&cursor.lk);
    235 		BPLONG(p+0, curs.offset.x);
    236 		BPLONG(p+4, curs.offset.y);
    237 		memmove(p+8, curs.clr, 2*16);
    238 		memmove(p+40, curs.set, 2*16);
    239 		unlock(&cursor.lk);
    240 		return n;
    241 
    242 	case Qmouse:
    243 		while(mousechanged(0) == 0)
    244 			sleep(&mouse.r, mousechanged, 0);
    245 
    246 		mouse.qfull = 0;
    247 		mousetime = seconds();
    248 
    249 		/*
    250 		 * No lock of the indices is necessary here, because ri is only
    251 		 * updated by us, and there is only one mouse reader
    252 		 * at a time.  I suppose that more than one process
    253 		 * could try to read the fd at one time, but such behavior
    254 		 * is degenerate and already violates the calling
    255 		 * conventions for sleep above.
    256 		 */
    257 		if(mouse.ri != mouse.wi) {
    258 			m = mouse.queue[mouse.ri];
    259 			if(++mouse.ri == nelem(mouse.queue))
    260 				mouse.ri = 0;
    261 		} else {
    262 			while(!canlock(&cursor.lk))
    263 				tsleep(&up->sleep, return0, 0, TK2MS(1));
    264 
    265 			m = mouse.mstate;
    266 			unlock(&cursor.lk);
    267 		}
    268 
    269 		b = buttonmap[m.buttons&7];
    270 		/* put buttons 4 and 5 back in */
    271 		b |= m.buttons & (3<<3);
    272 		if (scrollswap){
    273 			if (b == 8)
    274 				b = 16;
    275 			else if (b == 16)
    276 				b = 8;
    277 		}
    278 		sprint(buf, "m%11d %11d %11d %11lud ",
    279 			m.xy.x, m.xy.y,
    280 			b,
    281 			m.msec);
    282 		mouse.lastcounter = m.counter;
    283 		if(n > 1+4*12)
    284 			n = 1+4*12;
    285 		if(mouse.lastresize != mouse.resize){
    286 			mouse.lastresize = mouse.resize;
    287 			buf[0] = 'r';
    288 		}
    289 		memmove(va, buf, n);
    290 		return n;
    291 	
    292 	case Qsnarf:
    293 		if(offset == 0){
    294 			s = getsnarf();
    295 			if(c->aux)
    296 				free(c->aux);
    297 			c->aux = s;
    298 		}
    299 		if(c->aux == nil)
    300 			return 0;
    301 		return readstr(offset, va, n, c->aux);
    302 	}
    303 	return 0;
    304 }
    305 
    306 static void
    307 setbuttonmap(char* map)
    308 {
    309 	int i, x, one, two, three;
    310 
    311 	one = two = three = 0;
    312 	for(i = 0; i < 3; i++){
    313 		if(map[i] == 0)
    314 			error(Ebadarg);
    315 		if(map[i] == '1'){
    316 			if(one)
    317 				error(Ebadarg);
    318 			one = 1<<i;
    319 		}
    320 		else if(map[i] == '2'){
    321 			if(two)
    322 				error(Ebadarg);
    323 			two = 1<<i;
    324 		}
    325 		else if(map[i] == '3'){
    326 			if(three)
    327 				error(Ebadarg);
    328 			three = 1<<i;
    329 		}
    330 		else
    331 			error(Ebadarg);
    332 	}
    333 	if(map[i])
    334 		error(Ebadarg);
    335 
    336 	memset(buttonmap, 0, 8);
    337 	for(i = 0; i < 8; i++){
    338 		x = 0;
    339 		if(i & 1)
    340 			x |= one;
    341 		if(i & 2)
    342 			x |= two;
    343 		if(i & 4)
    344 			x |= three;
    345 		buttonmap[x] = i;
    346 	}
    347 }
    348 
    349 static long
    350 mousewrite(Chan *c, void *va, long n, vlong offset)
    351 {
    352 	char *p;
    353 	Point pt;
    354 	Cmdbuf *cb;
    355 	Cmdtab *ct;
    356 	char buf[64];
    357 	int nn;
    358 
    359 	p = va;
    360 	switch((ulong)c->qid.path){
    361 	case Qdir:
    362 		error(Eisdir);
    363 
    364 	case Qcursor:
    365 		if(n < 2*4+2*2*16){
    366 			curs = arrow;
    367 			Cursortocursor(&arrow);
    368 		}else{
    369 			n = 2*4+2*2*16;
    370 			curs.offset.x = BGLONG(p+0);
    371 			curs.offset.y = BGLONG(p+4);
    372 			memmove(curs.clr, p+8, 2*16);
    373 			memmove(curs.set, p+40, 2*16);
    374 			Cursortocursor(&curs);
    375 		}
    376 		qlock(&mouse.qlk);
    377 		mouse.redraw = 1;
    378 		qunlock(&mouse.qlk);
    379 		return n;
    380 
    381 	case Qmousectl:
    382 		cb = parsecmd(va, n);
    383 		if(waserror()){
    384 			free(cb);
    385 			nexterror();
    386 		}
    387 
    388 		ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
    389 
    390 		switch(ct->index){
    391 		case CMswap:
    392 			if(mouseswap)
    393 				setbuttonmap("123");
    394 			else
    395 				setbuttonmap("321");
    396 			mouseswap ^= 1;
    397 			break;
    398 
    399 		case CMscrollswap:
    400 			scrollswap ^= 1;
    401 			break;
    402 
    403 		case CMbuttonmap:
    404 			if(cb->nf == 1)
    405 				setbuttonmap("123");
    406 			else
    407 				setbuttonmap(cb->f[1]);
    408 			break;
    409 		}
    410 
    411 		free(cb);
    412 		poperror();
    413 		return n;
    414 
    415 	case Qmouse:
    416 		if(n > sizeof buf-1)
    417 			n = sizeof buf -1;
    418 		memmove(buf, va, n);
    419 		buf[n] = 0;
    420 		p = 0;
    421 		pt.x = strtoul(buf+1, &p, 0);
    422 		if(p == 0)
    423 			error(Eshort);
    424 		pt.y = strtoul(p, 0, 0);
    425 		qlock(&mouse.qlk);
    426 		if(ptinrect(pt, mouserect)){
    427 			mouse.mstate.xy = pt;
    428 			mouse.redraw = 1;
    429 			mouse.track = 1;
    430 		}
    431 		qunlock(&mouse.qlk);
    432 		setmouse(pt);
    433 		return n;
    434 	
    435 	case Qsnarf:
    436 		if(offset+n >= SnarfSize)
    437 			error("too much snarf");
    438 		if(n == 0)
    439 			return 0;
    440 		assert(mousedir[Qsnarf].qid.path == Qsnarf);
    441 		mousedir[Qsnarf].qid.vers++;
    442 		if(c->aux == nil)
    443 			nn = 0;
    444 		else
    445 			nn = strlen(c->aux);
    446 		if(offset+n > nn){
    447 			nn = offset+n;
    448 			p = smalloc(nn+1);
    449 			if(c->aux){
    450 				strcpy(p, c->aux);
    451 				free(c->aux);
    452 			}
    453 			c->aux = p;
    454 		}
    455 		memmove(c->aux+offset, va, n);
    456 		return n;
    457 	}
    458 
    459 	error(Egreg);
    460 	return -1;
    461 }
    462 
    463 Dev mousedevtab = {
    464 	'm',
    465 	"mouse",
    466 
    467 	mousereset,
    468 	mouseinit,
    469 	devshutdown,
    470 	mouseattach,
    471 	mousewalk,
    472 	mousestat,
    473 	mouseopen,
    474 	mousecreate,
    475 	mouseclose,
    476 	mouseread,
    477 	devbread,
    478 	mousewrite,
    479 	devbwrite,
    480 	devremove,
    481 	devwstat,
    482 };
    483 
    484 void
    485 Cursortocursor(Cursor *c)
    486 {
    487 	lock(&cursor.lk);
    488 	cursor.cursor = *c;
    489 	unlock(&cursor.lk);
    490 	setcursor(c);
    491 }
    492 
    493 int
    494 mousechanged(void *v)
    495 {
    496 	return mouse.lastcounter != mouse.mstate.counter ||
    497 		mouse.lastresize != mouse.resize;
    498 }
    499 
    500 Point
    501 mousexy(void)
    502 {
    503 	return mouse.mstate.xy;
    504 }
    505 
    506 /*
    507  * notify reader that screen has been resized
    508  */
    509 void
    510 mouseresize(void)
    511 {
    512 	mouse.resize++;
    513 	wakeup(&mouse.r);
    514 }
    515 
    516 /*
    517  *  called at interrupt level to update the structure and
    518  *  awaken any waiting procs.
    519  */
    520 void
    521 mousetrack(int x, int y, int b, int msec)
    522 {
    523 	int lastb;
    524 
    525 	if(x < mouserect.min.x)
    526 		x = mouserect.min.x;
    527 	if(x >= mouserect.max.x)
    528 		x = mouserect.max.x;
    529 	if(y < mouserect.min.y)
    530 		y = mouserect.min.y;
    531 	if(y >= mouserect.max.y)
    532 		y = mouserect.max.y;
    533 
    534 	lastb = mouse.mstate.buttons;
    535 	mouse.mstate.xy = Pt(x, y);
    536 	mouse.mstate.buttons = b;
    537 	mouse.redraw = 1;
    538 	mouse.mstate.counter++;
    539 	mouse.mstate.msec = msec;
    540 
    541 	/*
    542 	 * if the queue fills, we discard the entire queue and don't
    543 	 * queue any more events until a reader polls the mouse.
    544 	 */
    545 	if(!mouse.qfull && lastb != b) {	/* add to ring */
    546 		mouse.queue[mouse.wi] = mouse.mstate;
    547 		if(++mouse.wi == nelem(mouse.queue))
    548 			mouse.wi = 0;
    549 		if(mouse.wi == mouse.ri)
    550 			mouse.qfull = 1;
    551 	}
    552 	wakeup(&mouse.r);
    553 	drawactive(1);
    554 }
    555