PageRenderTime 28ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/libdevice/events/MouseDevice.cpp

#
C++ | 410 lines | 233 code | 56 blank | 121 comment | 54 complexity | d5537d5d80909b94dde6d1f3ed0c3f3d MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-3.0, BSD-3-Clause
  1. //
  2. // Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program; if not, write to the Free Software
  16. // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. #ifdef HAVE_CONFIG_H
  19. #include "gnashconfig.h"
  20. #endif
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <fcntl.h>
  24. #include <errno.h>
  25. #include "GnashSleep.h"
  26. #include "log.h"
  27. #include "InputDevice.h"
  28. namespace gnash {
  29. static const char *MOUSE_DEVICE = "/dev/input/mice";
  30. MouseDevice::MouseDevice()
  31. : _previous_x(0),
  32. _previous_y(0)
  33. {
  34. // GNASH_REPORT_FUNCTION;
  35. }
  36. MouseDevice::~MouseDevice()
  37. {
  38. // GNASH_REPORT_FUNCTION;
  39. }
  40. std::vector<std::shared_ptr<InputDevice> >
  41. MouseDevice::scanForDevices()
  42. {
  43. // GNASH_REPORT_FUNCTION;
  44. struct stat st;
  45. std::vector<std::shared_ptr<InputDevice> > devices;
  46. // Look for these files for mouse input
  47. struct mouse_types {
  48. InputDevice::devicetype_e type;
  49. const char *filespec;
  50. };
  51. // Debug strings to make output more readable
  52. const char *debug[] = {
  53. "UNKNOWN",
  54. "Keyboard",
  55. "User Mode Mouse",
  56. "PS/2 Mouse",
  57. "Touchscreen",
  58. "Touchscreen Mouse",
  59. "Power Button",
  60. "Sleep Button",
  61. "Serial-USB Adapter",
  62. "Infrared Receiver"
  63. };
  64. struct mouse_types mice[] = {
  65. {InputDevice::MOUSE, "/dev/input/mice"}, // PS/2 Mouse
  66. #ifdef MULTIPLE_DEVICES
  67. {InputDevice::MOUSE, "/dev/input/mouse0"},
  68. {InputDevice::MOUSE, "/dev/input/mouse1"},
  69. {InputDevice::MOUSE, "/dev/usb/tkpanel0"}, // eTurboTouch touchscreen
  70. #endif
  71. {InputDevice::UNKNOWN, 0}
  72. };
  73. int i = 0;
  74. while (mice[i].type != InputDevice::UNKNOWN) {
  75. int fd = 0;
  76. if (stat(mice[i].filespec, &st) == 0) {
  77. // Then see if we can open it
  78. if ((fd = open(mice[i].filespec, O_RDWR|O_NONBLOCK)) < 0) {
  79. log_error(_("You don't have the proper permissions to open %s"),
  80. mice[i].filespec);
  81. i++;
  82. continue;
  83. }
  84. log_debug(_("Found a %s device for mouse input using %s"),
  85. debug[mice[i].type], mice[i].filespec);
  86. std::shared_ptr<InputDevice> dev;
  87. #if defined(USE_MOUSE_PS2) || defined(USE_MOUSE_ETT)
  88. dev = std::shared_ptr<InputDevice>(new MouseDevice());
  89. // The User Mode Mouse is write only, so we don't consider
  90. // it an input device.
  91. dev->setType(mice[i].type);
  92. // Close the device now that we know the permissions are
  93. // correct. It's reopened correctly by init() when each
  94. // found device is instantiated.
  95. if (fd) {
  96. close(fd);
  97. }
  98. if (dev->init(mice[i].filespec, DEFAULT_BUFFER_SIZE)) {
  99. devices.push_back(dev);
  100. }
  101. // dev->dump();
  102. #endif
  103. } // stat()
  104. i++;
  105. } // while()
  106. return devices;
  107. }
  108. bool
  109. MouseDevice::init()
  110. {
  111. // GNASH_REPORT_FUNCTION;
  112. return init(MOUSE_DEVICE, DEFAULT_BUFFER_SIZE);
  113. }
  114. bool
  115. MouseDevice::init(const std::string &filespec, size_t size)
  116. {
  117. GNASH_REPORT_FUNCTION;
  118. _filespec = filespec;
  119. _fd = open(filespec.c_str(), O_RDWR | O_NDELAY);
  120. if (_fd < 0) {
  121. log_debug(_("Could not open %s: %s"), filespec, strerror(errno));
  122. return false;
  123. }
  124. #if 0
  125. if (fcntl(_fd, F_SETFL, fcntl(_fd, F_GETFL) | O_NONBLOCK) < 0) {
  126. log_error(_("Could not set non-blocking mode for mouse device: %s"),
  127. strerror(errno));
  128. if (_fd) {
  129. close(_fd);
  130. _fd = -1;
  131. return false;
  132. }
  133. }
  134. #endif
  135. // see http://www.computer-engineering.org/ps2mouse/
  136. // Clear input buffer
  137. unsigned char buf[10], byte;
  138. while (read(_fd, buf, size) > 0 ) { }
  139. // A touchscreen works similar to a Mouse, but not exactly...
  140. if (_type == InputDevice::MOUSE) {
  141. // Reset mouse
  142. if ((!command(0xFF, buf, 3)) || (buf[0] != 0xFA)) {
  143. log_error(_("Mouse reset failed"));
  144. if (_fd) {
  145. close(_fd);
  146. _fd = -1;
  147. return false;
  148. }
  149. }
  150. // Get Device ID (not crucial, debug only)
  151. if ((!command(0xF2, buf, 2)) || (buf[0] != 0xFA)) {
  152. log_debug(_("WARNING: Could not detect mouse device ID"));
  153. } else {
  154. unsigned char devid = buf[1];
  155. if (devid != 0)
  156. log_debug(_("WARNING: Non-standard mouse device ID %d"), devid);
  157. }
  158. // Enable mouse data reporting
  159. if ((!command(0xF4, &byte, 1)) || (byte != 0xFA)) {
  160. log_debug(_("Could not activate Data Reporting mode for mouse"));
  161. if (_fd) {
  162. close(_fd);
  163. _fd = -1;
  164. return false;
  165. }
  166. }
  167. log_debug(_("Mouse enabled for %s on fd #%d"), _filespec, _fd);
  168. }
  169. return true;
  170. }
  171. // From http://www.computer-engineering.org/ps2mouse
  172. //
  173. // PS/2 Mouse mouse data is always in a 3 byte packet that looks like this:
  174. //
  175. // Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
  176. // Byte 1 | Y overflow | X overflow | Y sign bit | X sign bit | Always 1 | Middle | Right | Left
  177. // Byte 2 X movement
  178. // Byte 3 Y movement
  179. //
  180. // The movement values are 9-bit 2's complement integers, where the
  181. // most significant bit appears as a "sign" bit in byte 1 of the
  182. // movement data packet. Their value represents the mouse's offset
  183. // relative to its position when the previous packet was sent, in
  184. // units determined by the current resolution. The range of values
  185. // that can be expressed is -255 to +255. If this range is exceeded,
  186. // the appropriate overflow bit is set.
  187. //
  188. // Note that reporting is disabled by default. The mouse will not
  189. // actually issue any movement data packets until it receives the
  190. // "Enable Data Reporting" (0xF4) command.
  191. //
  192. // Stream mode is the default operating mode, and is otherwise set
  193. // using the "Set Stream Mode" (0xEA) command.
  194. //
  195. // In remote mode the mouse reads its inputs and updates its
  196. // counters/flags at the current sample rate, but it does not
  197. // automatically issue data packets when movement has
  198. // occured. Instead, the host polls the mouse using the "Read Data"
  199. // (0xEB) command. Upon receiving this command the mouse will issue a
  200. // single movement data packet and reset its movement counters.
  201. // The mouse enters remote mode upon receiving the "Set Remote Mode"
  202. // (0xF0) command.
  203. bool
  204. MouseDevice::check()
  205. {
  206. // GNASH_REPORT_FUNCTION;
  207. int xmove, ymove, btn;
  208. std::unique_ptr<std::uint8_t[]> buf;
  209. if (_type == InputDevice::TOUCHMOUSE) {
  210. // The eTurboTouch has a 4 byte packet
  211. buf = readData(4);
  212. } else if (_type == InputDevice::MOUSE) {
  213. // PS/2 Mouse packets are always 3 bytes
  214. buf = readData(3);
  215. }
  216. if (!buf) {
  217. return false;
  218. }
  219. // resync
  220. if (!buf[0] & 8) { // bit 3 us always set in the first byte
  221. log_error(_("No sync in first byte!"));
  222. return false;
  223. }
  224. // A Touchscreen works similar to a Mouse, but not exactly.
  225. // At least for the eTurboTouch, it has a different layout
  226. // in the packet for the location as it has an additional byte
  227. btn = buf[0] & 0x7;
  228. if (_type == InputDevice::TOUCHMOUSE) {
  229. xmove = (buf[1] << 7) | (buf[2]);
  230. ymove = (buf[3] << 7) | (buf[4]);
  231. /*
  232. printf("touchscreen: %02x %02x %02x %02x %02x | status %d, pos: %d/%d\n",
  233. mouse_buf[0], mouse_buf[1], mouse_buf[2], mouse_buf[3], mouse_buf[4],
  234. new_btn, new_x, new_y);
  235. */
  236. xmove = static_cast<int>(((static_cast<double>(xmove)- 355) / (1702 - 355)
  237. * 1536 + 256));
  238. ymove = static_cast<int>(((static_cast<double>(ymove) - 482) / (1771 - 482)
  239. * 1536 + 256));
  240. // FIXME: don't calculate here, this should be done by the GUI
  241. xmove = xmove * _screen_width / 2048;
  242. ymove = (2048-ymove) * _screen_height / 2048;
  243. } else { // end of InputDevice::TOUCHMOUSE
  244. // PS/2 Mouse
  245. // The movement values are 9-bit 2's complement integers,
  246. // where the most significant bit appears as a "sign" bit in
  247. // byte 1 of the movement data packet. Their value represents
  248. // the mouse's offset relative to its position when the
  249. // previous packet was sent, in units determined by the
  250. // current resolution. The range of values that can be
  251. // expressed is -255 to +255. If this range is exceeded, the
  252. // appropriate overflow bit is set.
  253. // (from the manpage for the psm driver)
  254. // Byte 1
  255. // bit 7 One indicates overflow in the vertical movement count.
  256. // bit 6 One indicates overflow in the horizontal movement count.
  257. // bit 5 Set if the vertical movement count is negative.
  258. // bit 4 Set if the horizontal movement count is negative.
  259. // bit 3 Always one.
  260. // bit 2 Middle button status; set if pressed. For devices without
  261. // the middle button, this bit is always zero.
  262. // bit 1 Right button status; set if pressed.
  263. // bit 0 Left button status; set if pressed.
  264. // Byte 2 Horizontal movement count in two’s complement; -256 through 255.
  265. // Note that the sign bit is in the first byte.
  266. // Byte 3 Vertical movement count in two’s complement; -256 through 255.
  267. // Note that the sign bit is in the first byte.
  268. //
  269. xmove = (~buf[1])+1;
  270. ymove = (~buf[2])+1;
  271. if (buf[0] & 0x40) {
  272. log_debug(_("Vertical mouse movement overflow bit set"));
  273. }
  274. if (buf[0] & 0x80) {
  275. log_debug(_("Horizontal mouse movement overflow bit set"));
  276. }
  277. // 0,0 is the lower left of the display, so the negative bits are set
  278. // when going from the upper left to the lower right.
  279. if (buf[0] & 0x10) {
  280. log_debug(_("Horizontal mouse movement negative bit set"));
  281. } else {
  282. xmove *= -1;
  283. }
  284. if (buf[0] & 0x20) {
  285. log_debug(_("Vertical mouse movement negative bit set"));
  286. } else {
  287. ymove *= -1;
  288. }
  289. log_debug(_("PS/2 Mouse: Xmove=%d, Ymove=%d, Button %d"),
  290. xmove, ymove, btn);
  291. _input_data.x += xmove;
  292. if (_input_data.x < 0) {
  293. _input_data.x = 0;
  294. }
  295. _input_data.y += ymove;
  296. if (_input_data.y < 0) {
  297. _input_data.y = 0;
  298. }
  299. std::unique_ptr<int[]> coords =
  300. InputDevice::convertAbsCoords(_input_data.x, _input_data.y,
  301. _screen_width, _screen_height);
  302. // MouseDevice::convertCoordinates(_input_data.x, _input_data.y,
  303. // _screen_width, _screen_height);
  304. log_debug(_("convert: Xin=%d, Yin=%d, Xout=%d, Yout=%d"),
  305. _input_data.x, _input_data.y, coords[0],coords[1]);
  306. _input_data.x = coords[0];
  307. _input_data.y = coords[1];
  308. } // end of InputDevice::MOUSE
  309. log_debug(_("read mouse: X=%d, Y=%d, Btn: btn %d"), _input_data.x,
  310. _input_data.y, _input_data.button);
  311. addData(false, gnash::key::INVALID, 0, _input_data.x, _input_data.y);
  312. // button
  313. if (btn != _input_data.button) {
  314. _input_data.button = btn;
  315. addData(true, gnash::key::INVALID, 0, _input_data.x, _input_data.y);
  316. log_debug(_("mouse click! %d"), btn);
  317. }
  318. return true;
  319. }
  320. bool
  321. MouseDevice::command(unsigned char cmd, unsigned char *buf, int count)
  322. {
  323. // GNASH_REPORT_FUNCTION;
  324. int n;
  325. // flush input buffer
  326. char trash[16];
  327. do {
  328. n = ::read(_fd, trash, sizeof trash);
  329. if (n > 0)
  330. log_debug(_("mouse_command: discarded %d bytes from input buffer"), n);
  331. } while (n > 0);
  332. // send command
  333. if ( -1 == ::write(_fd, &cmd, 1) ) {
  334. return false;
  335. }
  336. // read response (if any)
  337. while (count > 0) {
  338. gnashSleep(250*1000); // 250 ms inter-char timeout (simple method)
  339. // TODO: use select() instead
  340. n = read(_fd, buf, count);
  341. if (n <= 0) {
  342. return false;
  343. }
  344. count -= n;
  345. buf += n;
  346. }
  347. return true;
  348. } // command()
  349. // end of namespace
  350. }
  351. // local Variables:
  352. // mode: C++
  353. // indent-tabs-mode: nil
  354. // End: