update copyright date
[gnash.git] / libdevice / events / MouseDevice.cpp
blob2ae8c7df5f1f4f2485f6a9ae260c9bc6fce09c3d
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.
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
19 #ifdef HAVE_CONFIG_H
20 #include "gnashconfig.h"
21 #endif
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <boost/shared_array.hpp>
29 #include "GnashSleep.h"
30 #include "log.h"
31 #include "InputDevice.h"
33 namespace gnash {
35 static const char *MOUSE_DEVICE = "/dev/input/mice";
37 MouseDevice::MouseDevice()
38 : _previous_x(0),
39 _previous_y(0)
41 // GNASH_REPORT_FUNCTION;
44 MouseDevice::~MouseDevice()
46 // GNASH_REPORT_FUNCTION;
49 std::vector<boost::shared_ptr<InputDevice> >
50 MouseDevice::scanForDevices()
52 // GNASH_REPORT_FUNCTION;
54 struct stat st;
56 std::vector<boost::shared_ptr<InputDevice> > devices;
58 // Look for these files for mouse input
59 struct mouse_types {
60 InputDevice::devicetype_e type;
61 const char *filespec;
64 // Debug strings to make output more readable
65 const char *debug[] = {
66 "UNKNOWN",
67 "Keyboard",
68 "User Mode Mouse",
69 "PS/2 Mouse",
70 "Touchscreen",
71 "Touchscreen Mouse",
72 "Power Button",
73 "Sleep Button",
74 "Serial-USB Adapter",
75 "Infrared Receiver"
76 };
78 struct mouse_types mice[] = {
79 {InputDevice::MOUSE, "/dev/input/mice"}, // PS/2 Mouse
80 #ifdef MULTIPLE_DEVICES
81 {InputDevice::MOUSE, "/dev/input/mouse0"},
82 {InputDevice::MOUSE, "/dev/input/mouse1"},
83 {InputDevice::MOUSE, "/dev/usb/tkpanel0"}, // eTurboTouch touchscreen
84 #endif
85 {InputDevice::UNKNOWN, 0}
88 int i = 0;
89 while (mice[i].type != InputDevice::UNKNOWN) {
90 int fd = 0;
91 if (stat(mice[i].filespec, &st) == 0) {
92 // Then see if we can open it
93 if ((fd = open(mice[i].filespec, O_RDWR|O_NONBLOCK)) < 0) {
94 log_error(_("You don't have the proper permissions to open %s"),
95 mice[i].filespec);
96 i++;
97 continue;
99 log_debug(_("Found a %s device for mouse input using %s"),
100 debug[mice[i].type], mice[i].filespec);
102 boost::shared_ptr<InputDevice> dev;
103 #if defined(USE_MOUSE_PS2) || defined(USE_MOUSE_ETT)
104 dev = boost::shared_ptr<InputDevice>(new MouseDevice());
105 // The User Mode Mouse is write only, so we don't consider
106 // it an input device.
107 dev->setType(mice[i].type);
108 // Close the device now that we know the permissions are
109 // correct. It's reopened correctly by init() when each
110 // found device is instantiated.
111 if (fd) {
112 close(fd);
114 if (dev->init(mice[i].filespec, DEFAULT_BUFFER_SIZE)) {
115 devices.push_back(dev);
117 // dev->dump();
118 #endif
119 } // stat()
121 i++;
122 } // while()
124 return devices;
127 bool
128 MouseDevice::init()
130 // GNASH_REPORT_FUNCTION;
132 return init(MOUSE_DEVICE, DEFAULT_BUFFER_SIZE);
135 bool
136 MouseDevice::init(const std::string &filespec, size_t size)
138 GNASH_REPORT_FUNCTION;
140 _filespec = filespec;
142 _fd = open(filespec.c_str(), O_RDWR | O_NDELAY);
144 if (_fd < 0) {
145 log_debug(_("Could not open %s: %s"), filespec, strerror(errno));
146 return false;
149 #if 0
150 if (fcntl(_fd, F_SETFL, fcntl(_fd, F_GETFL) | O_NONBLOCK) < 0) {
151 log_error(_("Could not set non-blocking mode for mouse device: %s"),
152 strerror(errno));
153 if (_fd) {
154 close(_fd);
155 _fd = -1;
156 return false;
159 #endif
161 // see http://www.computer-engineering.org/ps2mouse/
162 // Clear input buffer
163 unsigned char buf[10], byte;
164 while (read(_fd, buf, size) > 0 ) { }
166 // A touchscreen works similar to a Mouse, but not exactly...
167 if (_type == InputDevice::MOUSE) {
168 // Reset mouse
169 if ((!command(0xFF, buf, 3)) || (buf[0] != 0xFA)) {
170 log_error(_("Mouse reset failed"));
171 if (_fd) {
172 close(_fd);
173 _fd = -1;
174 return false;
178 // Get Device ID (not crucial, debug only)
179 if ((!command(0xF2, buf, 2)) || (buf[0] != 0xFA)) {
180 log_debug(_("WARNING: Could not detect mouse device ID"));
181 } else {
182 unsigned char devid = buf[1];
183 if (devid != 0)
184 log_debug(_("WARNING: Non-standard mouse device ID %d"), devid);
187 // Enable mouse data reporting
188 if ((!command(0xF4, &byte, 1)) || (byte != 0xFA)) {
189 log_debug(_("Could not activate Data Reporting mode for mouse"));
190 if (_fd) {
191 close(_fd);
192 _fd = -1;
193 return false;
197 log_debug(_("Mouse enabled for %s on fd #%d"), _filespec, _fd);
200 return true;
203 // From http://www.computer-engineering.org/ps2mouse
205 // PS/2 Mouse mouse data is always in a 3 byte packet that looks like this:
207 // Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
208 // Byte 1 | Y overflow | X overflow | Y sign bit | X sign bit | Always 1 | Middle | Right | Left
209 // Byte 2 X movement
210 // Byte 3 Y movement
212 // The movement values are 9-bit 2's complement integers, where the
213 // most significant bit appears as a "sign" bit in byte 1 of the
214 // movement data packet. Their value represents the mouse's offset
215 // relative to its position when the previous packet was sent, in
216 // units determined by the current resolution. The range of values
217 // that can be expressed is -255 to +255. If this range is exceeded,
218 // the appropriate overflow bit is set.
220 // Note that reporting is disabled by default. The mouse will not
221 // actually issue any movement data packets until it receives the
222 // "Enable Data Reporting" (0xF4) command.
224 // Stream mode is the default operating mode, and is otherwise set
225 // using the "Set Stream Mode" (0xEA) command.
227 // In remote mode the mouse reads its inputs and updates its
228 // counters/flags at the current sample rate, but it does not
229 // automatically issue data packets when movement has
230 // occured. Instead, the host polls the mouse using the "Read Data"
231 // (0xEB) command. Upon receiving this command the mouse will issue a
232 // single movement data packet and reset its movement counters.
234 // The mouse enters remote mode upon receiving the "Set Remote Mode"
235 // (0xF0) command.
236 bool
237 MouseDevice::check()
239 // GNASH_REPORT_FUNCTION;
241 int xmove, ymove, btn;
242 boost::shared_array<boost::uint8_t> buf;
243 if (_type == InputDevice::TOUCHMOUSE) {
244 // The eTurboTouch has a 4 byte packet
245 buf = readData(4);
246 } else if (_type == InputDevice::MOUSE) {
247 // PS/2 Mouse packets are always 3 bytes
248 buf = readData(3);
251 if (!buf) {
252 return false;
255 // resync
256 if (!buf[0] & 8) { // bit 3 us always set in the first byte
257 log_error(_("No sync in first byte!"));
258 return false;
261 // A Touchscreen works similar to a Mouse, but not exactly.
262 // At least for the eTurboTouch, it has a different layout
263 // in the packet for the location as it has an additional byte
264 btn = buf[0] & 0x7;
265 if (_type == InputDevice::TOUCHMOUSE) {
266 xmove = (buf[1] << 7) | (buf[2]);
267 ymove = (buf[3] << 7) | (buf[4]);
269 printf("touchscreen: %02x %02x %02x %02x %02x | status %d, pos: %d/%d\n",
270 mouse_buf[0], mouse_buf[1], mouse_buf[2], mouse_buf[3], mouse_buf[4],
271 new_btn, new_x, new_y);
274 xmove = static_cast<int>(((static_cast<double>(xmove)- 355) / (1702 - 355)
275 * 1536 + 256));
276 ymove = static_cast<int>(((static_cast<double>(ymove) - 482) / (1771 - 482)
277 * 1536 + 256));
278 // FIXME: don't calculate here, this should be done by the GUI
279 xmove = xmove * _screen_width / 2048;
280 ymove = (2048-ymove) * _screen_height / 2048;
281 } else { // end of InputDevice::TOUCHMOUSE
282 // PS/2 Mouse
283 // The movement values are 9-bit 2's complement integers,
284 // where the most significant bit appears as a "sign" bit in
285 // byte 1 of the movement data packet. Their value represents
286 // the mouse's offset relative to its position when the
287 // previous packet was sent, in units determined by the
288 // current resolution. The range of values that can be
289 // expressed is -255 to +255. If this range is exceeded, the
290 // appropriate overflow bit is set.
291 // (from the manpage for the psm driver)
292 // Byte 1
293 // bit 7 One indicates overflow in the vertical movement count.
294 // bit 6 One indicates overflow in the horizontal movement count.
295 // bit 5 Set if the vertical movement count is negative.
296 // bit 4 Set if the horizontal movement count is negative.
297 // bit 3 Always one.
298 // bit 2 Middle button status; set if pressed. For devices without
299 // the middle button, this bit is always zero.
300 // bit 1 Right button status; set if pressed.
301 // bit 0 Left button status; set if pressed.
302 // Byte 2 Horizontal movement count in two’s complement; -256 through 255.
303 // Note that the sign bit is in the first byte.
304 // Byte 3 Vertical movement count in two’s complement; -256 through 255.
305 // Note that the sign bit is in the first byte.
307 xmove = (~buf[1])+1;
308 ymove = (~buf[2])+1;
310 if (buf[0] & 0x40) {
311 log_debug(_("Vertical mouse movement overflow bit set"));
313 if (buf[0] & 0x80) {
314 log_debug(_("Horizontal mouse movement overflow bit set"));
316 // 0,0 is the lower left of the display, so the negative bits are set
317 // when going from the upper left to the lower right.
319 if (buf[0] & 0x10) {
320 log_debug(_("Horizontal mouse movement negative bit set"));
321 } else {
322 xmove *= -1;
324 if (buf[0] & 0x20) {
325 log_debug(_("Vertical mouse movement negative bit set"));
326 } else {
327 ymove *= -1;
330 log_debug(_("PS/2 Mouse: Xmove=%d, Ymove=%d, Button %d"),
331 xmove, ymove, btn);
333 _input_data.x += xmove;
334 if (_input_data.x < 0) {
335 _input_data.x = 0;
338 _input_data.y += ymove;
339 if (_input_data.y < 0) {
340 _input_data.y = 0;
342 boost::shared_array<int> coords =
343 InputDevice::convertAbsCoords(_input_data.x, _input_data.y,
344 _screen_width, _screen_height);
345 // MouseDevice::convertCoordinates(_input_data.x, _input_data.y,
346 // _screen_width, _screen_height);
347 log_debug(_("convert: Xin=%d, Yin=%d, Xout=%d, Yout=%d"),
348 _input_data.x, _input_data.y, coords[0],coords[1]);
350 _input_data.x = coords[0];
351 _input_data.y = coords[1];
352 } // end of InputDevice::MOUSE
354 log_debug(_("read mouse: X=%d, Y=%d, Btn: btn %d"), _input_data.x,
355 _input_data.y, _input_data.button);
356 addData(false, gnash::key::INVALID, 0, _input_data.x, _input_data.y);
358 // button
359 if (btn != _input_data.button) {
360 _input_data.button = btn;
361 addData(true, gnash::key::INVALID, 0, _input_data.x, _input_data.y);
362 log_debug(_("mouse click! %d"), btn);
365 return true;
368 bool
369 MouseDevice::command(unsigned char cmd, unsigned char *buf, int count)
371 // GNASH_REPORT_FUNCTION;
373 int n;
375 // flush input buffer
376 char trash[16];
377 do {
378 n = ::read(_fd, trash, sizeof trash);
379 if (n > 0)
380 log_debug(_("mouse_command: discarded %d bytes from input buffer"), n);
381 } while (n > 0);
383 // send command
384 if ( -1 == ::write(_fd, &cmd, 1) ) {
385 return false;
388 // read response (if any)
389 while (count > 0) {
390 gnashSleep(250*1000); // 250 ms inter-char timeout (simple method)
391 // TODO: use select() instead
393 n = read(_fd, buf, count);
394 if (n <= 0) {
395 return false;
397 count -= n;
398 buf += n;
401 return true;
403 } // command()
405 // end of namespace
408 // local Variables:
409 // mode: C++
410 // indent-tabs-mode: nil
411 // End: