Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include	"u.h"
2
#include	"../port/lib.h"
3
#include	"mem.h"
4
#include	"dat.h"
5
#include	"fns.h"
6
#include	"../port/error.h"
7
 
8
#define	Image	IMAGE
9
#include	<draw.h>
10
#include	<memdraw.h>
11
#include	<cursor.h>
12
#include	"screen.h"
13
 
14
enum {
15
	ScrollUp = 0x08,
16
	ScrollDown = 0x10,
17
	ScrollLeft = 0x20,
18
	ScrollRight = 0x40,
19
};
20
 
21
typedef struct Mouseinfo	Mouseinfo;
22
typedef struct Mousestate	Mousestate;
23
 
24
struct Mousestate
25
{
26
	Point	xy;		/* mouse.xy */
27
	int	buttons;	/* mouse.buttons */
28
	ulong	counter;	/* increments every update */
29
	ulong	msec;		/* time of last event */
30
};
31
 
32
struct Mouseinfo
33
{
34
	Lock;
35
	Mousestate;
36
	int	dx;
37
	int	dy;
38
	int	track;		/* dx & dy updated */
39
	int	redraw;		/* update cursor on screen */
40
	ulong	lastcounter;	/* value when /dev/mouse read */
41
	ulong	lastresize;
42
	ulong	resize;
43
	Rendez	r;
44
	Ref;
45
	QLock;
46
	int	open;
47
	int	acceleration;
48
	int	maxacc;
49
	Mousestate	queue[16];	/* circular buffer of click events */
50
	int	ri;		/* read index into queue */
51
	int	wi;		/* write index into queue */
52
	uchar	qfull;		/* queue is full */
53
};
54
 
55
enum
56
{
57
	CMbuttonmap,
58
	CMscrollswap,
59
	CMswap,
60
	CMwildcard,
61
};
62
 
63
static Cmdtab mousectlmsg[] =
64
{
65
	CMbuttonmap,	"buttonmap",	0,
66
	CMscrollswap,	"scrollswap",	0,
67
	CMswap,		"swap",		1,
68
	CMwildcard,	"*",		0,
69
};
70
 
71
Mouseinfo	mouse;
72
Cursorinfo	cursor;
73
int		mouseshifted;
74
int		kbdbuttons;
75
void		(*kbdmouse)(int);
76
Cursor		curs;
77
 
78
void	Cursortocursor(Cursor*);
79
int	mousechanged(void*);
80
 
81
static void mouseclock(void);
82
static void xkbdmouse(int);
83
 
84
enum{
85
	Qdir,
86
	Qcursor,
87
	Qmouse,
88
	Qmousein,
89
	Qmousectl,
90
};
91
 
92
static Dirtab mousedir[]={
93
	".",	{Qdir, 0, QTDIR},	0,			DMDIR|0555,
94
	"cursor",	{Qcursor},	0,			0666,
95
	"mouse",	{Qmouse},	0,			0666,
96
	"mousein",	{Qmousein},	0,			0220,
97
	"mousectl",	{Qmousectl},	0,			0220,
98
};
99
 
100
static uchar buttonmap[8] = {
101
	0, 1, 2, 3, 4, 5, 6, 7,
102
};
103
static int mouseswap;
104
static int scrollswap;
105
static ulong mousetime;
106
 
107
extern Memimage* gscreen;
108
extern ulong kerndate;
109
 
110
static void
111
mousereset(void)
112
{
113
	if(!conf.monitor)
114
		return;
115
 
116
	curs = arrow;
117
	Cursortocursor(&arrow);
118
	/* redraw cursor about 30 times per second */
119
	addclock0link(mouseclock, 33);
120
}
121
 
122
static void
123
mousefromkbd(int buttons)
124
{
125
	kbdbuttons = buttons;
126
	mousetrack(0, 0, 0, TK2MS(MACHP(0)->ticks));
127
}
128
 
129
static int
130
mousedevgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
131
{
132
	int rc;
133
 
134
	rc = devgen(c, name, tab, ntab, i, dp);
135
	if(rc != -1)
136
		dp->atime = mousetime;
137
	return rc;
138
}
139
 
140
static void
141
mouseinit(void)
142
{
143
	if(!conf.monitor)
144
		return;
145
 
146
	curs = arrow;
147
	Cursortocursor(&arrow);
148
	cursoron(1);
149
	kbdmouse = mousefromkbd;
150
	mousetime = seconds();
151
}
152
 
153
static Chan*
154
mouseattach(char *spec)
155
{
156
	if(!conf.monitor)
157
		error(Egreg);
158
	return devattach('m', spec);
159
}
160
 
161
static Walkqid*
162
mousewalk(Chan *c, Chan *nc, char **name, int nname)
163
{
164
	Walkqid *wq;
165
 
166
	/*
167
	 * We use devgen() and not mousedevgen() here
168
	 * see "Ugly problem" in dev.c/devwalk()
169
	 */
170
	wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen);
171
	if(wq != nil && wq->clone != c && wq->clone != nil && (wq->clone->qid.type&QTDIR)==0)
172
		incref(&mouse);
173
	return wq;
174
}
175
 
176
static int
177
mousestat(Chan *c, uchar *db, int n)
178
{
179
	return devstat(c, db, n, mousedir, nelem(mousedir), mousedevgen);
180
}
181
 
182
static Chan*
183
mouseopen(Chan *c, int omode)
184
{
185
	switch((ulong)c->qid.path){
186
	case Qdir:
187
		if(omode != OREAD)
188
			error(Eperm);
189
		break;
190
	case Qmouse:
191
		lock(&mouse);
192
		if(mouse.open){
193
			unlock(&mouse);
194
			error(Einuse);
195
		}
196
		mouse.open = 1;
197
		mouse.ref++;
198
		mouse.lastresize = mouse.resize;
199
		unlock(&mouse);
200
		break;
201
	case Qmousein:
202
		if(!iseve())
203
			error(Eperm);
204
		break;
205
	default:
206
		incref(&mouse);
207
	}
208
	c->mode = openmode(omode);
209
	c->flag |= COPEN;
210
	c->offset = 0;
211
	return c;
212
}
213
 
214
static void
215
mousecreate(Chan*, char*, int, ulong)
216
{
217
	if(!conf.monitor)
218
		error(Egreg);
219
	error(Eperm);
220
}
221
 
222
static void
223
mouseclose(Chan *c)
224
{
225
	if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){
226
		if(c->qid.path == Qmousein)
227
			return;
228
		lock(&mouse);
229
		if(c->qid.path == Qmouse)
230
			mouse.open = 0;
231
		if(--mouse.ref == 0){
232
			cursoroff(1);
233
			curs = arrow;
234
			Cursortocursor(&arrow);
235
			cursoron(1);
236
		}
237
		unlock(&mouse);
238
	}
239
}
240
 
241
 
242
static long
243
mouseread(Chan *c, void *va, long n, vlong off)
244
{
245
	char buf[1+4*12+1];
246
	uchar *p;
247
	static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 };
248
	ulong offset = off;
249
	Mousestate m;
250
	int b;
251
 
252
	p = va;
253
	switch((ulong)c->qid.path){
254
	case Qdir:
255
		return devdirread(c, va, n, mousedir, nelem(mousedir), mousedevgen);
256
 
257
	case Qcursor:
258
		if(offset != 0)
259
			return 0;
260
		if(n < 2*4+2*2*16)
261
			error(Eshort);
262
		n = 2*4+2*2*16;
263
		lock(&cursor);
264
		BPLONG(p+0, curs.offset.x);
265
		BPLONG(p+4, curs.offset.y);
266
		memmove(p+8, curs.clr, 2*16);
267
		memmove(p+40, curs.set, 2*16);
268
		unlock(&cursor);
269
		return n;
270
 
271
	case Qmouse:
272
		while(mousechanged(0) == 0)
273
			sleep(&mouse.r, mousechanged, 0);
274
 
275
		mouse.qfull = 0;
276
		mousetime = seconds();
277
 
278
		/*
279
		 * No lock of the indices is necessary here, because ri is only
280
		 * updated by us, and there is only one mouse reader
281
		 * at a time.  I suppose that more than one process
282
		 * could try to read the fd at one time, but such behavior
283
		 * is degenerate and already violates the calling
284
		 * conventions for sleep above.
285
		 */
286
		if(mouse.ri != mouse.wi) {
287
			m = mouse.queue[mouse.ri];
288
			if(++mouse.ri == nelem(mouse.queue))
289
				mouse.ri = 0;
290
		} else {
291
			while(!canlock(&cursor))
292
				tsleep(&up->sleep, return0, 0, TK2MS(1));
293
 
294
			m = mouse.Mousestate;
295
			unlock(&cursor);
296
		}
297
 
298
		b = buttonmap[m.buttons&7];
299
		/* put buttons 4 and 5 back in */
300
		b |= m.buttons & (3<<3);
301
		if (scrollswap)
302
			if (b == 8)
303
				b = 16;
304
			else if (b == 16)
305
				b = 8;
306
		snprint(buf, sizeof buf, "m%11d %11d %11d %11lud ",
307
			m.xy.x, m.xy.y,
308
			b,
309
			m.msec);
310
		mouse.lastcounter = m.counter;
311
		if(n > 1+4*12)
312
			n = 1+4*12;
313
		if(mouse.lastresize != mouse.resize){
314
			mouse.lastresize = mouse.resize;
315
			buf[0] = 'r';
316
		}
317
		memmove(va, buf, n);
318
		return n;
319
	}
320
	return 0;
321
}
322
 
323
static void
324
setbuttonmap(char* map)
325
{
326
	int i, x, one, two, three;
327
 
328
	one = two = three = 0;
329
	for(i = 0; i < 3; i++){
330
		if(map[i] == 0)
331
			error(Ebadarg);
332
		if(map[i] == '1'){
333
			if(one)
334
				error(Ebadarg);
335
			one = 1<<i;
336
		}
337
		else if(map[i] == '2'){
338
			if(two)
339
				error(Ebadarg);
340
			two = 1<<i;
341
		}
342
		else if(map[i] == '3'){
343
			if(three)
344
				error(Ebadarg);
345
			three = 1<<i;
346
		}
347
		else
348
			error(Ebadarg);
349
	}
350
	if(map[i])
351
		error(Ebadarg);
352
 
353
	memset(buttonmap, 0, 8);
354
	for(i = 0; i < 8; i++){
355
		x = 0;
356
		if(i & 1)
357
			x |= one;
358
		if(i & 2)
359
			x |= two;
360
		if(i & 4)
361
			x |= three;
362
		buttonmap[x] = i;
363
	}
364
}
365
 
366
static long
367
mousewrite(Chan *c, void *va, long n, vlong)
368
{
369
	char *p;
370
	Point pt;
371
	Cmdbuf *cb;
372
	Cmdtab *ct;
373
	char buf[64];
374
	int b, msec;
375
 
376
	p = va;
377
	switch((ulong)c->qid.path){
378
	case Qdir:
379
		error(Eisdir);
380
 
381
	case Qcursor:
382
		cursoroff(1);
383
		if(n < 2*4+2*2*16){
384
			curs = arrow;
385
			Cursortocursor(&arrow);
386
		}else{
387
			n = 2*4+2*2*16;
388
			curs.offset.x = BGLONG(p+0);
389
			curs.offset.y = BGLONG(p+4);
390
			memmove(curs.clr, p+8, 2*16);
391
			memmove(curs.set, p+40, 2*16);
392
			Cursortocursor(&curs);
393
		}
394
		qlock(&mouse);
395
		mouse.redraw = 1;
396
		mouseclock();
397
		qunlock(&mouse);
398
		cursoron(1);
399
		return n;
400
 
401
	case Qmousectl:
402
		cb = parsecmd(va, n);
403
		if(waserror()){
404
			free(cb);
405
			nexterror();
406
		}
407
 
408
		ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
409
 
410
		switch(ct->index){
411
		case CMswap:
412
			if(mouseswap)
413
				setbuttonmap("123");
414
			else
415
				setbuttonmap("321");
416
			mouseswap ^= 1;
417
			break;
418
 
419
		case CMscrollswap:
420
			scrollswap ^= 1;
421
			break;
422
 
423
		case CMbuttonmap:
424
			if(cb->nf == 1)
425
				setbuttonmap("123");
426
			else
427
				setbuttonmap(cb->f[1]);
428
			break;
429
 
430
		case CMwildcard:
431
			mousectl(cb);
432
			break;
433
		}
434
 
435
		free(cb);
436
		poperror();
437
		return n;
438
 
439
	case Qmousein:
440
		if(n > sizeof buf-1)
441
			n = sizeof buf -1;
442
		memmove(buf, va, n);
443
		buf[n] = 0;
444
		p = 0;
445
		pt.x = strtol(buf+1, &p, 0);
446
		if(p == 0)
447
			error(Eshort);
448
		pt.y = strtol(p, &p, 0);
449
		if(p == 0)
450
			error(Eshort);
451
		b = strtol(p, &p, 0);
452
		msec = strtol(p, &p, 0);
453
		if(msec == 0)
454
			msec = TK2MS(MACHP(0)->ticks);
455
		mousetrack(pt.x, pt.y, b, msec);
456
		return n;
457
 
458
	case Qmouse:
459
		if(n > sizeof buf-1)
460
			n = sizeof buf -1;
461
		memmove(buf, va, n);
462
		buf[n] = 0;
463
		p = 0;
464
		pt.x = strtoul(buf+1, &p, 0);
465
		if(p == 0)
466
			error(Eshort);
467
		pt.y = strtoul(p, 0, 0);
468
		qlock(&mouse);
469
		if(ptinrect(pt, gscreen->r)){
470
			mouse.xy = pt;
471
			mouse.redraw = 1;
472
			mouse.track = 1;
473
			mouseclock();
474
		}
475
		qunlock(&mouse);
476
		return n;
477
	}
478
 
479
	error(Egreg);
480
	return -1;
481
}
482
 
483
Dev mousedevtab = {
484
	'm',
485
	"mouse",
486
 
487
	mousereset,
488
	mouseinit,
489
	devshutdown,
490
	mouseattach,
491
	mousewalk,
492
	mousestat,
493
	mouseopen,
494
	mousecreate,
495
	mouseclose,
496
	mouseread,
497
	devbread,
498
	mousewrite,
499
	devbwrite,
500
	devremove,
501
	devwstat,
502
};
503
 
504
void
505
Cursortocursor(Cursor *c)
506
{
507
	lock(&cursor);
508
	memmove(&cursor.Cursor, c, sizeof(Cursor));
509
	setcursor(c);
510
	unlock(&cursor);
511
}
512
 
513
 
514
/*
515
 *  called by the clock routine to redraw the cursor
516
 */
517
static void
518
mouseclock(void)
519
{
520
	if(mouse.track){
521
		mousetrack(mouse.dx, mouse.dy, mouse.buttons, TK2MS(MACHP(0)->ticks));
522
		mouse.track = 0;
523
		mouse.dx = 0;
524
		mouse.dy = 0;
525
	}
526
	if(mouse.redraw && canlock(&cursor)){
527
		mouse.redraw = 0;
528
		cursoroff(0);
529
		mouse.redraw = cursoron(0);
530
		unlock(&cursor);
531
	}
532
	drawactive(0);
533
}
534
 
535
static int
536
scale(int x)
537
{
538
	int sign = 1;
539
 
540
	if(x < 0){
541
		sign = -1;
542
		x = -x;
543
	}
544
	switch(x){
545
	case 0:
546
	case 1:
547
	case 2:
548
	case 3:
549
		break;
550
	case 4:
551
		x = 6 + (mouse.acceleration>>2);
552
		break;
553
	case 5:
554
		x = 9 + (mouse.acceleration>>1);
555
		break;
556
	default:
557
		x *= mouse.maxacc;
558
		break;
559
	}
560
	return sign*x;
561
}
562
 
563
/*
564
 *  called at interrupt level to update the structure and
565
 *  awaken any waiting procs.
566
 */
567
void
568
mousetrack(int dx, int dy, int b, int msec)
569
{
570
	int x, y, lastb;
571
 
572
	if(gscreen==nil)
573
		return;
574
 
575
	if(mouse.acceleration){
576
		dx = scale(dx);
577
		dy = scale(dy);
578
	}
579
	x = mouse.xy.x + dx;
580
	if(x < gscreen->clipr.min.x)
581
		x = gscreen->clipr.min.x;
582
	if(x >= gscreen->clipr.max.x)
583
		x = gscreen->clipr.max.x;
584
	y = mouse.xy.y + dy;
585
	if(y < gscreen->clipr.min.y)
586
		y = gscreen->clipr.min.y;
587
	if(y >= gscreen->clipr.max.y)
588
		y = gscreen->clipr.max.y;
589
 
590
	lastb = mouse.buttons;
591
	mouse.xy = Pt(x, y);
592
	mouse.buttons = b|kbdbuttons;
593
	mouse.redraw = 1;
594
	mouse.counter++;
595
	mouse.msec = msec;
596
 
597
	/*
598
	 * if the queue fills, we discard the entire queue and don't
599
	 * queue any more events until a reader polls the mouse.
600
	 */
601
	if(!mouse.qfull && lastb != b) {	/* add to ring */
602
		mouse.queue[mouse.wi] = mouse.Mousestate;
603
		if(++mouse.wi == nelem(mouse.queue))
604
			mouse.wi = 0;
605
		if(mouse.wi == mouse.ri)
606
			mouse.qfull = 1;
607
	}
608
	wakeup(&mouse.r);
609
	drawactive(1);
610
}
611
 
612
/*
613
 *  microsoft 3 button, 7 bit bytes
614
 *
615
 *	byte 0 -	1  L  R Y7 Y6 X7 X6
616
 *	byte 1 -	0 X5 X4 X3 X2 X1 X0
617
 *	byte 2 -	0 Y5 Y4 Y3 Y2 Y1 Y0
618
 *	byte 3 -	0  M  x  x  x  x  x	(optional)
619
 *
620
 *  shift & right button is the same as middle button (for 2 button mice)
621
 */
622
int
623
m3mouseputc(Queue*, int c)
624
{
625
	static uchar msg[3];
626
	static int nb;
627
	static int middle;
628
	static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 3 };
629
	short x;
630
	int dx, dy, newbuttons;
631
	static ulong lasttick;
632
	ulong m;
633
 
634
	/* Resynchronize in stream with timing. */
635
	m = MACHP(0)->ticks;
636
	if(TK2SEC(m - lasttick) > 2)
637
		nb = 0;
638
	lasttick = m;
639
 
640
	if(nb==0){
641
		/*
642
		 * an extra byte comes for middle button motion.
643
		 * only two possible values for the extra byte.
644
		 */
645
		if(c == 0x00 || c == 0x20){
646
			/* an extra byte gets sent for the middle button */
647
			middle = (c&0x20) ? 2 : 0;
648
			newbuttons = (mouse.buttons & ~2) | middle;
649
			mousetrack(0, 0, newbuttons, TK2MS(MACHP(0)->ticks));
650
			return 0;
651
		}
652
	}
653
	msg[nb] = c;
654
	if(++nb == 3){
655
		nb = 0;
656
		newbuttons = middle | b[(msg[0]>>4)&3 | (mouseshifted ? 4 : 0)];
657
		x = (msg[0]&0x3)<<14;
658
		dx = (x>>8) | msg[1];
659
		x = (msg[0]&0xc)<<12;
660
		dy = (x>>8) | msg[2];
661
		mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
662
	}
663
	return 0;
664
}
665
 
666
/*
667
 * microsoft intellimouse 3 buttons + scroll
668
 * 	byte 0 -	1  L  R Y7 Y6 X7 X6
669
 *	byte 1 -	0 X5 X4 X3 X2 X1 X0
670
 *	byte 2 -	0 Y5 Y4 Y3 Y2 Y1 Y0
671
 *	byte 3 -	0  0  M  %  %  %  %
672
 *
673
 *	%: 0xf => U , 0x1 => D
674
 *
675
 *	L: left
676
 *	R: right
677
 *	U: up
678
 *	D: down
679
 */
680
int
681
m5mouseputc(Queue*, int c)
682
{
683
	static uchar msg[3];
684
	static int nb;
685
	static ulong lasttick;
686
	ulong m;
687
 
688
	/* Resynchronize in stream with timing. */
689
	m = MACHP(0)->ticks;
690
	if(TK2SEC(m - lasttick) > 2)
691
		nb = 0;
692
	lasttick = m;
693
 
694
	msg[nb++] = c & 0x7f;
695
	if (nb == 4) {
696
		schar dx,dy,newbuttons;
697
		dx = msg[1] | (msg[0] & 0x3) << 6;
698
		dy = msg[2] | (msg[0] & 0xc) << 4;
699
		newbuttons =
700
			(msg[0] & 0x10) >> (mouseshifted ? 3 : 2)
701
			| (msg[0] & 0x20) >> 5
702
			| ( msg[3] == 0x10 ? 0x02 :
703
			    msg[3] == 0x0f ? ScrollUp :
704
			    msg[3] == 0x01 ? ScrollDown : 0 );
705
		mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
706
		nb = 0;
707
	}
708
	return 0;
709
}
710
 
711
/*
712
 *  Logitech 5 byte packed binary mouse format, 8 bit bytes
713
 *
714
 *  shift & right button is the same as middle button (for 2 button mice)
715
 */
716
int
717
mouseputc(Queue*, int c)
718
{
719
	static short msg[5];
720
	static int nb;
721
	static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 3, 3, 7};
722
	int dx, dy, newbuttons;
723
	static ulong lasttick;
724
	ulong m;
725
 
726
	/* Resynchronize in stream with timing. */
727
	m = MACHP(0)->ticks;
728
	if(TK2SEC(m - lasttick) > 2)
729
		nb = 0;
730
	lasttick = m;
731
 
732
	if((c&0xF0) == 0x80)
733
		nb=0;
734
	msg[nb] = c;
735
	if(c & 0x80)
736
		msg[nb] |= ~0xFF;	/* sign extend */
737
	if(++nb == 5){
738
		newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)];
739
		dx = msg[1]+msg[3];
740
		dy = -(msg[2]+msg[4]);
741
		mousetrack(dx, dy, newbuttons, TK2MS(MACHP(0)->ticks));
742
		nb = 0;
743
	}
744
	return 0;
745
}
746
 
747
int
748
mousechanged(void*)
749
{
750
	return mouse.lastcounter != mouse.counter ||
751
		mouse.lastresize != mouse.resize;
752
}
753
 
754
Point
755
mousexy(void)
756
{
757
	return mouse.xy;
758
}
759
 
760
void
761
mouseaccelerate(int x)
762
{
763
	mouse.acceleration = x;
764
	if(mouse.acceleration < 3)
765
		mouse.maxacc = 2;
766
	else
767
		mouse.maxacc = mouse.acceleration;
768
}
769
 
770
/*
771
 * notify reader that screen has been resized
772
 */
773
void
774
mouseresize(void)
775
{
776
	mouse.resize++;
777
	wakeup(&mouse.r);
778
}
779