/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
- //
- // Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation; either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- //
- #ifdef HAVE_CONFIG_H
- #include "gnashconfig.h"
- #endif
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #include "GnashSleep.h"
- #include "log.h"
- #include "InputDevice.h"
- namespace gnash {
- static const char *MOUSE_DEVICE = "/dev/input/mice";
- MouseDevice::MouseDevice()
- : _previous_x(0),
- _previous_y(0)
- {
- // GNASH_REPORT_FUNCTION;
- }
- MouseDevice::~MouseDevice()
- {
- // GNASH_REPORT_FUNCTION;
- }
- std::vector<std::shared_ptr<InputDevice> >
- MouseDevice::scanForDevices()
- {
- // GNASH_REPORT_FUNCTION;
- struct stat st;
- std::vector<std::shared_ptr<InputDevice> > devices;
- // Look for these files for mouse input
- struct mouse_types {
- InputDevice::devicetype_e type;
- const char *filespec;
- };
- // Debug strings to make output more readable
- const char *debug[] = {
- "UNKNOWN",
- "Keyboard",
- "User Mode Mouse",
- "PS/2 Mouse",
- "Touchscreen",
- "Touchscreen Mouse",
- "Power Button",
- "Sleep Button",
- "Serial-USB Adapter",
- "Infrared Receiver"
- };
- struct mouse_types mice[] = {
- {InputDevice::MOUSE, "/dev/input/mice"}, // PS/2 Mouse
- #ifdef MULTIPLE_DEVICES
- {InputDevice::MOUSE, "/dev/input/mouse0"},
- {InputDevice::MOUSE, "/dev/input/mouse1"},
- {InputDevice::MOUSE, "/dev/usb/tkpanel0"}, // eTurboTouch touchscreen
- #endif
- {InputDevice::UNKNOWN, 0}
- };
- int i = 0;
- while (mice[i].type != InputDevice::UNKNOWN) {
- int fd = 0;
- if (stat(mice[i].filespec, &st) == 0) {
- // Then see if we can open it
- if ((fd = open(mice[i].filespec, O_RDWR|O_NONBLOCK)) < 0) {
- log_error(_("You don't have the proper permissions to open %s"),
- mice[i].filespec);
- i++;
- continue;
- }
- log_debug(_("Found a %s device for mouse input using %s"),
- debug[mice[i].type], mice[i].filespec);
-
- std::shared_ptr<InputDevice> dev;
- #if defined(USE_MOUSE_PS2) || defined(USE_MOUSE_ETT)
- dev = std::shared_ptr<InputDevice>(new MouseDevice());
- // The User Mode Mouse is write only, so we don't consider
- // it an input device.
- dev->setType(mice[i].type);
- // Close the device now that we know the permissions are
- // correct. It's reopened correctly by init() when each
- // found device is instantiated.
- if (fd) {
- close(fd);
- }
- if (dev->init(mice[i].filespec, DEFAULT_BUFFER_SIZE)) {
- devices.push_back(dev);
- }
- // dev->dump();
- #endif
- } // stat()
-
- i++;
- } // while()
-
- return devices;
- }
- bool
- MouseDevice::init()
- {
- // GNASH_REPORT_FUNCTION;
- return init(MOUSE_DEVICE, DEFAULT_BUFFER_SIZE);
- }
- bool
- MouseDevice::init(const std::string &filespec, size_t size)
- {
- GNASH_REPORT_FUNCTION;
- _filespec = filespec;
- _fd = open(filespec.c_str(), O_RDWR | O_NDELAY);
-
- if (_fd < 0) {
- log_debug(_("Could not open %s: %s"), filespec, strerror(errno));
- return false;
- }
-
- #if 0
- if (fcntl(_fd, F_SETFL, fcntl(_fd, F_GETFL) | O_NONBLOCK) < 0) {
- log_error(_("Could not set non-blocking mode for mouse device: %s"),
- strerror(errno));
- if (_fd) {
- close(_fd);
- _fd = -1;
- return false;
- }
- }
- #endif
- // see http://www.computer-engineering.org/ps2mouse/
- // Clear input buffer
- unsigned char buf[10], byte;
- while (read(_fd, buf, size) > 0 ) { }
-
- // A touchscreen works similar to a Mouse, but not exactly...
- if (_type == InputDevice::MOUSE) {
- // Reset mouse
- if ((!command(0xFF, buf, 3)) || (buf[0] != 0xFA)) {
- log_error(_("Mouse reset failed"));
- if (_fd) {
- close(_fd);
- _fd = -1;
- return false;
- }
- }
-
- // Get Device ID (not crucial, debug only)
- if ((!command(0xF2, buf, 2)) || (buf[0] != 0xFA)) {
- log_debug(_("WARNING: Could not detect mouse device ID"));
- } else {
- unsigned char devid = buf[1];
- if (devid != 0)
- log_debug(_("WARNING: Non-standard mouse device ID %d"), devid);
- }
-
- // Enable mouse data reporting
- if ((!command(0xF4, &byte, 1)) || (byte != 0xFA)) {
- log_debug(_("Could not activate Data Reporting mode for mouse"));
- if (_fd) {
- close(_fd);
- _fd = -1;
- return false;
- }
- }
-
- log_debug(_("Mouse enabled for %s on fd #%d"), _filespec, _fd);
- }
-
- return true;
- }
- // From http://www.computer-engineering.org/ps2mouse
- //
- // PS/2 Mouse mouse data is always in a 3 byte packet that looks like this:
- //
- // Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
- // Byte 1 | Y overflow | X overflow | Y sign bit | X sign bit | Always 1 | Middle | Right | Left
- // Byte 2 X movement
- // Byte 3 Y movement
- //
- // The movement values are 9-bit 2's complement integers, where the
- // most significant bit appears as a "sign" bit in byte 1 of the
- // movement data packet. Their value represents the mouse's offset
- // relative to its position when the previous packet was sent, in
- // units determined by the current resolution. The range of values
- // that can be expressed is -255 to +255. If this range is exceeded,
- // the appropriate overflow bit is set.
- //
- // Note that reporting is disabled by default. The mouse will not
- // actually issue any movement data packets until it receives the
- // "Enable Data Reporting" (0xF4) command.
- //
- // Stream mode is the default operating mode, and is otherwise set
- // using the "Set Stream Mode" (0xEA) command.
- //
- // In remote mode the mouse reads its inputs and updates its
- // counters/flags at the current sample rate, but it does not
- // automatically issue data packets when movement has
- // occured. Instead, the host polls the mouse using the "Read Data"
- // (0xEB) command. Upon receiving this command the mouse will issue a
- // single movement data packet and reset its movement counters.
- // The mouse enters remote mode upon receiving the "Set Remote Mode"
- // (0xF0) command.
- bool
- MouseDevice::check()
- {
- // GNASH_REPORT_FUNCTION;
- int xmove, ymove, btn;
- std::unique_ptr<std::uint8_t[]> buf;
- if (_type == InputDevice::TOUCHMOUSE) {
- // The eTurboTouch has a 4 byte packet
- buf = readData(4);
- } else if (_type == InputDevice::MOUSE) {
- // PS/2 Mouse packets are always 3 bytes
- buf = readData(3);
- }
-
- if (!buf) {
- return false;
- }
-
- // resync
- if (!buf[0] & 8) { // bit 3 us always set in the first byte
- log_error(_("No sync in first byte!"));
- return false;
- }
- // A Touchscreen works similar to a Mouse, but not exactly.
- // At least for the eTurboTouch, it has a different layout
- // in the packet for the location as it has an additional byte
- btn = buf[0] & 0x7;
- if (_type == InputDevice::TOUCHMOUSE) {
- xmove = (buf[1] << 7) | (buf[2]);
- ymove = (buf[3] << 7) | (buf[4]);
- /*
- printf("touchscreen: %02x %02x %02x %02x %02x | status %d, pos: %d/%d\n",
- mouse_buf[0], mouse_buf[1], mouse_buf[2], mouse_buf[3], mouse_buf[4],
- new_btn, new_x, new_y);
- */
-
- xmove = static_cast<int>(((static_cast<double>(xmove)- 355) / (1702 - 355)
- * 1536 + 256));
- ymove = static_cast<int>(((static_cast<double>(ymove) - 482) / (1771 - 482)
- * 1536 + 256));
- // FIXME: don't calculate here, this should be done by the GUI
- xmove = xmove * _screen_width / 2048;
- ymove = (2048-ymove) * _screen_height / 2048;
- } else { // end of InputDevice::TOUCHMOUSE
- // PS/2 Mouse
- // The movement values are 9-bit 2's complement integers,
- // where the most significant bit appears as a "sign" bit in
- // byte 1 of the movement data packet. Their value represents
- // the mouse's offset relative to its position when the
- // previous packet was sent, in units determined by the
- // current resolution. The range of values that can be
- // expressed is -255 to +255. If this range is exceeded, the
- // appropriate overflow bit is set.
- // (from the manpage for the psm driver)
- // Byte 1
- // bit 7 One indicates overflow in the vertical movement count.
- // bit 6 One indicates overflow in the horizontal movement count.
- // bit 5 Set if the vertical movement count is negative.
- // bit 4 Set if the horizontal movement count is negative.
- // bit 3 Always one.
- // bit 2 Middle button status; set if pressed. For devices without
- // the middle button, this bit is always zero.
- // bit 1 Right button status; set if pressed.
- // bit 0 Left button status; set if pressed.
- // Byte 2 Horizontal movement count in twoâs complement; -256 through 255.
- // Note that the sign bit is in the first byte.
- // Byte 3 Vertical movement count in twoâs complement; -256 through 255.
- // Note that the sign bit is in the first byte.
- //
- xmove = (~buf[1])+1;
- ymove = (~buf[2])+1;
-
- if (buf[0] & 0x40) {
- log_debug(_("Vertical mouse movement overflow bit set"));
- }
- if (buf[0] & 0x80) {
- log_debug(_("Horizontal mouse movement overflow bit set"));
- }
- // 0,0 is the lower left of the display, so the negative bits are set
- // when going from the upper left to the lower right.
-
- if (buf[0] & 0x10) {
- log_debug(_("Horizontal mouse movement negative bit set"));
- } else {
- xmove *= -1;
- }
- if (buf[0] & 0x20) {
- log_debug(_("Vertical mouse movement negative bit set"));
- } else {
- ymove *= -1;
- }
-
- log_debug(_("PS/2 Mouse: Xmove=%d, Ymove=%d, Button %d"),
- xmove, ymove, btn);
-
- _input_data.x += xmove;
- if (_input_data.x < 0) {
- _input_data.x = 0;
- }
- _input_data.y += ymove;
- if (_input_data.y < 0) {
- _input_data.y = 0;
- }
- std::unique_ptr<int[]> coords =
- InputDevice::convertAbsCoords(_input_data.x, _input_data.y,
- _screen_width, _screen_height);
- // MouseDevice::convertCoordinates(_input_data.x, _input_data.y,
- // _screen_width, _screen_height);
- log_debug(_("convert: Xin=%d, Yin=%d, Xout=%d, Yout=%d"),
- _input_data.x, _input_data.y, coords[0],coords[1]);
-
- _input_data.x = coords[0];
- _input_data.y = coords[1];
- } // end of InputDevice::MOUSE
-
- log_debug(_("read mouse: X=%d, Y=%d, Btn: btn %d"), _input_data.x,
- _input_data.y, _input_data.button);
- addData(false, gnash::key::INVALID, 0, _input_data.x, _input_data.y);
-
- // button
- if (btn != _input_data.button) {
- _input_data.button = btn;
- addData(true, gnash::key::INVALID, 0, _input_data.x, _input_data.y);
- log_debug(_("mouse click! %d"), btn);
- }
-
- return true;
- }
- bool
- MouseDevice::command(unsigned char cmd, unsigned char *buf, int count)
- {
- // GNASH_REPORT_FUNCTION;
- int n;
-
- // flush input buffer
- char trash[16];
- do {
- n = ::read(_fd, trash, sizeof trash);
- if (n > 0)
- log_debug(_("mouse_command: discarded %d bytes from input buffer"), n);
- } while (n > 0);
-
- // send command
- if ( -1 == ::write(_fd, &cmd, 1) ) {
- return false;
- }
- // read response (if any)
- while (count > 0) {
- gnashSleep(250*1000); // 250 ms inter-char timeout (simple method)
- // TODO: use select() instead
-
- n = read(_fd, buf, count);
- if (n <= 0) {
- return false;
- }
- count -= n;
- buf += n;
- }
-
- return true;
-
- } // command()
- // end of namespace
- }
- // local Variables:
- // mode: C++
- // indent-tabs-mode: nil
- // End: