]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/mouse.c
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / 9 / pc / mouse.c
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 #include "io.h"
8
9 #define Image   IMAGE
10 #include <draw.h>
11 #include <memdraw.h>
12 #include <cursor.h>
13 #include "screen.h"
14
15 /*
16  *  mouse types
17  */
18 enum
19 {
20         Mouseother=     0,
21         Mouseserial=    1,
22         MousePS2=       2,
23 };
24
25 extern int mouseshifted;
26
27 static QLock mousectlqlock;
28 static int mousetype;
29 static int intellimouse;
30 static int packetsize;
31 static int resolution;
32 static int accelerated;
33 static int mousehwaccel;
34 static char mouseport[5];
35
36 enum
37 {
38         CMaccelerated,
39         CMhwaccel,
40         CMintellimouse,
41         CMlinear,
42         CMps2,
43         CMps2intellimouse,
44         CMres,
45         CMreset,
46         CMserial,
47 };
48
49 static Cmdtab mousectlmsg[] =
50 {
51         CMaccelerated,          "accelerated",          0,
52         CMhwaccel,              "hwaccel",              2,
53         CMintellimouse,         "intellimouse",         1,
54         CMlinear,               "linear",               1,
55         CMps2,                  "ps2",                  1,
56         CMps2intellimouse,      "ps2intellimouse",      1,
57         CMres,                  "res",                  0,
58         CMreset,                "reset",                1,
59         CMserial,               "serial",               0,
60 };
61
62 /*
63  *  ps/2 mouse message is three bytes
64  *
65  *      byte 0 -        0 0 SDY SDX 1 M R L
66  *      byte 1 -        DX
67  *      byte 2 -        DY
68  *
69  *  shift & right button is the same as middle button
70  *
71  * Intellimouse and AccuPoint with extra buttons deliver
72  *      byte 3 -        00 or 01 or FF according to extra button state.
73  * extra buttons are mapped in this code to buttons 4 and 5.
74  * AccuPoint generates repeated events for these buttons;
75 *  it and Intellimouse generate 'down' events only, so
76  * user-level code is required to generate button 'up' events
77  * if they are needed by the application.
78  * Also on laptops with AccuPoint AND external mouse, the
79  * controller may deliver 3 or 4 bytes according to the type
80  * of the external mouse; code must adapt.
81  *
82  * On the NEC Versa series (and perhaps others?) we seem to
83  * lose a byte from the packet every once in a while, which
84  * means we lose where we are in the instruction stream.
85  * To resynchronize, if we get a byte more than two seconds
86  * after the previous byte, we assume it's the first in a packet.
87  */
88 static void
89 ps2mouseputc(int c, int shift)
90 {
91         static short msg[4];
92         static int nb;
93         static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 };
94         static ulong lasttick;
95         ulong m;
96         int buttons, dx, dy;
97
98         /*
99          * non-ps2 keyboards might not set shift
100          * but still set mouseshifted.
101          */
102         shift |= mouseshifted;
103         /*
104          * Resynchronize in stream with timing; see comment above.
105          */
106         m = MACHP(0)->ticks;
107         if(TK2SEC(m - lasttick) > 2)
108                 nb = 0;
109         lasttick = m;
110
111         /* 
112          *  check byte 0 for consistency
113          */
114         if(nb==0 && (c&0xc8)!=0x08)
115                 if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){
116                         /* last byte of 4-byte packet */
117                         packetsize = 4;
118                         return;
119                 }
120
121         msg[nb] = c;
122         if(++nb == packetsize){
123                 nb = 0;
124                 if(msg[0] & 0x10)
125                         msg[1] |= 0xFF00;
126                 if(msg[0] & 0x20)
127                         msg[2] |= 0xFF00;
128
129                 buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
130                 if(intellimouse && packetsize==4){
131                         if((msg[3]&0xc8) == 0x08){
132                                 /* first byte of 3-byte packet */
133                                 packetsize = 3;
134                                 msg[0] = msg[3];
135                                 nb = 1;
136                                 /* fall through to emit previous packet */
137                         }else{
138                                 /* The AccuPoint on the Toshiba 34[48]0CT
139                                  * encodes extra buttons as 4 and 5. They repeat
140                                  * and don't release, however, so user-level
141                                  * timing code is required. Furthermore,
142                                  * intellimice with 3buttons + scroll give a
143                                  * two's complement number in the lower 4 bits
144                                  * (bit 4 is sign extension) that describes
145                                  * the amount the scroll wheel has moved during
146                                  * the last sample. Here we use only the sign to
147                                  * decide whether the wheel is moving up or down
148                                  * and generate a single button 4 or 5 click
149                                  * accordingly.
150                                  */
151                                 if((msg[3] >> 3) & 1) 
152                                         buttons |= 1<<3;
153                                 else if(msg[3] & 0x7) 
154                                         buttons |= 1<<4;
155                         }
156                 }
157                 dx = msg[1];
158                 dy = -msg[2];
159                 mousetrack(dx, dy, buttons, TK2MS(MACHP(0)->ticks));
160         }
161         return;
162 }
163
164 /*
165  *  set up a ps2 mouse
166  */
167 static void
168 ps2mouse(void)
169 {
170         if(mousetype == MousePS2)
171                 return;
172
173         i8042auxenable(ps2mouseputc);
174         /* make mouse streaming, enabled */
175         i8042auxcmd(0xEA);
176         i8042auxcmd(0xF4);
177
178         mousetype = MousePS2;
179         packetsize = 3;
180         mousehwaccel = 1;
181 }
182
183 /*
184  * The PS/2 Trackpoint multiplexor on the IBM Thinkpad T23 ignores
185  * acceleration commands.  It is supposed to pass them on
186  * to the attached device, but my Logitech mouse is simply
187  * not behaving any differently.  For such devices, we allow
188  * the user to use "hwaccel off" to tell us to back off to
189  * software acceleration even if we're using the PS/2 port.
190  * (Serial mice are always software accelerated.)
191  * For more information on the Thinkpad multiplexor, see
192  * http://wwwcssrv.almaden.ibm.com/trackpoint/
193  */
194 static void
195 setaccelerated(int x)
196 {
197         accelerated = x;
198         if(mousehwaccel){
199                 switch(mousetype){
200                 case MousePS2:
201                         i8042auxcmd(0xE7);
202                         return;
203                 }
204         }
205         mouseaccelerate(x);
206 }
207
208 static void
209 setlinear(void)
210 {
211         accelerated = 0;
212         if(mousehwaccel){
213                 switch(mousetype){
214                 case MousePS2:
215                         i8042auxcmd(0xE6);
216                         return;
217                 }
218         }
219         mouseaccelerate(0);
220 }
221
222 static void
223 setres(int n)
224 {
225         resolution = n;
226         switch(mousetype){
227         case MousePS2:
228                 i8042auxcmd(0xE8);
229                 i8042auxcmd(n);
230                 break;
231         }
232 }
233
234 static void
235 setintellimouse(void)
236 {
237         intellimouse = 1;
238         packetsize = 4;
239         switch(mousetype){
240         case MousePS2:
241                 i8042auxcmd(0xF3);      /* set sample */
242                 i8042auxcmd(0xC8);
243                 i8042auxcmd(0xF3);      /* set sample */
244                 i8042auxcmd(0x64);
245                 i8042auxcmd(0xF3);      /* set sample */
246                 i8042auxcmd(0x50);
247                 break;
248         case Mouseserial:
249                 i8250setmouseputc(mouseport, m5mouseputc);
250                 break;
251         }
252 }
253
254 static void
255 resetmouse(void)
256 {
257         packetsize = 3;
258         switch(mousetype){
259         case MousePS2:
260                 i8042auxcmd(0xF6);
261                 i8042auxcmd(0xEA);      /* streaming */
262                 i8042auxcmd(0xE8);      /* set resolution */
263                 i8042auxcmd(3);
264                 i8042auxcmd(0xF4);      /* enabled */
265                 break;
266         }
267 }
268
269 void
270 mousectl(Cmdbuf *cb)
271 {
272         Cmdtab *ct;
273
274         qlock(&mousectlqlock);
275         if(waserror()){
276                 qunlock(&mousectlqlock);
277                 nexterror();
278         }
279
280         ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
281         switch(ct->index){
282         case CMaccelerated:
283                 setaccelerated(cb->nf == 1 ? 1 : atoi(cb->f[1]));
284                 break;
285         case CMintellimouse:
286                 setintellimouse();
287                 break;
288         case CMlinear:
289                 setlinear();
290                 break;
291         case CMps2:
292                 intellimouse = 0;
293                 ps2mouse();
294                 break;
295         case CMps2intellimouse:
296                 ps2mouse();
297                 setintellimouse();
298                 break;
299         case CMres:
300                 if(cb->nf >= 2)
301                         setres(atoi(cb->f[1]));
302                 else
303                         setres(1);
304                 break;
305         case CMreset:
306                 resetmouse();
307                 if(accelerated)
308                         setaccelerated(accelerated);
309                 if(resolution)
310                         setres(resolution);
311                 if(intellimouse)
312                         setintellimouse();
313                 break;
314         case CMserial:
315                 if(mousetype == Mouseserial)
316                         error(Emouseset);
317
318                 if(cb->nf > 2){
319                         if(strcmp(cb->f[2], "M") == 0)
320                                 i8250mouse(cb->f[1], m3mouseputc, 0);
321                         else if(strcmp(cb->f[2], "MI") == 0)
322                                 i8250mouse(cb->f[1], m5mouseputc, 0);
323                         else
324                                 i8250mouse(cb->f[1], mouseputc, cb->nf == 1);
325                 } else
326                         i8250mouse(cb->f[1], mouseputc, cb->nf == 1);
327
328                 mousetype = Mouseserial;
329                 strncpy(mouseport, cb->f[1], sizeof(mouseport)-1);
330                 packetsize = 3;
331                 break;
332         case CMhwaccel:
333                 if(strcmp(cb->f[1], "on")==0)
334                         mousehwaccel = 1;
335                 else if(strcmp(cb->f[1], "off")==0)
336                         mousehwaccel = 0;
337                 else
338                         cmderror(cb, "bad mouse control message");
339         }
340
341         qunlock(&mousectlqlock);
342         poperror();
343 }