MouseSerial.Mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE MouseSerial; (** AUTHOR "pjm"; PURPOSE "Serial mouse driver"; *)
  3. (**
  4. * Aos serial mouse driver (quick and dirty port from Native Input.Mod and ConfigInput.Mod).
  5. * Mouse protocol information from XFree in X11R6 distribution (Thomas Roell & David Dawes)
  6. *
  7. * Usage:
  8. *
  9. * Load mouse driver: MouseSerial.Install ~
  10. *
  11. * To unload the driver, use System.Free MouseSerial ~
  12. *
  13. * Note:
  14. *
  15. * Be sure that the serial port driver is loaded before this module. Use V24.Install to load the serial port driver.
  16. *
  17. * History:
  18. *
  19. * 12.03.2003 procedure Configure modified to support 8 serial ports i.o. 4 (AFI)
  20. * 03.07.2007 Quick and dirty port to Shell (staubesv)
  21. *)
  22. IMPORT SYSTEM, Machine, KernelLog, Modules, Kernel, Commands, Inputs, Serials;
  23. CONST
  24. Trace = TRUE;
  25. Debug = FALSE;
  26. (* If TRUE, call the V24.Install command when loading this module *)
  27. LoadV24 = TRUE;
  28. (* mouse types *)
  29. MinType = 0; MaxType = 9;
  30. MS = 0; MSC1 = 1; MM = 2; Logi = 3; MSC2 = 4; LogiMan = 5; PS2 = 6; MSI = 7; MSC3 = 8; MSC4 = 9;
  31. (*
  32. 0 Microsoft serial (2-button)
  33. 1 Mouse Systems Corp serial type a (dtr on, rts on)
  34. 2 Logitech serial Type a (old models)
  35. 3 Logitech serial Type b (old models)
  36. 4 Mouse Systems Corp serial type b (dtr off, rts off)
  37. 5 Logitech serial Type c (new models)
  38. 6 PS/2 mouse (default)
  39. 7 Microsoft serial IntelliMouse
  40. 8 Mouse Systems Corp serial type c (dtr off, rts on)
  41. 9 Mouse Systems Corp serial type d (dtr on, rts off)
  42. MT=PS2 PS/2 or built-in
  43. MT=LM1 Logitech 1
  44. MT=LM2 Logitech 2
  45. MT=LM3 Logitech 3
  46. MT=MS1 Mouse Systems 1
  47. MT=MS2 Mouse Systems 2
  48. MT=MS3 Mouse Systems 3
  49. MT=MS4 Mouse Systems 4
  50. MT=MSM Microsoft (2-button)
  51. MT=MSI Microsoft IntelliMouse
  52. MP=1
  53. MP=2
  54. *)
  55. Rate = 100; (* Sampling rate *)
  56. BPS = 1200; (* Speed *)
  57. DetectOffTime = 250; (* ms *)
  58. DetectOnTime = 250; (* ms *)
  59. DetectMaxIdent = 256;
  60. TYPE
  61. Mouse = OBJECT
  62. VAR
  63. type: LONGINT; (* mouse type *)
  64. mbufp, numb: SHORTINT; (* buffer pointer & protocol bytes *)
  65. mbuf: ARRAY 5 OF SET; (* protocol buffer *)
  66. mask0, val0, mask1, val1, lastkeys: SET; (* protocol parameters *)
  67. errors : LONGINT;
  68. res : WORD;
  69. port : Serials.Port;
  70. m : Inputs.MouseMsg; keys: SET;
  71. dead : BOOLEAN;
  72. (* Read a mouse event *)
  73. PROCEDURE GetMouseEvent(VAR keys: SET; VAR dx, dy: LONGINT): WORD;
  74. VAR ch : CHAR; b : SET; res : WORD;
  75. BEGIN
  76. b := {};
  77. port.ReceiveChar(ch, res);
  78. IF res # Serials.Ok THEN RETURN res; END;
  79. b := SYSTEM.VAL(SET, ch);
  80. (* check for resync *)
  81. IF (mbufp # 0) & ((b * mask1 # val1) OR (b = {7})) THEN mbufp := 0 END;
  82. IF (mbufp = 0) & (b * mask0 # val0) THEN
  83. (* skip package, unless it is a LogiMan middle button... *)
  84. IF ((type = MS) OR (type = LogiMan)) & (b * {2..4,6,7} = {}) THEN
  85. keys := lastkeys * {0,2};
  86. IF 5 IN b THEN INCL(keys, 1) END;
  87. dx := 0; dy := 0;
  88. RETURN Serials.Ok;
  89. ELSE
  90. INC(errors);
  91. END
  92. ELSE
  93. mbuf[mbufp] := b; INC(mbufp);
  94. IF mbufp = numb THEN
  95. CASE type OF
  96. |MS, LogiMan:
  97. keys := lastkeys * {1};
  98. IF 5 IN mbuf[0] THEN INCL(keys, 2) END;
  99. IF 4 IN mbuf[0] THEN INCL(keys, 0) END;
  100. dx := LONG(SYSTEM.VAL(SHORTINT, LSH(mbuf[0] * {0,1}, 6) + mbuf[1] * {0..5}));
  101. dy := LONG(SYSTEM.VAL(SHORTINT, LSH(mbuf[0] * {2,3}, 4) + mbuf[2] * {0..5}));
  102. |MSC1, MSC2, MSC3, MSC4:
  103. keys := {0..2} - (mbuf[0] * {0..2});
  104. dx := LONG(SYSTEM.VAL(SHORTINT, mbuf[1])) + LONG(SYSTEM.VAL(SHORTINT, mbuf[3]));
  105. dy := -(LONG(SYSTEM.VAL(SHORTINT, mbuf[2])) + LONG(SYSTEM.VAL(SHORTINT, mbuf[4])));
  106. |MM, Logi:
  107. keys := mbuf[0] * {0..2};
  108. dx := SYSTEM.VAL(INTEGER, mbuf[1]);
  109. IF ~(4 IN mbuf[0]) THEN dx := -dx END;
  110. dy := SYSTEM.VAL(INTEGER, mbuf[2]);
  111. IF 3 IN mbuf[0] THEN dy := -dy END;
  112. |MSI:
  113. keys := {};
  114. IF 4 IN mbuf[0] THEN INCL(keys, 0) END;
  115. IF 5 IN mbuf[0] THEN INCL(keys, 2) END;
  116. IF 3 IN mbuf[3] THEN INCL(keys, 3) END;
  117. IF 4 IN mbuf[3] THEN INCL(keys, 1) END;
  118. IF ~(3 IN mbuf[3]) & (mbuf[3] * {0..2} # {}) THEN INCL(keys, 4) END;
  119. dx := LONG(SYSTEM.VAL(SHORTINT, LSH(mbuf[0] * {0,1}, 6) + mbuf[1] * {0..7}));
  120. dy := LONG(SYSTEM.VAL(SHORTINT, LSH(mbuf[0] * {2,3}, 4) + mbuf[2] * {0..7}));
  121. ELSE
  122. (* ignore *)
  123. END;
  124. mbufp := 0;
  125. RETURN Serials.Ok;
  126. END;
  127. END;
  128. keys := lastkeys; dx := 0; dy := 0;
  129. RETURN 99; (* don't sent mouse message *)
  130. END GetMouseEvent;
  131. PROCEDURE Close;
  132. BEGIN
  133. port.Close();
  134. BEGIN {EXCLUSIVE} AWAIT(dead); END;
  135. END Close;
  136. (* Initialise mouse.
  137. "type" - mouse type from list
  138. "port" - V24.COM[12]
  139. "bps" - V24.BPS*
  140. "rate" - sample rate (not all mice support this) *)
  141. PROCEDURE &Init*(port : Serials.Port; type : LONGINT);
  142. VAR c: CHAR; res: WORD; n: LONGINT;
  143. BEGIN
  144. (* ASSERT: Port is already open *)
  145. ASSERT(port # NIL);
  146. ASSERT((MinType <= type) & (type <= MaxType));
  147. dead := FALSE;
  148. SELF.port := port;
  149. SELF.type := type;
  150. IF type # PS2 THEN
  151. errors := 0;
  152. IF type = LogiMan THEN
  153. SetSpeed(port, type, 1200, 1200);
  154. port.SendChar("*", res);
  155. port.SendChar("X", res);
  156. SetSpeed(port, type, 1200, BPS)
  157. ELSE
  158. SetSpeed(port, type, 9600, BPS);
  159. SetSpeed(port, type, 4800, BPS);
  160. SetSpeed(port, type, 2400, BPS);
  161. SetSpeed(port, type, 1200, BPS);
  162. IF type = Logi THEN
  163. port.SendChar("S", res);
  164. SetSpeed(port, MM, BPS, BPS);
  165. END;
  166. (* set sample rate *)
  167. IF Rate <= 0 THEN c := "O"; (* continuous - don't use *)
  168. ELSIF Rate <= 15 THEN c := "J"; (* 10 Hz *)
  169. ELSIF Rate <= 27 THEN c := "K"; (* 20 *)
  170. ELSIF Rate <= 42 THEN c := "L"; (* 35 *)
  171. ELSIF Rate <= 60 THEN c := "R"; (* 50 *)
  172. ELSIF Rate <= 85 THEN c := "M"; (* 70 *)
  173. ELSIF Rate <= 125 THEN c := "Q"; (* 100 *)
  174. ELSE c := "N"; (* 150 *)
  175. END;
  176. port.SendChar(c, res);
  177. IF type = MSC2 THEN port.ClearMC({Serials.DTR, Serials.RTS});
  178. ELSIF type = MSC3 THEN port.ClearMC( {Serials.DTR});
  179. ELSIF type = MSC4 THEN port.ClearMC( {Serials.RTS});
  180. END;
  181. END;
  182. mbufp := 0; lastkeys := {};
  183. (* protocol parameters *)
  184. CASE type OF
  185. |MS: numb := 3; mask0 := {6}; val0 := {6}; mask1 := {6}; val1 := {};
  186. |MSC1, MSC2, MSC3, MSC4: numb := 5; mask0 := {3..7}; val0 := {7}; mask1 := {}; val1 := {};
  187. |MM: numb := 3; mask0 := {5..7}; val0 := {7}; mask1 := {7}; val1 := {};
  188. |Logi: numb := 3; mask0 := {5..7}; val0 := {7}; mask1 := {7}; val1 := {};
  189. |LogiMan: numb := 3; mask0 := {6}; val0 := {6}; mask1 := {6}; val1 := {};
  190. |MSI: numb := 4; mask0 := {6}; val0 := {6}; mask1 := {6}; val1 := {};
  191. ELSE
  192. (* ignore *)
  193. END;
  194. (* ignore the first few bytes from the mouse (e.g. Logitech MouseMan Sensa) *)
  195. n := 4;
  196. REPEAT
  197. WHILE port.Available() # 0 DO port.ReceiveChar(c, res) END;
  198. Wait(1000 DIV n); (* wait 1/4s, 1/3s, 1/2s, 1s *)
  199. DEC(n);
  200. UNTIL (port.Available() = 0) OR (n = 0)
  201. END;
  202. (* Lower/Raise DTR/RTS for autodetection, and to start an Intellimouse *)
  203. port.ClearMC({Serials.DTR, Serials.RTS});
  204. Wait(DetectOffTime);
  205. port.SetMC({Serials.DTR, Serials.RTS});
  206. Wait(DetectOnTime);
  207. END Init;
  208. BEGIN {ACTIVE}
  209. LOOP
  210. res := GetMouseEvent(keys, m.dx, m.dy);
  211. IF res = Serials.Ok THEN
  212. m.dz := 0; m.keys := {};
  213. IF 0 IN keys THEN INCL(m.keys, 2); END;
  214. IF 1 IN keys THEN INCL(m.keys, 1); END;
  215. IF 2 IN keys THEN INCL(m.keys, 0); END;
  216. Inputs.mouse.Handle(m);
  217. ELSIF res = Serials.Closed THEN
  218. EXIT;
  219. END;
  220. END;
  221. BEGIN {EXCLUSIVE} dead := TRUE; END;
  222. END Mouse;
  223. VAR
  224. mouse : ARRAY Serials.MaxPorts + 1 OF Mouse; (* index 0 not used *)
  225. timer : Kernel.Timer;
  226. (* Set mouse speed *)
  227. PROCEDURE SetSpeed(port : Serials.Port; mouseType, oldBPS, newBPS: LONGINT);
  228. VAR word, stop, par : INTEGER; c : CHAR; res : WORD;
  229. BEGIN
  230. ASSERT(port # NIL);
  231. port.Close();
  232. CASE mouseType OF
  233. MS: word := 7; stop := Serials.Stop1; par := Serials.ParNo |
  234. MSC1, MSC2, MSC3, MSC4: word := 8; stop := Serials.Stop2; par := Serials.ParNo |
  235. MM: word := 8; stop := Serials.Stop1; par := Serials.ParOdd |
  236. Logi: word := 8; stop := Serials.Stop2; par := Serials.ParNo |
  237. LogiMan: word := 7; stop := Serials.Stop1; par := Serials.ParNo |
  238. MSI: word := 7; stop := Serials.Stop1; par := Serials.ParNo
  239. ELSE
  240. (* ignore *)
  241. END;
  242. IF (mouseType = Logi) OR (mouseType = LogiMan) THEN
  243. port.Open(oldBPS, word, par, stop, res);
  244. IF res = Serials.Ok THEN
  245. IF newBPS = 9600 THEN c := "q"
  246. ELSIF newBPS = 4800 THEN c := "p"
  247. ELSIF newBPS = 2400 THEN c := "o"
  248. ELSE c := "n"
  249. END;
  250. port.SendChar("*", res);
  251. port.SendChar(c, res);
  252. Wait(100);
  253. port.Close();
  254. END
  255. END;
  256. port.Open(newBPS, word, par, stop, res);
  257. IF res = Serials.Ok THEN
  258. port.SetMC({Serials.DTR, Serials.RTS})
  259. END;
  260. END SetSpeed;
  261. PROCEDURE GetMouseType(CONST s : ARRAY OF CHAR) : LONGINT;
  262. VAR type : LONGINT;
  263. BEGIN
  264. type := MinType-1;
  265. IF (s[0] >= "0") & (s[0] <= "9") THEN (* old style config *)
  266. type := SHORT(ORD(s[0])-ORD("0"))
  267. ELSE (* new style config *)
  268. IF s = "" THEN
  269. (* default if none specified *)
  270. ELSIF (CAP(s[0]) = "L") & (CAP(s[1]) = "M") THEN (* Logitech *)
  271. CASE s[2] OF
  272. |"1": type := LogiMan;
  273. |"2": type := MM;
  274. |"3": type := Logi;
  275. ELSE
  276. type := MinType-1; (* Unknown *)
  277. END;
  278. ELSIF (CAP(s[0]) = "M") & (CAP(s[1]) = "S") THEN (* Mouse Systems or Microsoft *)
  279. IF CAP(s[2]) = "M" THEN
  280. type := MS;
  281. ELSIF CAP(s[2]) = "I" THEN
  282. type := MSI;
  283. ELSE
  284. CASE s[2] OF
  285. |"1": type := MSC1;
  286. |"2": type := MSC2;
  287. |"3": type := MSC3|"4": type := MSC4;
  288. ELSE
  289. type := MinType-1;
  290. END;
  291. END;
  292. ELSIF CAP(s[0]) = "P" THEN (* PS/2 *)
  293. type := PS2
  294. END
  295. END;
  296. IF (type < MinType) OR (type > MaxType) THEN type := PS2 END; (* unknown mouse type *)
  297. RETURN type;
  298. END GetMouseType;
  299. PROCEDURE Detect(port : Serials.Port; VAR mouseType : LONGINT): BOOLEAN;
  300. VAR ch: CHAR; i : LONGINT; res : WORD; mouseIdent : ARRAY DetectMaxIdent OF CHAR;
  301. BEGIN
  302. ASSERT(port # NIL);
  303. port.Open(BPS, 7, Serials.ParNo, Serials.Stop1, res);
  304. IF res # Serials.Ok THEN RETURN FALSE; END;
  305. (* Lower/Raise DTR/RTS for autodetection, and to start an Intellimouse *)
  306. port.ClearMC({Serials.DTR, Serials.RTS});
  307. Wait(DetectOffTime);
  308. port.SetMC({Serials.DTR, Serials.RTS});
  309. Wait(DetectOnTime);
  310. REPEAT
  311. IF port.Available() = 0 THEN RETURN FALSE END;
  312. port.ReceiveChar(ch, res);
  313. IF ch >= 80X THEN ch := CHR(ORD(ch)-80H) END
  314. UNTIL ch = "M";
  315. mouseIdent[0] := ch; i := 1;
  316. WHILE (port.Available() # 0) & (i < DetectMaxIdent-1) DO
  317. port.ReceiveChar(ch, res);
  318. IF ch >= 80X THEN ch := CHR(ORD(ch)-80H) END;
  319. IF (ch < " ") OR (ch >= 7FX) THEN ch := "." END;
  320. mouseIdent[i] := ch; INC(i)
  321. END;
  322. mouseIdent[i] := 0X;
  323. IF Debug THEN
  324. KernelLog.Enter; KernelLog.String("Mouse ident:"); KernelLog.Ln; KernelLog.Buffer(mouseIdent, 0, i); KernelLog.Exit
  325. END;
  326. IF mouseIdent[1] = "3" THEN mouseType := LogiMan;
  327. ELSIF mouseIdent[1] = "Z" THEN mouseType := MSI;
  328. ELSE mouseType := MS;
  329. END;
  330. RETURN TRUE
  331. END Detect;
  332. PROCEDURE Init;
  333. VAR
  334. value: ARRAY DetectMaxIdent OF CHAR;
  335. port : Serials.Port;
  336. mouseType, portNbr : LONGINT;
  337. BEGIN
  338. Machine.GetConfig("MT", value);
  339. IF value[0] # 0X THEN (* manual config *)
  340. mouseType := GetMouseType(value);
  341. (* Get port number *)
  342. Machine.GetConfig("MP", value);
  343. IF (value[0] >= "1") & (value[0] <= "8") THEN
  344. portNbr := ORD(value[0]) - ORD("0");
  345. ELSE
  346. portNbr := 1;
  347. END;
  348. IF Trace THEN
  349. KernelLog.String("MouseSerial: Manual configuration (Port: "); KernelLog.Int(portNbr, 0);
  350. KernelLog.String(", type: "); KernelLog.Int(mouseType, 0); KernelLog.String(")"); KernelLog.Ln;
  351. END;
  352. port := Serials.GetPort(portNbr);
  353. IF port # NIL THEN
  354. IF mouse[portNbr] # NIL THEN mouse[portNbr].Close; END;
  355. NEW(mouse[portNbr], port, mouseType);
  356. IF Trace THEN KernelLog.String("MouseSerial: Mouse at COM port "); KernelLog.Int(portNbr, 0); KernelLog.String(" started."); KernelLog.Ln; END;
  357. ELSE
  358. IF Trace THEN KernelLog.String("MouseSerial: COM port "); KernelLog.Int(portNbr, 0); KernelLog.String(" not avaiable."); KernelLog.Ln; END;
  359. END;
  360. ELSE
  361. IF Trace THEN KernelLog.String("MouseSerial: Auto-detect serial mice..."); KernelLog.Ln; END;
  362. FOR portNbr := 1 TO Serials.MaxPorts DO
  363. port := Serials.GetPort(portNbr);
  364. IF port # NIL THEN
  365. IF Detect(port, mouseType) THEN
  366. IF Trace THEN
  367. KernelLog.String("MouseSerial: Detected mouse at port "); KernelLog.Int(portNbr, 0);
  368. KernelLog.String(" (type: "); KernelLog.Int(mouseType, 0); KernelLog.String(")"); KernelLog.Ln;
  369. END;
  370. IF mouse[portNbr] # NIL THEN mouse[portNbr].Close; END;
  371. NEW(mouse[portNbr], port, mouseType);
  372. END;
  373. END;
  374. END;
  375. IF Trace THEN KernelLog.String("MouseSerial: Auto-detection finished."); KernelLog.Ln; END;
  376. END;
  377. END Init;
  378. PROCEDURE Wait(milliseconds : LONGINT);
  379. BEGIN {EXCLUSIVE}
  380. ASSERT(milliseconds > 0);
  381. timer.Sleep(milliseconds);
  382. END Wait;
  383. PROCEDURE LoadSerialPortDriver;
  384. VAR msg : ARRAY 64 OF CHAR; res : WORD;
  385. BEGIN
  386. KernelLog.String("MouseSerial: Loading serial port driver..."); KernelLog.Ln;
  387. Commands.Call("V24.Install", {Commands.Wait}, res, msg);
  388. IF res = Commands.Ok THEN
  389. KernelLog.String("MouseSerial: Serial port driver loaded.");
  390. ELSE
  391. KernelLog.String("MouseSerial: Loading serial port driver failed, res: "); KernelLog.Int(res, 0); KernelLog.String(" ("); KernelLog.String(msg); KernelLog.String(")");
  392. END;
  393. KernelLog.Ln;
  394. END LoadSerialPortDriver;
  395. PROCEDURE Install*; (** ~ *)
  396. END Install;
  397. (** Manually set the mouse type *)
  398. PROCEDURE ToggleMouseType*(context : Commands.Context); (** PortNumber ~ *)
  399. VAR portNbr, type : LONGINT; m : Mouse;
  400. BEGIN
  401. IF context.arg.GetInteger(portNbr, FALSE) & (1 <= portNbr) & (portNbr <= Serials.MaxPorts) THEN
  402. m := mouse[portNbr];
  403. IF m # NIL THEN
  404. type := (m.type + 1) MOD (MaxType + 1);
  405. m.Close;
  406. NEW(mouse[portNbr], m.port, type);
  407. context.out.String("MouseSerial: Set type of mouse at port "); context.out.Int(portNbr, 0);
  408. context.out.String(" to "); context.out.Int(m.type, 0); context.out.Ln;
  409. ELSE
  410. context.out.String("MouseSerial: No mouse at port "); context.out.Int(portNbr, 0); context.out.Ln;
  411. END;
  412. ELSE context.error.String("MouseSerial: Invalid port number"); context.error.Ln;
  413. END;
  414. END ToggleMouseType;
  415. PROCEDURE Cleanup;
  416. VAR i : LONGINT;
  417. BEGIN
  418. FOR i := 0 TO LEN(mouse)-1 DO
  419. IF mouse[i] # NIL THEN
  420. mouse[i].Close; mouse[i] := NIL;
  421. END;
  422. END;
  423. END Cleanup;
  424. BEGIN
  425. NEW(timer);
  426. Modules.InstallTermHandler(Cleanup);
  427. IF LoadV24 THEN LoadSerialPortDriver; END;
  428. Init;
  429. END MouseSerial.
  430. MouseSerial.Install ~ MouseSerial.ToggleMouseType 1 ~
  431. System.Free MouseSerial ~