mouse.c 6.9 KB

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