diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..999ca85 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 2 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..a526d03 --- /dev/null +++ b/CREDITS @@ -0,0 +1,62 @@ +Multitouch X driver (GPL license) + +Copyright (C) 2008 Henrik Rydberg + +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 2 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 + +--- + +The Multitouch X driver extracts a lot of X knowledge from the +synaptics X driver (MIT license) + +Copyright (C) 1997 C. Scott Ananian +Copyright (C) 1998-2000 Bruce Kalk +Copyright (C) 1999 Henry Davies +Copyright (C) 2001 Stefan Gmeiner +Copyright (C) 2002 Linuxcare Inc. David Kennedy +Copyright (C) 2003 Fred Hucht +Copyright (C) 2003 Neil Brown +Copyright (C) 2003 Jörg Bösner +Copyright (C) 2003 Hartwig Felger +Copyright (C) 2002-2007 Peter Osterlund +Copyright (C) 2004 Arne Schwabe +Copyright (C) 2004 Matthias Ihmig +Copyright (C) 2004 Alexei Gilchrist +Copyright (C) 2006-2007 Christian Thaeter +Copyright (C) 2006 Stefan Bethge +Copyright (C) 2007 Joseph P. Skudlarek +Copyright (C) 2007 Florian Loitsch +Copyright (C) 2008 Fedor P. Goncharov +Copyright (C) 2008-2009 Red Hat, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +--- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bf5be5b --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +VERSION = 1 +PATCHLEVEL = 0 +EXTRAVERSION = rc2 + +LIBRARY = multitouch.so +MODULES = src +XMODULES = driver + +o_src = capabilities hwstate mtstate gestures mconfig mtouch trig + +o_driver= multitouch + +TARGETS += src/test + +OBJECTS = $(addsuffix .o,\ + $(foreach mod,$(MODULES),\ + $(addprefix $(mod)/,$(o_$(mod))))) +XOBJECTS= $(addsuffix .o,\ + $(foreach mod,$(XMODULES),\ + $(addprefix $(mod)/,$(o_$(mod))))) + +TBIN = $(addprefix bin/,$(TARGETS)) +TLIB = $(addprefix obj/,$(LIBRARY)) +TOBJ = $(addprefix obj/,$(addsuffix .o,$(TARGETS))) +OBJS = $(addprefix obj/,$(OBJECTS)) +XOBJS = $(addprefix obj/,$(XOBJECTS)) +LIBS = -lmtdev -lm + +DLIB = usr/lib/xorg/modules/input + +INCLUDE = -Iinclude -I/usr/include/xorg -I/usr/include/pixman-1 +OPTS = $(CFLAGS) -O3 -fPIC + +.PHONY: all clean +.PRECIOUS: obj/%.o + +all: $(OBJS) $(TLIB) $(TOBJ) $(TBIN) + +bin/%: obj/%.o $(OBJS) + @mkdir -p $(@D) + gcc $< -o $@ $(OBJS) $(LIBS) + +$(TLIB): $(OBJS) $(XOBJS) + @rm -f $(TLIB) + gcc -shared $(OBJS) $(XOBJS) $(LIBS) -Wl,-soname -Wl,$(LIBRARY) -o $@ + +obj/%.o: %.c + @mkdir -p $(@D) + gcc $(INCLUDE) $(OPTS) -c $< -o $@ + +obj/%.o: %.cc + @mkdir -p $(@D) + gcc $(INCLUDE) $(OPTS) -c $< -o $@ + +clean: + rm -rf bin obj + +distclean: clean + rm -rf debian/*.log debian/files debian/xf86-input-multitouch* + +install: $(TLIB) + install -d "$(DESTDIR)/$(DLIB)" + install -m 755 $(TLIB) "$(DESTDIR)/$(DLIB)" diff --git a/README b/README new file mode 100644 index 0000000..39f3304 --- /dev/null +++ b/README @@ -0,0 +1,200 @@ +xf86-input-multitouch + +The Multitouch X Driver provides gestures support for multitouch +touchpads, in particular those with integrated button. + +Copyright +----------------------------------------------------------- +Copyright (C) 2008 Henrik Rydberg +Copyright (C) 2011 Ryan Bourgeois + + +Configuration +----------------------------------------------------------- +The following is a minimal working InputClass section for xorg.conf: + + Section "InputClass" + MatchIsTouchpad "on" + Identifier "Touchpads" + Driver "multitouch" + EndSection + +Configuration options may be defined inside the InputClass section +to configure the driver. Available options and their defaults are: + +FingerHigh + Defines the pressure at which a finger is detected as a touch. + This is a percentage represented as an integer. Default is 5. + +FingerLow + Defines the pressure at which a finger is detected as a release. + This is a percentage represented as an integer. Default is 5. + +IgnoreThumb + Whether or not to ignore touches that are determined to be thumbs. + Boolean value. Defaults to true. + +IgnorePalm + Whether or not to ignore touches that are determined to be palms. + Boolean value. Defaults to true. + +DisableOnThumb + Whether or not to disable the entire trackpad when a thumb is touching. + Boolean value. Defaults to false. + +DisableOnPalm + Whether or not to disable the entire trackpad when a palm is touching. + Boolean value. Defaults to true. + +ThumbRatio + The width/length ratio of what's considered a thumb. It is expected + that a thumb is longer than it is wide. This tells the driver how much + longer. Percentage represented by an integer. Defaults to 70. + +ThumbSize + The minimum size of what's considered a thumb. It is expected that a + thumb will be larger than other fingers. This is represented as a + percentage of the touchpad height. Integer value. Defaults to 25. + +PalmSize + The minimum size of what's considered a palm. Palms are expected to + be very large on the trackpad. This is an integer value representing + a percentage of the height of the trackpad. Defaults to 40. + +ButtonEnable + Whether or not to enable the physical buttons on or near the trackpad. + Boolean value. Defaults to true. + +ButtonIntegrated + Whether or not the physical buttons are integrated with the trackpad. + If you have a one-piece trackpad like on newer MacBooks, this should + be set to true. Button emulation depends on this value being correct. + Boolean value. Defaults to true. + +ClickFinger1 + Which button to emulate when one finger is touching the trackpad during + a click. Integer value. A value of 0 disables one-touch emulation. + Defaults to 3. + +ClickFinger2 + Which button to emulate when two fingers are touching the trackpad + during a click. Integer value. A value of 0 disabled one-touch emulation. + Defaults to 2. + +TapButton1 + Which button to emulate for one-finger tapping. Integer value. A value of + 0 disables one-finger tapping. Defaults to 1. + +TapButton2 + Which button to emulate for two-finger tapping. Integer value. A value of + 0 disabled two-finger tapping. Defaults to 3. + +TapButton3 + Which button to emulate for three-finger tapping. Integer value. A value + of 0 disabled three-finger tapping. Defaults to 2. + +ClickTime + When tapping, how much time to hold down the emulated button. Integer + value representing milliseconds. Defaults to 100. + +MaxTapTime + The amount of time to wait for a tap to release before counting it as a + move. Integer value representing milliseconds. Defaults to 120. + +MaxTapMove + How far a touch is allowed to move before counting it is no longer + considered a tap. Integer value. Defaults to 400. + +GestureClickTime + When a gesture triggers a click, how much time to hold down the emulated + button. Integer value representing milliseconds. Defaults to 10. + +GestureWaitTime + Touches are allowed to transition from one gesture to another. For + example, you may go from scrolling to swiping without releasing your + fingers from the pad. This value is the amount of time you must be + performing the new gesture before it is triggered. This prevents + accidental touches from triggering other gestures. Integer value + representing milliseconds. Defaults to 100. + +ScrollDistance + For two finger scrolling. How far you must move your fingers before + a button click is triggered. Integer value. Defaults to 400. + +ScrollUpButton + For two finger scrolling. The button that is triggered by scrolling + up. Integer value. A value of 0 disables scrolling up. Defaults to 4. + +ScrollDownButton + For two finger scrolling. The button that is triggered by scrolling + down. Integer value. A value of 0 disables scrolling down. Defaults + to 5. + +ScrollLeftButton + For two finger scrolling. The button that is triggered by scrolling + left. Integer value. A value of 0 disables scrolling left. Defaults + to 6. + +ScrollDownButton + For two finger scrolling. The button that is triggered by scrolling + right. Integer value. A value of 0 disables scrolling right. Defaults + to 7. + +SwipeDistance + For three finger swiping. How far you must move your fingers before + a button click is triggered. Integer value. Defaults to 700. + +SwipeUpButton + For three finger swiping. The button that is triggered by swiping + up. Integer value. A value of 0 disables swiping up. Defaults to 8. + +SwipeDownButton + For three finger swiping. The button that is triggered by swiping + down. Integer value. A value of 0 disables swiping down. Defaults + to 9. + +SwipeLeftButton + For three finger swiping. The button that is triggered by swiping + left. Integer value. A value of 0 disables swiping left. Defaults + to 10. + +SwipeRightButton + For three finger swiping. The button that is triggered by swiping + right. Integer value. A value of 0 disables swiping right. Defaults + to 11. + +ScaleDistance + For pinch scaling. How far you must move your fingers before a + button click is triggered. Integer value. Defaults to 150. + +ScaleUpButton + For pinch scaling. The button that is triggered by scaling up. + Integer value. A value of 0 disables scaling up. Defaults to 12. + +ScaleDownButton + For pinch scaling. The button that is triggered by scaling down. + Integer value. A value of 0 disables scaling down. Defaults to 13. + +RotateDistance + For two finger rotation. How far you must move your fingers before + a button click is triggered. Integer value. Defaults to 150. + +RotateLeftButton + For two finger rotation. The button that is triggered by rotating + left. Integer value. A value of 0 disables rotation left. Defaults + to 14. + +RotateRightButton + For two finger rotation. The button that is triggered by rotating + right. Integer value. A value of 0 disables rotation right. Defaults + to 14. + +TapDragEnable + Whether or not to enable tap-to-drag functionality. Boolean value. + Defaults to true. + +TapDragTime + The tap-to-drag timeout. This is how long the driver will wait + after a single tap for a movement event before sending the click. + Integer value representing milliseconds. Defaults to 200. + diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..82e4cb1 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,9 @@ +xf86-input-multitouch Xorg driver + +* Install the debian package + +* Restart X + +Enjoy! + +Henrik Rydberg diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..7b0cbae --- /dev/null +++ b/debian/changelog @@ -0,0 +1,6 @@ +xf86-input-multitouch (1.0~rc2-mactel1) unstable; urgency=low + + * Introducing the multitouch Xorg input driver + + -- Henrik Rydberg Thu, 21 Oct 2010 19:30:17 +0200 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..0deffc6 --- /dev/null +++ b/debian/control @@ -0,0 +1,17 @@ +Source: xf86-input-multitouch +Section: misc +Priority: optional +Maintainer: Henrik Rydberg +Build-Depends: + debhelper (>= 5), + xserver-xorg-dev (>= 2:1.7.6.901), + libmtdev-dev (>= 1.0.10) +Standards-Version: 3.7.2 +Homepage: http://bitmath.org/code/multitouch/ + +Package: xf86-input-multitouch +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, libmtdev1 (>= 1.0) +Description: Multitouch X input driver + This X input driver provides gestures support for multitouch touchpads, + in particular those with integrated button. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..93b3ce3 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,27 @@ +Source URL: http://bitmath.org/code/multitouch/ + +Upstream Author: Henrik Rydberg + +Copyright: + Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) + +License: + + 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +Packaging: + Copyright (C) 2008 by Henrik Rydberg + released under GPL 2 + diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/debian/dirs @@ -0,0 +1 @@ + diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..c34780c --- /dev/null +++ b/debian/rules @@ -0,0 +1,46 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +build-arch: + dh_testdir + $(MAKE) + +build-indep: + +build: build-arch build-indep + +install: + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + $(MAKE) DESTDIR=$(CURDIR)/debian/xf86-input-multitouch install + +binary-arch: build-arch install + dh_testdir + dh_testroot + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary-indep: build-indep + +binary: binary-arch binary-indep + +clean: + dh_testdir + dh_testroot + $(MAKE) clean + dh_clean + +.PHONY: build-arch build-indep build install binary-arch binary-indep binary clean + diff --git a/driver/multitouch.c b/driver/multitouch.c new file mode 100644 index 0000000..cf4d29f --- /dev/null +++ b/driver/multitouch.c @@ -0,0 +1,317 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#include "mtouch.h" + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 +#include +#include +#endif + +#define TAP_HOLD 100 +#define TAP_TIMEOUT 200 +#define TAP_THRESHOLD 0.05 +#define TICK_TIMEOUT 50 +#define SCROLL_THRESHOLD 0.05 +#define SWIPE_THRESHOLD 0.15 +#define SCALE_THRESHOLD 0.15 +#define ROTATE_THRESHOLD 0.15 + +/* button mapping simplified */ +#define PROPMAP(m, x, y) m[x] = XIGetKnownProperty(y) + +static void pointer_control(DeviceIntPtr dev, PtrCtrl *ctrl) +{ +#if DEBUG_DRIVER + xf86Msg(X_INFO, "pointer_control\n"); +#endif +} + +static int pointer_property(DeviceIntPtr dev, + Atom property, + XIPropertyValuePtr prop, + BOOL checkonly) +{ +#if DEBUG_DRIVER + xf86Msg(X_INFO, "pointer_property\n"); +#endif + return Success; +} + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 +static void initAxesLabels(Atom map[2]) +{ + memset(map, 0, 2 * sizeof(Atom)); + PROPMAP(map, 0, AXIS_LABEL_PROP_REL_X); + PROPMAP(map, 1, AXIS_LABEL_PROP_REL_Y); +} + +static void initButtonLabels(Atom map[DIM_BUTTON]) +{ + memset(map, 0, DIM_BUTTON * sizeof(Atom)); + PROPMAP(map, MT_BUTTON_LEFT, BTN_LABEL_PROP_BTN_LEFT); + PROPMAP(map, MT_BUTTON_MIDDLE, BTN_LABEL_PROP_BTN_MIDDLE); + PROPMAP(map, MT_BUTTON_RIGHT, BTN_LABEL_PROP_BTN_RIGHT); + PROPMAP(map, MT_BUTTON_WHEEL_UP, BTN_LABEL_PROP_BTN_WHEEL_UP); + PROPMAP(map, MT_BUTTON_WHEEL_DOWN, BTN_LABEL_PROP_BTN_WHEEL_DOWN); + PROPMAP(map, MT_BUTTON_HWHEEL_LEFT, BTN_LABEL_PROP_BTN_HWHEEL_LEFT); + PROPMAP(map, MT_BUTTON_HWHEEL_RIGHT, BTN_LABEL_PROP_BTN_HWHEEL_RIGHT); + /* how to map swipe buttons? */ + PROPMAP(map, MT_BUTTON_SWIPE_UP, BTN_LABEL_PROP_BTN_0); + PROPMAP(map, MT_BUTTON_SWIPE_DOWN, BTN_LABEL_PROP_BTN_1); + PROPMAP(map, MT_BUTTON_SWIPE_LEFT, BTN_LABEL_PROP_BTN_2); + PROPMAP(map, MT_BUTTON_SWIPE_RIGHT, BTN_LABEL_PROP_BTN_3); + /* how to map scale and rotate? */ + PROPMAP(map, MT_BUTTON_SCALE_DOWN, BTN_LABEL_PROP_BTN_4); + PROPMAP(map, MT_BUTTON_SCALE_UP, BTN_LABEL_PROP_BTN_5); + PROPMAP(map, MT_BUTTON_ROTATE_LEFT, BTN_LABEL_PROP_BTN_6); + PROPMAP(map, MT_BUTTON_ROTATE_RIGHT, BTN_LABEL_PROP_BTN_7); +} +#endif + +static int device_init(DeviceIntPtr dev, LocalDevicePtr local) +{ + struct MTouch *mt = local->private; + unsigned char btmap[DIM_BUTTON + 1] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + }; +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + Atom axes_labels[2], btn_labels[DIM_BUTTON]; + initAxesLabels(axes_labels); + initButtonLabels(btn_labels); +#endif + + local->fd = xf86OpenSerial(local->options); + if (local->fd < 0) { + xf86Msg(X_ERROR, "multitouch: cannot open device\n"); + return !Success; + } + if (mtouch_configure(mt, local->fd)) { + xf86Msg(X_ERROR, "multitouch: cannot configure device\n"); + return !Success; + } + xf86CloseSerial(local->fd); + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3 + InitPointerDeviceStruct((DevicePtr)dev, + btmap, DIM_BUTTON, + GetMotionHistory, + pointer_control, + GetMotionHistorySize(), + 2); +#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 7 + InitPointerDeviceStruct((DevicePtr)dev, + btmap, DIM_BUTTON, + pointer_control, + GetMotionHistorySize(), + 2); +#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + InitPointerDeviceStruct((DevicePtr)dev, + btmap, DIM_BUTTON, btn_labels, + pointer_control, + GetMotionHistorySize(), + 2, axes_labels); +#else +#error "Unsupported ABI_XINPUT_VERSION" +#endif + + xf86InitValuatorAxisStruct(dev, 0, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + axes_labels[0], +#endif + mt->caps.abs[MTDEV_POSITION_X].minimum, + mt->caps.abs[MTDEV_POSITION_X].maximum, + 1, 0, 1); + xf86InitValuatorDefaults(dev, 0); + xf86InitValuatorAxisStruct(dev, 1, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + axes_labels[1], +#endif + mt->caps.abs[MTDEV_POSITION_Y].minimum, + mt->caps.abs[MTDEV_POSITION_Y].maximum, + 1, 0, 1); + xf86InitValuatorDefaults(dev, 1); + + XIRegisterPropertyHandler(dev, pointer_property, NULL, NULL); + + return Success; +} + +static int device_on(LocalDevicePtr local) +{ + struct MTouch *mt = local->private; + local->fd = xf86OpenSerial(local->options); + if (local->fd < 0) { + xf86Msg(X_ERROR, "multitouch: cannot open device\n"); + return !Success; + } + if (mtouch_open(mt, local->fd)) { + xf86Msg(X_ERROR, "multitouch: cannot grab device\n"); + return !Success; + } + xf86AddEnabledDevice(local); + return Success; +} + +static int device_off(LocalDevicePtr local) +{ + struct MTouch *mt = local->private; + xf86RemoveEnabledDevice(local); + if (mtouch_close(mt, local->fd)) + xf86Msg(X_WARNING, "multitouch: cannot ungrab device\n"); + xf86CloseSerial(local->fd); + return Success; +} + +static int device_close(LocalDevicePtr local) +{ + return Success; +} + +static void handle_gestures(LocalDevicePtr local, + const struct Gestures* gs) +{ + static bitmask_t buttons_prev = 0U; + int i; + + for (i = 0; i < 32; i++) { + if (GETBIT(gs->buttons, i) == GETBIT(buttons_prev, i)) + continue; + if (GETBIT(gs->buttons, i)) { + xf86PostButtonEvent(local->dev, FALSE, i+1, 1, 0, 0); +#if DEBUG_DRIVER + xf86Msg(X_INFO, "button %d down\n", i+1); +#endif + } + else { + xf86PostButtonEvent(local->dev, FALSE, i+1, 0, 0, 0); +#if DEBUG_DRIVER + xf86Msg(X_INFO, "button %d up\n", i+1); +#endif + } + } + buttons_prev = gs->buttons; + + if (gs->move_dx != 0 || gs->move_dy != 0) + xf86PostMotionEvent(local->dev, 0, 0, 2, gs->move_dx, gs->move_dy); +} + +/* called for each full received packet from the touchpad */ +static void read_input(LocalDevicePtr local) +{ + struct MTouch *mt = local->private; + while (read_packet(mt, local->fd) > 0) + handle_gestures(local, &mt->gs); + if (has_delayed(mt, local->fd)) + handle_gestures(local, &mt->gs); +} + +static Bool device_control(DeviceIntPtr dev, int mode) +{ + LocalDevicePtr local = dev->public.devicePrivate; + switch (mode) { + case DEVICE_INIT: + xf86Msg(X_INFO, "device control: init\n"); + return device_init(dev, local); + case DEVICE_ON: + xf86Msg(X_INFO, "device control: on\n"); + return device_on(local); + case DEVICE_OFF: + xf86Msg(X_INFO, "device control: off\n"); + return device_off(local); + case DEVICE_CLOSE: + xf86Msg(X_INFO, "device control: close\n"); + return device_close(local); + default: + xf86Msg(X_INFO, "device control: default\n"); + return BadValue; + } +} + + +static InputInfoPtr preinit(InputDriverPtr drv, IDevPtr dev, int flags) +{ + struct MTouch *mt; + InputInfoPtr local = xf86AllocateInput(drv, 0); + if (!local) + goto error; + mt = calloc(1, sizeof(struct MTouch)); + if (!mt) + goto error; + + local->name = dev->identifier; + local->type_name = XI_TOUCHPAD; + local->device_control = device_control; + local->read_input = read_input; + local->private = mt; + local->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS; + local->conf_idev = dev; + + xf86CollectInputOptions(local, NULL, NULL); + mconfig_configure(&mt->cfg, local); + xf86OptionListReport(local->options); + xf86ProcessCommonOptions(local, local->options); + + mconfig_configure(&mt->cfg, local); + + local->flags |= XI86_CONFIGURED; + error: + return local; +} + +static void uninit(InputDriverPtr drv, InputInfoPtr local, int flags) +{ + free(local->private); + local->private = 0; + xf86DeleteInput(local, 0); +} + +static InputDriverRec MULTITOUCH = { + 1, + "multitouch", + NULL, + preinit, + uninit, + NULL, + 0 +}; + +static XF86ModuleVersionInfo VERSION = { + "multitouch", + MODULEVENDORSTRING, + MODINFOSTRING1, + MODINFOSTRING2, + XORG_VERSION_CURRENT, + 0, 1, 0, + ABI_CLASS_XINPUT, + ABI_XINPUT_VERSION, + MOD_CLASS_XINPUT, + {0, 0, 0, 0} +}; + +static pointer setup(pointer module, pointer options, int *errmaj, int *errmin) +{ + xf86AddInputDriver(&MULTITOUCH, module, 0); + return module; +} + +XF86ModuleData multitouchModuleData = {&VERSION, &setup, NULL }; diff --git a/include/button.h b/include/button.h new file mode 100644 index 0000000..0ebfbc7 --- /dev/null +++ b/include/button.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef BUTTON_H +#define BUTTON_H + +#define DIM_BUTTON 15 + +#define MT_BUTTON_LEFT 0 +#define MT_BUTTON_MIDDLE 1 +#define MT_BUTTON_RIGHT 2 +#define MT_BUTTON_WHEEL_UP 3 +#define MT_BUTTON_WHEEL_DOWN 4 +#define MT_BUTTON_HWHEEL_LEFT 5 +#define MT_BUTTON_HWHEEL_RIGHT 6 +#define MT_BUTTON_SWIPE_UP 7 +#define MT_BUTTON_SWIPE_DOWN 8 +#define MT_BUTTON_SWIPE_LEFT 9 +#define MT_BUTTON_SWIPE_RIGHT 10 +#define MT_BUTTON_SCALE_DOWN 11 +#define MT_BUTTON_SCALE_UP 12 +#define MT_BUTTON_ROTATE_LEFT 13 +#define MT_BUTTON_ROTATE_RIGHT 14 + +#endif diff --git a/include/capabilities.h b/include/capabilities.h new file mode 100644 index 0000000..0dfc73b --- /dev/null +++ b/include/capabilities.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef CAPABILITIES_H +#define CAPABILITIES_H + +#include "common.h" +#include "button.h" + +struct Capabilities { + struct input_id devid; + char devname[32]; + int has_left, has_middle, has_right; + int has_mtdata, has_ibt; + int has_slot; + int has_abs[MT_ABS_SIZE]; + struct input_absinfo slot; + struct input_absinfo abs[MT_ABS_SIZE]; +}; + +int read_capabilities(struct Capabilities *cap, int fd); +int get_cap_xsize(const struct Capabilities *cap); +int get_cap_ysize(const struct Capabilities *cap); +int get_cap_wsize(const struct Capabilities *cap); + +int get_cap_xmid(const struct Capabilities *cap); +int get_cap_ymid(const struct Capabilities *cap); + +void output_capabilities(const struct Capabilities *cap); + +#endif diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..8b232fb --- /dev/null +++ b/include/common.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef COMMON_H +#define COMMON_H + +#include "xorg-server.h" +#include +#include +#include +#include +#include +#include + +#define DIM_FINGER 32 +#define DIM_TOUCHES 32 + +/* year-proof millisecond event time */ +typedef __u64 mstime_t; + +/* all bit masks have this type */ +typedef unsigned int bitmask_t; + +#define BITMASK(x) (1U << (x)) +#define BITONES(x) (BITMASK(x) - 1U) +#define GETBIT(m, x) (((m) >> (x)) & 1U) +#define SETBIT(m, x) (m |= BITMASK(x)) +#define CLEARBIT(m, x) (m &= ~BITMASK(x)) +#define MODBIT(m, x, b) ((b) ? SETBIT(m, x) : CLEARBIT(m, x)) + +#define ABSVAL(x) ((x) < 0 ? -1*(x) : (x)) +#define MINVAL(x, y) ((x) < (y) ? (x) : (y)) +#define MAXVAL(x, y) ((x) > (y) ? (x) : (y)) +#define MODVAL(x, y) ((x) - ((int)((x) / (y))) * (y)) +#define SQRVAL(x) ((x) * (x)) +#define CLAMPVAL(x, min, max) MAXVAL(MINVAL(x, max), min) + +static inline int clamp15(int x) +{ + return x < -32767 ? -32767 : x > 32767 ? 32767 : x; +} + +/* absolute scale is assumed to fit in 15 bits */ +static inline int dist2(int dx, int dy) +{ + dx = clamp15(dx); + dy = clamp15(dy); + return dx * dx + dy * dy; +} + +/* Count number of bits (Sean Eron Andersson's Bit Hacks) */ +static inline int bitcount(unsigned v) +{ + v -= ((v>>1) & 0x55555555); + v = (v&0x33333333) + ((v>>2) & 0x33333333); + return (((v + (v>>4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +/* Return index of first bit [0-31], -1 on zero */ +#define firstbit(v) (__builtin_ffs(v) - 1) + +/* boost-style foreach bit */ +#define foreach_bit(i, m) \ + for (i = firstbit(m); i >= 0; i = firstbit((m) & (~0U << i + 1))) + +/* robust system ioctl calls */ +#define SYSCALL(call) while (((call) == -1) && (errno == EINTR)) + +#endif diff --git a/include/gestures.h b/include/gestures.h new file mode 100644 index 0000000..7664611 --- /dev/null +++ b/include/gestures.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef GESTURES_H +#define GESTURES_H + +#include "common.h" +#include "mconfig.h" +#include "hwstate.h" +#include "mtstate.h" + +#define GS_TAP 0 +#define GS_BUTTON 1 + +#define GS_NONE 0 +#define GS_MOVE 1 +#define GS_SCROLL 2 +#define GS_SWIPE 3 +#define GS_SCALE 4 +#define GS_ROTATE 5 +#define GS_DRAG_READY 6 +#define GS_DRAG_ACTIVE 7 + +struct Gestures { + /* Taps, physical buttons, and gestures will trigger + * button events. If a bit is set, the button is down. + * If a bit is not set, the button is up. + * Bit 0 is button 1. + */ + bitmask_t buttons; + + /* Pointer movement is tracked here. + */ + int move_dx, move_dy; + + /* Internal state tracking. Not for direct access. + */ + int button_emulate; + int button_delayed; + mstime_t button_delayed_time; + int button_delayed_ms; + + mstime_t tap_time_down; + int tap_touching; + int tap_released; + int move_type; + int move_dist; + int move_dir; + int move_drag; + mstime_t move_wait; + mstime_t move_drag_expire; +}; + + +void gestures_init(struct Gestures* gs); +void gestures_extract(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + struct MTState* ms); +int gestures_delayed(struct Gestures* gs, + struct mtdev* dev, int fd); + +#endif + diff --git a/include/hwstate.h b/include/hwstate.h new file mode 100644 index 0000000..5b857ca --- /dev/null +++ b/include/hwstate.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef HWSTATE_H +#define HWSTATE_H + +#include "common.h" +#include "capabilities.h" + +struct FingerState { + int touch_major, touch_minor; + int width_major, width_minor; + int orientation, pressure; + int position_x, position_y; + int tracking_id; +}; + +struct HWState { + struct FingerState data[DIM_FINGER]; + bitmask_t used; + bitmask_t slot; + bitmask_t button; + mstime_t evtime; + int max_x, max_y; +}; + +void hwstate_init(struct HWState *s, + const struct Capabilities *caps); +int hwstate_modify(struct HWState *s, + struct mtdev *dev, int fd, + const struct Capabilities *caps); +void hwstate_output(const struct HWState *s); + +int find_finger(const struct HWState *s, int tracking_id); + +#endif diff --git a/include/mconfig.h b/include/mconfig.h new file mode 100644 index 0000000..7b8e85c --- /dev/null +++ b/include/mconfig.h @@ -0,0 +1,142 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef MCONFIG_H +#define MCONFIG_H + +#include "capabilities.h" + +#define DEFAULT_TOUCH_DOWN 5 +#define DEFAULT_TOUCH_UP 5 +#define DEFAULT_IGNORE_THUMB 1 +#define DEFAULT_IGNORE_PALM 1 +#define DEFAULT_DISABLE_ON_THUMB 0 +#define DEFAULT_DISABLE_ON_PALM 1 +#define DEFAULT_THUMB_RATIO 70 +#define DEFAULT_THUMB_SIZE 25 +#define DEFAULT_PALM_SIZE 40 +#define DEFAULT_BUTTON_ENABLE 1 +#define DEFAULT_BUTTON_INTEGRATED 1 +#define DEFAULT_BUTTON_1TOUCH 3 +#define DEFAULT_BUTTON_2TOUCH 2 +#define DEFAULT_TAP_1TOUCH 1 +#define DEFAULT_TAP_2TOUCH 3 +#define DEFAULT_TAP_3TOUCH 2 +#define DEFAULT_TAP_TIMEOUT 120 +#define DEFAULT_TAP_HOLD 100 +#define DEFAULT_TAP_DIST 400 +#define DEFAULT_GESTURE_HOLD 10 +#define DEFAULT_GESTURE_WAIT 100 +#define DEFAULT_SCROLL_DIST 400 +#define DEFAULT_SCROLL_UP_BTN 4 +#define DEFAULT_SCROLL_DN_BTN 5 +#define DEFAULT_SCROLL_LT_BTN 6 +#define DEFAULT_SCROLL_RT_BTN 7 +#define DEFAULT_SWIPE_DIST 700 +#define DEFAULT_SWIPE_UP_BTN 8 +#define DEFAULT_SWIPE_DN_BTN 9 +#define DEFAULT_SWIPE_LT_BTN 10 +#define DEFAULT_SWIPE_RT_BTN 11 +#define DEFAULT_SCALE_DIST 150 +#define DEFAULT_SCALE_UP_BTN 12 +#define DEFAULT_SCALE_DN_BTN 13 +#define DEFAULT_ROTATE_DIST 150 +#define DEFAULT_ROTATE_LT_BTN 14 +#define DEFAULT_ROTATE_RT_BTN 15 +#define DEFAULT_DRAG_ENABLE 1 +#define DEFAULT_DRAG_TIMEOUT 200 + +#define MCFG_NONE 0 +#define MCFG_SCALE 1 +#define MCFG_SIZE 2 +#define MCFG_PRESSURE 3 + +struct MConfig { + /* Used by MTState */ + + // Set by caps. + int touch_type; // How to determine touch? 0 for none, 1 for scale, 2 for size, 3 for pressure + int touch_minor; // Does the touchpad report touches as ellipses? 0 or 1 + int touch_min; // Minimum touch value. + int touch_max; // Maximum touch value. + int pad_width; // Width of the touchpad. + int pad_height; // Height of the touchpad. + + // Set by config. + int touch_down; // When is a finger touching? 0 - 100 (percentage) + int touch_up; // When is a finger released? 0 - 100 (percentage) + int ignore_thumb; // Whether or not to ignore thumbs. 0 or 1 + int ignore_palm; // Whether or not to ignore palms. 0 or 1 + int disable_on_thumb; // Disable the touchpad if thumb detected. 0 or 1 + int disable_on_palm; // Disable the touchpad if palm detected. 0 or 1 + int thumb_ratio; // Ratio of width to length that makes a touch a thumb. 0 - 100 + int thumb_size; // Minimum touch size for a thumb. 0 - 100 + int palm_size; // Minimum touch size for a palm. 0 - 100 + + /* Used by Gestures */ + + // Set by config. + int button_enable; // Enable physical buttons? 0 or 1 + int button_integrated; // Is the button under the touchpad? 0 or 1 + int button_1touch; // What button to emulate when one (extra) finger is on the pad? 0, 1, 2, or 3 + int button_2touch; // What button to emulate when two (extra) fingers are on the pad? 0, 1, 2, or 3 + int tap_1touch; // What button to emulate for one touch taps? 0, 1, 2, or 3 + int tap_2touch; // What button to emulate for two touch taps? 0, 1, 2, or 3 + int tap_3touch; // What button to emulate for three touch taps? 0, 1, 2, or 3 + int tap_timeout; // Window for touches when counting for the button. > 0 + int tap_hold; // How long to "hold down" the emulated button on tap. > 0 + int tap_dist; // How far to allow a touch to move before it's a moving touch. > 0 + int gesture_hold; // How long to "hold down" the emulated button for gestures. > 0 + int gesture_wait; // How long after a gesture to wait before movement is allowed. >= 0 + int scroll_dist; // Distance needed to trigger a button. >= 0, 0 disables + int scroll_up_btn; // Button to use for scroll up. >= 0, 0 is none + int scroll_dn_btn; // Button to use for scroll down. >= 0, 0 is none + int scroll_lt_btn; // Button to use for scroll left. >= 0, 0 is none + int scroll_rt_btn; // Button to use for scroll right. >= 0, 0 is none + int swipe_dist; // Distance needed to trigger a button. >= 0, 0 disables + int swipe_up_btn; // Button to use for swipe up. >= 0, 0 is none + int swipe_dn_btn; // Button to use for swipe down. >= 0, 0 is none + int swipe_lt_btn; // Button to use for swipe left. >= 0, 0 is none + int swipe_rt_btn; // Button to use for swipe right. >= 0, 0 is none + int scale_dist; // Distance needed to trigger a button. >= 0, 0 disables + int scale_up_btn; // Button to use for scale up. >= 0, 0 is none + int scale_dn_btn; // Button to use for scale down. >= 0, 0 is none + int rotate_dist; // Distance needed to trigger a button. >= 0, 0 disables + int rotate_lt_btn; // Button to use for rotate left. >= 0, 0 is none + int rotate_rt_btn; // Button to use for rotate right. >= 0, 0 is none + int drag_enable; // Enable tap-to-drag? 0 or 1 + int drag_timeout; // How long to wait for a move after tapping? > 0 +}; + +/* Load the MConfig struct with its defaults. + */ +void mconfig_defaults(struct MConfig* cfg); + +/* Initialize the MConfig struct. + */ +void mconfig_init(struct MConfig* cfg, + const struct Capabilities* caps); + +void mconfig_configure(struct MConfig* cfg, + InputInfoPtr local); + +#endif + diff --git a/include/mtouch.h b/include/mtouch.h new file mode 100644 index 0000000..e8756f2 --- /dev/null +++ b/include/mtouch.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef MTOUCH_H +#define MTOUCH_H + +#include "common.h" +#include "capabilities.h" +#include "hwstate.h" +#include "mtstate.h" +#include "mconfig.h" +#include "gestures.h" + +struct MTouch { + struct mtdev dev; + struct Capabilities caps; + struct HWState hs; + struct MTState state; + struct MConfig cfg; + struct Gestures gs; +}; + +int mtouch_configure(struct MTouch *mt, int fd); +int mtouch_open(struct MTouch *mt, int fd); +int mtouch_close(struct MTouch *mt, int fd); + +int read_packet(struct MTouch *mt, int fd); +int has_delayed(struct MTouch *mt, int fd); + +#endif diff --git a/include/mtstate.h b/include/mtstate.h new file mode 100644 index 0000000..eca8fa6 --- /dev/null +++ b/include/mtstate.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#ifndef MTSTATE_H +#define MTSTATE_H + +#include "common.h" +#include "mconfig.h" +#include "hwstate.h" + +#define MT_NEW 0 +#define MT_RELEASED 1 +#define MT_INVALID 2 +#define MT_THUMB 3 +#define MT_PALM 4 + +struct Touch { + bitmask_t state; + bitmask_t flags; + mstime_t down; + double direction; + int tracking_id; + int x, y, dx, dy; + int total_dx, total_dy; +}; + +struct MTState { + bitmask_t state; + mstime_t evtime; + struct Touch touch[DIM_TOUCHES]; + bitmask_t touch_used; +}; + +/* Initialize an MTState struct. + */ +void mtstate_init(struct MTState* ms); + +/* Extract the MTState from the current hardware state. + */ +void mtstate_extract(struct MTState* ms, + const struct MConfig* cfg, + const struct HWState* hs); + +#endif + diff --git a/include/trig.h b/include/trig.h new file mode 100644 index 0000000..7b4fc2e --- /dev/null +++ b/include/trig.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +/* Some bastardized trig functions. These calculations flip the + * Y axis since that axis on touchpads is opposite that of the + * Cartesian system. + */ + +#ifndef MT_TRIG_H +#define MT_TRIG_H + +#define TR_NONE -1 +#define TR_DIR_UP 0 +#define TR_DIR_RT 2 +#define TR_DIR_DN 4 +#define TR_DIR_LT 6 + +/* Determine in which quadrant a point lies. Counts from 0. Returns + * TR_NONE if the point lies at the origin. + */ +int trig_quadrant(double x, double y); + +/* Determine the direction of a vector. This uses the slope of the + * vector to approximate the angle, as such it is only accurate at + * increments of 45 degrees. This is sufficient for our uses. + * + * The returned value is 0 <= a < 8 such that the circle is split + * into 45 degree sections. Each whole number lies 45 degrees apart + * and so whole numbers are exact. All fractional parts are + * aproximations. + * + * TR_NONE will be returned if the magnitude of the vector is zero. + */ +double trig_direction(double dx, double dy); + +/* Generalize a direction. Returns TR_NONE, TR_DIR_UP, TR_DIR_RT, + * TR_DIR_DN, or TR_DIR_LT. + */ +int trig_generalize(double dir); + +/* Add two angles. + */ +double trig_angles_add(double a1, double a2); + +/* Subtract two angles. + */ +double trig_angles_sub(double a1, double a2); + +/* Calculate the acute angle between two angles. + */ +double trig_angles_acute(double a1, double a2); + +/* Compare two angles. Returns 0 if a1 == a2. Returns < 0 if a1 < a2. + * Returns > 0 if a1 > a2. + */ +int trig_angles_cmp(double a1, double a2); + +#endif + diff --git a/include/xbypass.h b/include/xbypass.h new file mode 100644 index 0000000..3836554 --- /dev/null +++ b/include/xbypass.h @@ -0,0 +1,24 @@ +#ifndef XBYPASS_H +#define XBYPASS_H + +#include + +void xf86Msg(int type, const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +int xf86SetIntOption(pointer optlist, const char *name, int deflt) +{ + return deflt; +} + +int xf86SetBoolOption(pointer list, const char *name, int deflt) +{ + return deflt; +} + +#endif diff --git a/rebuild b/rebuild new file mode 100755 index 0000000..bdc6b40 --- /dev/null +++ b/rebuild @@ -0,0 +1,4 @@ +#!/bin/sh +make clean +CFLAGS="-DDEBUG_GESTURES" make + diff --git a/run b/run new file mode 100755 index 0000000..c2c8199 --- /dev/null +++ b/run @@ -0,0 +1,3 @@ +#!/bin/sh +sudo ./bin/src/test /dev/input/event6 + diff --git a/src/.gestures.c.swp b/src/.gestures.c.swp new file mode 100644 index 0000000..ef7832d Binary files /dev/null and b/src/.gestures.c.swp differ diff --git a/src/capabilities.c b/src/capabilities.c new file mode 100644 index 0000000..3459408 --- /dev/null +++ b/src/capabilities.c @@ -0,0 +1,175 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * + * 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 2 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 + * + **************************************************************************/ + +#include "capabilities.h" + +#define SETABS(c, x, map, key, fd) \ + (c->has_##x = getbit(map, key) && getabs(&c->x, key, fd)) + +#define ADDCAP(s, c, x) strcat(s, c->has_##x ? " " #x : "") + +static const int SN_COORD = 250; /* coordinate signal-to-noise ratio */ +static const int SN_WIDTH = 100; /* width signal-to-noise ratio */ +static const int SN_ORIENT = 10; /* orientation signal-to-noise ratio */ + +static const int bits_per_long = 8 * sizeof(long); + +static inline int nlongs(int nbit) +{ + return (nbit + bits_per_long - 1) / bits_per_long; +} + +static inline int getbit(const unsigned long *map, int key) +{ + return (map[key / bits_per_long] >> (key % bits_per_long)) & 0x01; +} + +static int getabs(struct input_absinfo *abs, int key, int fd) +{ + int rc; + SYSCALL(rc = ioctl(fd, EVIOCGABS(key), abs)); + return rc >= 0; +} + +static int has_mt_data(const struct Capabilities *cap) +{ + return cap->has_abs[MTDEV_POSITION_X] && cap->has_abs[MTDEV_POSITION_Y]; +} + +static int has_integrated_button(const struct Capabilities *cap) +{ + static const int bcm5974_vmask_ibt = 1; + /* magic trackpad */ + if (cap->devid.vendor == 0x05ac && cap->devid.product == 0x030e) + return 1; + /* macbooks */ + if (strcmp(cap->devname, "bcm5974")) + return 0; + return cap->devid.version & bcm5974_vmask_ibt; +} + +static void default_fuzz(struct Capabilities *cap, unsigned int code, int sn) +{ + int bit = mtdev_abs2mt(code); + if (cap->has_abs[bit] && cap->abs[bit].fuzz == 0) + cap->abs[bit].fuzz = + (cap->abs[bit].maximum - cap->abs[bit].minimum) / sn; +} + +int read_capabilities(struct Capabilities *cap, int fd) +{ + unsigned long evbits[nlongs(EV_MAX)]; + unsigned long absbits[nlongs(ABS_MAX)]; + unsigned long keybits[nlongs(KEY_MAX)]; + int rc, i; + + memset(cap, 0, sizeof(struct Capabilities)); + + SYSCALL(rc = ioctl(fd, EVIOCGID, &cap->devid)); + if (rc < 0) + return rc; + SYSCALL(rc = ioctl(fd, EVIOCGNAME(sizeof(cap->devname)), cap->devname)); + if (rc < 0) + return rc; + SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_SYN, sizeof(evbits)), evbits)); + if (rc < 0) + return rc; + SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits)); + if (rc < 0) + return rc; + SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits)); + if (rc < 0) + return rc; + + cap->has_left = getbit(keybits, BTN_LEFT); + cap->has_middle = getbit(keybits, BTN_MIDDLE); + cap->has_right = getbit(keybits, BTN_RIGHT); + + SETABS(cap, slot, absbits, ABS_MT_SLOT, fd); + for (i = 0; i < MT_ABS_SIZE; i++) + SETABS(cap, abs[i], absbits, mtdev_mt2abs(i), fd); + + cap->has_mtdata = has_mt_data(cap); + cap->has_ibt = has_integrated_button(cap); + + default_fuzz(cap, ABS_MT_POSITION_X, SN_COORD); + default_fuzz(cap, ABS_MT_POSITION_Y, SN_COORD); + default_fuzz(cap, ABS_MT_TOUCH_MAJOR, SN_WIDTH); + default_fuzz(cap, ABS_MT_TOUCH_MINOR, SN_WIDTH); + default_fuzz(cap, ABS_MT_WIDTH_MAJOR, SN_WIDTH); + default_fuzz(cap, ABS_MT_WIDTH_MINOR, SN_WIDTH); + default_fuzz(cap, ABS_MT_ORIENTATION, SN_ORIENT); + + return 0; +} + +int get_cap_xsize(const struct Capabilities *cap) +{ + const struct input_absinfo *x = &cap->abs[MTDEV_POSITION_X]; + return x->maximum - x->minimum; +} + +int get_cap_ysize(const struct Capabilities *cap) +{ + const struct input_absinfo *y = &cap->abs[MTDEV_POSITION_Y]; + return y->maximum - y->minimum; +} + +int get_cap_wsize(const struct Capabilities *cap) +{ + const struct input_absinfo *w = &cap->abs[MTDEV_TOUCH_MAJOR]; + return w->maximum - w->minimum; +} + +int get_cap_xmid(const struct Capabilities *cap) +{ + const struct input_absinfo *x = &cap->abs[MTDEV_POSITION_X]; + return (x->maximum + x->minimum) >> 1; +} + +int get_cap_ymid(const struct Capabilities *cap) +{ + const struct input_absinfo *y = &cap->abs[MTDEV_POSITION_Y]; + return (y->maximum + y->minimum) >> 1; +} + +void output_capabilities(const struct Capabilities *cap) +{ + char line[1024]; + int i; + memset(line, 0, sizeof(line)); + ADDCAP(line, cap, left); + ADDCAP(line, cap, middle); + ADDCAP(line, cap, right); + ADDCAP(line, cap, mtdata); + ADDCAP(line, cap, ibt); + xf86Msg(X_INFO, "multitouch: devname: %s\n", cap->devname); + xf86Msg(X_INFO, "multitouch: devid: %x %x %x\n", + cap->devid.vendor, cap->devid.product, cap->devid.version); + xf86Msg(X_INFO, "multitouch: caps:%s\n", line); + for (i = 0; i < MT_ABS_SIZE; i++) { + if (cap->has_abs[i]) + xf86Msg(X_INFO, "multitouch: %d: min: %d max: %d\n", + i, + cap->abs[i].minimum, + cap->abs[i].maximum); + } +} diff --git a/src/gestures.c b/src/gestures.c new file mode 100644 index 0000000..0269b4d --- /dev/null +++ b/src/gestures.c @@ -0,0 +1,593 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * Gestures + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2010 Arturo Castro + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#include "gestures.h" +#include "trig.h" +#include + +#define IS_VALID_BUTTON(x) (x >= 0 && x <= 31) + +static void trigger_button_up(struct Gestures* gs, int button) +{ + if (IS_VALID_BUTTON(button)) { + if (button == 0 && gs->button_emulate > 0) { + button = gs->button_emulate; + gs->button_emulate = 0; + } + CLEARBIT(gs->buttons, button); +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_button_up: %d up\n", button); +#endif + } +} + +static void trigger_button_down(struct Gestures* gs, int button) +{ + if (IS_VALID_BUTTON(button) && (button != gs->button_delayed || gs->button_delayed_time == 0)) { + SETBIT(gs->buttons, button); +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_button_down: %d down\n", button); +#endif + } +#if DEBUG_GESTURES + else if (IS_VALID_BUTTON(button)) + xf86Msg(X_INFO, "trigger_button_down: %d down ignored, %d in delayed mode\n", button, button); +#endif +} + +static void trigger_button_emulation(struct Gestures* gs, int button) +{ + if (IS_VALID_BUTTON(button) && GETBIT(gs->buttons, 0)) { + CLEARBIT(gs->buttons, 0); + SETBIT(gs->buttons, button); + gs->button_emulate = button; +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_button_emulation: %d emulated\n", button); +#endif + } +} + +static void trigger_button_click(struct Gestures* gs, + int button, mstime_t trigger_up_time) +{ + if (IS_VALID_BUTTON(button) && gs->button_delayed_time == 0) { + trigger_button_down(gs, button); + gs->button_delayed = button; + gs->button_delayed_ms = 0; + gs->button_delayed_time = trigger_up_time; +#if DEBUG_GESTRUES + xf86Msg(X_INFO, "trigger_button_click: %d placed in delayed mode\n"); +#endif + } +#if DEBUG_GESTURES + else if (IS_VALID_BUTTON(button)) + xf86Msg(X_INFO, "trigger_button_click: %d ignored, in delayed mode\n", button); +#endif +} + +static void trigger_drag_ready(struct Gestures* gs, + mstime_t expire_time) +{ + gs->move_drag = GS_DRAG_READY; + gs->move_drag_expire = expire_time; +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_drag_ready: drag is ready\n"); +#endif +} + +static void trigger_drag_start(struct Gestures* gs) +{ + if (gs->move_drag == GS_DRAG_READY) { + gs->button_delayed = 0; + gs->button_delayed_ms = 0; + gs->button_delayed_time = 0; + gs->move_drag = GS_DRAG_ACTIVE; + gs->move_drag_expire = 0; +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_drag_start: drag is active\n"); +#endif + } +} + +static void trigger_drag_stop(struct Gestures* gs, int force) +{ + if (gs->move_drag == GS_DRAG_READY && force) { + gs->move_drag = GS_NONE; + gs->move_drag_expire = 0; +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_drag_stop: drag canceled\n"); +#endif + } + else if (gs->move_drag == GS_DRAG_ACTIVE) { + gs->move_drag = GS_NONE; + gs->move_drag_expire = 0; + trigger_button_up(gs, 0); +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_drag_stop: drag stopped\n"); +#endif + } +} + +static void buttons_update(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + struct MTState* ms) +{ + if (!cfg->button_enable) + return; + + static bitmask_t button_prev = 0U; + int i, n, down, emulate; + down = 0; + emulate = GETBIT(hs->button, 0) && !GETBIT(button_prev, 0); + + for (i = 0; i < 32; i++) { + if (GETBIT(hs->button, i) == GETBIT(button_prev, i)) + continue; + if (GETBIT(hs->button, i)) { + down++; + trigger_button_down(gs, i); + } + else + trigger_button_up(gs, i); + } + button_prev = hs->button; + + if (down) { + gs->move_type = GS_NONE; + gs->move_wait = hs->evtime + cfg->gesture_wait; + n = 0; + foreach_bit(i, ms->touch_used) { + if (GETBIT(ms->touch[i].state, MT_INVALID)) + continue; + if (cfg->button_integrated && !GETBIT(ms->touch[i].flags, GS_BUTTON)) + SETBIT(ms->touch[i].flags, GS_BUTTON); + n++; + } + + if (emulate) { + if (cfg->button_integrated) + n--; + + if (n == 1 && cfg->button_1touch > 0) + trigger_button_emulation(gs, cfg->button_1touch); + else if (n == 2 && cfg->button_2touch > 0) + trigger_button_emulation(gs, cfg->button_2touch); + } + } +} + +static void tapping_update(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + struct MTState* ms) +{ + if (!cfg->tap_1touch && !cfg->tap_2touch && !cfg->tap_3touch) + return; + + int i, n, dist, released_max; + released_max = MAXVAL(cfg->tap_1touch, MAXVAL(cfg->tap_2touch, cfg->tap_3touch)); + + if (gs->tap_time_down != 0 && hs->evtime >= gs->tap_time_down + cfg->tap_timeout) { + gs->tap_time_down = 0; + gs->tap_touching = 0; + gs->tap_released = 0; + foreach_bit(i, ms->touch_used) { + if (GETBIT(ms->touch[i].flags, GS_TAP)) + CLEARBIT(ms->touch[i].flags, GS_TAP); + } + } + else { + foreach_bit(i, ms->touch_used) { + if (GETBIT(ms->touch[i].state, MT_INVALID) || GETBIT(ms->touch[i].flags, GS_BUTTON)) { + if (GETBIT(ms->touch[i].flags, GS_TAP)) { + CLEARBIT(ms->touch[i].flags, GS_TAP); + gs->tap_touching--; + } + } + else { + if (GETBIT(ms->touch[i].state, MT_NEW)) { + SETBIT(ms->touch[i].flags, GS_TAP); + gs->tap_touching++; + if (gs->tap_time_down == 0) + gs->tap_time_down = hs->evtime; + } + + if (GETBIT(ms->touch[i].flags, GS_TAP)) { + dist = dist2(ms->touch[i].total_dx, ms->touch[i].total_dy); + if (dist >= cfg->tap_dist) { + CLEARBIT(ms->touch[i].flags, GS_TAP); + gs->tap_touching--; + } + else if (GETBIT(ms->touch[i].state, MT_RELEASED)) { + gs->tap_touching--; + gs->tap_released++; + } + } + } + } + } + + if ((gs->tap_touching == 0 && gs->tap_released > 0) || gs->tap_released >= released_max) { + foreach_bit(i, ms->touch_used) { + if (GETBIT(ms->touch[i].flags, GS_TAP)) + CLEARBIT(ms->touch[i].flags, GS_TAP); + } + + if (gs->tap_released == 1) + n = cfg->tap_1touch; + else if (gs->tap_released == 2) + n = cfg->tap_2touch; + else + n = cfg->tap_3touch; + + trigger_button_click(gs, n, hs->evtime + cfg->tap_hold); + if (cfg->drag_enable && n == 0) + trigger_drag_ready(gs, hs->evtime + cfg->drag_timeout); + + gs->move_type = GS_NONE; + gs->move_wait = hs->evtime + cfg->gesture_wait; + + gs->tap_time_down = 0; + gs->tap_touching = 0; + gs->tap_released = 0; + } +} + +static void trigger_move(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + int dx, int dy) +{ + if ((gs->move_type == GS_MOVE || hs->evtime >= gs->move_wait) && (dx != 0 || dy != 0)) { + trigger_drag_start(gs); + gs->move_dx = dx; + gs->move_dy = dy; + gs->move_type = GS_MOVE; + gs->move_wait = 0; + gs->move_dist = 0; + gs->move_dir = TR_NONE; + } +} + +static void trigger_scroll(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + int dist, int dir) +{ + if (gs->move_type == GS_SCROLL || hs->evtime >= gs->move_wait) { + trigger_drag_stop(gs, 1); + if (gs->move_type != GS_SCROLL || gs->move_dir != dir) + gs->move_dist = 0; + gs->move_dx = 0; + gs->move_dy = 0; + gs->move_type = GS_SCROLL; + gs->move_wait = hs->evtime + cfg->gesture_wait; + gs->move_dist += ABSVAL(dist); + gs->move_dir = dir; + if (gs->move_dist >= cfg->scroll_dist) { + gs->move_dist = MODVAL(gs->move_dist, cfg->scroll_dist); + if (dir == TR_DIR_UP) + trigger_button_click(gs, cfg->scroll_up_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_DN) + trigger_button_click(gs, cfg->scroll_dn_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_LT) + trigger_button_click(gs, cfg->scroll_lt_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_RT) + trigger_button_click(gs, cfg->scroll_rt_btn, hs->evtime + cfg->gesture_hold); + } +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_scroll: scrolling %+d in direction %d (at %d of %d)\n", dist, dir, gs->move_dist, cfg->scroll_dist); +#endif + } +} + +static void trigger_swipe(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + int dist, int dir) +{ + if (gs->move_type == GS_SWIPE || hs->evtime >= gs->move_wait) { + trigger_drag_stop(gs, 1); + if (gs->move_type != GS_SWIPE || gs->move_dir != dir) + gs->move_dist = 0; + gs->move_dx = 0; + gs->move_dy = 0; + gs->move_type = GS_SWIPE; + gs->move_wait = hs->evtime + cfg->gesture_wait; + gs->move_dist += ABSVAL(dist); + gs->move_dir = dir; + if (gs->move_dist >= cfg->swipe_dist) { + gs->move_dist = MODVAL(gs->move_dist, cfg->swipe_dist); + if (dir == TR_DIR_UP) + trigger_button_click(gs, cfg->swipe_up_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_DN) + trigger_button_click(gs, cfg->swipe_dn_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_LT) + trigger_button_click(gs, cfg->swipe_lt_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_RT) + trigger_button_click(gs, cfg->swipe_rt_btn, hs->evtime + cfg->gesture_hold); + } +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_swipe: swiping %+d in direction %d (at %d of %d)\n", dist, dir, gs->move_dist, cfg->swipe_dist); +#endif + } +} + +static void trigger_scale(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + int dist, int dir) +{ + if (gs->move_type == GS_SCALE || hs->evtime >= gs->move_wait) { + trigger_drag_stop(gs, 1); + if (gs->move_type != GS_SCALE || gs->move_dir != dir) + gs->move_dist = 0; + gs->move_dx = 0; + gs->move_dy = 0; + gs->move_type = GS_SCALE; + gs->move_wait = hs->evtime + cfg->gesture_wait; + gs->move_dist += ABSVAL(dist); + gs->move_dir = dir; + if (gs->move_dist >= cfg->scale_dist) { + gs->move_dist = MODVAL(gs->move_dist, cfg->scale_dist); + if (dir == TR_DIR_UP) + trigger_button_click(gs, cfg->scale_up_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_DN) + trigger_button_click(gs, cfg->scale_dn_btn, hs->evtime + cfg->gesture_hold); + } +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_scale: scaling %+d in direction %d (at %d of %d)\n", dist, dir, gs->move_dist, cfg->scale_dist); +#endif + } +} + +static void trigger_rotate(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + int dist, int dir) +{ + if (gs->move_type == GS_ROTATE || hs->evtime >= gs->move_wait) { + trigger_drag_stop(gs, 1); + if (gs->move_type != GS_ROTATE || gs->move_dir != dir) + gs->move_dist = 0; + gs->move_dx = 0; + gs->move_dy = 0; + gs->move_type = GS_ROTATE; + gs->move_wait = hs->evtime + cfg->gesture_wait; + gs->move_dist += ABSVAL(dist); + gs->move_dir = dir; + if (gs->move_dist >= cfg->scale_dist) { + gs->move_dist = MODVAL(gs->move_dist, cfg->scale_dist); + if (dir == TR_DIR_LT) + trigger_button_click(gs, cfg->rotate_lt_btn, hs->evtime + cfg->gesture_hold); + else if (dir == TR_DIR_RT) + trigger_button_click(gs, cfg->rotate_rt_btn, hs->evtime + cfg->gesture_hold); + } +#if DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_rotate: rotating %+d in direction %d (at %d of %d)\n", dist, dir, gs->move_dist, cfg->rotate_dist); +#endif + } +} + +static void trigger_reset(struct Gestures* gs) +{ + trigger_drag_stop(gs, 0); + gs->move_dx = 0; + gs->move_dy = 0; + gs->move_type = GS_NONE; + gs->move_wait = 0; + gs->move_dist = 0; + gs->move_dir = TR_NONE; +} + +static int get_scroll_dir(const struct Touch* t1, + const struct Touch* t2) +{ + if (trig_angles_acute(t1->direction, t2->direction) < 2.0) + return trig_generalize(t1->direction); + return TR_NONE; +} + +static int get_rotate_dir(const struct Touch* t1, + const struct Touch* t2) +{ + double v, d1, d2; + v = trig_direction(t2->x - t1->x, t2->y - t1->y); + d1 = trig_angles_add(v, 2); + d2 = trig_angles_sub(v, 2); + if (trig_angles_acute(t1->direction, d1) < 2 && trig_angles_acute(t2->direction, d2) < 2) + return TR_DIR_RT; + else if (trig_angles_acute(t1->direction, d2) < 2 && trig_angles_acute(t2->direction, d1) < 2) + return TR_DIR_LT; + return TR_NONE; +} + +static int get_scale_dir(const struct Touch* t1, + const struct Touch* t2) +{ + double v; + if (trig_angles_acute(t1->direction, t2->direction) >= 2) { + v = trig_direction(t2->x - t1->x, t2->y - t1->y); + if (trig_angles_acute(v, t1->direction) < 2) + return TR_DIR_DN; + else + return TR_DIR_UP; + } + return TR_NONE; +} + +static int get_swipe_dir(const struct Touch* t1, + const struct Touch* t2, + const struct Touch* t3) +{ + double d1, d2; + d1 = MINVAL(t1->direction, MINVAL(t2->direction, t3->direction)); + d2 = MAXVAL(t1->direction, MAXVAL(t2->direction, t3->direction)); + if (trig_angles_acute(d1, d2) < 2) + return trig_generalize(t1->direction); + return TR_NONE; +} + +static void moving_update(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + struct MTState* ms) +{ + int i, count, btn_count, dx, dy, dist, dir; + struct Touch* touches[3]; + count = btn_count = 0; + dx = dy = 0; + dir = 0; + + // Reset movement. + gs->move_dx = 0; + gs->move_dy = 0; + + // Count touches and aggregate touch movements. + foreach_bit(i, ms->touch_used) { + if (GETBIT(ms->touch[i].state, MT_INVALID)) + continue; + else if (GETBIT(ms->touch[i].flags, GS_BUTTON)) { + btn_count++; + dx += ms->touch[i].dx; + dy += ms->touch[i].dy; + } + else if (!GETBIT(ms->touch[i].flags, GS_TAP)) { + if (count < 3) + touches[count++] = &ms->touch[i]; + } + } + + // Determine gesture type. + if (count == 0) { + if (btn_count > 0) + trigger_move(gs, cfg, hs, dx, dy); + else + trigger_reset(gs); + } + else if (count == 1) { + dx += touches[0]->dx; + dy += touches[0]->dy; + trigger_move(gs, cfg, hs, dx, dy); + } + else if (count == 2) { + // scroll, scale, or rotate + if ((dir = get_scroll_dir(touches[0], touches[1])) != TR_NONE) { + if (dir == TR_DIR_LT || dir == TR_DIR_RT) + dist = touches[0]->dx + touches[1]->dx; + else + dist = touches[0]->dy + touches[1]->dy; + trigger_scroll(gs, cfg, hs, dist/2, dir); + } + else if ((dir = get_rotate_dir(touches[0], touches[1])) != TR_NONE) { + dist = dist2(touches[0]->dx, touches[0]->dy) + dist2(touches[1]->dx, touches[1]->dy); + trigger_rotate(gs, cfg, hs, dist/2, dir); + } + else if ((dir = get_scale_dir(touches[0], touches[1])) != TR_NONE) { + dist = dist2(touches[0]->dx, touches[0]->dy) + dist2(touches[1]->dx, touches[1]->dy); + trigger_scale(gs, cfg, hs, dist/2, dir); + } + } + else if (count == 3) { + if ((dir = get_swipe_dir(touches[0], touches[1], touches[2])) != TR_NONE) { + if (dir == TR_DIR_LT || dir == TR_DIR_RT) + dist = touches[0]->dx + touches[1]->dx + touches[2]->dx; + else + dist = touches[0]->dy + touches[1]->dy + touches[2]->dy; + trigger_swipe(gs, cfg, hs, dist/3, dir); + } + } +} + +static void dragging_update(struct Gestures* gs, + const struct HWState* hs) +{ + if (gs->move_drag == GS_DRAG_READY && hs->evtime > gs->move_drag_expire) { +#if DEBUG_GESTURES + xf86Msg(X_INFO, "dragging_update: drag expired\n"); +#endif + trigger_drag_stop(gs, 1); + } +} + +static void delayed_update(struct Gestures* gs, + const struct HWState* hs) +{ + if (gs->button_delayed_time == 0) + return; + + if (hs->evtime >= gs->button_delayed_time) { +#if DEBUG_GESTURES + xf86Msg(X_INFO, "delayed_update: %d delay expired, triggering up\n", gs->button_delayed); +#endif + trigger_button_up(gs, gs->button_delayed); + gs->button_delayed_time = 0; + gs->button_delayed_ms = 0; + gs->button_delayed = 0; + } + else + gs->button_delayed_ms = gs->button_delayed_time - hs->evtime; +} + +void gestures_init(struct Gestures* gs) +{ + memset(gs, 0, sizeof(struct Gestures)); +} + +void gestures_extract(struct Gestures* gs, + const struct MConfig* cfg, + const struct HWState* hs, + struct MTState* ms) +{ + dragging_update(gs, hs); + buttons_update(gs, cfg, hs, ms); + tapping_update(gs, cfg, hs, ms); + moving_update(gs, cfg, hs, ms); + delayed_update(gs, hs); +} + +int gestures_delayed(struct Gestures* gs, + struct mtdev* dev, int fd) +{ + if (gs->button_delayed_time > 0) { + if (mtdev_empty(dev) && mtdev_idle(dev, fd, gs->button_delayed_ms)) { +#if DEBUG_GESTURES + xf86Msg(X_INFO, "gestures_delayed: %d up, timer expired\n", gs->button_delayed); +#endif + trigger_button_up(gs, gs->button_delayed); + gs->move_dx = 0; + gs->move_dy = 0; + gs->button_delayed_time = 0; + gs->button_delayed_ms = 0; + gs->button_delayed = 0; + return 1; + } + } + return 0; +} + diff --git a/src/hwstate.c b/src/hwstate.c new file mode 100644 index 0000000..985e5f3 --- /dev/null +++ b/src/hwstate.c @@ -0,0 +1,131 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * + * 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 2 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 + * + **************************************************************************/ + +#include "hwstate.h" + +void hwstate_init(struct HWState *s, const struct Capabilities *caps) +{ + int i; + memset(s, 0, sizeof(struct HWState)); + for (i = 0; i < DIM_FINGER; i++) + s->data[i].tracking_id = MT_ID_NULL; + s->max_x = get_cap_xsize(caps); + s->max_y = get_cap_ysize(caps); +} + +static void finish_packet(struct HWState *s, const struct Capabilities *caps, + const struct input_event *syn) +{ + static const mstime_t ms = 1000; + int i; + foreach_bit(i, s->used) { + if (!caps->has_abs[MTDEV_TOUCH_MINOR]) + s->data[i].touch_minor = s->data[i].touch_major; + if (!caps->has_abs[MTDEV_WIDTH_MINOR]) + s->data[i].width_minor = s->data[i].width_major; + } + s->evtime = syn->time.tv_usec / ms + syn->time.tv_sec * ms; +} + +static int read_event(struct HWState *s, const struct Capabilities *caps, + const struct input_event *ev) +{ + switch (ev->type) { + case EV_SYN: + switch (ev->code) { + case SYN_REPORT: + finish_packet(s, caps, ev); + return 1; + } + break; + case EV_KEY: + switch (ev->code) { + case BTN_LEFT: + MODBIT(s->button, MT_BUTTON_LEFT, ev->value); + break; + case BTN_MIDDLE: + MODBIT(s->button, MT_BUTTON_MIDDLE, ev->value); + break; + case BTN_RIGHT: + MODBIT(s->button, MT_BUTTON_RIGHT, ev->value); + break; + } + break; + case EV_ABS: + switch (ev->code) { + case ABS_MT_SLOT: + if (ev->value >= 0 && ev->value < DIM_FINGER) + s->slot = ev->value; + break; + case ABS_MT_TOUCH_MAJOR: + s->data[s->slot].touch_major = ev->value; + break; + case ABS_MT_TOUCH_MINOR: + s->data[s->slot].touch_minor = ev->value; + break; + case ABS_MT_WIDTH_MAJOR: + s->data[s->slot].width_major = ev->value; + break; + case ABS_MT_WIDTH_MINOR: + s->data[s->slot].width_minor = ev->value; + break; + case ABS_MT_ORIENTATION: + s->data[s->slot].orientation = ev->value; + break; + case ABS_MT_PRESSURE: + s->data[s->slot].pressure = ev->value; + break; + case ABS_MT_POSITION_X: + s->data[s->slot].position_x = ev->value; + break; + case ABS_MT_POSITION_Y: + s->data[s->slot].position_y = ev->value; + break; + case ABS_MT_TRACKING_ID: + s->data[s->slot].tracking_id = ev->value; + MODBIT(s->used, s->slot, ev->value != MT_ID_NULL); + break; + } + break; + } + return 0; +} + +int hwstate_modify(struct HWState *s, struct mtdev *dev, int fd, + const struct Capabilities *caps) +{ + struct input_event ev; + int ret; + while ((ret = mtdev_get(dev, fd, &ev, 1)) > 0) { + if (read_event(s, caps, &ev)) + return 1; + } + return ret; +} + +int find_finger(const struct HWState *s, int tracking_id) { + int i; + foreach_bit(i, s->used) { + if (s->data[i].tracking_id == tracking_id) + return i; + } + return -1; +} diff --git a/src/mconfig.c b/src/mconfig.c new file mode 100644 index 0000000..ea672a4 --- /dev/null +++ b/src/mconfig.c @@ -0,0 +1,156 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#include "mconfig.h" + +void mconfig_defaults(struct MConfig* cfg) +{ + // Configure MTState + cfg->touch_down = DEFAULT_TOUCH_DOWN; + cfg->touch_up = DEFAULT_TOUCH_UP; + cfg->ignore_thumb = DEFAULT_IGNORE_THUMB; + cfg->ignore_palm = DEFAULT_IGNORE_PALM; + cfg->disable_on_palm = DEFAULT_DISABLE_ON_PALM; + cfg->disable_on_thumb = DEFAULT_DISABLE_ON_THUMB; + cfg->thumb_ratio = DEFAULT_THUMB_RATIO; + cfg->thumb_size = DEFAULT_THUMB_SIZE; + cfg->palm_size = DEFAULT_PALM_SIZE; + + // Configure Gestures + cfg->button_enable = DEFAULT_BUTTON_ENABLE; + cfg->button_integrated = DEFAULT_BUTTON_INTEGRATED; + cfg->button_1touch = DEFAULT_BUTTON_1TOUCH - 1; + cfg->button_2touch = DEFAULT_BUTTON_2TOUCH - 1; + cfg->tap_1touch = DEFAULT_TAP_1TOUCH - 1; + cfg->tap_2touch = DEFAULT_TAP_2TOUCH - 1; + cfg->tap_3touch = DEFAULT_TAP_3TOUCH - 1; + cfg->tap_timeout = DEFAULT_TAP_TIMEOUT; + cfg->tap_hold = DEFAULT_TAP_HOLD; + cfg->tap_dist = SQRVAL(DEFAULT_TAP_DIST); + cfg->gesture_hold = DEFAULT_GESTURE_HOLD; + cfg->gesture_wait = DEFAULT_GESTURE_WAIT; + cfg->scroll_dist = DEFAULT_SCROLL_DIST; + cfg->scroll_up_btn = DEFAULT_SCROLL_UP_BTN - 1; + cfg->scroll_dn_btn = DEFAULT_SCROLL_DN_BTN - 1; + cfg->scroll_lt_btn = DEFAULT_SCROLL_LT_BTN - 1; + cfg->scroll_rt_btn = DEFAULT_SCROLL_RT_BTN - 1; + cfg->swipe_dist = DEFAULT_SWIPE_DIST; + cfg->swipe_up_btn = DEFAULT_SWIPE_UP_BTN - 1; + cfg->swipe_dn_btn = DEFAULT_SWIPE_DN_BTN - 1; + cfg->swipe_lt_btn = DEFAULT_SWIPE_LT_BTN - 1; + cfg->swipe_rt_btn = DEFAULT_SWIPE_RT_BTN - 1; + cfg->scale_dist = SQRVAL(DEFAULT_SCALE_DIST); + cfg->scale_up_btn = DEFAULT_SCALE_UP_BTN - 1; + cfg->scale_dn_btn = DEFAULT_SCALE_DN_BTN - 1; + cfg->rotate_dist = SQRVAL(DEFAULT_ROTATE_DIST); + cfg->rotate_lt_btn = DEFAULT_ROTATE_LT_BTN - 1; + cfg->rotate_rt_btn = DEFAULT_ROTATE_RT_BTN - 1; + cfg->drag_enable = DEFAULT_DRAG_ENABLE; + cfg->drag_timeout = DEFAULT_DRAG_TIMEOUT; +} + +void mconfig_init(struct MConfig* cfg, + const struct Capabilities* caps) +{ + cfg->touch_minor = caps->has_abs[MTDEV_TOUCH_MINOR]; + cfg->pad_width = get_cap_xsize(caps); + cfg->pad_height = get_cap_ysize(caps); + + if (caps->has_abs[MTDEV_TOUCH_MAJOR] && caps->has_abs[MTDEV_WIDTH_MAJOR]) { + cfg->touch_type = MCFG_SCALE; + cfg->touch_min = caps->abs[MTDEV_TOUCH_MAJOR].minimum; + cfg->touch_max = caps->abs[MTDEV_TOUCH_MAJOR].maximum; + xf86Msg(X_INFO, "Touchpad supports regular and approaching touches.\n"); + xf86Msg(X_INFO, " touch_min = %d, touch_max = %d\n", cfg->touch_min, cfg->touch_max); + } + else if (caps->has_abs[MTDEV_TOUCH_MAJOR]) { + cfg->touch_type = MCFG_SIZE; + cfg->touch_min = caps->abs[MTDEV_TOUCH_MAJOR].minimum; + cfg->touch_max = caps->abs[MTDEV_TOUCH_MAJOR].maximum; + xf86Msg(X_INFO, "Touchpad supports regular touches.\n"); + xf86Msg(X_INFO, " touch_min = %d, touch_max = %d\n", cfg->touch_min, cfg->touch_max); + } + else if (caps->has_abs[MTDEV_PRESSURE]) { + cfg->touch_type = MCFG_PRESSURE; + cfg->touch_min = caps->abs[MTDEV_PRESSURE].minimum; + cfg->touch_max = caps->abs[MTDEV_PRESSURE].maximum; + xf86Msg(X_INFO, "Touchpad is pressure based.\n"); + xf86Msg(X_INFO, " touch_min = %d, touch_max = %d\n", cfg->touch_min, cfg->touch_max); + } + else { + cfg->touch_type = MCFG_NONE; + xf86Msg(X_WARNING, "Touchpad has minimal capabilities. Some features will be unavailable.\n"); + } + + if (cfg->touch_minor) + xf86Msg(X_INFO, "Touchpad supports minor touch widths.\n"); +} + +void mconfig_configure(struct MConfig* cfg, + InputInfoPtr local) +{ + pointer opts = local->options; + + // Configure MTState + cfg->touch_down = CLAMPVAL(xf86SetIntOption(opts, "FingerHigh", DEFAULT_TOUCH_DOWN), 0, 100); + cfg->touch_up = CLAMPVAL(xf86SetIntOption(opts, "FingerLow", DEFAULT_TOUCH_UP), 0, 100); + cfg->ignore_thumb = xf86SetBoolOption(opts, "IgnoreThumb", DEFAULT_IGNORE_THUMB); + cfg->ignore_palm = xf86SetBoolOption(opts, "IgnorePalm", DEFAULT_IGNORE_PALM); + cfg->disable_on_thumb = xf86SetBoolOption(opts, "DisableOnThumb", DEFAULT_DISABLE_ON_THUMB); + cfg->disable_on_palm = xf86SetBoolOption(opts, "DisableOnPalm", DEFAULT_DISABLE_ON_PALM); + cfg->thumb_ratio = CLAMPVAL(xf86SetIntOption(opts, "ThumbRatio", DEFAULT_THUMB_RATIO), 0, 100); + cfg->thumb_size = CLAMPVAL(xf86SetIntOption(opts, "ThumbSize", DEFAULT_THUMB_SIZE), 0, 100); + cfg->palm_size = CLAMPVAL(xf86SetIntOption(opts, "PalmSize", DEFAULT_PALM_SIZE), 0, 100); + + // Configure Gestures + cfg->button_enable = xf86SetBoolOption(opts, "ButtonEnable", DEFAULT_BUTTON_ENABLE); + cfg->button_integrated = xf86SetBoolOption(opts, "ButtonIntegrated", DEFAULT_BUTTON_INTEGRATED); + cfg->button_1touch = CLAMPVAL(xf86SetIntOption(opts, "ClickFinger1", DEFAULT_BUTTON_1TOUCH), 0, 32) - 1; + cfg->button_2touch = CLAMPVAL(xf86SetIntOption(opts, "ClickFinger2", DEFAULT_BUTTON_2TOUCH), 0, 32) - 1; + cfg->tap_1touch = CLAMPVAL(xf86SetIntOption(opts, "TapButton1", DEFAULT_TAP_1TOUCH), 0, 32) - 1; + cfg->tap_2touch = CLAMPVAL(xf86SetIntOption(opts, "TapButton2", DEFAULT_TAP_2TOUCH), 0, 32) - 1; + cfg->tap_3touch = CLAMPVAL(xf86SetIntOption(opts, "TapButton3", DEFAULT_TAP_3TOUCH), 0, 32) - 1; + cfg->tap_hold = MAXVAL(xf86SetIntOption(opts, "ClickTime", DEFAULT_TAP_HOLD), 1); + cfg->tap_timeout = MAXVAL(xf86SetIntOption(opts, "MaxTapTime", DEFAULT_TAP_TIMEOUT), 1); + cfg->tap_dist = SQRVAL(MAXVAL(xf86SetIntOption(opts, "MaxTapMove", DEFAULT_TAP_DIST), 1)); + cfg->gesture_hold = MAXVAL(xf86SetIntOption(opts, "GestureClickTime", DEFAULT_GESTURE_HOLD), 1); + cfg->gesture_wait = MAXVAL(xf86SetIntOption(opts, "GestureWaitTime", DEFAULT_GESTURE_WAIT), 0); + cfg->scroll_dist = MAXVAL(xf86SetIntOption(opts, "ScrollDistance", DEFAULT_SCROLL_DIST), 1); + cfg->scroll_up_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollUpButton", DEFAULT_SCROLL_UP_BTN), 0, 32) - 1; + cfg->scroll_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollDownButton", DEFAULT_SCROLL_DN_BTN), 0, 32) - 1; + cfg->scroll_lt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollLeftButton", DEFAULT_SCROLL_LT_BTN), 0, 32) - 1; + cfg->scroll_rt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollRightButton", DEFAULT_SCROLL_RT_BTN), 0, 32) - 1; + cfg->swipe_dist = MAXVAL(xf86SetIntOption(opts, "SwipeDistance", DEFAULT_SWIPE_DIST), 1); + cfg->swipe_up_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeUpButton", DEFAULT_SWIPE_UP_BTN), 0, 32) - 1; + cfg->swipe_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeDownButton", DEFAULT_SWIPE_DN_BTN), 0, 32) - 1; + cfg->swipe_lt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeLeftButton", DEFAULT_SWIPE_LT_BTN), 0, 32) - 1; + cfg->swipe_rt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeRightButton", DEFAULT_SWIPE_RT_BTN), 0, 32) - 1; + cfg->scale_dist = SQRVAL(MAXVAL(xf86SetIntOption(opts, "ScaleDistance", DEFAULT_SCALE_DIST), 1)); + cfg->scale_up_btn = CLAMPVAL(xf86SetIntOption(opts, "ScaleUpButton", DEFAULT_SCALE_UP_BTN), 0, 32) - 1; + cfg->scale_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "ScaleDownButton", DEFAULT_SCALE_DN_BTN), 0, 32) - 1; + cfg->rotate_dist = SQRVAL(MAXVAL(xf86SetIntOption(opts, "RotateDistance", DEFAULT_ROTATE_DIST), 1)); + cfg->rotate_lt_btn = CLAMPVAL(xf86SetIntOption(opts, "RotateLeftButton", DEFAULT_ROTATE_LT_BTN), 0, 32) - 1; + cfg->rotate_rt_btn = CLAMPVAL(xf86SetIntOption(opts, "RotateRightButton", DEFAULT_ROTATE_RT_BTN), 0, 23) - 1; + cfg->drag_enable = xf86SetBoolOption(opts, "TapDragEnable", DEFAULT_DRAG_ENABLE); + cfg->drag_timeout = MAXVAL(xf86SetIntOption(opts, "TapDragTime", DEFAULT_DRAG_TIMEOUT), 1); +} + + diff --git a/src/mtouch.c b/src/mtouch.c new file mode 100644 index 0000000..018cf56 --- /dev/null +++ b/src/mtouch.c @@ -0,0 +1,86 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#include "mtouch.h" + +static const int use_grab = 0; + +int mtouch_configure(struct MTouch *mt, int fd) +{ + int rc = read_capabilities(&mt->caps, fd); + if (rc < 0) + return rc; + output_capabilities(&mt->caps); + return 0; +} + + +int mtouch_open(struct MTouch *mt, int fd) +{ + int ret; + ret = mtdev_open(&mt->dev, fd); + if (ret) + goto error; + mconfig_init(&mt->cfg, &mt->caps); + hwstate_init(&mt->hs, &mt->caps); + mtstate_init(&mt->state); + gestures_init(&mt->gs); + if (use_grab) { + SYSCALL(ret = ioctl(fd, EVIOCGRAB, 1)); + if (ret) + goto close; + } + return 0; + close: + mtdev_close(&mt->dev); + error: + return ret; +} + + +int mtouch_close(struct MTouch *mt, int fd) +{ + int ret; + if (use_grab) { + SYSCALL(ret = ioctl(fd, EVIOCGRAB, 0)); + if (ret) + xf86Msg(X_WARNING, "mtouch: ungrab failed\n"); + } + mtdev_close(&mt->dev); + return 0; +} + +int read_packet(struct MTouch *mt, int fd) +{ + int ret = hwstate_modify(&mt->hs, &mt->dev, fd, &mt->caps); + if (ret <= 0) + return ret; + mtstate_extract(&mt->state, &mt->cfg, &mt->hs); + gestures_extract(&mt->gs, &mt->cfg, &mt->hs, &mt->state); + return 1; +} + +int has_delayed(struct MTouch *mt, int fd) +{ + return gestures_delayed(&mt->gs, &mt->dev, fd); +} + diff --git a/src/mtstate.c b/src/mtstate.c new file mode 100644 index 0000000..838874c --- /dev/null +++ b/src/mtstate.c @@ -0,0 +1,311 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#include "mtstate.h" +#include "trig.h" + +static int inline percentage(int dividend, int divisor) +{ + return (double)dividend / (double)divisor * 100; +} + +static int inline touch_range_ratio(const struct MConfig* cfg, int value) +{ + return (double)(value - cfg->touch_min) / (double)(cfg->touch_max - cfg->touch_min) * 100; +} + +/* Check if a finger is touching the trackpad. + */ +static int is_touch(const struct MConfig* cfg, + const struct FingerState* hw) +{ + if (cfg->touch_type == MCFG_SCALE) + return percentage(hw->touch_major, hw->width_major) > cfg->touch_down; + else if (cfg->touch_type == MCFG_SIZE) + return touch_range_ratio(cfg, hw->touch_major) > cfg->touch_down; + else if (cfg->touch_type == MCFG_PRESSURE) + return touch_range_ratio(cfg, hw->pressure) > cfg->touch_down; + else + return 1; +} + +/* Check if a finger is released from the touchpad. + */ +static int is_release(const struct MConfig* cfg, + const struct FingerState* hw) +{ + if (cfg->touch_type == MCFG_SCALE) + return percentage(hw->touch_major, hw->width_major) < cfg->touch_up; + else if (cfg->touch_type == MCFG_SIZE) + return touch_range_ratio(cfg, hw->touch_major) < cfg->touch_up; + else if (cfg->touch_type == MCFG_PRESSURE) + return touch_range_ratio(cfg, hw->pressure) < cfg->touch_up; + else + return 0; +} + +static int is_thumb(const struct MConfig* cfg, + const struct FingerState* hw) +{ + if (!cfg->touch_minor) + return 0; + + int min = MINVAL(hw->touch_minor, hw->touch_major); + int max = MAXVAL(hw->touch_minor, hw->touch_major); + int pct = percentage(min, max); + int size = touch_range_ratio(cfg, hw->touch_major); + + if (percentage(min, max) > cfg->thumb_ratio && size > cfg->thumb_size) { +#if DEBUG_MTSTATE + xf86Msg(X_INFO, "is_thumb: yes %d > %d && %d > %d\n", + pct, cfg->thumb_ratio, size, cfg->thumb_size); +#endif + return 1; + } + else { +#if DEBUG_MTSTATE + xf86Msg(X_INFO, "is_thumb: no %d > %d && %d > %d\n", + pct, cfg->thumb_ratio, size, cfg->thumb_size); +#endif + return 0; + } +} + +static int is_palm(const struct MConfig* cfg, + const struct FingerState* hw) +{ + if (cfg->touch_type != MCFG_SCALE && cfg->touch_type != MCFG_SIZE) + return 0; + + int size = touch_range_ratio(cfg, hw->touch_major); + if (size > cfg->palm_size) { +#if DEBUG_MTSTATE + xf86Msg(X_INFO, "is_palm: yes %d > %d\n", size, cfg->palm_size); +#endif + return 1; + } + else { +#if DEBUG_MTSTATE + xf86Msg(X_INFO, "is_palm: no %d > %d\n", size, cfg->palm_size); +#endif + return 0; + } +} + +/* Find a touch by its tracking ID. Return -1 if not found. + */ +static int find_touch(struct MTState* ms, + int tracking_id) +{ + int i; + foreach_bit(i, ms->touch_used) { + if (ms->touch[i].tracking_id == tracking_id) + return i; + } + return -1; +} + +/* Add a touch to the MTState. Return the new index of the touch. + */ +static int touch_append(struct MTState* ms, + const struct FingerState* fs) +{ + int n = firstbit(~ms->touch_used); + if (n < 0) + xf86Msg(X_WARNING, "Too many touches to track. Ignoring touch %d.\n", fs->tracking_id); + else { + ms->touch[n].state = 0U; + ms->touch[n].flags = 0U; + ms->touch[n].down = ms->evtime; + ms->touch[n].direction = TR_NONE; + ms->touch[n].tracking_id = fs->tracking_id; + ms->touch[n].x = fs->position_x; + ms->touch[n].y = fs->position_y; + ms->touch[n].dx = 0; + ms->touch[n].dy = 0; + ms->touch[n].total_dx = 0; + ms->touch[n].total_dy = 0; + SETBIT(ms->touch[n].state, MT_NEW); + SETBIT(ms->touch_used, n); + } + return n; +} + +/* Update a touch. + */ +static void touch_update(struct MTState* ms, + const struct FingerState* fs, + int touch) +{ + ms->touch[touch].dx = fs->position_x - ms->touch[touch].x; + ms->touch[touch].dy = fs->position_y - ms->touch[touch].y; + ms->touch[touch].total_dx += ms->touch[touch].dx; + ms->touch[touch].total_dy += ms->touch[touch].dy; + ms->touch[touch].x = fs->position_x; + ms->touch[touch].y = fs->position_y; + ms->touch[touch].direction = trig_direction(ms->touch[touch].dx, ms->touch[touch].dy); + CLEARBIT(ms->touch[touch].state, MT_NEW); +} + +/* Release a touch. + */ +static void touch_release(struct MTState* ms, + int touch) +{ + ms->touch[touch].dx = 0; + ms->touch[touch].dy = 0; + ms->touch[touch].direction = TR_NONE; + CLEARBIT(ms->touch[touch].state, MT_NEW); + SETBIT(ms->touch[touch].state, MT_RELEASED); +} + +/* Invalidate all touches. + */ +static void touches_invalidate(struct MTState* ms) +{ + int i; + foreach_bit(i, ms->touch_used) + SETBIT(ms->touch[i].state, MT_INVALID); +} + +/* Update all touches. + */ +static void touches_update(struct MTState* ms, + const struct MConfig* cfg, + const struct HWState* hs) +{ + int i, n; + // Release missing touches. + foreach_bit(i, ms->touch_used) { + if (find_finger(hs, ms->touch[i].tracking_id) == -1) + touch_release(ms, i); + } + // Add and update touches. + foreach_bit(i, hs->used) { + n = find_touch(ms, hs->data[i].tracking_id); + if (n >= 0) { + if (is_release(cfg, &hs->data[i])) + touch_release(ms, n); + else + touch_update(ms, &hs->data[i], n); + } + else if (is_touch(cfg, &hs->data[i])) + n = touch_append(ms, &hs->data[i]); + + if (n >= 0) { + // Track and invalidate thumb and palm touches. + if (!GETBIT(ms->touch[n].state, MT_INVALID)) { + if (is_thumb(cfg, &hs->data[i])) { + if (cfg->ignore_thumb) + SETBIT(ms->touch[n].state, MT_INVALID); + SETBIT(ms->touch[n].state, MT_THUMB); + } + if (is_palm(cfg, &hs->data[i])) { + if (cfg->ignore_palm) + SETBIT(ms->touch[n].state, MT_INVALID); + SETBIT(ms->touch[n].state, MT_PALM); + } + } + if (GETBIT(ms->touch[n].state, MT_THUMB)) { + SETBIT(ms->state, MT_THUMB); + if (cfg->disable_on_thumb) { + touches_invalidate(ms); + break; + } + } + if (GETBIT(ms->touch[n].state, MT_PALM)) { + SETBIT(ms->state, MT_PALM); + if (cfg->disable_on_palm) { + touches_invalidate(ms); + break; + } + } + } + } +} + +/* Remove released touches. + */ +static void touches_clean(struct MTState* ms) +{ + int i, used; + used = ms->touch_used; + foreach_bit(i, used) { + if (GETBIT(ms->touch[i].state, MT_RELEASED)) + CLEARBIT(ms->touch_used, i); + } +} + +#if DEBUG_MTSTATE +void mtstate_output(const struct MTState* ms) +{ + int i, n; + char* type; + n = bitcount(ms->touch_used); + //if (bitcount(ms->touch_used) > 0) + // xf86Msg(X_INFO, "mtstate: %d touches at event time is %llu\n", n, ms->evtime); + foreach_bit(i, ms->touch_used) { + if (GETBIT(ms->touch[i].state, MT_RELEASED)) { + xf86Msg(X_INFO, " released p(%d, %d) d(%+d, %+d) dir(%f) down(%llu) time(%lld)\n", + ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy, + ms->touch[i].direction, ms->touch[i].down, ms->evtime - ms->touch[i].down); + } + else if (GETBIT(ms->touch[i].state, MT_NEW)) { + xf86Msg(X_INFO, " new p(%d, %d) d(%+d, %+d) dir(%f) down(%llu)\n", + ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy, + ms->touch[i].direction, ms->touch[i].down); + } + else if (GETBIT(ms->touch[i].state, MT_INVALID)) { + xf86Msg(X_INFO, " invalid p(%d, %d) d(%+d, %+d) dir(%f) down(%llu) time(%lld)\n", + ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy, + ms->touch[i].direction, ms->touch[i].down, ms->evtime - ms->touch[i].down); + } + else { + xf86Msg(X_INFO, " touching p(%d, %d) d(%+d, %+d) dir(%f) down(%llu)\n", + ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy, + ms->touch[i].direction, ms->touch[i].down); + } + } +} +#endif + +void mtstate_init(struct MTState* ms) +{ + memset(ms, 0, sizeof(struct MTState)); +} + +// Process changes in touch state. +void mtstate_extract(struct MTState* ms, + const struct MConfig* cfg, + const struct HWState* hs) +{ + ms->state = 0; + ms->evtime = hs->evtime; + + touches_clean(ms); + touches_update(ms, cfg, hs); + +#if DEBUG_MTSTATE + mtstate_output(ms); +#endif +} + diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..089fe19 --- /dev/null +++ b/src/test.c @@ -0,0 +1,87 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2008 Henrik Rydberg + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#include +#include +#include + +static void print_gestures(const struct Gestures* gs) +{ + int i; + static bitmask_t buttons_prev = 0U; + for (i = 0; i < 32; i++) { + if (GETBIT(gs->buttons, i) == GETBIT(buttons_prev, i)) + continue; + + if (GETBIT(gs->buttons, i)) + printf("button %d down\n", i+1); + else + printf("button %d up\n", i+1); + } + + if (gs->move_dx != 0 || gs->move_dy != 0) + printf("moving (%+4d, %+4d)\n", gs->move_dx, gs->move_dy); + + buttons_prev = gs->buttons; +} + +static void loop_device(int fd) +{ + struct MTouch mt; + if (mtouch_configure(&mt, fd)) { + fprintf(stderr, "error: could not configure device\n"); + return; + } + if (mtouch_open(&mt, fd)) { + fprintf(stderr, "error: could not open device\n"); + return; + } + + mconfig_defaults(&mt.cfg); + printf("width: %d\n", mt.hs.max_x); + printf("height: %d\n", mt.hs.max_y); + + //while (!mtdev_idle(&mt.dev, fd, 5000)) { + while (1) { + while (read_packet(&mt, fd) > 0) + print_gestures(&mt.gs); + if (has_delayed(&mt, fd)) + print_gestures(&mt.gs); + } + mtouch_close(&mt, fd); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: test \n"); + return -1; + } + int fd = open(argv[1], O_RDONLY | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "error: could not open file\n"); + return -1; + } + loop_device(fd); + close(fd); + return 0; +} diff --git a/src/trig.c b/src/trig.c new file mode 100644 index 0000000..1b2cae7 --- /dev/null +++ b/src/trig.c @@ -0,0 +1,114 @@ +/*************************************************************************** + * + * Multitouch X driver + * Copyright (C) 2011 Ryan Bourgeois + * + * 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 2 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 + * + **************************************************************************/ + +#include "trig.h" +#include "common.h" + +/* Determine the angle of a vector within the given quadrant. + * All this really does is calculate the slope in relation to + * which quadrant it is in. + */ +static double trig_quadrant_angle(int quadrant, double dx, double dy) { + dx = ABSVAL(dx); + dy = ABSVAL(dy); + if (quadrant == 1 || quadrant == 3) + return dx < dy ? 2-dx/dy : dy/dx; + else + return dy < dx ? 2-dy/dx : dx/dy; +} + +int trig_quadrant(double dx, double dy) +{ + if (dx > 0 && dy < 0) + return 0; + else if (dx > 0 && dy > 0) + return 1; + else if (dx < 0 && dy > 0) + return 2; + else if (dx < 0 && dy < 0) + return 3; + else + return -1; +} + +double trig_direction(double dx, double dy) +{ + int qn; + if (dx == 0 && dy == 0) + return TR_NONE; + else if (dx == 0) + return (dy < 0) ? 0 : 4; + else if (dy == 0) + return (dx > 0) ? 2 : 6; + else { + qn = trig_quadrant(dx, dy); + return qn*2 + trig_quadrant_angle(qn, dx, dy); + } +} + +int trig_generalize(double dir) +{ + if (dir == -1) + return TR_NONE; + else if (dir > 1 && dir <= 3) + return TR_DIR_RT; + else if (dir > 3 && dir <= 5) + return TR_DIR_DN; + else if (dir > 5 && dir <= 7) + return TR_DIR_LT; + else + return TR_DIR_UP; +} + +double trig_angles_add(double a1, double a2) +{ + double a = MODVAL(a1 + a2, 8.0); + if (a < 0) + a = a + 8.0; + return a; +} + +double trig_angles_sub(double a1, double a2) +{ + return trig_angles_add(a1, -1.0*a2); +} + +double trig_angles_acute(double a1, double a2) +{ + if (a1 > a2) + return trig_angles_sub(a1, a2); + else + return trig_angles_sub(a2, a1); +} + +int trig_angles_cmp(double a1, double a2) +{ + double m1, m2; + m1 = MODVAL(a1, 8); + m2 = MODVAL(a2, 8); + if (m1 == m2) + return 0; + else if (m1 > m2) + return 1; + else + return -1; +} +