2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 // -----------------------------------------------------------------------------
21 /// \page fb_input FB GUI input devices
23 /// The framebuffer GUI supports various input devices through the modern
24 /// Linux Input Subsystem (/dev/input/event*). Both standard mice and
25 /// touchscreen devices are supported.
27 /// Since there can be multiple input devices in /dev/input/ you have to
28 /// specify which device to use using the
29 // POINTING_DEVICE environment variable for the mouse and
30 /// KEYBOARD_DEVICE environment variable for the keyboard
32 /// \page fb_calibration FB GUI Touchscreen Calibration
34 /// The touchscreen drivers (like "usbtouchscreen") provide raw data from the
35 /// devices. It is up to the user space program to translate this data to
36 /// screen coordinates. Normally this is done by the X server so this
37 /// conversion needs to be done internally by Gnash itself.
39 /// The current implementation uses a very simple 2-point calibration where
40 /// the first point is at one fifth of the screen width and height and the
41 /// second point is at the exact opposite part of the screen (at it's lower
42 /// right). The SWF file calibrate.swf provides a simple graphical reference
43 /// for 4:3 sized screens (don't use it for other formats!).
45 /// With the current preliminary implementation it's a bit uncomfortable to do
48 /// 1) starting gnash with DUMP_RAW environment variable will show raw
49 /// coordinates on STDOUT:
50 /// DUMP_RAW=1 gnash calibrate.swf
52 /// 2) Keep touching the upper left reference point for a while. You'll get
53 /// lots of (similar) coordinates printed. Choose a X/Y coordinate pair you
54 /// think is the best one (ie. the average) and write it down.
56 /// 3) Do the same for the lower right reference point.
58 /// From now on, start gnash with the TSCALIB enivronment variable set to
59 /// the coordinates you just found out. Write the coordinates separated by
60 /// commas (X,Y,X,Y), like this:
61 /// TSCALIB=491,1635,1581,639 gnash yourmovie.swf
64 /// 10-4-2008 N. Coesel
65 /// Added support for tslib. Tslib is a library which allows to create
66 /// a stack / cascade of filters to filter and scale touch-screen output.
67 /// The source doesn't come with much documentation, but writing your
68 /// own filter based on an existing filter is really easy. Tslib can
69 /// deal with old style H3600 style touchscreens and the newer event
70 /// interface. See http://tslib.berlios.de/ to get the source.
72 /// The check_tslib() routine assumes filtering for movement and presses
73 /// is properly setup. Scaling is supposed to be performed by tslib or
74 /// the underlying touchscreen driver.
77 #include "gnashconfig.h"
80 #include "GnashSystemIOHeaders.h"
81 #include "GnashNumeric.h"
83 #include <sys/types.h>
88 #include <sys/ioctl.h>
94 #include <cstdlib> // getenv
99 #if defined(ENABLE_TSLIB) && !defined(HAVE_TSLIB_H)
100 # warning "No tslib.h! Disabling touchscreen support"
108 #include "movie_root.h"
109 #include "RunResources.h"
110 #include "GnashSleep.h" // for gnashSleep
111 #include "Renderer.h"
113 #include <linux/input.h> // for /dev/input/event*
114 #include <events/InputDevice.h>
117 # include "fb_glue_agg.h"
118 # include "agg/Renderer_agg.h"
121 #ifdef RENDERER_OPENVG
122 # include "VG/openvg.h"
123 # include "openvg/OpenVGRenderer.h"
124 # include "fb_glue_ovg.h"
127 #ifdef RENDERER_GLES1
128 # include "fb_glue_gles1.h"
135 int terminate_request
= false; // global scope to avoid GUI access
137 std::auto_ptr
<Gui
> createFBGui(unsigned long windowid
, float scale
,
138 bool do_loop
, RunResources
& r
)
140 // GNASH_REPORT_FUNCTION;
141 return std::auto_ptr
<Gui
>(new FBGui(windowid
, scale
, do_loop
, r
));
144 /// Called on CTRL-C and alike
146 terminate_signal(int /*signo*/) {
147 terminate_request
= true;
150 FBGui::FBGui(unsigned long xid
, float scale
, bool loop
, RunResources
& r
)
151 : Gui(xid
, scale
, loop
, r
),
161 // GNASH_REPORT_FUNCTION;
163 // initializing to zero helps with debugging and prevents weird bugs
164 // memset(mouse_buf, 0, 256);
165 signal(SIGINT
, terminate_signal
);
166 signal(SIGTERM
, terminate_signal
);
171 // GNASH_REPORT_FUNCTION;
175 log_debug(_("Closing framebuffer device"));
181 FBGui::init(int argc
, char *** argv
)
183 // GNASH_REPORT_FUNCTION;
185 // the current renderer as set on the command line or gnashrc file
186 std::string renderer
= _runResources
.getRenderBackend();
188 #ifdef RENDERER_OPENVG
189 if (renderer
.empty()) {
192 if ((renderer
== "openvg") || (renderer
== "ovg")) {
194 _glue
.reset(new FBOvgGlue(0));
195 // Initialize the glue layer between the renderer and the gui toolkit
196 _glue
->init(argc
, argv
);
198 FBOvgGlue
*ovg
= reinterpret_cast<FBOvgGlue
*>(_glue
.get());
200 _width
= ovg
->getWidth();
201 _height
= ovg
->getHeight();
202 log_debug("Width:%d, Height:%d", _width
, _height
);
203 _renderer
.reset(renderer::openvg::create_handler(0));
204 renderer::openvg::Renderer_ovg
*rend
= reinterpret_cast
205 <renderer::openvg::Renderer_ovg
*>(_renderer
.get());
206 rend
->init(_width
, _height
);
210 // map framebuffer into memory
211 // Create a new Glue layer
213 if (renderer
.empty()) {
216 if (renderer
== "agg") {
217 _glue
.reset(new FBAggGlue());
218 // Initialize the glue layer between the renderer and the gui toolkit
219 _glue
->init(argc
, argv
);
220 FBAggGlue
*agg
= reinterpret_cast<FBAggGlue
*>(_glue
.get());
222 _width
= agg
->width();
223 _height
= agg
->height();
224 log_debug("Width:%d, Height:%d", _width
, _height
);
225 _renderer
.reset(agg
->createRenderHandler());
228 if ((renderer
!= "openvg") && (renderer
!= "agg")) {
229 log_error("No renderer! %s not supported.", renderer
);
234 // Look for the User Mode Input (Uinput) device, which is used to
235 // control the movement and coordinates of the mouse cursor.
236 if (_uinput
.scanForDevice()) {
238 _uinput
.moveTo(0, 0);
240 log_error("Found no accessible User mode input event device");
243 // Initialize all the input devices
245 // Look for Mice that use the PS/2 mouse protocol
246 std::vector
<boost::shared_ptr
<InputDevice
> > possibles
247 = InputDevice::scanForDevices();
248 if (possibles
.empty()) {
249 log_error("Found no accessible input event devices");
251 log_debug("Found %d input event devices.", possibles
.size());
254 std::vector
<boost::shared_ptr
<InputDevice
> >::iterator it
;
255 for (it
=possibles
.begin(); it
!=possibles
.end(); ++it
) {
256 // Set the screen size, which is used for calculating absolute
257 // mouse locations from relative ones.
258 (*it
)->setScreenSize(_width
, _height
);
260 if ((*it
)->getType() == InputDevice::MOUSE
) {
262 log_debug("WARNING: Mouse support disabled as it conflicts with the input event support.");
263 // For now we only want keyboard input events, as the mouse
264 // interface default of /dev/input/mice supports hotpluging devices,
265 // unlike the regular events.
267 _inputs
.push_back(*it
);
270 if ((*it
)->getType() == InputDevice::KEYBOARD
) {
271 _inputs
.push_back(*it
);
273 // TSLib also supports linux input events, so we
274 // use that instead of handling the events directly. The
275 // Babbage is configured as a tablet when using input events.
276 if ((*it
)->getType() == InputDevice::TOUCHSCREEN
) {
277 log_debug("Enabling Touchscreen support.");
278 _inputs
.push_back(*it
);
280 if ((*it
)->getType() == InputDevice::TABLET
) {
282 log_debug("WARNING: Babbage Tablet support disabled as it conflicts with TSlib");
284 log_debug("Enabling Babbage Touchscreen support");
285 _inputs
.push_back(*it
);
288 if ((*it
)->getType() == InputDevice::POWERBUTTON
) {
289 _inputs
.push_back(*it
);
293 // Let -j -k override "window" size
294 optind
= 0; opterr
= 0; char c
;
295 while ((c
= getopt (argc
, *argv
, "j:k:X:Y:")) != -1) {
298 _width
= clamp
<int>(atoi(optarg
), 1, _width
);
301 _height
= clamp
<int>(atoi(optarg
), 1, _height
);
304 _xpos
= atoi(optarg
);
307 _ypos
= atoi(optarg
);
313 // FIXME: this allows to draw in a subsection of the screen. OpenVG
314 // should be able to support this, but right now it just gets in
315 // the way of debugging.
317 if ( _xpos
< 0 ) _xpos
+= _var_screeninfo
.xres
- _width
;
318 _xpos
= clamp
<int>(_xpos
, 0, _var_screeninfo
.xres
-_width
);
320 if ( _ypos
< 0 ) _ypos
+= _var_screeninfo
.yres
- _height
;
321 _ypos
= clamp
<int>(_ypos
, 0, _var_screeninfo
.yres
-_height
);
323 log_debug("X:%d, Y:%d", _xpos
, _ypos
);
326 _validbounds
.setTo(0, 0, _width
- 1, _height
- 1);
332 FBGui::resize_view(int width
, int height
)
334 GNASH_REPORT_FUNCTION
;
336 // _glue.prepDrawingArea(width, height, 0);
337 Gui::resize_view(width
, height
);
345 // GNASH_REPORT_FUNCTION;
351 VirtualClock
& timer
= getClock();
354 // let the GUI recompute the x/y scale factors to best fit the whole screen
355 resize_view(_validbounds
.width(), _validbounds
.height());
357 float fps
= getFPS();
359 // FIXME: this value is arbitrary, and will make any movie with
360 // less than 12 frames eat up more of the cpu. It should probably
361 // be a much lower value, like 2.
363 delay
= static_cast<int>(100000/fps
);
365 // 10ms per heart beat
368 log_debug("Movie Frame Rate is %d, adjusting delay to %dms", fps
,
371 // This loops endlessly at the frame rate
372 while (!terminate_request
) {
373 // wait the "heartbeat" inteval. _interval is in milliseconds,
374 // but gnashSleep() wants nanoseconds, so adjust by 1000.
375 gnashSleep(_interval
* 1000);
376 // TODO: Do we need to check the real time slept or is it OK when we woke
377 // up early because of some Linux signal sent to our process (and thus
378 // "advance" faster than the "heartbeat" interval)? - Udo
381 ts_loop_count
++; //increase loopcount
384 // check input devices
388 Gui::advance_movie(this);
390 // check if we've reached a timeout
391 if (_timeout
&& timer
.elapsed() >= _timeout
) {
400 FBGui::renderBuffer()
402 // GNASH_REPORT_FUNCTION;
408 FBGui::createWindow(const char* /*title*/, int /*width*/, int /*height*/,
409 int /*xPosition*/, int /*yPosition*/)
411 // GNASH_REPORT_FUNCTION;
413 _runResources
.setRenderer(_renderer
);
428 // events currently not supported!
433 FBGui::setInterval(unsigned int interval
)
435 _interval
= interval
;
439 FBGui::setTimeout(unsigned int timeout
)
444 // The Framebuffer GUI usually always runs fullscreen, which is
445 // the default, but occasionally users want to run the SWF movie
446 // it's actual size, or in different locations.
448 FBGui::setFullscreen()
454 FBGui::unsetFullscreen()
460 FBGui::showMenu(bool /*show*/)
462 log_unimpl(_("This GUI does not yet support menus"));
466 FBGui::showMouse(bool /*show*/)
468 log_unimpl(_("This GUI does not yet support a mouse pointer"));
469 // Should return true if the pointer was visible before call,
475 FBGui::setInvalidatedRegion(const SWFRect
& bounds
)
477 // GNASH_REPORT_FUNCTION;
479 setInvalidatedRegion(bounds
);
483 FBGui::setInvalidatedRegions(const InvalidatedRanges
& ranges
)
485 // GNASH_REPORT_FUNCTION;
487 _glue
->setInvalidatedRegions(ranges
);
491 FBGui::find_accessible_tty(int no
)
493 // GNASH_REPORT_FUNCTION;
497 fn
= find_accessible_tty("/dev/vc/%d", no
); if (fn
) return fn
;
498 fn
= find_accessible_tty("/dev/tty%d", no
); if (fn
) return fn
;
499 fn
= find_accessible_tty("/dev/tty%02x", no
); if (fn
) return fn
;
500 fn
= find_accessible_tty("/dev/tty%x", no
); if (fn
) return fn
;
501 fn
= find_accessible_tty("/dev/tty%02d", no
); if (fn
) return fn
;
504 fn
= find_accessible_tty("/dev/tty", no
); // just "/dev/tty"
512 FBGui::find_accessible_tty(const char* format
, int no
)
514 static char fname
[1024];
516 snprintf(fname
, sizeof fname
, format
, no
);
518 if (access(fname
, R_OK
|W_OK
) != -1) {
526 FBGui::disable_terminal()
528 // GNASH_REPORT_FUNCTION;
533 // Find the TTY device name
535 char* tty
= find_accessible_tty(0);
540 log_debug(_("WARNING: Could not detect controlling TTY"));
545 // Detect the currently active virtual terminal (so we can switch back to
548 fd
= open(tty
, O_RDWR
);
550 log_debug(_("WARNING: Could not open %s"), tty
);
555 if (ioctl(fd
, VT_GETSTATE
, &vts
) == -1) {
556 log_debug(_("WARNING: Could not get current VT state"));
561 _original_vt
= vts
.v_active
;
562 log_debug(_("Original TTY NO = %d"), _original_vt
);
564 #ifdef REQUEST_NEW_VT
565 // Request a new VT number
566 if (ioctl(fd
, VT_OPENQRY
, &_own_vt
) == -1) {
567 log_debug(_("WARNING: Could not request a new VT"));
572 log_debug(_("Own TTY NO = %d"), _own_vt
);
578 // Activate our new VT
579 tty
= find_accessible_tty(_own_vt
);
581 log_debug(_("WARNING: Could not find device for VT number %d"), _own_vt
);
585 _fd
= open(tty
, O_RDWR
);
587 log_debug(_("WARNING: Could not open %s"), tty
);
591 if (ioctl(fd
, VT_ACTIVATE
, _own_vt
) == -1) {
592 log_debug(_("WARNING: Could not activate VT number %d"), _own_vt
);
597 if (ioctl(fd
, VT_WAITACTIVE
, _own_vt
) == -1) {
598 log_debug(_("WARNING: Error waiting for VT %d becoming active"),
601 //return false; don't abort
606 _own_vt
= _original_vt
; // keep on using the original VT
612 // Activate our new VT
613 tty
= find_accessible_tty(_own_vt
);
615 log_debug(_("WARNING: Could not find device for VT number %d"),
620 fd
= open(tty
, O_RDWR
);
622 log_debug(_("WARNING: Could not open %s"), tty
);
627 // Become session leader and attach to terminal
629 if (ioctl(fd
, TIOCSCTTY
, 0) == -1) {
630 log_debug(_("WARNING: Could not attach controlling terminal (%s)"), tty
);
633 #endif // end of if REQUEST_NEW_VT
635 // Disable keyboard cursor
637 if (ioctl(fd
, KDGETMODE
, &_original_kd
) == -1) {
638 log_debug(_("WARNING: Could not query current keyboard mode on VT"));
641 if (ioctl(fd
, KDSETMODE
, KD_GRAPHICS
) == -1) {
642 log_debug(_("WARNING: Could not switch to graphics mode on new VT"));
649 log_debug(_("VT %d ready"), _own_vt
);
651 // NOTE: We could also implement virtual console switching by using
652 // VT_GETMODE / VT_SETMODE ioctl calls and handling their signals, but
653 // probably nobody will ever want to switch consoles, so I don't bother...
659 FBGui::enable_terminal()
661 // GNASH_REPORT_FUNCTION;
663 log_debug(_("Restoring terminal..."));
665 char* tty
= find_accessible_tty(_own_vt
);
667 log_debug(_("WARNING: Could not find device for VT number %d"), _own_vt
);
671 int fd
= open(tty
, O_RDWR
);
673 log_debug(_("WARNING: Could not open %s"), tty
);
677 if (ioctl(fd
, VT_ACTIVATE
, _original_vt
)) {
678 log_debug(_("WARNING: Could not activate VT number %d"), _original_vt
);
683 if (ioctl(fd
, VT_WAITACTIVE
, _original_vt
)) {
684 log_debug(_("WARNING: Error waiting for VT %d becoming active"),
687 //return false; don't abort
692 if (ioctl(fd
, KDSETMODE
, _original_kd
)) {
693 log_debug(_("WARNING: Could not restore keyboard mode"));
704 FBGui::checkForData()
706 // GNASH_REPORT_FUNCTION;
708 std::vector
<boost::shared_ptr
<InputDevice
> >::iterator it
;
710 for (it
=_inputs
.begin(); it
!=_inputs
.end(); ++it
) {
712 boost::shared_ptr
<InputDevice::input_data_t
> ie
= (*it
)->popData();
714 // notifyMouseMove(ie->x, ie->y);
716 std::cerr
<< "Got data: " << ((ie
->pressed
) ? "true" : "false");
717 std::cerr
<< ", " << ie
->key
<< ", " << ie
->modifier
;
718 std::cerr
<< ", " << ie
->x
<< ", " << ie
->y
<< std::endl
;
719 // cerr << "X = " << coords[0] << endl;
720 // cerr << "Y = " << coords[1] << endl;
723 // Range check and convert the position from relative to
725 boost::shared_array
<int> coords
=
726 MouseDevice::convertCoordinates(ie
->x
, ie
->y
,
727 getStage()->getStageWidth(),
728 getStage()->getStageHeight());
729 // The mouse was moved
730 _uinput
.moveTo(ie
.x
, ie
.y
);
732 notifyMouseMove(coords
[0], coords
[1]);
735 // See if a mouse button was clicked
737 notifyMouseClick(true);
739 double x
= 0.655 * ie
->x
;
740 double y
= 0.46875 * ie
->y
;
741 log_debug("Mouse clicked at: %g:%g", x
, y
);
742 notifyMouseMove(int(x
), int(y
));
744 notifyMouseMove(ie
->x
, ie
->y
);
751 } // end of namespace gui
752 } // end of namespace gnash
756 // indent-tabs-mode: nil