Repository: p2rkw/xf86-input-mtrack Branch: master Commit: d38088b43306 Files: 44 Total size: 267.0 KB Directory structure: gitextract_jeyfc4m8/ ├── .gitignore ├── COPYING ├── CREDITS ├── Makefile.am ├── README.md ├── RELEASE.md ├── configure.ac ├── debian/ │ ├── README.Debian │ ├── changelog │ ├── compat │ ├── control │ ├── copyright │ ├── dirs │ └── rules ├── driver/ │ ├── mprops.c │ └── mtrack.c ├── examples/ │ ├── acer-aspire-s3.conf │ ├── asus-zenbook-ux330ua-ah55 │ ├── dell-precision-5520.conf │ ├── dell-xps13-9333.conf │ ├── lenovo-710s.conf │ ├── lenovo-yoga-2-pro.conf │ ├── macbook-pro-retina-2013.conf │ └── macbook.conf ├── include/ │ ├── button.h │ ├── capabilities.h │ ├── common.h │ ├── gestures.h │ ├── hwstate.h │ ├── mconfig.h │ ├── mprops.h │ ├── mtouch.h │ ├── mtstate.h │ └── trig.h ├── required_packages.txt ├── src/ │ ├── capabilities.c │ ├── gestures.c │ ├── hwstate.c │ ├── mconfig.c │ ├── mtouch.c │ ├── mtstate.c │ └── trig.c └── tools/ ├── mtrack-test.c └── reconfigure-xinput.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.o *.lo *.la *.swp *.dirstamp .deps/ .libs/ Makefile Makefile.in /m4 /missing /libtool install-sh ltmain.sh /aclocal.m4 /autom4te.cache/ /build-aux/ /config.* /configure /stamp-h1 /depcomp /compile debian/files debian/xserver-xorg-input-mtrack.debhelper.log debian/xserver-xorg-input-mtrack.substvars debian/xserver-xorg-input-mtrack/ mtrack-test ================================================ FILE: COPYING ================================================ 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. ================================================ FILE: CREDITS ================================================ This is a copy of the CREDITS file from the xf86-input-multitouch driver. --- 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. --- ================================================ FILE: Makefile.am ================================================ SOURCES_COMMON = \ src/capabilities.c \ src/gestures.c \ src/hwstate.c \ src/mconfig.c \ src/mtouch.c \ src/mtstate.c \ src/trig.c @DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la @DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version @DRIVER_NAME@_drv_la_SOURCES = $(SOURCES_COMMON) \ driver/mtrack.c \ driver/mprops.c @DRIVER_NAME@_drv_ladir = @inputdir@ noinst_PROGRAMS = mtrack-test mtrack_test_SOURCES = $(SOURCES_COMMON) \ tools/mtrack-test.c mtrack_test_CFLAGS = $(AM_CFLAGS) ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/include/ \ -I/usr/include/xorg \ -I/usr/include/pixman-1 AM_CFLAGS = $(XORG_CFLAGS) .PHONY: ChangeLog INSTALL INSTALL: $(INSTALL_CMD) ChangeLog: $(CHANGELOG_CMD) dist-hook: ChangeLog INSTALL ================================================ FILE: README.md ================================================ xf86-input-mtrack ================= ###### v0.5.1 An Xorg driver for multitouch trackpads. Supports any trackpad whose kernel driver uses the slotted multitouch protocol. For more information on the protocol see the [kernel documentation][1]. This driver is compatible with Xorg server versions 1.7 to 1.19.0 It requires the [mtdev][4] library to operate. ## License * Copyright (C) 2015-2018 Paweł Turkowski * Copyright (C) 2011 Ryan Bourgeois * Copyright (C) 2008 Henrik Rydberg This software is licensed under the [GPLv2][2] and is a fork of the [xf86-input-multitouch][3] driver by Henrik Rydberg. ## Building and Installing This is a standard autoconf package. So: ``` ./configure make && make install ``` It is likely that you will need to change system-dependent paths such as the xorg module directory. Otherwise mtrack may be not installed in xserver search path. See `configure --help` for options. On Debian systems (including Ubuntu) you may also need to install the `xorg-dev` package prior to these commands. To build deb package and install in system wide you will usually have to change installation prefix to /usr like so: ``` ./configure --prefix=/usr dpkg-buildpackage ``` ## Bug Reporting If you found a bug, and you are going to submit it, first run ``` xinput --list ``` then find your touchpad name/ID on the list and create report with output of ``` xinput --list-props ``` attached. ## Configuration The following is a minimal working InputClass section for xorg.conf: ``` Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" Driver "mtrack" # In case of problems enable/disable this line: MatchDevicePath "/dev/input/event*" EndSection ``` --- To apply changes made in xorg.conf (or related files) X server have to be restarted. Changes made with `xinput` are applied immedietly but they are not saved between sessions. ### Options Configuration options may be defined inside the InputClass section to configure the driver. See examples/ directory for example configuration files. Feel free to submit yours, named as your laptop's model. Available options and their defaults are as follows. --- #### Basic **[TrackpadDisable](#TrackpadDisable)** Disables trackpad touch input. A value of 0 will enable the trackpad. A value of 1 will disable tapping and gestures but not movement. A value of 2 will disable all input. A value of 3 will also disable physical buttons. Integer. Default is 0. **[ButtonEnable](#ButtonEnable)** Whether or not to enable the physical buttons on or near the trackpad. Boolean value. Defaults to true. **[ButtonIntegrated](#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. --- #### Responsiveness **[Sensitivity](#Sensitivity)** Adjusts the sensitivity (movement speed) of the touchpad. This is a real number greater than or equal to zero. A value of 0 will disable pointer movement. Float value. Default is 1. **[FingerHigh](#FingerHigh)** Defines the pressure at which a finger is detected as a touch. This is a percentage represented as an integer. Integer value. Default is 5. **[FingerLow](#FingerLow)** Defines the pressure at which a finger is detected as a release. This is a percentage represented as an integer. Integer value. Default is 5. **[IgnoreThumb](#IgnoreThumb)** Whether or not to ignore touches that are determined to be thumbs. Boolean value. Defaults to false. **[IgnorePalm](#IgnorePalm)** Whether or not to ignore touches that are determined to be palms. Boolean value. Defaults to false. **[DisableOnThumb](#DisableOnThumb)** Whether or not to disable the entire trackpad when a thumb is touching. Boolean value. Defaults to false. **[DisableOnPalm](#DisableOnPalm)** Whether or not to disable the entire trackpad when a palm is touching. Boolean value. Defaults to false. **[ThumbSize](#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 maximum touch value and is dependent on the trackpad hardware. Integer value. Defaults to 25. **[PalmSize](#PalmSize)** The minimum size of what's considered a palm. Palms are expected to be very large on the trackpad. This is represented as a percentage of the maximum touch value and is dependent on the trackpad hardware. Integer value. Defaults to 40. **[ThumbRatio](#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. Integer value. Defaults to 70. --- #### Zones Divide the touchpad into "zones". Clicking the integrated button in one of these zones will send the button event configured for each {First, Second, Third}ZoneButton. The driver will only add zones for the ZoneButton values that are enabled. The zone splitting start from the left to right using the first to third value. So enabling only SecondZoneButton and ThirdZoneButton will create two zones, the left-middle part will fire SecondZoneButton and the middle-right part ThirdZoneButton. **[ButtonZonesEnable](#ButtonZonesEnable)** Whether or not to enable button zones. If button zones are enabled then the trackpad will be split into one, two, or three vertical zones. Boolean value. Defaults to false. **[FirstZoneButton](#FirstZoneButton)** The button to emulate when the zone is pressed. This is the leftmost part of the pad. Integer value. A value of 0 disables this zone split. Integer value. Defaults to 1. **[SecondZoneButton](#SecondZoneButton)** The button to emulate when the zone is pressed. This will float to the right of the leftmost zone. Integer value. A value of 0 disables this zone split. Integer value. Defaults to 2. **[ThirdZoneButton](#ThirdZoneButton)** The button to emulate when the zone is pressed. This will float to the right of the leftmost zone. Integer value. A value of 0 disables this zone split. Integer value. Defaults to 0. **[LimitButtonZonesToBottomEdge](#LimitButtonZonesToBottomEdge)** Restrict button zones inside the [EdgeBottom](#EdgeBottomSize) area. So instead of enabling zones on the full pad height, the zone is limited to the percentage set for the [EdgeBottom](#EdgeBottomSize). Boolean value. Default to false. --- #### Physical click **[ClickFinger0](#ClickFinger0)** Which button to emulate when no valid finger placement is touching the trackpad during a click, as on "EdgeBottom". Integer value. A value of 0 disables one-touch emulation. Defaults to 0. **[ClickFinger1](#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 1. **[ClickFinger2](#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. **[ClickFinger3](#ClickFinger3)** Which button to emulate when three fingers are touching the trackpad during a click. Integer value. A value of 0 disabled one-touch emulation. Defaults to 3. **[ButtonMoveEmulate](#ButtonMoveEmulate)** Whether or not to count the moving finger when emulating button clicks. Useful to disable if you use two hands on trackpad. Boolean value. Defaults to true. **[ButtonTouchExpire](#ButtonTouchExpire)** How long (in ms) to consider a touching finger as part of button emulation. A value of 0 will not expire touches. Integer value. Defaults to 100. --- #### Tap clicking **[TapButton1](#TapButton1)** Which button to emulate for one-finger tapping. Integer value. A value of 0 disables one-finger tapping. Integer value. Defaults to 1. **[TapButton2](#TapButton2)** Which button to emulate for two-finger tapping. Integer value. A value of 0 disables two-finger tapping. Integer value. Defaults to 3. **[TapButton3](#TapButton3)** Which button to emulate for three-finger tapping. Integer value. A value of 0 disables three-finger tapping. Integer value. Defaults to 2. **[TapButton4](#TapButton4)** Which button to emulate for four-finger tapping. Integer value. A value of 0 disables three-finger tapping. Integer value. Defaults to 0. **[ClickTime](#ClickTime)** When tapping, how much time to hold down the emulated button. Integer value representing milliseconds. Defaults to 50. **[MaxTapTime](#MaxTapTime)** The amount of time to wait for incoming touches after first one before counting it as emulated button click. Integer value representing milliseconds. Defaults to 120. **[MaxTapMove](#MaxTapMove)** How far a touch is allowed to move before counting it is no longer considered a tap. Integer value. Defaults to 400. #### Gesture --- ##### Basic **[GestureClickTime](#GestureClickTime)** When a gesture triggers a click, how much time to hold down the emulated button. Integer value representing milliseconds. Defaults to 10. **[GestureWaitTime](#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. --- ##### Two fingers Scrolling **[ScrollDistance](#ScrollDistance)** For two finger scrolling. This sets the speed of your scrolling: the lower the number, the faster you scroll. Technically, it is how far you must move your fingers before a button click registering scrolling is triggered. Integer value. Defaults to 150. **[ScrollClickTime](#ScrollClickTime)** For two finger scrolling. How long button triggered by scrolling will be hold down. A value of 0 will hold button down till end of gesture. 0 - emit button click only once pre "instance" of gesture. Integer value representing milliseconds. Integer value. Defaults to 20. **[ScrollSensitivity](#ScrollSensitivity)** For two finger scrolling. Sensitivity (movement speed) of pointer during two finger scrolling. A value of 0 disables pointer movement during gesture. Integer value expressed as parts per thousand of normal sensivity. A value of 1000 results with normal movement speed. Integer value. Defaults to 0. **[ScrollUpButton](#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](#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](#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. **[ScrollRightButton](#ScrollRightButton)** For two finger scrolling. The button that is triggered by scrolling right. Integer value. A value of 0 disables scrolling right. Defaults to 7. **[ScrollSmooth](#ScrollSmooth)** For two finger scrolling. Whether to generate high precision scroll events. Boolean value. Defaults to 1. Property: "Trackpad High Smooth Scroll" **[ScrollCoastDuration](#ScrollCoastDuration)** How long after finished scrolling movement should be continued. Works only with smooth scrolling enabled. Floating value representing miliseconds. Defaults to 200.0. Property: "Trackpad Scroll Coasting" **[ScrollCoastEnableSpeed](#ScrollCoastEnableSpeed)** How fast scroll should be to enable coasting feature. Floating value. Defaults to 0.1. Property: "Trackpad Scroll Coasting" **[ScrollCoastNoBoost](#ScrollCoastNoBoost)** Disable boosting on second scroll gesture during coasting Boolean value. Defaults to false. Property: "Trackpad Scroll Coasting" **[ScrollCoastEase](#ScrollCoastEase)** Apply easing effect on coasting Boolean value. Defaults to false. Property: "Trackpad Scroll Coasting" --- ##### Three fingers swipe **[SwipeDistance](#SwipeDistance)** For three finger swiping. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 700. **[SwipeClickTime](#SwipeClickTime)** For three finger swiping. How long button triggered by swiping will be hold down. Integer value representing milliseconds. Defaults to 300. **[SwipeSensitivity](#SwipeSensitivity)** For three finger scrolling. Sensitivity (movement speed) of pointer during three finger scrolling. A value of 0 disables pointer movement during gesture. Integer value expressed as parts per thousand of normal sensivity. A value of 1000 results with normal movement speed. Defaults to 0. **[SwipeUpButton](#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](#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](#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](#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. --- ##### Four fingers swipe **[Swipe4Distance](#Swipe4Distance)** For four finger swiping. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 700. **[Swipe4ClickTime](#Swipe4ClickTime)** For four finger swiping. How long button triggered by swiping will be hold down. Integer value representing milliseconds. Defaults to 300. **[Swipe4Sensitivity](#Swipe4Sensitivity)** For four finger scrolling. Sensitivity (movement speed) of pointer during four finger scrolling. A value of 0 disables pointer movement during gesture. Integer value expressed as parts per thousand of normal sensivity. A value of 1000 results with normal movement speed. Defaults to 0. **[Swipe4UpButton](#Swipe4UpButton)** For four finger swiping. The button that is triggered by swiping up. Integer value. A value of 0 disables swiping up. Defaults to 8. **[Swipe4DownButton](#Swipe4DownButton)** For four finger swiping. The button that is triggered by swiping down. Integer value. A value of 0 disables swiping down. Defaults to 9. **[Swipe4LeftButton](#Swipe4LeftButton)** For four finger swiping. The button that is triggered by swiping left. Integer value. A value of 0 disables swiping left. Defaults to 10. **[Swipe4RightButton](#Swipe4RightButton)** For four finger swiping. The button that is triggered by swiping right. Integer value. A value of 0 disables swiping right. Defaults to 11. --- ##### Edge Scrolling - One finger Should be used in conjuction with the [`Edge disabling`](#EdgeDisabling) options. **[EdgeScrollDist](#EdgeScrollDist)** For one finger edge scrolling. How far you must move your finger on edge before a button click is triggered. Integer value. Defaults to 105. **[EdgeScrollClickTime](#EdgeScrollClickTime)** For one finger edge scrolling. How long button triggered by edge scrolling will be hold down. A value of 0 will hold button down till end of gesture. 0 - emit button click only once pre "instance" of gesture. Integer value representing milliseconds. Defaults to 20. **[EdgeScrollSensitivity](#EdgeScrollSensitivity)** For one finger edge scrolling. Sensitivity (movement speed) of pointer during one finger scrolling. A value of 0 disables pointer movement during gesture. Integer value expressed as parts per thousand of normal sensivity. A value of 1000 results with normal movement speed. Defaults to 0. **[EdgeScrollUpButton](#EdgeScrollUpButton)** For one finger edge scrolling. The button that is triggered by edge scrolling up. Integer value. A value of 0 disables scrolling up. Defaults to 4. **[EdgeScrollDownButton](#EdgeScrollDownButton)** For one finger edge scrolling. The button that is triggered by edge scrolling down. Integer value. A value of 0 disables scrolling down. Defaults to 5. **[EdgeScrollLeftButton](#EdgeScrollLeftButton)** For one finger edge scrolling. The button that is triggered by edge scrolling left. Integer value. A value of 0 disables scrolling left. Defaults to 6. **[EdgeScrollRightButton](#EdgeScrollRightButton)** For one finger edge scrolling. The button that is triggered by edge scrolling right. Integer value. A value of 0 disables scrolling right. Defaults to 7. --- ##### Pinch scaling - Two fingers **[ScaleDistance](#ScaleDistance)** For pinch scaling. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 150. **[ScaleUpButton](#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](#ScaleDownButton)** For pinch scaling. The button that is triggered by scaling down. Integer value. A value of 0 disables scaling down. Defaults to 13. --- ##### Rotation - Two fingers **[RotateDistance](#RotateDistance)** For two finger rotation. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 150. **[RotateLeftButton](#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](#RotateRightButton)** For two finger rotation. The button that is triggered by rotating right. Integer value. A value of 0 disables rotation right. Defaults to 15. --- #### Edge disabling ~~[EdgeSize](#EdgeSize)~~ DEPRECATED. The size of an area around the trackpad where new touches are ignored (fingers traveling into this area from above will still be tracked). This is represented as a percentage of the total trackpad height. Integer value. Defaults to 0. Value set here is overwriten by EdgeLeftSize, EdgeRightSize, EdgeTopSize and EdgeBottomSize **[EdgeTopSize](#EdgeTopSize)** The size of an area at the top of the trackpad where new touches are ignored (fingers travelling into this area from the bottom will still be tracked). Integer value representing a percentage of the total trackpad height. Defaults to 0. **[EdgeBottomSize](#EdgeBottomSize)** The size of an area at the bottom of the trackpad where new touches are ignored (fingers travelling into this area from the top will still be tracked). Integer value representing a percentage of the total trackpad height. Defaults to 10. **[EdgeLeftSize](#EdgeLeftSize)** The size of an area at the left of the trackpad where new touches are ignored (fingers travelling into this area from the right will still be tracked). Integer value representing a percentage of the total trackpad width. Defaults to 0. **[EdgeRightSize](#EdgeRightSize)** The size of an area at the right of the trackpad where new touches are ignored (fingers travelling into this area from the left will still be tracked). Integer value representing a percentage of the total trackpad width. Defaults to 0. --- #### Special features **[Hold1Move1StationaryButton](#Hold1Move1StationaryButton)** For two finger hold-and-move functionality. The button that is triggered by holding one finger and moving another one. Integer value. A value of 0 disables hold-and-move. Value of 0 disables this functionality. Defaults to 1. **[Hold1Move1StationaryMaxMove](#Hold1Move1StationaryMaxMove)** For two finger hold-and-move functionality. Fow far stationary finger can be moved berfore gesture invalidation. Integer value. Default to 20. **[TapDragEnable](#TapDragEnable)** Whether or not to enable tap-to-drag functionality. Boolean value. Defaults to true. **[TapDragTime](#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 350. **[TapDragWait](#TapDragWait)** How long after detecting movement to trigger a button down event. During this time pointer movement will be disabled. Increase this value if you find you're draggin when you don't wish it. Integer value representing milliseconds. Defaults to 40. **[TapDragDist](#TapDragDist)** How far the finger is allowed to move during drag wait time. If the finger moves farther than this distance during the wait time then dragging will be canceled and pointer movement will resume. Integer value. Defaults to 200. **[TapDragLockTimeout](#TapDragLockTimeout)** This is how long the driver will wait after initial drag in 'drag ready' state in which it will be able to resume previous drag without additional `up`, `down` sequence. Value of 0 disables this functionality. Values of less than zero will make mtrack require additional tap to finish drag by sending `button up`. Integer value representing milliseconds. Defaults to 500. **[AxisXInvert](#AxisXInvert)** Whether or not to invert the X axis. Boolean value. Defaults to false. **[AxisYInvert](#AxisYInvert)** Whether or not to invert the Y axis. Boolean value. Defaults to false. Tips ------------- #### Swipe to drag To setup swipe to drag functionality you have to choose which swipe gesture ([Scroll](two-finger-scrolling), [Swipe](#three-finger-swipe), [Swipe4](#four-finger-swipe)) will be used for dragging. Example configuration for three finger drag: ``` Option "SwipeDistance" "1" Option "SwipeLeftButton" "1" Option "SwipeRightButton" "1" Option "SwipeUpButton" "1" Option "SwipeDownButton" "1" Option "SwipeClickTime" "0" Option "SwipeSensitivity" "1000" ``` This will enable dragging with three fingers. Change [sensitivity](#SwipeSensitivity) for faster/slower movements. Scroll, and Swipe4 are also supported. #### Hold and move Hold down one finger in place to initiate hold-and-move gesture. Then move another finger to drag configured button. Gesture will last as long as fist finger (a.k.a. stationary finger) will be held down in place. Increase [TapDragDist](#TapDragDist) to give stationary finger more freedom. Set [Hold1Move1StationaryButton](#Hold1Move1StationaryButton) to 0 to disable, set to other value to send button other than "1". #### Persistent dragging If you're using lot of tools that require dragging you can make it a little bit easier by enabling [persistent tap-to-drag](#TapDragLockTimeout): ``` Option "TapDragLockTimeout" "-1" ``` With that change you will have to perform additional tap when dragging with tap-to-drag. Other positive values will let you continue yor drag within specified time. #### Enabling soft button ##### Basic If you like to keep a finger on the bottom of the pad to click and use another one to move the cursor, you should enable edge restriction and [ClickFinger0](#ClickFinger0) parameters. ``` Option "EdgeBottomSize" "20" # Disable tap and movement detection in the bottom 20% of the pad Option "ClickFinger0" "1" # Enable clicking action "1" when no finger is detected ``` You could also use [ClickFinger0](#ClickFinger0) with [EdgeTop/Right/LeftSize](#EdgeDisabling). ##### Advanced If you want more than one button in the bottom edge, you need to use a more advanced configuration. It will enable you use up to [3 buttons](#Zones) inside that [edge](#EdgeBottomSize). ``` Option "ButtonZonesEnable" "true" # Enable "Zones" Option "LimitButtonZonesToBottomEdge" "true" # Limit the zones to the bottom edge Option "EdgeBottomSize" "20" # Disable tap and movement detection in the bottom 20% of the pad # Zones stack from left to right inside the 20% height defined above Option "FirstZoneButton" "1" # Left part fire click 1 Option "SecondZoneButton" "3" # Middle part fire click 3 Option "ThirdZoneButton" "2" # Right part fire click 2 ``` [1]: http://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt "Kernel Multitouch Protocol" [2]: http://www.gnu.org/licenses/gpl-2.0.html "GNU General Public License, version 2" [3]: http://bitmath.org/code/multitouch/ "xf86-input-multitouch website" [4]: http://bitmath.org/code/mtdev/ "mtdev library website" ================================================ FILE: RELEASE.md ================================================ ## v0.5.1 * Add button emulation when no finger placement is valid * Add dedicated zone button * Enable the restriction of the zone inside the bottom edge * Readme configuration options are grouped according to their relationship * Readme anchor has been moved to the options themselves ## v0.5.0 * Add tap-to-drag lock timeout * Make edge size configurable for each edge individually * Add edge scrolling * Fix movement truncation * Fix crash during suspend/wake up * Make movement speed resolution independent * Lock smooth scroll to one axis at time * Post button events before move events * New emulated buttons implementation * Reduce max recognized fingers/touch points from 32 to 16 * New fancy readme with anchors * Fix warnings, improve logging * New examples * Helper script to parse output of xinput --list-props ## v0.4.1 * Smooth scroll: detect and handle flipped up&down, left&right buttons * Add examples with my current configuration as first example. * Proper initialization of scroll axes(2 and 3). * Setup scroll valuators also during device initialization. * Do not consider edge clicks when calculating emulated buttons * Change type of variables holding movement delta to double for better precision. * Rewrite tap implementation from scratch. Remove 'status' field from Touch structure. * Clear the MT_BUTTON bit of touches when an integrated button is released. * Scale gesture reimplementation. ## v0.4.0 * Add smooth scroll * Add scroll/swipe/swipe4 to drag functionality * Add scroll coasting * Add Hold And Move gesture * Replace EdgeSize with BottomEdge property * Replace busy waiting with timers * Fix button down -> up delays * Initial support for absolute mode devices * Improve support for pressure based devices * Many minor improvements/bugfixes ## v0.3.1 * Include a configure script. * Update the README. * Add the RELEASE file. ================================================ FILE: configure.ac ================================================ # Initialize Autoconf AC_PREREQ([2.60]) AC_INIT([xf86-input-mtrack], [0.2.0], [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], [xf86-input-mtrack]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([Makefile.am]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_AUX_DIR(.) # Initialize Automake AM_INIT_AUTOMAKE([foreign subdir-objects]) AM_MAINTAINER_MODE # Initialize libtool AC_DISABLE_STATIC AC_PROG_LIBTOOL # Initialize X.Org macros 1.8 or later for MAN_SUBSTS set by XORG_MANPAGE_SECTIONS m4_ifndef([XORG_MACROS_VERSION], [m4_fatal([must install xorg-macros 1.8 or later before running autoconf/autogen])]) XORG_MACROS_VERSION(1.8) XORG_DEFAULT_OPTIONS # Checks for libraries. AC_CHECK_LIB([mtdev], [mtdev_open]) AC_CHECK_LIB([m], [atan2]) # Obtain compiler/linker options for the mtrack driver dependencies PKG_CHECK_MODULES(XORG, [xorg-server >= 1.7] xproto inputproto $REQUIRED_MODULES) # Set driver name DRIVER_NAME=mtrack AC_SUBST([DRIVER_NAME]) # configure option for module install directory AC_ARG_WITH(xorg-module-dir, AC_HELP_STRING([--with-xorg-module-dir=DIR], [Default xorg module directory [[default=$libdir/xorg/modules]]]), [moduledir="$withval"], [moduledir="$libdir/xorg/modules"]) inputdir=${moduledir}/input AC_SUBST(inputdir) # configure option to build extra tools AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], [Build extra tools (default: disabled)]), [ENABLE_TOOLS=yes], [ENABLE_TOOLS=no]) AM_CONDITIONAL([BUILD_TOOLS], [test "x$ENABLE_TOOLS" = xyes]) # configure option to enable gesture ate debugging AC_ARG_ENABLE(debug-gestures, AS_HELP_STRING([--enable-debug-gestures], [Enable gesture debugging (default: disabled)]), [DEBUG_GESTURES=yes], [DEBUG_GESTURES=no]) if test "x$DEBUG_GESTURES" = xyes; then AC_DEFINE(DEBUG_GESTURES, 1, [Enable gesture debugging.]) fi # configure option to enable multitouch state debugging AC_ARG_ENABLE(debug-mtstate, AS_HELP_STRING([--enable-debug-mtstate], [Enable multitouch state debugging (default: disabled)]), [DEBUG_MTSTATE=yes], [DEBUG_MTSTATE=no]) if test "x$DEBUG_MTSTATE" = xyes; then AC_DEFINE(DEBUG_MTSTATE, 1, [Enable multitouch state debugging.]) fi # configure option to enable property debugging AC_ARG_ENABLE(debug-props, AS_HELP_STRING([--enable-debug-props], [Enable property debugging (default: disabled)]), [DEBUG_PROPS=yes], [DEBUG_PROPS=no]) if test "x$DEBUG_PROPS" = xyes; then AC_DEFINE(DEBUG_PROPS, 1, [Enable property debugging.]) fi # configure option to enable driver debugging AC_ARG_ENABLE(debug-driver, AS_HELP_STRING([--enable-debug-driver], [Enable property debugging (default: disabled)]), [DEBUG_DRIVER=yes], [DEBUG_DRIVER=no]) if test "x$DEBUG_DRIVER" = xyes; then AC_DEFINE(DEBUG_DRIVER, 1, [Enable property debugging.]) fi # configure option to enable all debugging AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [Enable all debugging (default: disabled)]), [DEBUG_ALL=$enableval], [DEBUG_ALL=no]) if test "x$DEBUG_ALL" = xyes; then AC_DEFINE(DEBUG_GESTURES, 1, [Enable gesture debugging.]) AC_DEFINE(DEBUG_MTSTATE, 1, [Enable multitouch state debugging.]) AC_DEFINE(DEBUG_PROPS, 1, [Enable property debugging.]) AC_DEFINE(DEBUG_DRIVER, 1, [Enable driver debugging.]) fi # Everything else AC_PROG_CC AC_PROG_INSTALL AC_CONFIG_FILES([Makefile]) AC_OUTPUT ================================================ FILE: debian/README.Debian ================================================ Xorg Multitouch Trackpad Driver * Install the Debian package * Configure xorg.conf * Restart X ================================================ FILE: debian/changelog ================================================ xserver-xorg-input-mtrack (0.5.0) unstable; urgency=medium * Add tap-to-drag lock timeout * Make edge size configurable for each edge individually * Add edge scrolling * Fix movement truncation * Fix crash during suspend/wake up * Make movement speed resolution independent * Lock smooth scroll to one axis at time * Post button events before move events * New emulated buttons implementation * Reduce max recognized fingers/touch points from 32 to 16 * New fancy readme with anchors * Fix warnings, improve logging * New examples * Helper script to parse output of xinput --list-props -- Paweł Turkowski Sun, 28 Jan 2018 20:02:28 +0100 xserver-xorg-input-mtrack (0.4.1) unstable; urgency=low * Smooth scroll: detect and handle flipped up&down, left&right buttons * Add examples with my current configuration as first example. * Proper initialization of scroll axes(2 and 3). * Setup scroll valuators also during device initialization. * Do not consider edge clicks when calculating emulated buttons * Change type of variables holding movement delta to double for better precision. * Rewrite tap implementation from scratch. Remove 'status' field from Touch structure. * Clear the MT_BUTTON bit of touches when an integrated button is released. * Scale gesture reimplementation. -- Paweł Turkowski Wed, 02 Nov 2016 00:13:04 +0100 xserver-xorg-input-mtrack (0.4.0) unstable; urgency=low * Add smooth scroll * Add scroll/swipe/swipe4 to drag functionality * Add scroll coasting * Add scroll/swipe/swipe4 click time * Add Hold And Move gesture * Replace EdgeSize with BottomEdge property * Replace busy waiting with timers * Fix button down -> up delays * Initial support for absolute mode devices * Improve support for pressure based devices * Many minor improvements/bugfixes -- Paweł Turkowski Mon, 28 Sep 2015 01:20:38 +0200 xserver-xorg-input-mtrack (0.2.0) unstable; urgency=low * Now built via autoconf/automake. * Fully configurable via XInput. * Sensitivify now configurable. * Trackpad can now be disabled on keystroke via an external daemon. * Four finger tapping/swiping added. * Button zones added, a new button emulation method. * Tap-to-drag fixed. * Three finger tapping responsiveness fixed. -- Ryan Bourgeois Thu, 26 May 2011 09:44:43 -0500 xserver-xorg-input-mtrack (0.1.1) unstable; urgency=low * Bugfix release. * Disabling tapping no longer (wrongly) disables pointer movement. * Button emulation set to ignore "old" touches. What consitutes an old touch can be adjusted with the ButtonTouchExpire option. -- Ryan Bourgeois Thu, 28 Apr 2011 01:14:58 -0500 xserver-xorg-input-mtrack (0.1.0) unstable; urgency=low * Initial release. -- Ryan Bourgeois Fri, 15 Apr 2011 14:46:10 -0500 ================================================ FILE: debian/compat ================================================ 5 ================================================ FILE: debian/control ================================================ Source: xserver-xorg-input-mtrack Section: misc Priority: optional Maintainer: Paweł Turkowski Build-Depends: debhelper (>= 5), xserver-xorg-dev (>= 2:1.7.6), libmtdev-dev (>= 1.0.10) Standards-Version: 3.7.2 Homepage: https://github.com/p2rkw/xf86-input-mtrack Package: xserver-xorg-input-mtrack Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libmtdev1 (>= 1.0) Description: Xorg Multitouch Trackpad Driver This X input driver provides gestures support for multitouch touchpads, with or without an integrated button. ================================================ FILE: debian/copyright ================================================ Source URL: https://github.com/p2rkw/xf86-input-mtrack Upstream Authors: Ryan Bourgeois Paweł Turkowski Copyright: Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) Copyright (C) 2011 Ryan Bourgeois (bluedragonx@gmail.com) Copyright (C) 2018 Paweł Turkowski (p2rkw0@gmail.com) 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 Copyright (C) 2011 by Ryan Bourgeois Copyright (C) 2018 by Paweł Turkowski released under GPL 2 ================================================ FILE: debian/dirs ================================================ ================================================ FILE: debian/rules ================================================ #!/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/xserver-xorg-input-mtrack 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 ================================================ FILE: driver/mprops.c ================================================ /*************************************************************************** * * 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 "mprops.h" #include "common.h" #include "mtouch.h" #ifdef DEBUG_PROPS # define LOG_DEBUG_PROPS LOG_DEBUG #else # define LOG_DEBUG_PROPS LOG_DISABLED #endif #define MAX_INT_VALUES 7 #define MAX_FLOAT_VALUES 4 #define MAX_BUTTON_VALUES 6 #define VALID_BUTTON(x) (x >= 0 && x <= 32) #define VALID_BOOL(x) (x == 0 || x == 1) #define VALID_PCNT(x) (x >= 0 && x <= 100) struct MProps mprops; Atom atom_init_integer(DeviceIntPtr dev, char* name, int nvalues, int* values, int size) { Atom atom; int i; uint8_t uvals8[MAX_INT_VALUES]; uint16_t uvals16[MAX_INT_VALUES]; uint32_t uvals32[MAX_INT_VALUES]; pointer uvals; nvalues = MINVAL(MAX_INT_VALUES, nvalues); switch(size) { case 8: for (i = 0; i < nvalues; i++) { uvals8[i] = values[i]; } uvals = uvals8; break; case 16: for (i = 0; i < nvalues; i++) { uvals16[i] = values[i]; } uvals = uvals16; break; default: for (i = 0; i < nvalues; i++) { uvals32[i] = values[i]; } uvals = uvals32; break; } atom = MakeAtom(name, strlen(name), TRUE); XIChangeDeviceProperty(dev, atom, XA_INTEGER, size, PropModeReplace, nvalues, uvals, FALSE); XISetDevicePropertyDeletable(dev, atom, FALSE); return atom; } static Atom atom_init_integers32(DeviceIntPtr dev, char* name, int nvalues, int* values) { int size = 32; Atom atom; atom = MakeAtom(name, strlen(name), TRUE); XIChangeDeviceProperty(dev, atom, XA_INTEGER, size, PropModeReplace, nvalues, values, FALSE); XISetDevicePropertyDeletable(dev, atom, FALSE); return atom; } Atom atom_init_float(DeviceIntPtr dev, char* name, int nvalues, float* values, Atom float_type) { Atom atom = MakeAtom(name, strlen(name), TRUE); XIChangeDeviceProperty(dev, atom, float_type, 32, PropModeReplace, nvalues, values, FALSE); XISetDevicePropertyDeletable(dev, atom, FALSE); return atom; } static void init_swipe_props(DeviceIntPtr dev, struct MPropsSwipe* props_swipe, struct MConfigSwipe* cfg_swipe, char const* settings_prop_name, char const* buttons_prop_name){ int ivals[MAX_INT_VALUES]; ivals[0] = cfg_swipe->dist; ivals[1] = cfg_swipe->hold; ivals[2] = cfg_swipe->drag_sens; props_swipe->settings = atom_init_integer(dev, (char*)settings_prop_name, 3, ivals, 32); ivals[0] = cfg_swipe->up_btn; ivals[1] = cfg_swipe->dn_btn; ivals[2] = cfg_swipe->lt_btn; ivals[3] = cfg_swipe->rt_btn; props_swipe->buttons = atom_init_integer(dev, (char*)buttons_prop_name, 4, ivals, 8); } static void init_edge_props(DeviceIntPtr dev, Atom* props_edge, struct MConfigSwipe* cfg_edge, char const* settings_prop_name){ static const int nvalues = 7; int ivals[nvalues]; ivals[0] = cfg_edge->dist; ivals[1] = cfg_edge->hold; ivals[2] = cfg_edge->drag_sens; ivals[3] = cfg_edge->up_btn; ivals[4] = cfg_edge->dn_btn; ivals[5] = cfg_edge->lt_btn; ivals[6] = cfg_edge->rt_btn; *props_edge = atom_init_integers32(dev, (char*)settings_prop_name, nvalues, ivals); } void mprops_init(struct MConfig* cfg, InputInfoPtr local) { int ivals[MAX_INT_VALUES]; float fvals[MAX_FLOAT_VALUES]; mprops.float_type = XIGetKnownProperty(XATOM_FLOAT); if (!mprops.float_type) { mprops.float_type = MakeAtom(XATOM_FLOAT, strlen(XATOM_FLOAT), TRUE); if (!mprops.float_type) { LOG_ERROR("%s: Failed to init float atom. Property support is disabled.\n", local->name); return; } } ivals[0] = cfg->trackpad_disable; mprops.trackpad_disable = atom_init_integer(local->dev, MTRACK_PROP_TRACKPAD_DISABLE, 1, ivals, 8); fvals[0] = (float)cfg->sensitivity; mprops.sensitivity = atom_init_float(local->dev, MTRACK_PROP_SENSITIVITY, 1, fvals, mprops.float_type); ivals[0] = cfg->touch_down; ivals[1] = cfg->touch_up; mprops.pressure = atom_init_integer(local->dev, MTRACK_PROP_PRESSURE, 2, ivals, 8); ivals[0] = cfg->button_enable; ivals[1] = cfg->button_integrated; mprops.button_settings = atom_init_integer(local->dev, MTRACK_PROP_BUTTON_SETTINGS, 2, ivals, 8); ivals[0] = cfg->button_zones; ivals[1] = cfg->button_move; ivals[2] = cfg->button_expire; ivals[3] = cfg->is_button_zones_height_limited; mprops.button_emulate_settings = atom_init_integer(local->dev, MTRACK_PROP_BUTTON_EMULATE_SETTINGS, 4, ivals, 16); ivals[0] = cfg->button_0touch; ivals[1] = cfg->button_1touch; ivals[2] = cfg->button_2touch; ivals[3] = cfg->button_3touch; ivals[4] = cfg->button_first_zone; ivals[5] = cfg->button_second_zone; ivals[6] = cfg->button_third_zone; mprops.button_emulate_values = atom_init_integer(local->dev, MTRACK_PROP_BUTTON_EMULATE_VALUES, 7, ivals, 8); ivals[0] = cfg->tap_hold; ivals[1] = cfg->tap_timeout; ivals[2] = cfg->tap_dist; mprops.tap_settings = atom_init_integer(local->dev, MTRACK_PROP_TAP_SETTINGS, 3, ivals, 32); ivals[0] = cfg->tap_1touch; ivals[1] = cfg->tap_2touch; ivals[2] = cfg->tap_3touch; ivals[3] = cfg->tap_4touch; mprops.tap_emulate = atom_init_integer(local->dev, MTRACK_PROP_TAP_EMULATE, 4, ivals, 8); ivals[0] = cfg->ignore_thumb; ivals[1] = cfg->disable_on_thumb; mprops.thumb_detect = atom_init_integer(local->dev, MTRACK_PROP_THUMB_DETECT, 2, ivals, 8); ivals[0] = cfg->thumb_size; ivals[1] = cfg->thumb_ratio; mprops.thumb_size = atom_init_integer(local->dev, MTRACK_PROP_THUMB_SIZE, 2, ivals, 32); ivals[0] = cfg->ignore_palm; ivals[1] = cfg->disable_on_palm; mprops.palm_detect = atom_init_integer(local->dev, MTRACK_PROP_PALM_DETECT, 2, ivals, 8); ivals[0] = cfg->palm_size; mprops.palm_size = atom_init_integer(local->dev, MTRACK_PROP_PALM_SIZE, 1, ivals, 32); ivals[0] = cfg->gesture_hold; ivals[1] = cfg->gesture_wait; mprops.gesture_settings = atom_init_integer(local->dev, MTRACK_PROP_GESTURE_SETTINGS, 2, ivals, 16); ivals[0] = cfg->scroll_smooth; mprops.scroll_smooth = atom_init_integer(local->dev, MTRACK_PROP_SMOOTH_SCROLL, 1, ivals, 8); init_swipe_props(local->dev, &mprops.scroll, &cfg->scroll, MTRACK_PROP_SCROLL_SETTINGS, MTRACK_PROP_SCROLL_BUTTONS); init_swipe_props(local->dev, &mprops.swipe3, &cfg->swipe3, MTRACK_PROP_SWIPE_SETTINGS, MTRACK_PROP_SWIPE_BUTTONS); init_swipe_props(local->dev, &mprops.swipe4, &cfg->swipe4, MTRACK_PROP_SWIPE4_SETTINGS, MTRACK_PROP_SWIPE4_BUTTONS); fvals[0] = cfg->scroll_coast.min_speed; fvals[1] = cfg->scroll_coast.duration; /* = duration in miliseconds */ mprops.scroll_coast = atom_init_float(local->dev, MTRACK_PROP_SCROLL_COAST, 2, fvals, mprops.float_type); init_edge_props(local->dev, &mprops.edge_scroll, &cfg->edge_scroll, MTRACK_PROP_EDGE_SCROLL_SETTINGS); ivals[0] = cfg->edge_top_size; ivals[1] = cfg->edge_bottom_size; ivals[2] = cfg->edge_left_size; ivals[3] = cfg->edge_right_size; mprops.edge_sizes = atom_init_integer(local->dev, MTRACK_PROP_EDGE_SIZES, 4, ivals, 8); ivals[0] = cfg->scale_dist; mprops.scale_dist = atom_init_integer(local->dev, MTRACK_PROP_SCALE_DIST, 1, ivals, 32); ivals[0] = cfg->scale_up_btn; ivals[1] = cfg->scale_dn_btn; mprops.scale_buttons = atom_init_integer(local->dev, MTRACK_PROP_SCALE_BUTTONS, 2, ivals, 8); ivals[0] = cfg->rotate_dist; mprops.rotate_dist = atom_init_integer(local->dev, MTRACK_PROP_ROTATE_DIST, 1, ivals, 32); ivals[0] = cfg->rotate_lt_btn; ivals[1] = cfg->rotate_rt_btn; mprops.rotate_buttons = atom_init_integer(local->dev, MTRACK_PROP_ROTATE_BUTTONS, 2, ivals, 8); ivals[0] = cfg->hold1_move1_stationary.max_move; ivals[1] = cfg->hold1_move1_stationary.button; mprops.hold1_move1_stationary = atom_init_integer(local->dev, MTRACK_PROP_HOLD1_MOVE1_STATIONARY_SETTINGS, 2, ivals, 32); init_swipe_props(local->dev, &mprops.hold1_move1, &cfg->hold1_move1, MTRACK_PROP_HOLD1_MOVE1_SETTINGS, MTRACK_PROP_HOLD1_MOVE1_BUTTONS); #if 0 ivals[0] = cfg->hold1_move2_stationary.max_move; ivals[1] = cfg->hold1_move2_stationary.button; mprops.hold1_move2_stationary = atom_init_integer(local->dev, MTRACK_PROP_HOLD1_MOVE2_STATIONARY_SETTINGS, 2, ivals, 32); init_swipe_props(local->dev, &mprops.hold1_move2, &cfg->hold1_move2, MTRACK_PROP_HOLD1_MOVE2_SETTINGS, MTRACK_PROP_HOLD1_MOVE2_BUTTONS); ivals[0] = cfg->hold1_move3_stationary.max_move; ivals[1] = cfg->hold1_move3_stationary.button; mprops.hold1_move3_stationary = atom_init_integer(local->dev, MTRACK_PROP_HOLD1_MOVE3_STATIONARY_SETTINGS, 2, ivals, 32); init_swipe_props(local->dev, &mprops.hold1_move3, &cfg->hold1_move3, MTRACK_PROP_HOLD1_MOVE3_SETTINGS, MTRACK_PROP_HOLD1_MOVE3_BUTTONS); #endif ivals[0] = cfg->drag_enable; ivals[1] = cfg->drag_timeout; ivals[2] = cfg->drag_wait; ivals[3] = cfg->drag_dist; ivals[4] = cfg->drag_lock_timeout; mprops.drag_settings = atom_init_integer(local->dev, MTRACK_PROP_DRAG_SETTINGS, 5, ivals, 32); ivals[0] = cfg->axis_x_invert; ivals[1] = cfg->axis_y_invert; mprops.axis_invert = atom_init_integer(local->dev, MTRACK_PROP_AXIS_INVERT, 2, ivals, 8); } int check_buttons_property(XIPropertyValuePtr prop, uint8_t** buttons_ret_arr, int buttons_count) { uint8_t* ivals8; int i; if (prop->size != buttons_count || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; for (i = 0; i < buttons_count; ++i) { if (!VALID_BUTTON(ivals8[i])) return BadMatch; } *buttons_ret_arr = ivals8; return Success; } /* Return: * 1 - property was recognized and handled with or without error, check error code for details * 0 - property not recognized, don't trust returned error code - it's invalid */ static int set_swipe_properties(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly, struct MPropsSwipe* props_swipe, struct MConfigSwipe* cfg_swipe, int* error_code){ uint8_t* ivals8; uint32_t* ivals32; *error_code = Success; if (property == props_swipe->settings) { if (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER) return *error_code = BadMatch, 1; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 1 || (int)(ivals32[1]) < 0) return *error_code = BadMatch, 1; if (!checkonly) { cfg_swipe->dist = ivals32[0]; cfg_swipe->hold = ivals32[1]; cfg_swipe->drag_sens = ivals32[2]; LOG_DEBUG_PROPS("set swipe settings: dist: %d hold: %d\n", cfg_swipe->dist, cfg_swipe->hold); } } else if (property == props_swipe->buttons) { if (check_buttons_property(prop, &ivals8, 4) == Success){ if (!checkonly) { cfg_swipe->up_btn = ivals8[0]; cfg_swipe->dn_btn = ivals8[1]; cfg_swipe->lt_btn = ivals8[2]; cfg_swipe->rt_btn = ivals8[3]; } } else return *error_code = BadMatch, 1; } else{ return 0; } return 1; } int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly) { InputInfoPtr local = dev->public.devicePrivate; struct MConfig* cfg = &((struct MTouch*)local->private)->cfg; uint8_t* ivals8; uint16_t* ivals16; uint32_t* ivals32; float* fvals; int error_code; if (property == mprops.trackpad_disable) { if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (ivals8[0] < 0 || ivals8[0] > 3) return BadMatch; if (!checkonly) { cfg->trackpad_disable = ivals8[0]; if (cfg->trackpad_disable) LOG_DEBUG_PROPS("trackpad input disabled\n"); else LOG_DEBUG_PROPS("trackpad input enabled\n"); } } else if (property == mprops.sensitivity) { if (prop->size != 1 || prop->format != 32 || prop->type != mprops.float_type) return BadMatch; fvals = (float*)prop->data; if (fvals[0] < 0) return BadMatch; if (!checkonly) { cfg->sensitivity = fvals[0]; LOG_DEBUG_PROPS("set sensitivity to %f\n", cfg->sensitivity); } } else if (property == mprops.pressure) { if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_PCNT(ivals8[0]) || !VALID_PCNT(ivals8[1])) return BadMatch; if (!checkonly) { cfg->touch_down = ivals8[0]; cfg->touch_up = ivals8[1]; LOG_DEBUG_PROPS("set touch pressure to %d %d\n", cfg->touch_down, cfg->touch_up); } } else if (property == mprops.button_settings) { if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1])) return BadMatch; if (!checkonly) { cfg->button_enable = ivals8[0]; cfg->button_integrated = ivals8[1]; LOG_DEBUG_PROPS("set button settings to %d %d\n", cfg->button_enable, cfg->button_integrated); } } else if (property == mprops.button_emulate_settings) { if (prop->size != 3 || prop->format != 16 || prop->type != XA_INTEGER) return BadMatch; ivals16 = (uint16_t*)prop->data; if (!VALID_BOOL(ivals16[0]) || !VALID_BOOL(ivals16[1]) || ivals16[2] < 0) return BadMatch; if (!checkonly) { cfg->button_zones = ivals16[0]; cfg->button_move = ivals16[1]; cfg->button_expire = ivals16[2]; LOG_DEBUG_PROPS("set button emulate settings to %d %d %d\n", cfg->button_zones, cfg->button_move, cfg->button_expire); } } else if (property == mprops.button_emulate_values) { if (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3])) return BadMatch; if (!checkonly) { cfg->button_0touch = ivals8[0]; cfg->button_1touch = ivals8[1]; cfg->button_2touch = ivals8[2]; cfg->button_3touch = ivals8[3]; LOG_DEBUG_PROPS("set button emulation to %d %d %d %d\n", cfg->button_0touch, cfg->button_1touch, cfg->button_2touch, cfg->button_3touch); } } else if (property == mprops.tap_settings) { if (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 1 || ivals32[1] < 1 || ivals32[2] < 1) return BadMatch; if (!checkonly) { cfg->tap_hold = ivals32[0]; cfg->tap_timeout = ivals32[1]; cfg->tap_dist = ivals32[2]; LOG_DEBUG_PROPS("set tap settings to %d %d %d\n", cfg->tap_hold, cfg->tap_timeout, cfg->tap_dist); } } else if (property == mprops.tap_emulate) { if (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3])) return BadMatch; if (!checkonly) { cfg->tap_1touch = ivals8[0]; cfg->tap_2touch = ivals8[1]; cfg->tap_3touch = ivals8[2]; cfg->tap_4touch = ivals8[3]; LOG_DEBUG_PROPS("set tap emulation to %d %d %d %d\n", cfg->tap_1touch, cfg->tap_2touch, cfg->tap_3touch, cfg->tap_4touch); } } else if (property == mprops.thumb_detect) { if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1])) return BadMatch; if (!checkonly) { cfg->ignore_thumb = ivals8[0]; cfg->disable_on_thumb = ivals8[1]; LOG_DEBUG_PROPS("set thumb detect to %d %d\n", cfg->ignore_thumb, cfg->disable_on_thumb); } } else if (property == mprops.thumb_size) { if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 0 || !VALID_PCNT(ivals32[1])) return BadMatch; if (!checkonly) { cfg->thumb_size = ivals32[0]; cfg->thumb_ratio = ivals32[0]; LOG_DEBUG_PROPS("set thumb size to %d %d\n", cfg->thumb_size, cfg->thumb_ratio); } } else if (property == mprops.palm_detect) { if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1])) return BadMatch; if (!checkonly) { cfg->ignore_palm = ivals8[0]; cfg->disable_on_palm = ivals8[1]; LOG_DEBUG_PROPS("set palm detect to %d %d\n", cfg->ignore_palm, cfg->disable_on_palm); } } else if (property == mprops.palm_size) { if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 0) return BadMatch; if (!checkonly) { cfg->palm_size = ivals32[0]; LOG_DEBUG_PROPS("set palm size to %d\n", cfg->palm_size); } } else if (property == mprops.gesture_settings) { if (prop->size != 2 || prop->format != 16 || prop->type != XA_INTEGER) return BadMatch; ivals16 = (uint16_t*)prop->data; if (ivals16[0] < 1 || ivals16[1] < 0) return BadMatch; if (!checkonly) { cfg->gesture_hold = ivals16[0]; cfg->gesture_wait = ivals16[1]; LOG_DEBUG_PROPS("set gesture settings to %d %d\n", cfg->gesture_hold, cfg->gesture_wait); } } else if (property == mprops.scroll_smooth) { if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BOOL(ivals8[0])) return BadMatch; if (!checkonly) { cfg->scroll_smooth = ivals8[0]; LOG_DEBUG_PROPS("set high precision scrolling to %d\n", cfg->scroll_smooth); } } else if (set_swipe_properties(dev, property, prop, checkonly, &mprops.scroll, &cfg->scroll, &error_code)) { return error_code; } else if (property == mprops.scroll_coast) { if (prop->size != 2 || prop->type != mprops.float_type) return BadMatch; fvals = (float*)prop->data; if (fvals[0] < 0.0f || fvals[1] < 0.0f) return BadMatch; if (!checkonly) { cfg->scroll_coast.min_speed = fvals[0]; cfg->scroll_coast.duration = fvals[1]; LOG_DEBUG_PROPS("set scroll coasting to %f %f\n", cfg->scroll_coast.min_speed, cfg->scroll_coast.duration); } } else if (property == mprops.edge_scroll) { if (prop->size != 7 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (!checkonly) { cfg->edge_scroll.dist = ivals32[0]; cfg->edge_scroll.hold = ivals32[1]; cfg->edge_scroll.drag_sens = ivals32[2]; cfg->edge_scroll.up_btn = ivals32[3]; cfg->edge_scroll.dn_btn = ivals32[4]; cfg->edge_scroll.lt_btn = ivals32[5]; cfg->edge_scroll.rt_btn = ivals32[6]; } } else if (property == mprops.scale_dist) { if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 1) return BadMatch; if (!checkonly) { cfg->scale_dist = ivals32[0]; LOG_DEBUG_PROPS("set scale distance to %d\n", cfg->scale_dist); } } else if (set_swipe_properties(dev, property, prop, checkonly, &mprops.swipe3, &cfg->swipe3, &error_code)) { return error_code; } else if (set_swipe_properties(dev, property, prop, checkonly, &mprops.swipe4, &cfg->swipe4, &error_code)) { return error_code; } else if (property == mprops.scale_buttons) { if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1])) return BadMatch; if (!checkonly) { cfg->scale_up_btn = ivals8[0]; cfg->scale_dn_btn = ivals8[1]; LOG_DEBUG_PROPS("set scale buttons to %d %d\n", cfg->scale_up_btn, cfg->scale_dn_btn); } } else if (property == mprops.rotate_dist) { if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 1) return BadMatch; if (!checkonly) { cfg->rotate_dist = ivals32[0]; LOG_DEBUG_PROPS("set rotate distance to %d\n", cfg->rotate_dist); } } else if (property == mprops.rotate_buttons) { if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1])) return BadMatch; if (!checkonly) { cfg->rotate_lt_btn = ivals8[0]; cfg->rotate_rt_btn = ivals8[1]; LOG_DEBUG_PROPS("set rotate buttons to %d %d\n", cfg->rotate_lt_btn, cfg->rotate_rt_btn); } } else if (property == mprops.hold1_move1_stationary){ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 0|| !VALID_BUTTON(ivals32[1])) return BadMatch; if (!checkonly) { cfg->hold1_move1_stationary.max_move = ivals32[0]; cfg->hold1_move1_stationary.button = ivals32[1]; LOG_DEBUG_PROPS("hold1_move1: max_move %d; button: %d\n", cfg->hold1_move1_stationary.max_move, cfg->hold1_move1_stationary.button); } } else if (set_swipe_properties(dev, property, prop, checkonly, &mprops.hold1_move1, &cfg->hold1_move1, &error_code)) { return error_code; } #if 0 else if (property == mprops.hold1_move2_stationary){ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 0|| !VALID_BUTTON(ivals32[1])) return BadMatch; if (!checkonly) { cfg->hold1_move2_stationary.max_move = ivals32[0]; cfg->hold1_move2_stationary.button = ivals32[1]; LOG_DEBUG_PROPS("hold1_move2: max_move %d; button: %d\n", cfg->hold1_move2_stationary.max_move, cfg->hold1_move2_stationary.button); } } else if (set_swipe_properties(dev, property, prop, checkonly, &mprops.hold1_move2, &cfg->hold1_move2, &error_code)) { return error_code; } else if (property == mprops.hold1_move3_stationary){ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (ivals32[0] < 0|| !VALID_BUTTON(ivals32[1])) return BadMatch; if (!checkonly) { cfg->hold1_move3_stationary.max_move = ivals32[0]; cfg->hold1_move3_stationary.button = ivals32[1]; LOG_DEBUG_PROPS("hold1_move1: max_move %d; button: %d\n", cfg->hold1_move3_stationary.max_move, cfg->hold1_move3_stationary.button); } } else if (set_swipe_properties(dev, property, prop, checkonly, &mprops.hold1_move3, &cfg->hold1_move3, &error_code)) { return error_code; } #endif else if (property == mprops.drag_settings) { if (prop->size != 5 || prop->format != 32 || prop->type != XA_INTEGER) return BadMatch; ivals32 = (uint32_t*)prop->data; if (!VALID_BOOL(ivals32[0]) || ivals32[1] < 1 || (int)(ivals32[2]) < 0 || (int)(ivals32[3]) < 0) return BadMatch; if (!checkonly) { cfg->drag_enable = ivals32[0]; cfg->drag_timeout = ivals32[1]; cfg->drag_wait = ivals32[2]; cfg->drag_dist = ivals32[3]; cfg->drag_lock_timeout = ivals32[4]; LOG_DEBUG_PROPS("set drag settings to %d %d %d %d %d\n", cfg->drag_enable, cfg->drag_timeout, cfg->drag_wait, cfg->drag_dist, cfg->drag_lock_timeout); } } else if (property == mprops.axis_invert) { if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1])) return BadMatch; if (!checkonly) { cfg->axis_x_invert = ivals8[0]; cfg->axis_y_invert = ivals8[1]; LOG_DEBUG_PROPS("set axis inversion to %d %d\n", cfg->axis_x_invert, cfg->axis_y_invert); } } else if (property == mprops.edge_sizes) { if (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; if (!VALID_PCNT(ivals8[0]) || !VALID_PCNT(ivals8[1]) || !VALID_PCNT(ivals8[2]) || !VALID_PCNT(ivals8[3])) return BadMatch; if (!checkonly) { cfg->edge_top_size = ivals8[0]; cfg->edge_bottom_size = ivals8[1]; cfg->edge_left_size = ivals8[2]; cfg->edge_right_size = ivals8[3]; LOG_DEBUG_PROPS("set edge sizes to %d %d %d %d\n", cfg->edge_left_size, cfg->edge_right_size, cfg->edge_top_size, cfg->edge_bottom_size); } } return Success; } ================================================ FILE: driver/mtrack.c ================================================ /*************************************************************************** * * Multitouch X driver * Copyright (C) 2008 Henrik Rydberg * Copyright (C) 2011 Ryan Bourgeois * Copyright (C) 2015-2018 Paweł Turkowski * * 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" #include "mprops.h" #include "capabilities.h" #include "os.h" #include "trig.h" /* xorg/os.h for timers */ #include #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 #include #include #endif #include /* cos */ #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 #define PI 3.14159265 #define NUM_AXES 4 #ifdef DEBUG_DRIVER # define LOG_DEBUG_DRIVER LOG_DEBUG #else # define LOG_DEBUG_DRIVER LOG_DISABLED #endif #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12 typedef InputInfoPtr LocalDevicePtr; #endif /* button mapping simplified */ #define PROPMAP(m, x, y) m[x] = XIGetKnownProperty(y) static void pointer_control(DeviceIntPtr dev, PtrCtrl *ctrl) { LOG_DEBUG_DRIVER("pointer_control\n"); } #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 static void init_axes_labels(Atom map[NUM_AXES]) { memset(map, 0, NUM_AXES * sizeof(Atom)); PROPMAP(map, 0, AXIS_LABEL_PROP_REL_X); PROPMAP(map, 1, AXIS_LABEL_PROP_REL_Y); PROPMAP(map, 2, AXIS_LABEL_PROP_REL_VSCROLL); PROPMAP(map, 3, AXIS_LABEL_PROP_REL_HSCROLL); } static void init_button_labels(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 /** * How to handle multitouch: http://www.x.org/wiki/Development/Documentation/Multitouch/ * Howto about xinput: http://www.x.org/wiki/Development/Documentation/XorgInputHOWTO/ * Example usage: https://gitlab.com/at-home-modifier/at-home-modifier-evdev/commit/d171b3d9194581cb6ed59dbe45d6cbf009dc0eaa?view=parallel * Patch were smooth scrolling were introduced: https://lists.x.org/archives/xorg-devel/2011-September/025835.html * @param dev * @param axnum * @param label * @param min * @param max * @param resolution */ static void init_axle_absolute(DeviceIntPtr dev, int axnum, Atom* label) { /* Inform server about reported range of axis values. */ xf86InitValuatorAxisStruct(dev, axnum, #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 *label, #endif /* minval, maxval */ NO_AXIS_LIMITS, NO_AXIS_LIMITS, /*resolution*/ 1, /*min res*/ 0, /*max res*/ 1 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12 /* mode */, Absolute #endif ); xf86InitValuatorDefaults(dev, axnum); } static void init_axle_relative(DeviceIntPtr dev, int axnum, Atom* label) { xf86InitValuatorAxisStruct(dev, axnum, *label, NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative); xf86InitValuatorDefaults(dev, axnum); } 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[NUM_AXES], btn_labels[DIM_BUTTON]; init_axes_labels(axes_labels); init_button_labels(btn_labels); #endif local->fd = xf86OpenSerial(local->options); if (local->fd < 0) { LOG_ERROR("cannot open device %s\n", local->name); return !Success; } if (mtouch_configure(mt, local->fd)) { LOG_ERROR("cannot configure device %s\n", local->name); 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(), NUM_AXES, axes_labels); #else #error "Unsupported ABI_XINPUT_VERSION" #endif init_axle_absolute(dev, 0, &axes_labels[0]); init_axle_absolute(dev, 1, &axes_labels[1]); init_axle_relative(dev, 2, &axes_labels[2]); init_axle_relative(dev, 3, &axes_labels[3]); /* Always set valuator distance to 1.0 because it reported values will be * adjusted accordingly by smooth scroll trigger. */ SetScrollValuator(dev, 2, SCROLL_TYPE_VERTICAL, 1.0, SCROLL_FLAG_PREFERRED); SetScrollValuator(dev, 3, SCROLL_TYPE_HORIZONTAL, 1.0, SCROLL_FLAG_NONE); mprops_init(&mt->cfg, local); XIRegisterPropertyHandler(dev, mprops_set_property, NULL, NULL); mt->timer = NULL; /* allocated later */ mt->timer_kind = 0; mt->absolute_mode = FALSE; return Success; } static int device_on(LocalDevicePtr local) { struct MTouch *mt = local->private; local->fd = xf86OpenSerial(local->options); if (local->fd < 0) { LOG_ERROR("cannot open device\n"); return !Success; } if (mtouch_open(mt, local->fd)) { LOG_ERROR("cannot grab device\n"); return !Success; } /* * xf86AddEnabledDevice() will add our device's fd to the list of SIGIO handlers. * When a SIGIO occurs, our read_input will get called. */ xf86AddEnabledDevice(local); if(mt->timer != NULL) TimerFree(mt->timer); // release any existing timer mt->timer = NULL; mt->timer_kind = 0; return Success; } static int device_off(LocalDevicePtr local) { struct MTouch *mt = local->private; xf86RemoveEnabledDevice(local); if (mtouch_close(mt)) LOG_WARNING("cannot ungrab device\n"); xf86CloseSerial(local->fd); if(mt->timer != NULL) TimerFree(mt->timer); // release any existing timer mt->timer = NULL; mt->timer_kind = 0; return Success; } static int device_close(LocalDevicePtr local) { return Success; } static void post_button(struct MTouch* mt, int button, int new_state); void mt_timer_start(struct MTouch *mt, int kind) { struct Gestures* gs = &mt->gs; mstime_t timeout; /*< Set this variable to required timeout. */ if(kind == mt->timer_kind){ LOG_DEBUG_DRIVER("Timer %i already started\n", kind); return; } mt_timer_stop(mt); switch(kind){ case MT_TIMER_DELAYED_BUTTON: { struct timeval delta; timersub(&gs->button_delayed_time, &gs->time, &delta); timeout = timertoms(&delta); break; } case MT_TIMER_COASTING: gs->move_dx = gs->move_dy = 0.0; gs->move_type = GS_NONE; gs->coasting_duration_left = mt->cfg.scroll_coast.duration - 1; timeout = mt->cfg.scroll_coast.tick_ms; break; case MT_TIMER_ANY: case MT_TIMER_NONE: return; /* do nothing */ default: LOG_INFO("Unimplemented timer, ID: %i\n", kind); return; } LOG_DEBUG_DRIVER("Start timer, ID: %i with timeout: %llu\n", kind, timeout); mt->timer = TimerSet(mt->timer, 0, timeout, mt_timer_callback, mt); mt->timer_kind = kind; } void mt_timer_stop(struct MTouch *mt) { struct Gestures* gs = &mt->gs; LOG_DEBUG_DRIVER("Stop timer, ID: %i\n", mt->timer_kind); TimerCancel(mt->timer); switch(mt->timer_kind){ case MT_TIMER_DELAYED_BUTTON: { int button; button = trigger_delayed_button_uncond(gs); post_button(mt, button, GETBIT(gs->buttons, button)); gs->move_dx = gs->move_dy = 0.0; gs->move_type = GS_NONE; break; } case MT_TIMER_COASTING: mt->gs.scroll_speed_x = mt->gs.scroll_speed_y = 0.0; break; case MT_TIMER_ANY: case MT_TIMER_NONE: break; /* do nothing */ default: LOG_INFO("Unimplemented timer, ID: %i\n", mt->timer_kind); break; } mt->timer_kind = MT_TIMER_NONE; } CARD32 mt_timer_callback(OsTimerPtr timer, CARD32 time, void *arg) { struct MTouch *mt = arg; struct Gestures* gs = &mt->gs; ValuatorMask* mask = mt->valuator_mask; switch(mt->timer_kind){ case MT_TIMER_DELAYED_BUTTON: { int button; button = trigger_delayed_button_uncond(gs); post_button(mt, button, GETBIT(gs->buttons, button)); gs->move_dx = gs->move_dy = 0.0; gs->move_type = GS_NONE; mt_timer_stop(mt); break; } case MT_TIMER_COASTING: { if (gs->coasting_duration_left <= 0) return mt_timer_stop(mt), 0; mstime_t delta_ms; double coasting_progress; valuator_mask_zero(mask); delta_ms = mt->cfg.scroll_coast.tick_ms; if (mt->cfg.scroll_coast.ease) { /* Calculate easing effect */ coasting_progress = 1 - gs->coasting_duration_left / (double)mt->cfg.scroll_coast.duration; delta_ms = delta_ms - (delta_ms * coasting_progress / 2); coasting_progress = (cos(PI * coasting_progress) + 1) / 2; } else coasting_progress = gs->coasting_duration_left / (double)mt->cfg.scroll_coast.duration; valuator_mask_set_double(mask, 2, gs->scroll_speed_y * coasting_progress * delta_ms); valuator_mask_set_double(mask, 3, gs->scroll_speed_x * coasting_progress * delta_ms); gs->coasting_duration_left -= delta_ms; xf86PostMotionEventM(mt->local_dev, Relative, mask); mt_timer_continue(mt, mt->cfg.scroll_coast.tick_ms); break; } case MT_TIMER_ANY: case MT_TIMER_NONE: return 0; /* do nothing */ default: LOG_INFO("Unimplemented timer id: %i\n", mt->timer_kind); break; } return 0; } void mt_timer_start_or_stop(struct MTouch *mt, int kind) { if(kind < 0) mt_timer_stop_if(mt, -kind); else if(kind > 0) mt_timer_start(mt, kind); } void mt_timer_stop_if(struct MTouch *mt, int kind) { if(mt->timer_kind == kind || kind == MT_TIMER_ANY || kind == -MT_TIMER_ANY) mt_timer_stop(mt); } void mt_timer_continue(struct MTouch *mt, mstime_t timeout) { LOG_DEBUG_DRIVER("Continue timer, ID: %i with timeout: %llu\n", mt->timer_kind, timeout); mt->timer = TimerSet(mt->timer, 0, timeout, mt_timer_callback, mt); } /** * @param mt * @return */ static int can_start_coasting(struct MTouch *mt) { struct Gestures* gs = &mt->gs; struct MTState* ms = &mt->state; int i; if(mt->cfg.scroll_smooth && mt->cfg.scroll_coast.duration > 0 && mt->cfg.scroll_coast.duration > 0 && hypot_cmpf(gs->scroll_speed_x, gs->scroll_speed_y, mt->cfg.scroll_coast.min_speed) == 1) { foreach_bit(i, ms->touch_used) { if (GETBIT(ms->touch[i].flags, MT_INVALID) || GETBIT(ms->touch[i].flags, MT_BUTTON) || GETBIT(ms->touch[i].flags, MT_TAP) ) continue; if(GETBIT(mt->state.touch[i].flags, MT_RELEASED)) return 1; } /* foreach */ } return 0; } static bitmask_t buttons_posted = 0U; static void post_gestures(struct MTouch *mt) { struct Gestures* gs = &mt->gs; int i; const double delta = timertoms(&gs->dt); if(mt->absolute_mode == FALSE){ if (mt->cfg.scroll_smooth){ /* Never post these buttons in smooth mode. */ CLEARBIT(gs->buttons, MT_BUTTON_WHEEL_UP); CLEARBIT(gs->buttons, MT_BUTTON_WHEEL_DOWN); CLEARBIT(gs->buttons, MT_BUTTON_HWHEEL_LEFT); CLEARBIT(gs->buttons, MT_BUTTON_HWHEEL_RIGHT); } } for (i = 0; i < 32; i++) { post_button(mt, i, GETBIT(gs->buttons, i)); } if(mt->absolute_mode == FALSE){ if (mt->cfg.scroll_smooth){ ValuatorMask* mask; mask = mt->valuator_mask; valuator_mask_zero(mask); if (gs->move_dx) valuator_mask_set_double(mask, 0, gs->move_dx); if (gs->move_dy) valuator_mask_set_double(mask, 1, gs->move_dy); gs->move_dx = gs->move_dy = 0.0; /* if is any swipe */ if(gs->move_type == GS_SWIPE2 || gs->move_type == GS_SWIPE3 || gs->move_type == GS_SWIPE4){ if(gs->scroll_speed_valid) { /* Delta encoded in scroll_speed. */ valuator_mask_set_double(mask, 2, gs->scroll_speed_y); valuator_mask_set_double(mask, 3, gs->scroll_speed_x); } gs->scroll_speed_valid = 0; /* Start coasting here if needed. */ if(delta != 0.0 && can_start_coasting(mt)){ /* Remove delta component from scroll_speed. */ gs->scroll_speed_x /= delta; gs->scroll_speed_y /= delta; mt_timer_start(mt, MT_TIMER_COASTING); } } xf86PostMotionEventM(mt->local_dev, Relative, mask); } /* if smooth scroll */ else{ // mt->absolute_mode == false if (gs->move_dx != 0.0 || gs->move_dy != 0.0) xf86PostMotionEvent(mt->local_dev, 0, 0, 2, (int)gs->move_dx, (int)gs->move_dy); } } else{ /* Give the HW coordinates to Xserver as absolute coordinates, these coordinates * are not scaled, this is oke if the touchscreen has the same resolution as the display. */ xf86PostMotionEvent(mt->local_dev, 1, 0, 2, mt->state.touch[0].x + get_cap_xmid(&mt->caps), mt->state.touch[0].y + get_cap_ymid(&mt->caps)); } } static void post_button(struct MTouch* mt, int button, int new_state) { struct Gestures* gs = &mt->gs; if (new_state == GETBIT(buttons_posted, button)) return; if (new_state) { xf86PostButtonEvent(mt->local_dev, FALSE, button+1, 1, 0, 0); LOG_DEBUG_DRIVER("button %d down\n", button+1); } else { xf86PostButtonEvent(mt->local_dev, FALSE, button+1, 0, 0, 0); LOG_DEBUG_DRIVER("button %d up\n", button+1); } MODBIT(gs->buttons, button, new_state); MODBIT(buttons_posted, button, new_state); } /* * Called for each full received packet from the touchpad. * Any xf86 input event generated by int this callback function fill be queued by * xorg server, and fired when control return from this function. * So to fire event as early as possible this function should return quickly. * For delayed events we can't simply wait in this function, because it will delay * all events generated by 'post_gestures'. * Moreover we don't know when next input event will occur, so to guarantee proper * timing I have to use timer. * * More on input event processing: * http://www.x.org/wiki/Development/Documentation/InputEventProcessing/ * * HowTo: * https://www.x.org/wiki/Development/Documentation/XorgInputHOWTO/ */ static void read_input(LocalDevicePtr local) { struct MTouch *mt = local->private; int timer_kind; mt->local_dev = local->dev; while (mtouch_read(mt) > 0){ timer_kind = mtouch_delayed(mt); mt_timer_start_or_stop(mt, timer_kind); post_gestures(mt); } } static int switch_mode(ClientPtr client, DeviceIntPtr dev, int mode) { LocalDevicePtr local = dev->public.devicePrivate; struct MTouch *mt = local->private; switch (mode) { case Absolute: mt->absolute_mode = TRUE; LOG_INFO("switch_mode: switing to absolute mode\n"); break; case Relative: mt->absolute_mode = FALSE; LOG_INFO("switch_mode: switing to relative mode\n"); break; default: return XI_BadMode; } return Success; } static Bool device_control(DeviceIntPtr dev, int mode) { LocalDevicePtr local = dev->public.devicePrivate; switch (mode) { case DEVICE_INIT: LOG_INFO("device control: init\n"); return device_init(dev, local); case DEVICE_ON: LOG_INFO("device control: on\n"); return device_on(local); case DEVICE_OFF: LOG_INFO("device control: off\n"); return device_off(local); case DEVICE_CLOSE: LOG_INFO("device control: close\n"); return device_close(local); default: LOG_INFO("device control: default\n"); return BadValue; } } #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12 static int preinit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) { struct MTouch *mt; mt = calloc(1, sizeof(*mt)); if (!mt) return BadAlloc; pInfo->private = mt; pInfo->type_name = XI_TOUCHPAD; pInfo->device_control = device_control; pInfo->read_input = read_input; pInfo->switch_mode = switch_mode; xf86CollectInputOptions(pInfo, NULL); xf86OptionListReport(pInfo->options); xf86ProcessCommonOptions(pInfo, pInfo->options); mconfig_configure(&mt->cfg, pInfo->options); // set the defaults mt->valuator_mask = valuator_mask_new(4); return Success; } #else 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->switch_mode = switch_mode; local->private = mt; local->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS; local->conf_idev = dev; xf86CollectInputOptions(local, NULL, NULL); xf86OptionListReport(local->options); xf86ProcessCommonOptions(local, local->options); mconfig_configure(&mt->cfg, local->options); mt->vm = NULL; local->flags |= XI86_CONFIGURED; error: return local; } #endif static void uninit(InputDriverPtr drv, InputInfoPtr local, int flags) { struct MTouch *mt = local->private; if (mt->valuator_mask) valuator_mask_free(&mt->valuator_mask); free(local->private); local->private = 0; xf86DeleteInput(local, 0); } /* About xorg drivers, modules: * http://www.x.org/wiki/Development/Documentation/XorgInputHOWTO/ */ static InputDriverRec MTRACK = { 1, "mtrack", NULL, preinit, uninit, NULL, 0 }; static XF86ModuleVersionInfo moduleVersion = { "mtrack", MODULEVENDORSTRING, MODINFOSTRING1, MODINFOSTRING2, XORG_VERSION_CURRENT, PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL, 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(&MTRACK, module, 0); return module; } _X_EXPORT XF86ModuleData mtrackModuleData = {&moduleVersion, &setup, NULL }; ================================================ FILE: examples/acer-aspire-s3.conf ================================================ Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" Driver "mtrack" Option "Sensitivity" "0.60" Option "FingerHigh" "5" Option "FingerLow" "1" Option "IgnoreThumb" "true" Option "ThumbRatio" "70" Option "ThumbSize" "25" Option "IgnorePalm" "true" Option "TapButton1" "1" Option "TapButton2" "3" Option "TapButton3" "0" Option "TapButton4" "0" Option "ClickFinger1" "3" Option "ClickFinger2" "3" Option "ClickFinger3" "3" Option "ButtonMoveEmulate" "false" Option "ButtonIntegrated" "true" Option "ClickTime" "25" Option "SwipeLeftButton" "8" Option "SwipeRightButton" "9" Option "SwipeUpButton" "0" Option "SwipeDownButton" "0" Option "SwipeDistance" "700" Option "ScrollCoastDuration" "500" Option "ScrollCoastEnableSpeed" ".8" Option "ScrollUpButton" "4" Option "ScrollDownButton" "5" Option "ScrollLeftButton" "6" Option "ScrollRightButton" "7" Option "ScrollDistance" "75" Option "ScrollSmooth" "1" Option "ScrollSensitivity" "0" Option "ScrollClickTime" "0" Option "EdgeSize" "20" EndSection ================================================ FILE: examples/asus-zenbook-ux330ua-ah55 ================================================ # mtrack config made for asus-zenbook-ux330ua-ah55 ###--------------------------------------------------------------------------### # Properties in comments are "xinput set-prop" command to enable setting live (without restarting X) # Refer to source code to read more about those: xf86-input-mtrack/include/mprops.h ###--------------------------------------------------------------------------### Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" Driver "mtrack" Option "DeviceEnabled" "1" ###--------------------------------------------------------------------------### ### http://www.x.org/wiki/Development/Documentation/PointerAcceleration/ ### Base velocity setting ### Options for polynomial("2") profile ###--------------------------------------------------------------------------### Option "AccelerationProfile" "4" Option "Sensitivity" "0.75" # Adjusts the sensitivity (movement speed) of the touchpad. This is a real number greater than or equal to zero. Default is 1. A value of 0 will disable pointer movement. Option "AdaptiveDeceleration" "2.0" Option "ConstantDeceleration" "1.02" Option "VelocityScale" "1.0" ###---------------------------------------------------------------------------### ### Ignore certain touch type ###---------------------------------------------------------------------------### Option "PalmSize" "30" # The minimum size of what's considered a palm. Palms are expected to be very large on the trackpad. This is represented as a percentage of the maximum touch value and is dependent on the trackpad hardware. Integer value. Defaults to 40. Option "IgnorePalm" "true" # Whether or not to ignore touches that are determined to be palms. Boolean value. Defaults to false. Option "DisableOnPalm" "true" # Whether or not to disable the entire trackpad when a palm is touching. Boolean value. Defaults to false. Option "IgnoreThumb" "true" # Whether or not to ignore touches that are determined to be thumbs. Boolean value. Defaults to false. Option "ThumbRatio" "70" # 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. Option "ThumbSize" "15" # 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 maximum touch value and is dependent on the trackpad hardware. Integer value. Defaults to 25. ###--------------------------------------------------------------------------### ### Set click interactions ###--------------------------------------------------------------------------### Option "ButtonEnable" "true" # Whether or not to enable the physical buttons on or near the trackpad. Boolean value. Defaults to true. Option "ButtonMoveEmulate" "true" # Whether or not to count the moving finger when emulating button clicks. Useful to disable if you use two hands on trackpad. Boolean value. Defaults to true. Option "ButtonIntegrated" "true" # 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. Option "ButtonZonesEnable" "true" # Whether or not to enable button zones. If button zones are enabled then the trackpad will be split into one, two, or three vertical zones. Clicking the integrated button in one of these zones will send the button event for ClickFinger1, ClickFinger2, or ClickFinger3. The driver will only add zones for those ClickFinger values that are enabled. So setting ClickFinger1 to 0 and enabling the other two will create two zones, one for ClickFinger2 and one for ClickFinger3. Boolean value. Defaults to false. Option "LimitButtonZonesToBottomEdge" "true" # Restrict button zones inside the EdgeBottom area. So instead of enabling zones on the full pad height, the zone is limited to the percentage set for the EdgeBottom. Boolean value. Default to false. # Zones stack from left to right inside the 20% height defined above (EdgeBottomSize) Option "FirstZoneButton" "1" # The button to emulate when the zone is pressed. This is the leftmost part of the pad. Integer value. A value of 0 disables this zone split. Defaults to 1. Option "SecondZoneButton" "3" # The button to emulate when the zone is pressed. This will float to the right of the leftmost zone. Integer value. A value of 0 disables this zone split. Defaults to 2. # Next one is not needed and defaut to zero #Option "ThirdZoneButton" "0" # The button to emulate when the zone is pressed. This will float to the right of the leftmost zone. Integer value. A value of 0 disables this zone split. Defaults to 0. Option "ClickFinger0" "1" # Which button to emulate when no valid finger placement is touching the trackpad during a click. Integer value. A value of 0 disables one-touch emulation. Defaults to 1. Option "ClickFinger1" "1" # 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 1. Option "ClickFinger2" "3" # 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. Option "ClickFinger3" "2" # Which button to emulate when three fingers are touching the trackpad during a click. Integer value. A value of 0 disabled one-touch emulation. Defaults to 3. ###---------------------- Gesture Settings ----------------------------------### ### property: "Trackpad Gesture Settings" Option "GestureClickTime" "20" # When a gesture triggers a click, how much time to hold down the emulated button. Integer value representing milliseconds. Defaults to 10. Option "GestureWaitTime" "3" # 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. ###--------------------------------------------------------------------------### Option "ButtonTouchExpire" "120" # How long (in ms) to consider a touching finger as part of button emulation. A value of 0 will not expire touches. Integer value. Defaults to 100. Option "FingerLow" "0" # Defines the pressure at which a finger is detected as a release. This is a percentage represented as an integer. Default is 5. Option "FingerHigh" "0" # Defines the pressure at which a finger is detected as a touch. This is a percentage represented as an integer. Default is 5. ###---------------------- Tap Settings --------------------------------------### ### property "Trackpad Tap Settings" Option "ClickTime" "15" # When tapping, how much time to hold down the emulated button. Integer value representing milliseconds. Integer value representing miliseconds. Defaults to 50. Option "MaxTapTime" "165" # The amount of time to wait for incoming touches after first one before counting it as emulated button click. Integer value representing milliseconds. Defaults to 120. Option "MaxTapMove" "155" # How far a touch is allowed to move before counting it is no longer considered a tap. Integer value. Defaults to 400. ###--------------------------------------------------------------------------### Option "TapButton1" "1" # Which button to emulate for one-finger tapping. Integer value. A value of 0 disables one-finger tapping. Defaults to 1. Option "TapButton2" "3" # Which button to emulate for two-finger tapping. Integer value. A value of 0 disables two-finger tapping. Defaults to 3. Option "TapButton3" "2" # Which button to emulate for three-finger tapping. Integer value. A value of 0 disables three-finger tapping. Defaults to 2. #Option "TapButton4" "0" # Which button to emulate for four-finger tapping. Integer value. A value of 0 disables three-finger tapping. Defaults to 0. ###------------------------ Swipe2 as mouse wheel ---------------------------### Option "ScrollDistance" "22" # For two finger scrolling. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 150. Option "ScrollClickTime" "12" # For two finger scrolling. How long button triggered by scrolling will be hold down. A value of 0 will hold button down till end of gesture. 0 - emit button click only once pre "instance" of gesture. Integer value representing milliseconds. Defaults to 20. Option "ScrollSensitivity" "0" # For two finger scrolling. Sensitivity (movement speed) of pointer during two finger scrolling. A value of 0 disables pointer movement during gesture. Integer value expressed as parts per thousand of normal sensivity. A value of 1000 results with normal movement speed. Defaults to 0. Option "ScrollUpButton" "5" # For two finger scrolling. The button that is triggered by scrolling up. Integer value. A value of 0 disables scrolling up. Defaults to 4. Option "ScrollDownButton" "4" # For two finger scrolling. The button that is triggered by scrolling down. Integer value. A value of 0 disables scrolling down. Defaults to 5. Option "ScrollLeftButton" "6" # For two finger scrolling. The button that is triggered by scrolling left. Integer value. A value of 0 disables scrolling left. Defaults to 6. Option "ScrollRightButton" "7" # For two finger scrolling. The button that is triggered by scrolling right. Integer value. A value of 0 disables scrolling right. Defaults to 7. ###------------------------ Swipe3 - act as mouse wheel ---------------------### Option "SwipeDistance" "22" Option "SwipeClickTime" "12" ### property: "Trackpad Swipe Buttons" Option "SwipeUpButton" "4" Option "SwipeDownButton" "5" Option "SwipeLeftButton" "6" Option "SwipeRightButton" "7" ###--------------------------------------------------------------------------### ###---------------------- Swipe4 to drag buttons 9 10 11 12 -----------------### Option "Swipe4Distance" "280" Option "Swipe4ClickTime" "50" Option "Swipe4Sensitivity" "0" ## property: "Trackpad Swipe4 Buttons" Option "Swipe4UpButton" "9" Option "Swipe4DownButton" "10" Option "Swipe4LeftButton" "11" Option "Swipe4RightButton" "12" ###--------------------------------------------------------------------------### Option "ScaleDistance" "70" Option "ScaleUpButton" "12" Option "ScaleDownButton" "13" Option "RotateDistance" "350" Option "RotateLeftButton" "14" Option "RotateRightButton" "15" ###--------------------------------------------------------------------------### ### xinput set-prop 11 "Trackpad Drag Settings" 1 350 60 200 Option "TapDragEnable" "true" Option "TapDragTime" "350" Option "TapDragWait" "40" Option "TapDragDist" "150" Option "TapDragLockTimeout" "0" # This is how long the driver will wait after initial drag in 'drag ready' state in which it will be able to resume previous drag without additional up, down sequence. Value of 0 disables this functionality. Values less than zero will make mtrack requre additional tap to finish drag by sending button up. Integer value representing milliseconds. Defaults to 500. ###--------------------------------------------------------------------------### Option "EdgeBottomSize" "17" # Disable tap and movement detection in the bottom 17% of the pad #Option "EdgeTopSize" "2" #Option "EdgeLeftSize" "4" #Option "EdgeRightSize" "6" #Option "EdgeScrollUpButton" "4" #Option "EdgeScrollDownButton" "5" Option "Hold1Move1StationaryMaxMove" "120" Option "Hold1Move1StationaryButton" "1" # For two finger hold-and-move functionality. The button that is triggered by holding one finger and moving another one. Integer value. A value of 0 disables hold-and-move. Value of 0 disables this functionality. Defaults to 1. EndSection ================================================ FILE: examples/dell-precision-5520.conf ================================================ # Settings for Dell Precision 5520. Includes three-finger drag and reversed two finger scrolling. Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" Driver "mtrack" Option "DeviceEnabled" "1" Option "AccelerationProfile" "1" Option "Sensitivity" "1.20" Option "ClickTime" "25" # Tried these, didn't like them, YMMV. # Option "AdaptiveDeceleration" "2.0" # Option "ConstantDeceleration" "1.2" # Option "VelocityScale" "3.0" Option "SwipeDistance" "1" Option "SwipeLeftButton" "1" Option "SwipeRightButton" "1" Option "SwipeUpButton" "1" Option "SwipeDownButton" "1" Option "SwipeClickTime" "0" Option "SwipeSensitivity" "1000" Option "ScrollDownButton" "4" Option "ScrollUpButton" "5" Option "ScrollDistance" "25" Option "ScrollCoastDuration" "500" Option "IgnorePalm" "true" Option "IgnoreThumb" "true" Option "ThumbSize" "25" Option "ThumbRatio" "70" Option "EdgeRightSize" "0" Option "TapDragEnable" "0" Option "Hold1Move1StationaryButton" "0" # I hate edge scrolling. Option "EdgeScrollDist" "2000000000" Option "FingerHigh" "5" Option "FingerLow" "1" EndSection ================================================ FILE: examples/dell-xps13-9333.conf ================================================ # MatchIsTouchpad "on" #Section "InputClass" # Identifier "Touchpads" # Driver "mtrack" #EndSection Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" Driver "mtrack" Option "DeviceEnabled" "1" ###--------------------------------------------------------------------------### # http://www.x.org/wiki/Development/Documentation/PointerAcceleration/ ###--------------------------------------------------------------------------### ###--------------------------------------------------------------------------### ### Options for acceleration profile "0" ###--------------------------------------------------------------------------### # some curved deceleration ### Option "AccelerationProfile" "0" ### Option "AdaptiveDeceleration" "5.5" # linear deceleration (mouse speed reduction) ### Option "ConstantDeceleration" "1.01" ### Option "Sensitivity" "0.49" ### Option "VelocityScale" "10.0" ###--------------------------------------------------------------------------### ###--------------------------------------------------------------------------### ### Options for polynomial("2") profile ###--------------------------------------------------------------------------### Option "AccelerationProfile" "4" Option "Sensitivity" "0.75" Option "AdaptiveDeceleration" "2.0" Option "ConstantDeceleration" "1.02" Option "VelocityScale" "1.0" #------------------------------------------------------------------------------# Option "PalmSize" "40" Option "IgnorePalm" "true" # Option "IgnoreThumb" "true" # Option "ThumbRatio" "70" # Option "ThumbSize" "35" ###---------------------- Gesture Settings ----------------------------------### ### property: "Trackpad Gesture Settings" Option "GestureClickTime" "20" Option "GestureWaitTime" "3" ###--------------------------------------------------------------------------### Option "ButtonTouchExpire" "120" Option "FingerLow" "0" Option "FingerHigh" "0" ###---------------------- Tap Settings --------------------------------------### ### property "Trackpad Tap Settings" Option "ClickTime" "35" Option "MaxTapTime" "145" Option "MaxTapMove" "85" ###--------------------------------------------------------------------------### Option "TapButton1" "1" Option "TapButton2" "2" Option "TapButton3" "3" ###------------------------ Swipe2 as mouse wheel ---------------------------### Option "ScrollDistance" "22" Option "ScrollClickTime" "12" Option "ScrollSensitivity" "0" Option "ScrollUpButton" "4" Option "ScrollDownButton" "5" Option "ScrollLeftButton" "6" Option "ScrollRightButton" "7" ###--------------------------------------------------------------------------### ###------------------------ Swipe to drag button 8 --------------------------### Option "SwipeDistance" "1" Option "SwipeClickTime" "0" Option "SwipeSensitivity" "1100" Option "SwipeUpButton" "8" Option "SwipeDownButton" "8" Option "SwipeLeftButton" "8" Option "SwipeRightButton" "8" ###--------------------------------------------------------------------------### ###------------------------ Scroll to drag button 8 -------------------------### Option "ScrollDistance" "1" Option "ScrollClickTime" "0" Option "ScrollSensitivity" "1100" Option "ScrollUpButton" "8" Option "ScrollDownButton" "8" Option "ScrollLeftButton" "8" Option "ScrollRightButton" "8" ###--------------------------------------------------------------------------### ###------------------------ Swipe3 - act as mouse wheel ---------------------### Option "SwipeDistance" "22" Option "SwipeClickTime" "12" Option "ScrollSensitivity" "0" ### property: "Trackpad Swipe Buttons" Option "SwipeUpButton" "4" Option "SwipeDownButton" "5" Option "SwipeLeftButton" "6" Option "SwipeRightButton" "7" ###--------------------------------------------------------------------------### ###---------------------- Swipe4 to drag buttons 9 10 11 12 -----------------### Option "Swipe4Distance" "280" Option "Swipe4ClickTime" "50" Option "Swipe4Sensitivity" "0" ## property: "Trackpad Swipe4 Buttons" Option "Swipe4UpButton" "9" Option "Swipe4DownButton" "10" Option "Swipe4LeftButton" "11" Option "Swipe4RightButton" "12" ###--------------------------------------------------------------------------### ###---------------------------- Swipe4 to drag button 1 ---------------------### Option "Swipe4Distance" "1" Option "Swipe4ClickTime" "0" Option "Swipe4Sensitivity" "1350" ## property: "Trackpad Swipe4 Buttons" Option "Swipe4UpButton" "1" Option "Swipe4DownButton" "1" Option "Swipe4LeftButton" "1" Option "Swipe4RightButton" "1" ###--------------------------------------------------------------------------### Option "ScaleDistance" "70" Option "ScaleUpButton" "12" Option "ScaleDownButton" "13" Option "RotateDistance" "350" Option "RotateLeftButton" "14" Option "RotateRightButton" "15" Option "ButtonEnable" "true" Option "ButtonIntegrated" "true" Option "ButtonZonesEnable" "true" Option "ClickFinger1" "1" Option "ClickFinger2" "2" Option "ClickFinger3" "3" ###--------------------------------------------------------------------------### ### xinput set-prop 11 "Trackpad Drag Settings" 1 350 60 200 Option "TapDragEnable" "true" Option "TapDragTime" "350" Option "TapDragWait" "40" Option "TapDragDist" "150" ###--------------------------------------------------------------------------### Option "EdgeBottomSize" "5" # dont count touches from bottom 5% of area Option "EdgeTopSize" "2" Option "EdgeLeftSize" "4" Option "EdgeRightSize" "6" Option "EdgeScrollUpButton" "4" Option "EdgeScrollDownButton" "5" Option "Hold1Move1StationaryMaxMove" "90" EndSection ================================================ FILE: examples/lenovo-710s.conf ================================================ Section "InputClass" MatchIsTouchpad "true" Identifier "Touchpads" Driver "mtrack" Option "MaxTapTime" "400" Option "MaxTapMove" "20" Option "ScrollDistance" "55" Option "ScrollSmooth" "1" Option "ScrollCoastDuration" "360" Option "ScrollCoastEnableSpeed" "0.3" Option "AccelerationProfile" "3" Option "VelocityScale" "1.3" Option "Sensitivity" "0.8" Option "ScrollUpButton" "5" Option "ScrollDownButton" "4" Option "SwipeDistance" "300" Option "Hold1Move1StationaryMaxMove" "50" Option "TapDragDist" "400" Option "GestureWaitTime" "20" Option "ButtonZonesEnable" "true" Option "ClickFinger1" "1" Option "ClickFinger2" "3" Option "ClickFinger3" "0" Option "SwipeClickTime" "60" Option "SwipeDistance" "200" Option "SwipeUpButton" "16" Option "SwipeDownButton" "17" Option "Swipe4UpButton" "18" Option "Swipe4DownButton" "19" Option "Swipe4LeftButton" "20" Option "Swipe4RightButton" "21" Option "Swipe4Distance" "300" Option "EdgeLeftSize" "4" Option "EdgeRightSize" "7" Option "EdgeTopSize" "0" Option "EdgeBottomSize" "15" EndSection ================================================ FILE: examples/lenovo-yoga-2-pro.conf ================================================ ## Works pretty well on Ubuntu 18.04 with deepin DE & Xfce ########### 1. Install the mtrack driver ################ #cd /tmp #git clone https://github.com/p2rkw/xf86-input-mtrack.git #cd xf86-input-mtrack #./configure --with-xorg-module-dir=/usr/lib/xorg/modules #make #sudo make install ########### 2. Copy this file to /usr/share/X11/xorg.conf.d/99-mtrack.conf ################ # precedence should be higher than synaptics and/or libinput (so not necessarily 99). ########### 3. I recommend using this together with dispad, to disable the touchpad while typing ############# ## 1. install dependencies for dispad #sudo apt install libconfuse-dev libxi-dev #cd /tmp #git clone https://github.com/BlueDragonX/dispad.git #installation will fail if you have autocrlf=true in your gitconf #cd dispad #./configure #make #sudo make install ## 2. launch dispad from terminal >dispad ## 3. configure config file >gedit ~/.dispad like so: # # default dispad config file # # name of the property used to enable/disable the trackpad # property = "Trackpad Disable Input" # # the value used to enable the trackpad # enable = 0 # # the value used to disable the trackpad # disable = 3 # # whether or not modifier keys disable the trackpad # modifiers = false # # how long (in ms) to sleep between keyboard polls # poll = 44 # # how long (in ms) to disable the trackpad after a keystroke # delay = 500 ## 4. edit bashrc to launch dispad at terminal-startup # echo -e "\n#Turn off touchpad upon keypress\ndispad\n" >> ~/.bashrc # alternatively, you can start the daemon via putting this line in ~/.profile # this profile option has the advantage of working straight from the beginning. However, it throws a warning (without consequences) after login. Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" Driver "mtrack" Option "DeviceEnabled" "1" # ----- Acceleration --- most important part! --- Option "AccelerationProfile" "2" # 2 is polynomial, recommended as "very usable"; see https://www.x.org/wiki/Development/Documentation/PointerAcceleration/#Introduction Option "AdaptiveDeceleration" "3.5" #"if you like the speed but need some more control at pixel-level, you should set ?AdaptiveDeceleration to 2 or more."(www.x.org) # Option "ConstantDeceleration" "1.02" Option "VelocityScale" "4" # ---- Basic setup # Option "TrackPadDisable" "0" # dont disable trackpad # Option "ButtonEnable" "true" # enable physical buttons # Option "ButtonIntegrated" "true" # Y2p has a 1-piece pad # ---- Responsiveness Option "Sensitivity" "0.3" # how fast does the mouse move Option "FingerLow" "15" # more than 10% pressure --> is a touch Option "FingerHigh" "15" # less than 10% pressure --> not a touch Option "IgnoreThumb" "true" # don't use thumb touches Option "IgnorePalm" "true" # don't use palm touches Option "DisableOnThumb" "true" # Disable complete pad once thumb detected Option "DisableOnPalm" "true" # ...same for palm Option "PalmSize" "30" # 30 is still a bit jumpy, but less results in bad detection of fingers # Option "ThumbSize" "15" # Thumb is 25% of trackpad-max # Option "ThumbRatio" "40" # thumb is 70% longer than wide # ---- Zones (I don't really understand zones) # Option "ButtonZonesEnable" "true" # Split pad in zones for clicks # Option "FirstZoneButton" "1" # Option "SecondZoneButton" "0" # Option "ThirdZoneButton" "3" # Option "LimitButtonZonesToBottomEdge" "false" # ---- Physical Clicks # Option "ClickFinger0" "0" # Option "ClickFinger1" "1" # Option "ClickFinger2" "0" # Option "ClickFinger3" "0" # ---- Tap-to-click # Option "TapButton 1" "1" #left click on 1-finger tap # Option "TapButton 2" "3" #right click on 2-finger tap # Option "TapButton 3" "0" #no click on 3-finger tap # Option "TapButton 4" "0" #no click on 4-finger tap # ---- 2-finger Scrolling # Option "ScrollDistance" "150" # how far to move fingers before scrolling starts (unit?) # Option "ScrollClickTime" "0" # how long (in ms) to press "scroll" - 0=only as long as fingers move # Option "ScrollSensitivity" "0" # how fast to move pointer during scrolling (0=not at all) # Option "ScrollSmooth" "1" # High precision smooth scrolling # Option "ScrollCoastDuration" "100" # how long to move screen after finger movement (in ms) # Option "ScrollCoastEnableSpeed" ".3" # # ---- 3/4-finger Swiping # I never use that, so I set a threshold so high, it won't be used # Option "SwipeDistance" "9999" # Option "SwipeUpButton" "0" # Option "SwipeDownButton" "0" # Option "SwipeLeftButton" "0" # Option "SwipeRightButton" "0" # Option "Swipe4Distance" "9999" # Option "Swipe4UpButton" "0" # Option "Swipe4DownButton" "0" # Option "Swipe4LeftButton" "0" # Option "Swipe4RightButton" "0" # ---- Edge scrolling # I never use that, so I set a threshold so high, it won't be used # Option "EdgeScrollDist" "9999" # Option "EdgeScrollUpButton" "0" # Option "EdgeScrollDownButton" "0" # Option "EdgeScrollLeftButton" "0" # Option "EdgeScrollRightButton" "0" # ---- Pinch zoom # Option "ScaleDistance" "100" # how far to move fingers? (unit?) # Option "ScaleUpButton" "12" # Option "ScaleDownButton" "13" # ---- 2 finger rotation # I never use that, so I set a threshold so high, it won't be used # Option "RotateDistance" "9999" # how far to move fingers? (unit?) # Option "RotateLeftButton" "0" # Option "RotateRightButton" "0" # ---- highlight text with holding one and moving another finger # Option "Hold1Move1StationaryButton" "1" # left-mouse click = 1 # Option "Hold1Move1StationaryMaxMove" "90" # threshold for how far 2nd finger can be moved (unit?) # ---- tap to drag # Option "TapDragEnable" "true" # Option "TapDragWait" "60" # Option "TapDragTime" "300" # how long after tap does the driver wait for drag (in ms)? # Option "TapDragDist" "200" # threshold, after this dist it's seen as mousemove not as drag (unit?) # ---- Invert axes for mousemovement (not scrolling!) # Option "AxisXInvert" "false" # Option "AxisYInvert" "false" # ---- Natural Scrolling (uncommenting will disable it, as default buttons are reversed) Option "ScrollDownButton" "4" Option "ScrollUpButton" "5" Option "ScrollLeftButton" "7" Option "ScrollRightButton" "6" EndSection ================================================ FILE: examples/macbook-pro-retina-2013.conf ================================================ Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" MatchDevicePath "/dev/input/event*" Driver "mtrack" # The faster you move, the more distance pointer will travel, using "polynomial" profile Option "AccelerationProfile" "2" # Tweak cursor movement speed with this Option "Sensitivity" "0.05" # Pressure at which a finger is detected as a touch Option "FingerHigh" "1" # Pressure at which a finger is detected as a release Option "FingerLow" "1" # I often use thumb to press down the physical button, so let's not ignore it Option "IgnoreThumb" "false" Option "ThumbRatio" "70" Option "ThumbSize" "23" # Ignore palm, with palm takes up to 30% of your touch pad Option "IgnorePalm" "true" Option "PalmSize" "30" # Trigger mouse button when tap: 1 finger - left click, 2 finger - right click, 3 - middle click Option "TapButton1" "1" Option "TapButton2" "3" Option "TapButton3" "2" Option "TapButton4" "0" Option "ClickTime" "1" # Disable tap-to-drag, we're using three finger drag instead Option "TapDragEnable" "false" # While touching the touch pad with # fingers, press the touchpad physical click button Option "ClickFinger1" "1" Option "ClickFinger2" "3" Option "ClickFinger3" "2" Option "ButtonMoveEmulate" "false" Option "ButtonIntegrated" "true" # The momentum after scroll fingers released Option "ScrollCoastDuration" "300" Option "ScrollCoastEnableSpeed" ".1" # Natural scrolling with two fingers Option "ScrollSmooth" "true" Option "ScrollUpButton" "5" Option "ScrollDownButton" "4" Option "ScrollLeftButton" "7" Option "ScrollRightButton" "6" # Tweak scroll sensitivity with ScrollDistance, don't touch ScrollSensitivity Option "ScrollDistance" "270" Option "ScrollClickTime" "1" # Three finger drag Option "SwipeDistance" "1" Option "SwipeLeftButton" "1" Option "SwipeRightButton" "1" Option "SwipeUpButton" "1" Option "SwipeDownButton" "1" Option "SwipeClickTime" "1" Option "SwipeSensitivity" "1500" # Four finger swipe, 8 & 9 are for browsers navigating back and forth respectively Option "Swipe4LeftButton" "9" Option "Swipe4RightButton" "8" # Mouse button >= 10 are not used by Xorg, so we'll map them with xbindkeys and xdotool later Option "Swipe4UpButton" "11" Option "Swipe4DownButton" "10" # Mouse buttons triggered by 2-finger pinching gesture Option "ScaleDistance" "300" Option "ScaleUpButton" "12" Option "ScaleDownButton" "13" # Mouse buttons trigger by 2-finger rotating gesture, disabled to enhance the pinch gesture Option "RotateLeftButton" "0" Option "RotateRightButton" "0" Option "TabDragEnable" "false" Option "GestureClickTime" "1" Option "GestureWaitTime" "0" EndSection ================================================ FILE: examples/macbook.conf ================================================ # Taken from: https://howchoo.com/g/mdy0ngziogm/the-perfect-almost-touchpad-settings-on-linux-2 Section "InputClass" MatchIsTouchpad "on" Identifier "Touchpads" Driver "mtrack" Option "Sensitivity" "0.60" Option "FingerHigh" "5" Option "FingerLow" "1" Option "IgnoreThumb" "true" Option "ThumbRatio" "70" Option "ThumbSize" "25" Option "IgnorePalm" "true" Option "TapButton1" "0" Option "TapButton2" "0" Option "TapButton3" "0" Option "TapButton4" "0" Option "ClickFinger1" "3" Option "ClickFinger2" "3" Option "ClickFinger3" "3" Option "ButtonMoveEmulate" "false" Option "ButtonIntegrated" "true" Option "ClickTime" "25" Option "BottomEdge" "30" Option "SwipeLeftButton" "8" Option "SwipeRightButton" "9" Option "SwipeUpButton" "0" Option "SwipeDownButton" "0" Option "SwipeDistance" "700" Option "ScrollCoastDuration" "500" Option "ScrollCoastEnableSpeed" ".3" Option "ScrollUpButton" "5" Option "ScrollDownButton" "4" Option "ScrollLeftButton" "7" Option "ScrollRightButton" "6" Option "ScrollDistance" "350" Option "Hold1Move1StationaryButton" "0" EndSection ================================================ FILE: include/button.h ================================================ /*************************************************************************** * * 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 ================================================ FILE: include/capabilities.h ================================================ /*************************************************************************** * * 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[128]; 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); int translate_cap_x(const struct Capabilities *cap, int x); int translate_cap_y(const struct Capabilities *cap, int y); int get_cap_xmin(const struct Capabilities *cap); int get_cap_ymin(const struct Capabilities *cap); void output_capabilities(const struct Capabilities *cap); #endif ================================================ FILE: include/common.h ================================================ /*************************************************************************** * * 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 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #define DIM_FINGER 16 #define DIM_TOUCHES 16 /* year-proof millisecond event time */ typedef __u64 mstime_t; /* all bit masks have this type */ typedef unsigned int bitmask_t; /** * m - bit set (integer) * x - modified bit * b - new value * @{ */ #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) #define SGNVAL (x) ((x) < 0 ? -1 : (((x) > 0) ? 1 : 0)) #define LOG_ERROR(...) \ do{ \ xf86Msg(X_ERROR, "mtrack[%i] %s:%i: ", get_next_log_number(), __FILE__, __LINE__); \ xf86Msg(X_ERROR, __VA_ARGS__); \ }while(0) #define LOG_WARNING(...) \ do{ \ xf86Msg(X_WARNING, "mtrack[%i] %s:%i: ", get_next_log_number(), __FILE__, __LINE__); \ xf86Msg(X_WARNING, __VA_ARGS__); \ }while(0) #define LOG_INFO(...) \ do{ \ xf86Msg(X_INFO, "mtrack[%i] %s:%i: ", get_next_log_number(), __FILE__, __LINE__); \ xf86Msg(X_INFO, __VA_ARGS__); \ }while(0) #define LOG_INFO_CONT(...) xf86Msg(X_INFO, "mtrack[...]: " __VA_ARGS__) #define LOG_DISABLED(...) do { } while(0) #define LOG_INFO_ENABLED(...) LOG_INFO(__VA_ARGS__) #define LOG_INFO_DISABLED(...) #define LOG_INFO2(ENABLED_or_DISABLED, ...) LOG_INFO_##ENABLED_or_DISABLED(__VA_ARGS__) #if defined(DEBUG_DRIVER) && (DEBUG_DRIVER != 0) # define LOG_DEBUG LOG_INFO #else # define LOG_DEBUG LOG_DISABLED #endif static inline int get_next_log_number(){ static int last = 0; return ++last; } /* Retrieve the current time and place it in tv. */ static inline void microtime(struct timeval* tv) { gettimeofday(tv, NULL); } /* Copy one time value to another. */ static inline void timercp(struct timeval* dest, const struct timeval* src) { memcpy(dest, src, sizeof(struct timeval)); } /* Convert a timeval to milliseconds since the epoch. Truncates additional * timer resolution effectively rounding down. */ static inline mstime_t timertoms(const struct timeval* tv) { return (mstime_t)(tv->tv_sec*1000) + (mstime_t)(tv->tv_usec/1000); } /* Convert a value in milliseconds to a timeval and place the value in tv. */ static inline void timerfromms(struct timeval* tv, const mstime_t ms) { tv->tv_sec = (time_t)(ms/1000); tv->tv_usec = (suseconds_t)((ms%1000)*1000); } /* Convert a timeval to microseconds. */ static inline suseconds_t timertomicro(const struct timeval* tv) { return tv->tv_sec * 1000000 + tv->tv_usec; } /* Add milliseconds to a timeval and place the resulting value in dest. */ static inline void timeraddms(const struct timeval* a, const mstime_t b, struct timeval* dest) { struct timeval tv; timerfromms(&tv, b); timeradd(a, &tv, dest); } /* Check if given timeval a is set to epoch time. */ static inline int isepochtime(const struct timeval* a) { struct timeval epoch; timerclear(&epoch); return timercmp(a, &epoch, ==); } /* Clamp value to 15 bits. */ 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)) /** * \defgroup Gestures.c Functions from gestures.c * * These functions are implemented in gestures.c * * @{ */ struct Gestures; /** * Compute x*x+y*y and compare it with value*value. * It's equal to following comparision: sqrt(x*x+y*y) [?] value. * Implementation in gestures.c * * @param x * @param y * @param value * @return -1 when lhs is less than rhs, 0 when equal, 1 when greater */ int hypot_cmpf(double x, double y, double value); /** * It's called 'uncond' because caller have to check that all conditions required to * trigger delayed button were met. * * @return triggered button */ int trigger_delayed_button_uncond(struct Gestures* gs); /** }@ */ #endif ================================================ FILE: include/gestures.h ================================================ /*************************************************************************** * * Multitouch X driver * Copyright (C) 2008 Henrik Rydberg * Copyright (C) 2011 Ryan Bourgeois * Copyright (C) 2015-2018 Paweł Turkowski * * 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" struct MTouch; #define GS_NONE 0 #define GS_MOVE 1 #define GS_SCROLL 2 #define GS_SWIPE2 GS_SCROLL #define GS_SWIPE3 3 #define GS_SWIPE4 4 #define GS_SCALE 5 #define GS_ROTATE 6 #define GS_DRAG_READY 7 #define GS_DRAG_WAIT 8 #define GS_DRAG_ACTIVE 9 #define GS_HOLD1_MOVE1 10 #define GS_HOLD1_MOVE2 11 #define GS_HOLD1_MOVE3 12 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. */ double move_dx, move_dy; /* Current time and time delta. Updated after each event and after sleeping. */ struct timeval time; struct timeval dt; /* Internal state tracking. Not for direct access. */ /** Lastly emulated button, by pressing integrated button. */ int button_emulate; int integrated_emulated_button; /* Invalid button_delayed means that there's no delayed button. */ int button_delayed; /* If equals to epoch time then button is delayed till gesture end. */ struct timeval button_delayed_time; int tap_touching; int tap_released; struct timeval tap_timeout; int move_type; double move_dist; int move_dir; int drag_state; int move_drag_dx; int move_drag_dy; struct timeval move_wait; struct timeval move_drag_wait; struct timeval move_drag_expire; struct timeval move_start; /* Scroll vertical, horizontal. */ double scroll_speed_x, scroll_speed_y; int scroll_speed_valid; /* Duration left */ int coasting_duration_left; }; void gestures_init(struct MTouch* mt); void gestures_extract(struct MTouch* mt); int gestures_delayed(struct MTouch* mt); static int buttons_zone_update( struct Gestures* gs, const struct MConfig* cfg, const struct MTState* ms, int lowest); static void touch_detect_update( struct Gestures* gs, const struct MConfig* cfg, const struct MTState* ms, int latest); #endif ================================================ FILE: include/hwstate.h ================================================ /*************************************************************************** * * 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 { /* The size of the contact area */ int touch_major, touch_minor; /* The size of the approaching tool */ 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; int max_x, max_y; struct timeval evtime; }; 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 ================================================ FILE: include/mconfig.h ================================================ /*************************************************************************** * * 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_TRACKPAD_DISABLE 0 #define DEFAULT_TOUCH_DOWN 5 #define DEFAULT_TOUCH_UP 5 #define DEFAULT_IGNORE_THUMB 0 #define DEFAULT_IGNORE_PALM 0 #define DEFAULT_DISABLE_ON_THUMB 0 #define DEFAULT_DISABLE_ON_PALM 0 #define DEFAULT_THUMB_RATIO 70 #define DEFAULT_THUMB_SIZE 25 #define DEFAULT_PALM_SIZE 40 #define DEFAULT_EDGE_LEFT_SIZE 0 #define DEFAULT_EDGE_RIGHT_SIZE 0 #define DEFAULT_EDGE_TOP_SIZE 0 #define DEFAULT_EDGE_BOTTOM_SIZE 10 #define DEFAULT_BUTTON_ENABLE 1 #define DEFAULT_BUTTON_INTEGRATED 1 #define DEFAULT_BUTTON_ZONES 0 /* Button zones are disabled */ #define DEFAULT_IS_BUTTON_ZONES_HEIGHT_LIMITED 0 /* Button zones are emulated on the whole pad (Not limited to the bottom edge)*/ #define DEFAULT_BUTTON_FIRST_ZONE 1 #define DEFAULT_BUTTON_SECOND_ZONE 2 #define DEFAULT_BUTTON_THIRD_ZONE 0 #define DEFAULT_BUTTON_0TOUCH 0 /* "invalid" finger placement do not emulate any button */ #define DEFAULT_BUTTON_1TOUCH 1 #define DEFAULT_BUTTON_2TOUCH 2 #define DEFAULT_BUTTON_3TOUCH 3 #define DEFAULT_BUTTON_MOVE 1 #define DEFAULT_BUTTON_EXPIRE 100 #define DEFAULT_TAP_1TOUCH 1 #define DEFAULT_TAP_2TOUCH 3 #define DEFAULT_TAP_3TOUCH 2 #define DEFAULT_TAP_4TOUCH 0 #define DEFAULT_TAP_TIMEOUT 120 #define DEFAULT_TAP_HOLD 50 #define DEFAULT_TAP_DIST 400 #define DEFAULT_GESTURE_HOLD 10 #define DEFAULT_GESTURE_WAIT 100 #define DEFAULT_SCROLL_DIST 150 #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_SCROLL_HOLD 20 #define DEFAULT_SCROLL_COAST_MIN_SPEED 0.1f #define DEFAULT_SCROLL_COAST_DURATION 200 #define DEFAULT_SCROLL_COAST_TICK_MS 30 /* Schould be configurable? */ #define DEFAULT_SCROLL_COAST_NO_BOOST 0 #define DEFAULT_SCROLL_COAST_EASE 0 #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_SWIPE_HOLD 300 #define DEFAULT_SWIPE4_DIST 700 #define DEFAULT_SWIPE4_UP_BTN 0 #define DEFAULT_SWIPE4_DN_BTN 0 #define DEFAULT_SWIPE4_LT_BTN 0 #define DEFAULT_SWIPE4_RT_BTN 0 #define DEFAULT_SWIPE_SENS 0 #define DEFAULT_EDGE_SCROLL_DIST (DEFAULT_SCROLL_DIST*0.7) #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_HOLD1_MOVE1_STATIONARY_BTN 1 #define DEFAULT_HOLD1_MOVE1_STATIONARY_MAX_MOVE 20 #define DEFAULT_HOLD1_MOVE1_DIST 1 #define DEFAULT_HOLD1_MOVE1_HOLD 0 #define DEFAULT_HOLD1_MOVE1_SENS 1000 #define DEFAULT_HOLD1_MOVE1_BTN 1 #define DEFAULT_HOLD1_MOVE2_STATIONARY_BTN 3 #define DEFAULT_HOLD1_MOVE2_STATIONARY_MAX_MOVE 30 #define DEFAULT_HOLD1_MOVE2_DIST 1 #define DEFAULT_HOLD1_MOVE2_HOLD 0 #define DEFAULT_HOLD1_MOVE2_SENS 1000 #define DEFAULT_HOLD1_MOVE2_BTN 3 #define DEFAULT_HOLD1_MOVE3_STATIONARY_BTN 10 #define DEFAULT_HOLD1_MOVE3_STATIONARY_MAX_MOVE 60 #define DEFAULT_HOLD1_MOVE3_DIST 100 #define DEFAULT_HOLD1_MOVE3_HOLD 50 #define DEFAULT_HOLD1_MOVE3_SENS 0 #define DEFAULT_HOLD1_MOVE3_BTN 11 #define DEFAULT_DRAG_ENABLE 1 #define DEFAULT_DRAG_TIMEOUT 350 #define DEFAULT_DRAG_WAIT 40 #define DEFAULT_DRAG_DIST 200 #define DEFAULT_DRAG_LOCK_TIMEOUT 500 #define DEFAULT_AXIS_X_INVERT 0 #define DEFAULT_AXIS_Y_INVERT 0 #define DEFAULT_SENSITIVITY 1.0 #define DEFAULT_SCROLL_HIGH_PREC 1 #define MCFG_NONE 0 #define MCFG_SCALE 1 #define MCFG_SIZE 2 #define MCFG_PRESSURE_SIZE 3 #define MCFG_SIZE_PRESSURE 4 /* same capabilities as above, but with higher resolution of touches*/ #define MCFG_PRESSURE 5 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 pressure_min; // Minimum pressure value. int pressure_max; // Maximum pressure 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 int edge_left_size; // Percent of left edge of trackpad to ignore for new touches. 0 - 100 int edge_right_size; // Percent of right edge of trackpad to ignore for new touches. 0 - 100 int edge_top_size; // Percent of top edge of trackpad to ignore for new touches. 0 - 100 int edge_bottom_size; // Percent of bottom edge of trackpad to ignore for new touches. 0 - 100 int axis_x_invert; // Whether or not to invert the x axis. 0 or 1. int axis_y_invert; // Whether or not to invert the y axis. 0 or 1. /* Used by Gestures */ // Set by config. int trackpad_disable; // Disable the trackpad? 0 or 1 int button_enable; // Enable physical buttons? 0 or 1 int button_integrated; // Is the button under the touchpad? 0 or 1 int button_expire; // How long to consider a touch for button emulation. >= 0 int button_zones; // Use button zones for emulation? int is_button_zones_height_limited; // Limit the button zones to the height of the bottom edge? int button_first_zone; // Button to execute when clicking in the leftmost part of the zone (button_#_zones > 0) int button_second_zone; // Button to execute when clicking right of zone_button1 part of the zone (button_#_zones > 0) int button_third_zone; // Button to execute when clicking the rightmost part of the zone (button_#_zones > 0) int button_0touch; // What button to emulate when no finger is on the // pad and a click is registered? 0 to 32 int button_1touch; // What button to emulate when one finger is on the // pad or the first zone is clicked? 0 to 32 int button_2touch; // What button to emulate when two fingers are on the // pad or the second zone is clicked? 0 to 32 int button_3touch; // What button to emulate when three fingers are on the // pad or the third zone is clicked? 0 to 32 int button_move; // Whether or not to count the moving touch towards button // emulation. int tap_1touch; // What button to emulate for one touch taps? 0 to 32 int tap_2touch; // What button to emulate for two touch taps? 0 to 32 int tap_3touch; // What button to emulate for three touch taps? 0 to 32 int tap_4touch; // What button to emulate for four touch taps? 0 to 32 int tap_timeout; // Window for touches when counting for the button. // How long to wait for incoming touches after first one. > 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 struct MConfigSwipe{ int dist; // Distance needed to trigger a button. >= 0, 0 disables int hold; // How long to "hold down" the emulated button for swipe gesture. > 0 int drag_sens; // Should this gesture emit movement events? 0 disables movement, 1000 set speed same as during normal movement int up_btn; // Button to use for swipe up. >= 0, 0 is none int dn_btn; // Button to use for swipe down. >= 0, 0 is none int lt_btn; // Button to use for swipe left. >= 0, 0 is none int rt_btn; // Button to use for swipe right. >= 0, 0 is none } scroll, swipe3, swipe4/*, swipe5*/; struct MConfigSwipeCoasting{ float min_speed; // What speed to start scroll coasting at. >= 0 int tick_ms; // How fast events will be generated during coasting >= 1 int duration; // How long coasting ticks will last >= 0, 0 disables coasting int no_boost; // Disable boosting on second scroll gesture during coasting. 0 or 1 int ease; // Apply easing effect on coasting. 0 or 1 } scroll_coast; struct MConfigSwipe edge_scroll; 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 struct MConfigStationary{ int max_move; // How far stationary finger can move, before interrupting the gesture. >= 0 int button; // Button to be pressed/released when gesture starts/ends. >= 0, 0 disables } hold1_move1_stationary; #if 0 struct MConfigStationary hold1_move2_stationary, hold1_move3_stationary; #endif struct MConfigSwipe hold1_move1; #if 0 struct MConfigSwipe hold1_move2, hold1_move3; #endif int drag_enable; // Enable tap-to-drag? 0 or 1 int drag_timeout; // How long to wait for a move after tapping? > 0 int drag_wait; // How long to wait before triggering button down? >= 0 int drag_dist; // How far is the finger allowed to move during wait time? >= 0 int drag_lock_timeout; // How long to wait in 'drag ready' state after // dragging finger was released? // < 0 - wait of tap to break drag; 0 - disable; > 0 - time in ms double sensitivity; // Mouse movement multiplier. >= 0 int scroll_smooth; // Enable high precision (smooth) scrolling. 0 or 1. }; /* 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, pointer opts); #endif ================================================ FILE: include/mprops.h ================================================ /*************************************************************************** * * 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 MTRACK_PROPS_H #define MTRACK_PROPS_H #include #include #include #include #include #include #include "mconfig.h" #ifndef XATOM_FLOAT #define XATOM_FLOAT "FLOAT" #endif // int, 1 value #define MTRACK_PROP_TRACKPAD_DISABLE "Trackpad Disable Input" // float, 1 value #define MTRACK_PROP_SENSITIVITY "Trackpad Sensitivity" // int, 2 values - finger low, finger high #define MTRACK_PROP_PRESSURE "Trackpad Touch Pressure" // int, 2 values - enable buttons, has integrated button #define MTRACK_PROP_BUTTON_SETTINGS "Trackpad Button Settings" // int, 4 values - enable button zones, button move emulation, emulation touch expiration, button zone in edge bottom #define MTRACK_PROP_BUTTON_EMULATE_SETTINGS "Trackpad Button Emulation Settings" // int, 7 values - button to emulate with 0 touch, 1 touch, 2 touches, 3 touches AND zone 1, zone 2, zone 3 #define MTRACK_PROP_BUTTON_EMULATE_VALUES "Trackpad Button Emulation Values" // int, 3 values - click time, touch timeout, invalidate distance #define MTRACK_PROP_TAP_SETTINGS "Trackpad Tap Settings" // int, 4 values - 1 touch button, 2 touch button, 3 touch button, 4 touch button #define MTRACK_PROP_TAP_EMULATE "Trackpad Tap Button Emulation" // int, 2 values - ignore thumb touches, disable trackpad on thumb touches #define MTRACK_PROP_THUMB_DETECT "Trackpad Thumb Detection" // int, 2 values - size, width to length ratio #define MTRACK_PROP_THUMB_SIZE "Trackpad Thumb Size" // int, 2 values - ignore palm touches, disable trackpad on palm touches #define MTRACK_PROP_PALM_DETECT "Trackpad Palm Detection" // int, 1 value - size #define MTRACK_PROP_PALM_SIZE "Trackpad Palm Size" // int, 2 value - button hold, wait time #define MTRACK_PROP_GESTURE_SETTINGS "Trackpad Gesture Settings" // int, 1 value - enable high precision scrolling #define MTRACK_PROP_SMOOTH_SCROLL "Trackpad Smooth Scroll" // int, 3 values // first: distance before a scroll event (two finger swipe) is triggered // second: how much milliseconds button will be hold after {up,down,left,right} scroll // third: sensitivity of the pointer during scroll gesture multiplied by 1000, value of 0 disables drag-to-move #define MTRACK_PROP_SCROLL_SETTINGS "Trackpad Scroll Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_SCROLL_BUTTONS "Trackpad Scroll Buttons" // int, 3 values - enable coasting, coasting enable speed, decelaration amount #define MTRACK_PROP_SCROLL_COAST "Trackpad Scroll Coasting" // int, 2 values // first: distance before a three finger swipe event is triggered // second: how much milliseconds button will be hold after {up,down,left,right} swipe // third: sensitivity of the pointer during three finger swipe gesture multiplied by 1000, value of 0 disables drag-to-move #define MTRACK_PROP_SWIPE_SETTINGS "Trackpad Swipe Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_SWIPE_BUTTONS "Trackpad Swipe Buttons" // int, 3 values // first: distance before a four finger swipe event is triggered // second: how much milliseconds button will be hold after {up,down,left,right} swipe4 // third: sensitivity of the pointer during four finger swipe gesture multiplied by 1000, value of 0 disables drag-to-move #define MTRACK_PROP_SWIPE4_SETTINGS "Trackpad Swipe4 Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_SWIPE4_BUTTONS "Trackpad Swipe4 Buttons" // int, 7 values // first: distance before event triggered (0 disable) // second: how much miliseconds to hold button down // third: sensitivity of the pointer during gesture // rest: {up,down,left,right} buttons #define MTRACK_PROP_EDGE_SCROLL_SETTINGS "Trackpad Edge Scroll Settings" // int, 4 value - size of edge regions that are ignored: {up,down,left,right} #define MTRACK_PROP_EDGE_SIZES "Trackpad Edge Sizes" // int, 1 value - distance before a scale event is triggered #define MTRACK_PROP_SCALE_DIST "Trackpad Scale Distance" // int, 2 values - up button, down button #define MTRACK_PROP_SCALE_BUTTONS "Trackpad Scale Buttons" // int, 1 value - distance before a rotate event is triggered #define MTRACK_PROP_ROTATE_DIST "Trackpad Rotate Distance" // int, 2 values - left button, right button #define MTRACK_PROP_ROTATE_BUTTONS "Trackpad Rotate Buttons" // int, 2 values - hold&move with one stationary and one moving finger // first: stationary finger max move distance // second: button #define MTRACK_PROP_HOLD1_MOVE1_STATIONARY_SETTINGS "Trackpad Hold1Move1 Stationary Settings" // int, 2 values // first: distance before a four swipe event is triggered // second: how much milliseconds button will be hold after gesture #define MTRACK_PROP_HOLD1_MOVE1_SETTINGS "Trackpad Hold1Move1 Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_HOLD1_MOVE1_BUTTONS "Trackpad Hold1Move1 Buttons" // int, 2 values - hold&move with one stationary and two moving fingers // first: stationary finger max move distance // second: button #define MTRACK_PROP_HOLD1_MOVE2_STATIONARY_SETTINGS "Trackpad Hold1Move2 Stationary Settings" // int, 2 values // first: distance before a four swipe event is triggered // second: how much milliseconds button will be hold after gesture #define MTRACK_PROP_HOLD1_MOVE2_SETTINGS "Trackpad Hold1Move2 Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_HOLD1_MOVE2_BUTTONS "Trackpad Hold1Move2 Buttons" // int, 2 values - hold&move with one stationary and three moving finger // first: stationary finger max move distance // second: button #define MTRACK_PROP_HOLD1_MOVE3_STATIONARY_SETTINGS "Trackpad Hold1Move3 Stationary Settings" // int, 2 values // first: distance before a four swipe event is triggered // second: how much milliseconds button will be hold after gesture #define MTRACK_PROP_HOLD1_MOVE3_SETTINGS "Trackpad Hold1Move3 Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_HOLD1_MOVE3_BUTTONS "Trackpad Hold1Move3 Buttons" // int, 5 values - enable, timeout, wait, dist, lock timeout #define MTRACK_PROP_DRAG_SETTINGS "Trackpad Drag Settings" // int, 2 values - invert x axis, invert y axis #define MTRACK_PROP_AXIS_INVERT "Trackpad Axis Inversion" struct MProps { // Properties Config Atom float_type; // Adjustable Properties Atom api; Atom trackpad_disable; Atom sensitivity; Atom pressure; Atom button_settings; Atom button_emulate_settings; Atom button_emulate_values; Atom tap_settings; Atom tap_emulate; Atom thumb_detect; Atom thumb_size; Atom palm_detect; Atom palm_size; Atom gesture_settings; Atom scroll_smooth; struct MPropsSwipe{ Atom settings; Atom buttons; } scroll, swipe3, swipe4/*, swipe5*/; Atom scroll_coast; Atom edge_scroll; Atom scale_dist; Atom scale_buttons; Atom rotate_dist; Atom rotate_buttons; Atom hold1_move1_stationary; struct MPropsSwipe hold1_move1; #if 0 Atom hold1_move2_stationary; struct MPropsSwipe hold1_move2; Atom hold1_move3_stationary; struct MPropsSwipe hold1_move3; #endif Atom drag_settings; Atom axis_invert; Atom edge_sizes; }; void mprops_init(struct MConfig* cfg, InputInfoPtr local); int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly); #endif ================================================ FILE: include/mtouch.h ================================================ /*************************************************************************** * * Multitouch X driver * Copyright (C) 2008 Henrik Rydberg * Copyright (C) 2011 Ryan Bourgeois * Copyright (C) 2015-2018 Paweł Turkowski * * 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" #include "os.h" /* xorg/os.h for timers */ struct MTouch { int fd; struct mtdev dev; struct Capabilities caps; struct HWState hs; struct MTState state; struct MConfig cfg; struct Gestures gs; int timer_kind; // to avoid setting timer multiple times /* * Timers documentation: * http://www.x.org/releases/X11R7.7/doc/xorg-server/Xserver-spec.html#id2536042 */ OsTimerPtr timer; int absolute_mode; // Should absolute mode be enabled ? 0 or 1 ValuatorMask* valuator_mask; DeviceIntPtr local_dev; }; int mtouch_configure(struct MTouch* mt, int fd); int mtouch_open(struct MTouch* mt, int fd); int mtouch_close(struct MTouch* mt); int mtouch_read(struct MTouch* mt); int mtouch_delayed(struct MTouch* mt); /** * \defgroup Timers Perform asynchronous tasks * * Timers are defined by labels (IDs). To add new timer create new ID, and implement * appropriate section in mt_timer_start, mt_timer_stop, and mt_timer_callback. * * All timers share same X resource, so generally you can't have more than one timer * ticking at time. For now it's fine. * * Functions are implemented in mtrack.c * * @{ */ /** Will cancel any timer */ #define MT_TIMER_ANY -1 #define MT_TIMER_NONE 0 /* #define -MT_TIMER_ANY 1 - reserved */ #define MT_TIMER_DELAYED_BUTTON 2 #define MT_TIMER_COASTING 3 void mt_timer_start(struct MTouch *mt, int kind); /** * Executed on early stop. * For example when one timer is replaced by naother. * @param mt */ void mt_timer_stop(struct MTouch *mt); CARD32 mt_timer_callback(OsTimerPtr timer, CARD32 time, void *arg); void mt_timer_stop_if(struct MTouch *mt, int kind); /** * Restart timer with same kind. * Does not call start nor stop callbacks. * @param mt */ void mt_timer_continue(struct MTouch *mt, mstime_t timeout); /** * Kind > 0 starts timer, kind < 0 stops timer with ID == abs(kind). * MT_TIMER_ANY stops whatever timer is running. * @param mt * @param kind */ void mt_timer_start_or_stop(struct MTouch *mt, int kind); /** }@ */ #endif ================================================ FILE: include/mtstate.h ================================================ /*************************************************************************** * * 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" #include "capabilities.h" #define MT_NEW 0 #define MT_RELEASED 1 #define MT_INVALID 2 #define MT_THUMB 3 #define MT_PALM 4 #define MT_EDGE 5 #define MT_TAP 6 #define MT_BUTTON 7 struct Touch { bitmask_t flags; double direction; int tracking_id; int x, y, dx, dy; int total_dx, total_dy; struct timeval down; }; struct MTState { bitmask_t touch_used; struct Touch touch[DIM_TOUCHES]; }; /* * |1| 2 |3| * |-|-------|-| * |4| 5 |6| * |-|-------|-| * |7| 8 |9| */ int touch_which_edge(const struct MConfig* cfg, const struct Touch* t); /* 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, const struct Capabilities* caps); #endif ================================================ FILE: include/trig.h ================================================ /*************************************************************************** * * 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 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); /* Average a collection of angles. */ double trig_angles_avg(double* angles, int len); /* 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 ================================================ FILE: required_packages.txt ================================================ xserver-xorg-dev mtdev-dev libmtdev-dev xutils-dev ================================================ FILE: src/capabilities.c ================================================ /*************************************************************************** * * 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) - 1), 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); //#define MIMIC_CUSTOM_CAPS #ifdef MIMIC_CUSTOM_CAPS cap->has_abs[MTDEV_POSITION_X] = 1; cap->has_abs[MTDEV_POSITION_Y] = 1; cap->has_abs[MTDEV_TRACKING_ID] = 1; cap->has_abs[MTDEV_TOUCH_MAJOR] = 0; cap->has_abs[MTDEV_TOUCH_MINOR] = 0; cap->has_abs[MTDEV_WIDTH_MAJOR] = 0; cap->has_abs[MTDEV_WIDTH_MINOR] = 0; cap->has_abs[MTDEV_ORIENTATION] = 0; cap->has_abs[MTDEV_PRESSURE] = 0; #endif 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; } int translate_cap_x(const struct Capabilities *cap, int x) { int mid = get_cap_xmid(cap); return x - mid; } int translate_cap_y(const struct Capabilities *cap, int y) { int mid = get_cap_ymid(cap); return y - mid; } int get_cap_xmin(const struct Capabilities *cap) { const struct input_absinfo *x = &cap->abs[MTDEV_POSITION_X]; return x->minimum; } int get_cap_ymin(const struct Capabilities *cap) { const struct input_absinfo *y = &cap->abs[MTDEV_POSITION_Y]; return y->minimum; } void output_capabilities(const struct Capabilities *cap) { char line[1024]; int i; LOG_INFO("Capabilities: \n"); LOG_INFO_CONT("devname: %s\n", cap->devname); LOG_INFO_CONT("devid: %x %x %x\n", cap->devid.vendor, cap->devid.product, cap->devid.version); char cap_names[MT_ABS_SIZE][24] = { "ABS_MT_TOUCH_MAJOR", "ABS_MT_TOUCH_MINOR", "ABS_MT_WIDTH_MAJOR", "ABS_MT_WIDTH_MINOR", "ABS_MT_ORIENTATION", "ABS_MT_POSITION_X", "ABS_MT_POSITION_Y", "ABS_MT_TOOL_TYPE", "ABS_MT_BLOB_ID", "ABS_MT_TRACKING_ID", "ABS_MT_PRESSURE" }; for (i = 0; i < MT_ABS_SIZE; i++) { if (cap->has_abs[i]) LOG_INFO_CONT("%s: min: %d max: %d\n", cap_names[i], cap->abs[i].minimum, cap->abs[i].maximum); } if(cap->has_slot) LOG_INFO_CONT("ABS_MT_SLOT: min: %d max: %d\n", cap->slot.minimum, cap->slot.maximum); 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); ADDCAP(line, cap, slot); LOG_INFO_CONT("enabled caps:%s\n", line); } ================================================ FILE: src/gestures.c ================================================ /*************************************************************************** * * Multitouch X driver * Copyright (C) 2008 Henrik Rydberg * Copyright (C) 2011 Ryan Bourgeois * Copyright (C) 2015-2018 Paweł Turkowski * * Gestures * Copyright (C) 2008 Henrik Rydberg * Copyright (C) 2010 Arturo Castro * Copyright (C) 2011 Ryan Bourgeois * Copyright (C) 2015-2018 Paweł Turkowski * * 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 "mtouch.h" #include "trig.h" //#define DEBUG_GESTURES 1 #ifdef DEBUG_GESTURES # define LOG_DEBUG_GESTURES LOG_DEBUG # define LOG_EMULATED LOG_INFO #else # define LOG_DEBUG_GESTURES LOG_DISABLED # define LOG_EMULATED LOG_DISABLED #endif #define IS_VALID_BUTTON(x) (x >= 0 && x <= 31) static void break_coasting(struct Gestures* gs){ gs->scroll_speed_x = gs->scroll_speed_y = 0.0f; } 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); LOG_DEBUG_GESTURES("trigger_button_up: %d up\n", button); } } static void trigger_button_down(struct Gestures* gs, int button) { struct timeval epoch; timerclear(&epoch); if (IS_VALID_BUTTON(button) && (button != gs->button_delayed || !IS_VALID_BUTTON(gs->button_delayed)) ) { SETBIT(gs->buttons, button); LOG_DEBUG_GESTURES("trigger_button_down: %d down\n", button); } else if (IS_VALID_BUTTON(button)) LOG_DEBUG_GESTURES("trigger_button_down: %d down ignored, in delayed mode\n", button); } static void trigger_button_emulation(struct Gestures* gs, int button) { #if 0 if (IS_VALID_BUTTON(button) && GETBIT(gs->buttons, 0)) { CLEARBIT(gs->buttons, 0); SETBIT(gs->buttons, button); gs->button_emulate = button; LOG_DEBUG_GESTURES("trigger_button_emulation: %d emulated\n", button); } #else trigger_button_down(gs, button); gs->integrated_emulated_button = button + 1; #endif } static void trigger_button_emulation_end(struct Gestures* gs) { if(gs->integrated_emulated_button > 0){ trigger_button_up(gs, gs->integrated_emulated_button - 1); gs->integrated_emulated_button = 0; } } int trigger_delayed_button_uncond(struct Gestures* gs) { int button; // clear button before timer (unless compiler decide otherwise) button = gs->button_delayed; gs->button_delayed = -1; timerclear(&gs->button_delayed_time); gs->move_dist = 0; /* don't count movement from delayed button phase in next stroke */ LOG_DEBUG_GESTURES("trigger_delayed_button: %d up, timer expired\n", button); trigger_button_up(gs, button); return button; } /* * If trigger_up_time is NULL or epoch time it will set timer to infinity - button up will * be send when user finish gesture. */ static void trigger_button_click(struct Gestures* gs, int button, struct timeval* trigger_up_time) { #ifdef DEBUG_GESTURES struct timeval delta; #endif if (!IS_VALID_BUTTON(button)) return; if (!IS_VALID_BUTTON(gs->button_delayed)) { trigger_button_down(gs, button); gs->button_delayed = button; if(trigger_up_time == NULL) timerclear(&gs->button_delayed_time); // "infinite timer", wait for gesture end else timercp(&gs->button_delayed_time, trigger_up_time); // may be also "infinite" #ifdef DEBUG_GESTURES timersub(&gs->button_delayed_time, &gs->time, &delta); LOG_DEBUG_GESTURES("trigger_button_click: %d placed in delayed mode; delta: %lld ms\n", button, timertoms(&delta)); #endif } #ifdef DEBUG_GESTURES else LOG_DEBUG_GESTURES("trigger_button_click: %d ignored, in delayed mode\n", button); #endif } static void reset_drag(struct Gestures* gs){ if (gs->drag_state == GS_DRAG_ACTIVE) { gs->drag_state = GS_NONE; timerclear(&gs->move_drag_expire); trigger_button_up(gs, 0); LOG_DEBUG_GESTURES("reset_drag: drag stopped\n"); } } static void trigger_drag_ready(struct Gestures* gs, const struct MConfig* cfg) { gs->drag_state = GS_DRAG_READY; timeraddms(&gs->time, cfg->drag_timeout, &gs->move_drag_expire); LOG_DEBUG_GESTURES("trigger_drag_ready: drag is ready\n"); /* Break coasting */ break_coasting(gs); } static int trigger_drag_start(struct Gestures* gs, const struct MConfig* cfg, int dx, int dy) { if (gs->drag_state == GS_DRAG_READY) { timerclear(&gs->move_drag_expire); if (cfg->drag_wait == 0) { gs->drag_state = GS_DRAG_ACTIVE; trigger_button_down(gs, 0); LOG_DEBUG_GESTURES("trigger_drag_start: drag is active\n"); } else { gs->drag_state = GS_DRAG_WAIT; gs->move_drag_dx = dx; gs->move_drag_dy = dy; timeraddms(&gs->time, cfg->drag_wait, &gs->move_drag_wait); LOG_DEBUG_GESTURES("trigger_drag_start: drag in wait\n"); } } else if (gs->drag_state == GS_DRAG_WAIT) { gs->move_drag_dx += dx; gs->move_drag_dy += dy; if (!timercmp(&gs->time, &gs->move_drag_wait, <)) { gs->drag_state = GS_DRAG_ACTIVE; trigger_button_down(gs, 0); LOG_DEBUG_GESTURES("trigger_drag_start: drag is active\n"); } else if (dist2(gs->move_drag_dx, gs->move_drag_dy) > SQRVAL(cfg->drag_dist)) { gs->drag_state = GS_NONE; LOG_DEBUG_GESTURES("trigger_drag_start: drag canceled, moved too far\n"); } } return gs->drag_state != GS_DRAG_WAIT; } static void trigger_drag_stop(struct Gestures* gs, const struct MConfig* cfg) { int lock_timeout; lock_timeout = cfg->drag_lock_timeout; if (gs->drag_state == GS_DRAG_READY) { /* if timeout < 0 then tap-dragging will require another tap to break the drag */ if (lock_timeout >= 0 && timercmp(&gs->time, &gs->move_drag_expire, >=)) { LOG_DEBUG_GESTURES("trigger_drag_stop: locked drag expored\n"); trigger_button_up(gs, 0); } gs->drag_state = GS_NONE; timerclear(&gs->move_drag_expire); LOG_DEBUG_GESTURES("trigger_drag_stop: drag canceled\n"); } else if (gs->drag_state == GS_DRAG_ACTIVE) { if(lock_timeout == 0){ reset_drag(gs); } else{ /* Tap to drag lock timeout implementaion: * Instead of breaking dragging completely, just take one step back * to ready state and start timer waiting for future dragging */ gs->drag_state = GS_DRAG_READY; timeraddms(&gs->time, lock_timeout, &gs->move_drag_expire); LOG_DEBUG_GESTURES("trigger_drag_stop: drag in wait lock\n"); } } } /* Return 0 if current time stamp is greater than move_wait time stamp. */ static int can_change_gesture_type(struct Gestures* gs, int desired_gesture){ if (gs->move_type == desired_gesture) return 1; if(gs->move_type == GS_NONE || gs->move_type == GS_MOVE) return 1; return timercmp(&gs->time, &gs->move_wait, >); } static void buttons_update(struct Gestures* gs, const struct MConfig* cfg, const struct HWState* hs, struct MTState* ms) { if (!cfg->button_enable || cfg->trackpad_disable >= 3) return; static bitmask_t button_prev = 0U; int i, integrated_down, integrated_up; int integrated_just_clicked, integrated_just_released; integrated_down = 0; integrated_up = 0; integrated_just_clicked = GETBIT(hs->button, 0) && !GETBIT(button_prev, 0); integrated_just_released = !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)) { integrated_down++; } else { integrated_up++; } } button_prev = hs->button; if (integrated_down) { int earliest, latest, lowest; gs->move_type = GS_NONE; timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait); earliest = -1; latest = -1; lowest = -1; foreach_bit(i, ms->touch_used) { if (!cfg->button_zones && GETBIT(ms->touch[i].flags, MT_INVALID)) continue; if (cfg->button_integrated) SETBIT(ms->touch[i].flags, MT_BUTTON); /* Mark all existing touches as physical button press */ if (lowest == -1 || ms->touch[i].y > ms->touch[lowest].y) /* The logic/naming seems to be inverted here */ lowest = i; if (earliest == -1 || timercmp(&ms->touch[i].down, &ms->touch[earliest].down, <)) earliest = i; if (latest == -1 || timercmp(&ms->touch[i].down, &ms->touch[latest].down, >)) latest = i; } if (integrated_just_clicked) { LOG_EMULATED("buttons_update: integrated_just_clicked=true\n"); int is_emulation_triggered = 0; if (cfg->button_zones && lowest >= 0) { is_emulation_triggered = buttons_zone_update(gs, cfg, ms, lowest); } if (!is_emulation_triggered) { touch_detect_update(gs, cfg, ms, latest); } } } if (integrated_just_released) { foreach_bit(i, ms->touch_used) { if (cfg->button_integrated) { /* <-- I know it can be outide loop, but this is less error prone */ /* Physical button is no longer down, so unmark touches */ CLEARBIT(ms->touch[i].flags, MT_BUTTON); } } trigger_button_emulation_end(gs); } } /* * Handle the physical button click in relation to touch properties. * It count the number of valid finger touching the pad while clicking it to * emulate the action in the user config. See "ClickFinger#" configuration parameter. */ static void touch_detect_update( struct Gestures* gs, const struct MConfig* cfg, const struct MTState* ms, int latest) { int i = 0, touching = 0; struct timeval expire; /*If latest is not set, the finger placement is not valid*/ if (latest >= 0) { foreach_bit(i, ms->touch_used) { microtime(&expire); timeraddms(&ms->touch[i].down, cfg->button_expire, &expire); if ((cfg->button_move || cfg->button_expire == 0 || timercmp(&ms->touch[latest].down, &expire, <)) && !(cfg->ignore_thumb && GETBIT(ms->touch[i].flags, MT_THUMB)) && !(cfg->ignore_palm && GETBIT(ms->touch[i].flags, MT_PALM)) && !(GETBIT(ms->touch[i].flags, MT_EDGE))) { touching++; LOG_EMULATED("touch_detect_udpate: latest >=0: touching++ =%d\n", touching); } } } LOG_EMULATED("touch_detect_udpate: latest=%d: touching=%d\n", latest, touching); if (touching == 0 && cfg->button_0touch > 0) { /* The integrated physical button have been pressed but no finger are valid * This code path can be reached by enabling the ClickFinger0 in the config file. */ trigger_button_emulation(gs, cfg->button_0touch - 1); } else if (touching == 1 && cfg->button_1touch > 0) trigger_button_emulation(gs, cfg->button_1touch - 1); else if (touching == 2 && cfg->button_2touch > 0) trigger_button_emulation(gs, cfg->button_2touch - 1); else if (touching == 3 && cfg->button_3touch > 0) trigger_button_emulation(gs, cfg->button_3touch - 1); } /* * Handle the button zone options for triggering button emulation * Up to three zones can be setup, one for each button_touch type. * This function take into account a maximum height for zone action. * * Return: "true" if a button_emulation was triggered, else false */ static int buttons_zone_update( struct Gestures* gs, const struct MConfig* cfg, const struct MTState* ms, int lowest) { int i, zones, left, right, pos_x, pos_y; double width; double limit_height; zones = 0; if (cfg->button_first_zone > 0) zones++; if (cfg->button_second_zone > 0) zones++; if (cfg->button_third_zone > 0) zones++; if (zones > 0) { limit_height = -1; pos_y = 0; /* Check if the zone need to be limited in height */ if(cfg->is_button_zones_height_limited != 0 && cfg->edge_bottom_size != 0) { limit_height = cfg->pad_height - cfg->pad_height * ((double)cfg->edge_bottom_size / 100.0); pos_y = cfg->pad_height / 2 + ms->touch[lowest].y; LOG_EMULATED("button_zone_update: limit_height %f, pos_y %d, pad_height %d, edge_bottom_size %d\n", limit_height, pos_y, cfg->pad_height, cfg->edge_bottom_size); } /* If no height limit is set in the config, it will always be true */ if(pos_y >= limit_height) { width = ((double)cfg->pad_width) / ((double)zones); pos_x = cfg->pad_width / 2 + ms->touch[lowest].x; /* Why not mean x of all touches? */ LOG_EMULATED("button_zone_update: pad width %d, zones %d, zone width %f, zone height %f, x %d\n", cfg->pad_width, zones, width, limit_height, pos_x); for (i = 0; i < zones; i++) { left = width*i; right = width*(i+1); if ((i == 0 || pos_x >= left) && (i == zones - 1 || pos_x <= right)) { LOG_EMULATED("button_zone_update: button %d, left %d, right %d (found)\n", i, left, right); break; } else LOG_EMULATED("button_zone_update: button %d, left %d, right %d\n", i, left, right); } if (i == 0) trigger_button_emulation(gs, cfg->button_first_zone - 1); else if (i == 1) trigger_button_emulation(gs, cfg->button_second_zone - 1); else if (i == 2) trigger_button_emulation(gs, cfg->button_third_zone - 1); return 1; } } return 0; } /* * So, tapping. Tap begins with 0 fingers on trackpad, * then one or more are coming down, stay down for a moment, * and then all of them are released more or less simultaneously * * These 3 steps must be completed in relative short time. * Another requirement is that no other gesture can be made duting tapping. * * What can break taps: * too much time passed by * one of the fingers moved too far */ static void abort_tapping(struct Gestures* gs, struct MTState* ms){ int i; gs->tap_touching = 0; gs->tap_released = 0; timerclear(&gs->tap_timeout); foreach_bit(i, ms->touch_used) { CLEARBIT(ms->touch[i].flags, MT_TAP); } } #ifdef DEBUG_GESTURES #define LOG_TAP LOG_INFO #else #define LOG_TAP(...) #endif static void tapping_update(struct Gestures* gs, const struct MConfig* cfg, struct MTState* ms) { int i, dist; struct timeval tv_tmp; struct Touch* iTouch; int final_touch_count; int button; final_touch_count = 0; if (cfg->trackpad_disable >= 1) return; /* Check conditions for early exit - posibilities to break active tap */ if (!isepochtime(&gs->tap_timeout)){ /* Tap was started, check exit conditions */ if (timercmp(&gs->time, &gs->tap_timeout, >=)) { /* too much time passed by from first touch, stop waiting for incoming touches */ abort_tapping(gs, ms); LOG_TAP("tapping_update: break: too slow; !isepoch:%d, timercmp:%d\n", !isepochtime(&gs->tap_timeout), timercmp(&gs->time, &gs->tap_timeout, >=)); return; } foreach_bit(i, ms->touch_used) { iTouch = ms->touch + i; if (GETBIT(iTouch->flags, MT_TAP)) { /* Finger moved too far. */ dist = dist2(iTouch->total_dx, iTouch->total_dy); if (dist >= SQRVAL(cfg->tap_dist)) { abort_tapping(gs, ms); LOG_TAP("tapping_update: break: too far\n"); return; } } if (GETBIT(iTouch->flags, MT_INVALID) || GETBIT(iTouch->flags, MT_BUTTON)){ /* Invalid or physical button */ abort_tapping(gs, ms); LOG_TAP("tapping_update: break: invalid or button\n"); return; } /* If there is touch with any other flag than NEW, TAP, or RELEASED, * tap will be discontinued */ if (!(GETBIT(iTouch->flags, MT_NEW)|| GETBIT(iTouch->flags, MT_TAP)|| GETBIT(iTouch->flags, MT_RELEASED) ) ){ abort_tapping(gs, ms); LOG_TAP("tapping_update: break: not NEW, TAP, or RELEASED\n"); return; } } } /* Conditions satisfied, check for new touches/releases * All touches are either in NEW, TAP, or RELEASED state. */ foreach_bit(i, ms->touch_used) { iTouch = ms->touch + i; if (GETBIT(iTouch->flags, MT_NEW)) { /* New touch is coming */ gs->tap_touching += 1; SETBIT(iTouch->flags, MT_TAP); if(gs->tap_touching == 1){ /* That was first touch, start timer */ LOG_TAP("tapping_update: start new tap; timeout=%d\n", cfg->tap_timeout); timeraddms(&gs->time, cfg->tap_timeout, &gs->tap_timeout); } } if (GETBIT(iTouch->flags, MT_RELEASED)) { gs->tap_touching -= 1; if(gs->tap_touching == 0){ /* Last finger released, time for decision */ final_touch_count = MAXVAL(gs->tap_released, gs->tap_touching + 1); /* At least one */ }else if(gs->tap_touching > 0){ /* Store how many fingers where down at tap's peak */ gs->tap_released = MAXVAL(gs->tap_released, gs->tap_touching + 1); } else{ /* gs->tap_touching is < 0 */ /* That means finges(s) were down, while tap wasn't active and finger was released */ gs->tap_touching = 0; trigger_drag_stop(gs, cfg); return; /* Pretty common situation; do nothing */ } LOG_TAP("tapping_update: touch released; gs->tap_touching=%d, gs->tap_released=%d\n", gs->tap_touching, gs->tap_released); } } if(final_touch_count == 0){ /* Tap is still posible, it's just not finished yet */ return; } switch(final_touch_count){ case 1: button = cfg->tap_1touch - 1; break; case 2: button = cfg->tap_2touch - 1; break; case 3: button = cfg->tap_3touch - 1; break; case 4: button = cfg->tap_4touch - 1; break; default: LOG_TAP("tapping_update: Something went really bad; final_touch_count=%d\n", final_touch_count); button = cfg->tap_4touch - 1; } timeraddms(&gs->time, cfg->tap_hold, &tv_tmp); /* How long button should be hold down */ trigger_button_click(gs, button, &tv_tmp); if (cfg->drag_enable && button == 0) trigger_drag_ready(gs, cfg); gs->move_type = GS_NONE; timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait); abort_tapping(gs, ms); } #undef LOG_TAP static void trigger_move(struct Gestures* gs, const struct MConfig* cfg, int dx, int dy) { if ((gs->move_type == GS_MOVE || timercmp(&gs->time, &gs->move_wait, >=)) && (dx != 0 || dy != 0)) { if (trigger_drag_start(gs, cfg, dx, dy)) { gs->move_dx = dx*cfg->sensitivity; gs->move_dy = dy*cfg->sensitivity; break_coasting(gs); gs->move_type = GS_MOVE; gs->move_dist = 0; gs->move_dir = TR_NONE; timerclear(&gs->move_wait); LOG_DEBUG_GESTURES("trigger_move: %.2f, %.2f\n", gs->move_dx, gs->move_dy); } } } static double get_swipe_dir_n(const struct Touch* touches[DIM_TOUCHES], int count) { if(count > DIM_TOUCHES || count <= 0) return TR_NONE; if(count == 1) return touches[0]->direction; int i; double avg_dir; double angles[DIM_TOUCHES]; /* Find average direction */ for (i = 0; i < count; ++i) { if (touches[i]->direction == TR_NONE) return TR_NONE; angles[i] = touches[i]->direction; } avg_dir = trig_angles_avg(angles, count); /* Check if all touches have (almost) same direction */ for (i = 0; i < count; ++i) { if (trig_angles_acute(avg_dir, touches[i]->direction) > 0.5) return TR_NONE; } return avg_dir; } static void get_swipe_avg_xy(const struct Touch* touches[DIM_TOUCHES], int count, double* out_x, double* out_y){ double x, y; x = y = 0.0; int i; for (i = 0; i < count; ++i) { x += touches[i]->dx; y += touches[i]->dy; } *out_x = x/(double)count; *out_y = y/(double)count; } /* To avoid users' confusion accept diagonal direction only if same button was * bound to aligned directions. * If different buttons were used and it's diagonal movement we can't decide * which button schould be generated. * It's better to ignore one gesture than confuse user. */ int get_button_for_dir(const struct MConfigSwipe* cfg_swipe, int dir){ switch (dir){ case TR_NONE: if(cfg_swipe->up_btn == cfg_swipe->lt_btn && cfg_swipe->lt_btn == cfg_swipe->dn_btn && cfg_swipe->dn_btn == cfg_swipe->rt_btn) return cfg_swipe->up_btn; return -1; case TR_DIR_UP: return cfg_swipe->up_btn; case TR_DIR_DN: return cfg_swipe->dn_btn; case TR_DIR_LT: return cfg_swipe->lt_btn; case TR_DIR_RT: return cfg_swipe->rt_btn; case (8 + TR_DIR_UP + TR_DIR_LT) / 2: if(cfg_swipe->up_btn != cfg_swipe->lt_btn) return -1; return cfg_swipe->up_btn; case (TR_DIR_LT + TR_DIR_DN) / 2: if(cfg_swipe->lt_btn != cfg_swipe->dn_btn) return -1; return cfg_swipe->lt_btn; case (TR_DIR_DN + TR_DIR_RT) / 2: if(cfg_swipe->dn_btn != cfg_swipe->rt_btn) return -1; return cfg_swipe->dn_btn; case (TR_DIR_RT + TR_DIR_UP) / 2: if(cfg_swipe->rt_btn != cfg_swipe->up_btn) return -1; return cfg_swipe->rt_btn; default: return -1; } } /* It's called 'unsafe' because caller have to check that all conditions required to * trigger swipe were met. */ static int trigger_swipe_unsafe(struct Gestures* gs, const struct MConfig* cfg, const struct MConfigSwipe* cfg_swipe, const struct Touch* touches[4], int touches_count, int move_type_to_trigger) { double avg_move_x, avg_move_y; int button; double dist; int dir; struct timeval tv_tmp; if (touches_count <= 0) return 0; /** Is that kind of scroll enabled? */ if (!cfg->scroll_smooth && cfg_swipe->dist <= 0) return 0; dir = trig_generalize(get_swipe_dir_n(touches, touches_count)); button = get_button_for_dir(cfg_swipe, dir); if(button == -1){ /* No button? Probably fingers were still down, * but without movement. */ return 0; } trigger_drag_stop(gs, cfg); get_swipe_avg_xy(touches, touches_count, &avg_move_x, &avg_move_y); // hypot(1/n * (x0 + ... + xn); 1/n * (y0 + ... + yn)) <=> 1/n * hypot(x0 + ... + xn; y0 + ... + yn) dist = hypot(avg_move_x, avg_move_y); if(cfg_swipe->drag_sens){ gs->move_dx = cfg->sensitivity * avg_move_x * cfg_swipe->drag_sens * 0.001; gs->move_dy = cfg->sensitivity * avg_move_y * cfg_swipe->drag_sens * 0.001; } else{ gs->move_dx = gs->move_dy = 0.0; } if (gs->move_type != move_type_to_trigger){ trigger_delayed_button_uncond(gs); gs->move_dist = 0; } else if (gs->move_dir != dir){ gs->move_dist = 0; } gs->move_type = move_type_to_trigger; gs->move_dist += ABSVAL(dist); gs->move_dir = dir; timeraddms(&gs->time, cfg->gesture_wait + 5 /*bonus from me*/, &gs->move_wait); /* Special case for smooth scrolling */ if(cfg->scroll_smooth && button >= 4 && button <= 7){ /* Calculate speed vector */ /* Forbid perpendicular movement: this pre-set will disable h-scroll * while v-scroll is active. See Github #19 */ gs->scroll_speed_x = 0.0; gs->scroll_speed_y = 0.0; /* Map selected _button_ (instead of dir) to scroll axis */ /* Detect 'natural scrolling' - situation when user fipped scroll up and * down buttons. Sign of speed vactor have to match selected button. */ switch(button){ case 4: gs->scroll_speed_y = -ABSVAL(avg_move_y); break; /* scroll up - always negative */ case 5: gs->scroll_speed_y = ABSVAL(avg_move_y); break; /* scroll down - always positve */ case 6: gs->scroll_speed_x = -ABSVAL(avg_move_x); break; /* scroll left */ case 7: gs->scroll_speed_x = ABSVAL(avg_move_x); break; /* scroll right */ } /* Valuator increment=1.0 so I can scale by dist manually here. */ /* Delta component will be kept in scroll_speed. */ gs->scroll_speed_x = gs->scroll_speed_x / (double)(cfg_swipe->dist /* * timertoms(&gs->dt) */); gs->scroll_speed_y = gs->scroll_speed_y / (double)(cfg_swipe->dist /* * timertoms(&gs->dt) */); gs->scroll_speed_valid = 1; LOG_INFO2(DISABLED, "smooth scrolling: speed: x: %lf, y: %lf\n", gs->scroll_speed_x, gs->scroll_speed_y); if (cfg->scroll_coast.no_boost) /* Prevent scroll coasting boost */ gs->coasting_duration_left = 0; else /* Reset coasting duration 'to go' ticks. */ gs->coasting_duration_left = cfg->scroll_coast.duration - 1; /* Don't modulo move_dist */ } else if (gs->move_dist >= cfg_swipe->dist) { if(cfg_swipe->hold != 0) timeraddms(&gs->time, cfg_swipe->hold, &tv_tmp); else timerclear(&tv_tmp); // wait for gesture end gs->move_dist = MODVAL(gs->move_dist, cfg_swipe->dist); trigger_button_click(gs, button - 1, &tv_tmp); } LOG_DEBUG_GESTURES("trigger_swipe_button: swiping %f in direction %d (at %f of %d)\n", dist, dir, gs->move_dist, cfg_swipe->dist); return 1; } /* Return: * 0 - it wasn't swipe * 1 - it was swipe and was executed * other value - it was swipe, but couldn't be executed */ static int trigger_swipe(struct Gestures* gs, const struct MConfig* cfg, const struct Touch* touches[4], int touches_count) { int move_type_to_trigger; const struct MConfigSwipe* cfg_swipe; /* Feature: allow transition from low order swipes into higher ones. * Motivation: it's extremly hard to start fast swipe3 or swipe4 without triggering * swipe2 earlier. */ int can_transit_swipe; can_transit_swipe = FALSE; switch(touches_count){ case 2: cfg_swipe = &cfg->scroll; move_type_to_trigger = GS_SCROLL; break; case 3: cfg_swipe = &cfg->swipe3; move_type_to_trigger = GS_SWIPE3; can_transit_swipe = gs->move_type == GS_SCROLL; break; case 4: cfg_swipe = &cfg->swipe4; move_type_to_trigger = GS_SWIPE4; can_transit_swipe = gs->move_type == GS_SCROLL || gs->move_type == GS_SWIPE3; break; default: goto not_a_swipe; } if (can_change_gesture_type(gs, move_type_to_trigger) || can_transit_swipe != FALSE){ if (trigger_swipe_unsafe(gs, cfg, cfg_swipe, touches, touches_count, move_type_to_trigger)) return 1; goto not_a_swipe; } not_a_swipe:{ //gs->scroll_speed_x = gs->scroll_speed_y = 0.0; //if(is_any_swipe(gs->move_type)){ // gs->move_type = GS_NONE; //} return 0; } } /* Return: * 0 - it wasn't swipe * 1 - it was swipe and was executed * other value - it was swipe, but couldn't be executed */ static int trigger_edge(struct Gestures* gs, const struct MConfig* cfg, const struct Touch* touch) { int dir = TR_NONE; int edge = touch_which_edge(cfg, touch); switch (edge){ case 5: case 7: case 8: case 9: /* Always disable scrolling for bottom edge. */ return 0; } if (GETBIT(touch->flags, MT_EDGE) == 0) return 0; dir = trig_generalize(touch->direction); if (dir == TR_NONE) return 0; return trigger_swipe_unsafe(gs, cfg, &cfg->edge_scroll, &touch, 1, GS_SCROLL); } /* Compute hypot from x, y and compare it with given value */ static int hypot_cmp(int x, int y, int value) { int lhs, rhs; lhs = x * x + y * y; rhs = value * value; if(lhs == rhs) return 0; if(lhs < rhs) return -1; return 1; } /* Compute hypot from x, y and compare it with given value */ int hypot_cmpf(double x, double y, double value) { double lhs, rhs; lhs = x * x + y * y; rhs = value * value; if(lhs == rhs) return 0; if(lhs < rhs) return -1; return 1; } static int is_touch_stationary(const struct Touch* touch, int max_movement) { return touch->direction == TR_NONE || (hypot_cmp(touch->total_dx, touch->total_dy, max_movement) <= 0); } static int can_trigger_hold_move(const struct Gestures* gs, const struct Touch* touches[DIM_TOUCHES], int touches_count, const struct MConfig* cfg, int max_move) { struct timeval tv_tmp; if (touches_count <= 1) return 0; /* Condition: allow only translation from 'neutral' move type. */ if(gs->move_type != GS_NONE && gs->move_type != GS_MOVE) return 0; /* Conditions: was first finger hold in place for some time */ if (!is_touch_stationary(touches[0], max_move)) return 0; /* todo: make this time configurable */ timeraddms(&touches[0]->down, MAXVAL(cfg->tap_timeout * 1.2, 350), &tv_tmp); if (timercmp(&gs->time, &tv_tmp, <)) /* time < down + wait ?*/ return 0; if (timercmp(&gs->time, &gs->move_wait, <)) return 0; /* Condition: are other fingers making swipe gesture */ if (get_swipe_dir_n(touches+1, touches_count-1) == TR_NONE) return 0; return 1; } /* Map hold-and-move gesture type to touches */ static int hold_move_gesture_to_touches(int move_type, int real_touches_count){ switch (move_type){ case GS_HOLD1_MOVE1: return 2; case GS_HOLD1_MOVE2: return 3; case GS_HOLD1_MOVE3: return 4; } return real_touches_count; } static int is_hold_move(struct Gestures* gs) { static int invalid_mark = -1; return hold_move_gesture_to_touches(gs->move_type, invalid_mark) != invalid_mark; } /* Right now only gesture with one stationary finger and one moving finger is supported. * Gestures with two or more stationary fingers or two or more moving fingers * are not implemented, but I guess it will be easy to extend this function to support * them. */ static int trigger_hold_move(struct Gestures* gs, const struct MConfig* cfg, const struct Touch* touches[DIM_TOUCHES], int touches_count) { int move_type_to_trigger; const struct MConfigSwipe* cfg_swipe; int stationary_max_move, stationary_btn; /* At the moment there's only one Hold1Move* gesture, so this line is fine, * but after addition of other similar gestures it will become invalid, since we * can't initialize stationary button yet (wrom which cfg it should be read?) */ stationary_btn = cfg->hold1_move1_stationary.button - 1; move_type_to_trigger = -1; /* Additional code for future features like hold1move{2,3} */ switch(hold_move_gesture_to_touches(gs->move_type, touches_count)){ case 0: case 1: /* Process them later. */ break; case 2: move_type_to_trigger = GS_HOLD1_MOVE1; stationary_btn = cfg->hold1_move1_stationary.button - 1; stationary_max_move = cfg->hold1_move1_stationary.max_move; cfg_swipe = &cfg->hold1_move1; break; #if 0 case 3: move_type_to_trigger = GS_HOLD1_MOVE2; stationary_btn = cfg->hold1_move2_stationary.button - 1; stationary_max_move = cfg->hold1_move2_stationary.max_move; cfg_swipe = &cfg->hold1_move2; break; case 4: move_type_to_trigger = GS_HOLD1_MOVE3; stationary_btn = cfg->hold1_move3_stationary.button - 1; stationary_max_move = cfg->hold1_move3_stationary.max_move; cfg_swipe = &cfg->hold1_move3; break; #endif default: return 0; } if (stationary_btn < 0){ /* feature disabled, reurn immediately */ return 0; } if (is_hold_move(gs)){ /* Condition: no fingers or stationary just released or stationary moved */ if (touches_count == 0 || GETBIT(touches[0]->flags, MT_RELEASED) || !is_touch_stationary(touches[0], stationary_max_move)){ /* Stationary finger released or moved too far */ gs->move_type = GS_NONE; trigger_delayed_button_uncond(gs); trigger_button_up(gs, stationary_btn); return 1; } else if (touches_count == 1){ /* Only one finger is touching, it's stationary. * The gesture was initiated earlier. * Block other actions/movements. */ return 1; } } if (touches_count <= 1 || move_type_to_trigger == -1) return 0; if (gs->move_type == move_type_to_trigger){ return trigger_swipe_unsafe(gs, cfg, cfg_swipe, touches + 1, touches_count - 1, move_type_to_trigger); } else if (can_trigger_hold_move(gs, touches, touches_count, cfg, stationary_max_move)){ trigger_button_down(gs, stationary_btn); return trigger_swipe_unsafe(gs, cfg, cfg_swipe, touches + 1, touches_count - 1, move_type_to_trigger); } return 0; } #if defined(DEBUG_GESTURES) #define LOG_SCALE LOG_INFO #else #define LOG_SCALE(...) #endif #define ZOOM_IN TR_DIR_UP #define ZOOM_OUT TR_DIR_DN #define ZOOM_UNKNOWN TR_NONE static int calc_scale_dir(const struct Touch* t0, const struct Touch* t1){ int dist_sq, dist_prev_sq; int x0_prev, y0_prev; int x1_prev, y1_prev; x0_prev = t0->x - t0->dx; y0_prev = t0->y - t0->dy; x1_prev = t1->x - t1->dx; y1_prev = t1->y - t1->dy; dist_sq = dist2(t0->x - t1->x, t0->y - t1->y); dist_prev_sq = dist2(x0_prev - x1_prev, y0_prev - y1_prev); if(dist_prev_sq == dist_sq) return ZOOM_UNKNOWN; return dist_sq > dist_prev_sq ? ZOOM_IN : ZOOM_OUT; } static int trigger_scale(struct Gestures* gs, const struct MConfig* cfg, const struct Touch* t0, const struct Touch* t1){ double d0, d1, angles_diff, max_error; int dir; int dist; max_error = 45; /* = sum of difference of directions, in degrees */ max_error = max_error * 8.0/360.0; /* Convert degrees to our from 0 to 8 angle system */ dir = TR_NONE; if (gs->move_type != GS_SCALE && timercmp(&gs->time, &gs->move_wait, <)) { return 0; } d0 = t0->direction; d1 = t1->direction; if(d0 == TR_NONE && d1 == TR_NONE){ LOG_SCALE("trigger_scale: no directions\n"); return 0; } /* Handle scaling with one finger */ if(d0 == TR_NONE || d1 == TR_NONE){ LOG_SCALE("trigger_scale: scaling with one finger\n"); return 0; } /* Check if both vectors are on more or less same axis, but with different directions: */ angles_diff = trig_angles_acute(d0, d1); /* less eq 4.0 */ angles_diff = trig_angles_sub(4.0, angles_diff); if(angles_diff > max_error){ LOG_SCALE("trigger_scale: movement foo tar from scaling axis, diff: %lf, max_error: %lf\n", angles_diff, max_error); return 0; } dist = dist2(t0->dx,t0->dy) + dist2(t1->dx, t1->dy); /* Determinate is it zoom in or zoom out */ dir = calc_scale_dir(t0, t1); struct timeval tv_tmp; trigger_drag_stop(gs, cfg); if (gs->move_type != GS_SCALE || gs->move_dir != dir) gs->move_dist = 0; gs->move_dx = gs->move_dy = 0.0; gs->move_type = GS_SCALE; gs->move_dist += ABSVAL(dist); gs->move_dir = dir; timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait); if (gs->move_dist >= cfg->scale_dist) { gs->move_dist = MODVAL(gs->move_dist, cfg->scale_dist); timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp); if (dir == ZOOM_IN) trigger_button_click(gs, cfg->scale_up_btn - 1, &tv_tmp); else if (dir == ZOOM_OUT) trigger_button_click(gs, cfg->scale_dn_btn - 1, &tv_tmp); } return 1; } #undef ZOOM_IN #undef ZOOM_OUT #undef ZOOM_UNKNOWN static void trigger_rotate(struct Gestures* gs, const struct MConfig* cfg, double dist, int dir) { if (gs->move_type == GS_ROTATE || !timercmp(&gs->time, &gs->move_wait, <)) { struct timeval tv_tmp; trigger_drag_stop(gs, cfg); if (gs->move_type != GS_ROTATE || gs->move_dir != dir) gs->move_dist = 0; gs->move_dx = 0.0; gs->move_dy = 0.0; gs->move_type = GS_ROTATE; gs->move_dist += ABSVAL(dist); gs->move_dir = dir; timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait); if (gs->move_dist >= cfg->rotate_dist) { gs->move_dist = MODVAL(gs->move_dist, cfg->rotate_dist); timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp); if (dir == TR_DIR_LT) trigger_button_click(gs, cfg->rotate_lt_btn - 1, &tv_tmp); else if (dir == TR_DIR_RT) trigger_button_click(gs, cfg->rotate_rt_btn - 1, &tv_tmp); } LOG_DEBUG_GESTURES("trigger_rotate: rotating %f in direction %d (at %f of %d)\n", dist, dir, gs->move_dist, cfg->rotate_dist); } } static void trigger_reset(struct Gestures* gs) { reset_drag(gs); gs->move_dx = gs->move_dy = 0.0; /* Don't reset scroll speed cuz it may break things. */ gs->move_type = GS_NONE; gs->move_dist = 0; gs->move_dir = TR_NONE; timerclear(&gs->move_wait); } 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 void moving_update(struct Gestures* gs, const struct MConfig* cfg, struct MTState* ms) { int i, count, btn_count, dx, dy, dir; double dist; const struct Touch* touches[DIM_TOUCHES]; const struct Touch* edge_touch; edge_touch = NULL; count = btn_count = 0; dx = dy = 0; dir = 0; // Reset movement. gs->move_dx = gs->move_dy = 0.0; // Count touches and aggregate touch movements. foreach_bit(i, ms->touch_used) { if (GETBIT(ms->touch[i].flags, MT_INVALID)){ if (GETBIT(ms->touch[i].flags, MT_EDGE)) edge_touch = &ms->touch[i]; // only valid when count == 1, so could be overwritten here continue; } else if (GETBIT(ms->touch[i].flags, MT_BUTTON)) { btn_count++; dx += ms->touch[i].dx; dy += ms->touch[i].dy; } else if (!GETBIT(ms->touch[i].flags, MT_TAP)) { if (count < DIM_TOUCHES) touches[count++] = &ms->touch[i]; } } // Determine gesture type. if (cfg->trackpad_disable < 1 && trigger_hold_move(gs, cfg, touches, count)){ /* nothing to do */ } else if (count == 0) { if (btn_count >= 1 && cfg->trackpad_disable < 2) trigger_move(gs, cfg, dx, dy); // one touch down, but count == 0 because it was not counted there else if(edge_touch != NULL && trigger_edge(gs, cfg, edge_touch) != 0){ } else if (btn_count < 1) trigger_reset(gs); } else if (count == 1 && cfg->trackpad_disable < 2) { dx += touches[0]->dx; dy += touches[0]->dy; trigger_move(gs, cfg, dx, dy); } else if (count == 2 && cfg->trackpad_disable < 1) { // scroll, scale, or rotate if (trigger_swipe(gs, cfg, touches, count)) { /* nothing to do */ } else if ((dir = get_rotate_dir(touches[0], touches[1])) != TR_NONE) { dist = ABSVAL(hypot(touches[0]->dx, touches[0]->dy)) + ABSVAL(hypot(touches[1]->dx, touches[1]->dy)); trigger_rotate(gs, cfg, dist/2, dir); } else if(trigger_scale(gs,cfg, touches[0], touches[1])){ /* nothing to do */ } } else if ((count == 3 || count == 4) && cfg->trackpad_disable < 1) { if (trigger_swipe(gs, cfg, touches, count)) { /* nothing to do */ } } } static void dragging_update(struct Gestures* gs, const struct MConfig* cfg) { if (gs->drag_state == GS_DRAG_READY && timercmp(&gs->time, &gs->move_drag_expire, >)) { LOG_DEBUG_GESTURES("dragging_update: drag expired\n"); trigger_drag_stop(gs, cfg); } } static int is_timer_infinite(struct Gestures* gs){ return isepochtime(&gs->button_delayed_time); } static void delayed_update(struct Gestures* gs) { // if there's no delayed button - return if(!IS_VALID_BUTTON(gs->button_delayed)) return; if (!is_timer_infinite(gs) && timercmp(&gs->time, &gs->button_delayed_time, >=)) { LOG_DEBUG_GESTURES("delayed_update: %d delay expired, triggering up\n", gs->button_delayed); trigger_delayed_button_uncond(gs); } else { #ifdef DEBUG_GESTURES struct timeval delta; timersub(&gs->button_delayed_time, &gs->time, &delta); LOG_DEBUG_GESTURES("delayed_update: %d still waiting, new delta %lld ms\n", gs->button_delayed, timertoms(&delta)); #endif } } void gestures_init(struct MTouch* mt) { memset(&mt->gs, 0, sizeof(struct Gestures)); timerclear(&mt->gs.tap_timeout); } void gestures_extract(struct MTouch* mt) { timersub(&mt->hs.evtime, &mt->gs.time, &mt->gs.dt); timercp(&mt->gs.time, &mt->hs.evtime); dragging_update(&mt->gs, &mt->cfg); buttons_update(&mt->gs, &mt->cfg, &mt->hs, &mt->state); tapping_update(&mt->gs, &mt->cfg, &mt->state); moving_update(&mt->gs, &mt->cfg, &mt->state); delayed_update(&mt->gs); } /** * Executed every input time frame, at least once. First time from 'read_input' to check if * timer is needed. * This function returns timer ID which should be installed/disabled(if negative). * * Return vale meaning: * - 0 - no delay to handle, don't install timer, do nothing * - MT_TIMER_* - install timer * - -MT_TIMER_* - remove timer * - MT_TIMER_ANY - remove any timer */ int gestures_delayed(struct MTouch* mt) { struct Gestures* gs = &mt->gs; struct MTState* ms = &mt->state; struct timeval now, delta; int i, fingers_released, fingers_down; // count released fingers fingers_released = fingers_down = 0; foreach_bit(i, ms->touch_used) { if (GETBIT(ms->touch[i].flags, MT_RELEASED)) ++fingers_released; else if (!GETBIT(ms->touch[i].flags, MT_INVALID)) ++fingers_down; } // if there's no delayed button - do nothing if(!IS_VALID_BUTTON(gs->button_delayed)) return MT_TIMER_NONE; /* Condition: was finger released and gesture is 'infinite' and it's not hold&move */ if(fingers_released != 0 && is_timer_infinite(gs) && !is_hold_move(gs)){ /* Gesture finished - it's time to send "button up" event immediately without * checking for delivery time. */ trigger_delayed_button_uncond(gs); gs->move_dx = gs->move_dy = 0.0; gs->move_type = GS_NONE; return -MT_TIMER_DELAYED_BUTTON; /* remove delayed button timer */ } if(is_timer_infinite(gs)) return MT_TIMER_NONE; microtime(&now); //timersub(&now, &mt->gs.time, &mt->gs.dt); //timercp(&mt->gs.time, &now); if(timercmp(&gs->button_delayed_time, &now, >)){ timersub(&gs->button_delayed_time, &now, &delta); /* That second check may seem unnecessary, but it is not. * Even if button delayed time is > than now time, timertoms may still return 0 * because it truncates time to miliseconds. It's important because truncated time * is used to setup timer. */ if(timertoms(&delta) > 1){ LOG_DEBUG_GESTURES("gestures_delayed: %d delayed, new delta: %lld ms\n", gs->button_delayed, timertoms(&delta)); return MT_TIMER_DELAYED_BUTTON; } } trigger_delayed_button_uncond(gs); gs->move_dx = gs->move_dy = 0.0; return -MT_TIMER_DELAYED_BUTTON; /* remove delayed button timer */ } ================================================ FILE: src/hwstate.c ================================================ /*************************************************************************** * * 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) { 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; } timercp(&s->evtime, &syn->time); } /* * This function will return: * 1 - when SYN_REPORT received * 0 - otherwise * * More on kernel input events: * https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt * https://www.kernel.org/doc/Documentation/input/event-codes.txt * http://linuxwacom.sourceforge.net/wiki/index.php/Kernel_Input_Event_Overview#Touchpad_Overview */ 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; } ================================================ FILE: src/mconfig.c ================================================ /*************************************************************************** * * 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; cfg->edge_top_size = DEFAULT_EDGE_TOP_SIZE; cfg->edge_bottom_size = DEFAULT_EDGE_BOTTOM_SIZE; cfg->edge_left_size = DEFAULT_EDGE_LEFT_SIZE; cfg->edge_right_size = DEFAULT_EDGE_RIGHT_SIZE; // Configure Gestures cfg->trackpad_disable = DEFAULT_TRACKPAD_DISABLE; cfg->button_enable = DEFAULT_BUTTON_ENABLE; cfg->button_integrated = DEFAULT_BUTTON_INTEGRATED; cfg->button_expire = DEFAULT_BUTTON_EXPIRE; cfg->button_zones = DEFAULT_BUTTON_ZONES; cfg->is_button_zones_height_limited = DEFAULT_IS_BUTTON_ZONES_HEIGHT_LIMITED; cfg->button_first_zone = DEFAULT_BUTTON_FIRST_ZONE; cfg->button_second_zone = DEFAULT_BUTTON_SECOND_ZONE; cfg->button_third_zone = DEFAULT_BUTTON_THIRD_ZONE; cfg->button_0touch = DEFAULT_BUTTON_0TOUCH; cfg->button_1touch = DEFAULT_BUTTON_1TOUCH; cfg->button_2touch = DEFAULT_BUTTON_2TOUCH; cfg->button_3touch = DEFAULT_BUTTON_3TOUCH; cfg->tap_1touch = DEFAULT_TAP_1TOUCH; cfg->tap_2touch = DEFAULT_TAP_2TOUCH; cfg->tap_3touch = DEFAULT_TAP_3TOUCH; cfg->tap_4touch = DEFAULT_TAP_4TOUCH; cfg->tap_timeout = DEFAULT_TAP_TIMEOUT; cfg->tap_hold = DEFAULT_TAP_HOLD; cfg->tap_dist = DEFAULT_TAP_DIST; cfg->gesture_hold = DEFAULT_GESTURE_HOLD; cfg->gesture_wait = DEFAULT_GESTURE_WAIT; cfg->scroll.dist = DEFAULT_SCROLL_DIST; cfg->scroll.hold = DEFAULT_SCROLL_HOLD; cfg->scroll.drag_sens = DEFAULT_SWIPE_SENS; cfg->scroll.up_btn = DEFAULT_SCROLL_UP_BTN; cfg->scroll.dn_btn = DEFAULT_SCROLL_DN_BTN; cfg->scroll.lt_btn = DEFAULT_SCROLL_LT_BTN; cfg->scroll.rt_btn = DEFAULT_SCROLL_RT_BTN; cfg->scroll_coast.min_speed = DEFAULT_SCROLL_COAST_MIN_SPEED; cfg->scroll_coast.tick_ms = DEFAULT_SCROLL_COAST_TICK_MS; cfg->scroll_coast.duration = DEFAULT_SCROLL_COAST_DURATION; cfg->scroll_coast.no_boost = DEFAULT_SCROLL_COAST_NO_BOOST; cfg->scroll_coast.ease = DEFAULT_SCROLL_COAST_EASE; cfg->swipe3.dist = DEFAULT_SWIPE_DIST; cfg->swipe3.hold = DEFAULT_SWIPE_HOLD; cfg->swipe3.drag_sens = DEFAULT_SWIPE_SENS; cfg->swipe3.up_btn = DEFAULT_SWIPE_UP_BTN; cfg->swipe3.dn_btn = DEFAULT_SWIPE_DN_BTN; cfg->swipe3.lt_btn = DEFAULT_SWIPE_LT_BTN; cfg->swipe3.rt_btn = DEFAULT_SWIPE_RT_BTN; cfg->swipe4.dist = DEFAULT_SWIPE4_DIST; cfg->swipe4.hold = DEFAULT_SWIPE_HOLD; cfg->swipe4.drag_sens = DEFAULT_SWIPE_SENS; cfg->swipe4.up_btn = DEFAULT_SWIPE4_UP_BTN; cfg->swipe4.dn_btn = DEFAULT_SWIPE4_DN_BTN; cfg->swipe4.lt_btn = DEFAULT_SWIPE4_LT_BTN; cfg->swipe4.rt_btn = DEFAULT_SWIPE4_RT_BTN; cfg->edge_scroll.dist = DEFAULT_EDGE_SCROLL_DIST; cfg->edge_scroll.hold = DEFAULT_SCROLL_HOLD; cfg->edge_scroll.drag_sens = DEFAULT_SWIPE_SENS; cfg->edge_scroll.up_btn = DEFAULT_SCROLL_UP_BTN; cfg->edge_scroll.dn_btn = DEFAULT_SCROLL_DN_BTN; cfg->edge_scroll.lt_btn = DEFAULT_SCROLL_LT_BTN; cfg->edge_scroll.rt_btn = DEFAULT_SCROLL_RT_BTN; cfg->scale_dist = DEFAULT_SCALE_DIST; cfg->scale_up_btn = DEFAULT_SCALE_UP_BTN; cfg->scale_dn_btn = DEFAULT_SCALE_DN_BTN; cfg->rotate_dist = SQRVAL(DEFAULT_ROTATE_DIST); cfg->rotate_lt_btn = DEFAULT_ROTATE_LT_BTN; cfg->rotate_rt_btn = DEFAULT_ROTATE_RT_BTN; cfg->hold1_move1_stationary.max_move = DEFAULT_HOLD1_MOVE1_STATIONARY_MAX_MOVE; cfg->hold1_move1_stationary.button = DEFAULT_HOLD1_MOVE1_STATIONARY_BTN; cfg->hold1_move1.dist = DEFAULT_HOLD1_MOVE1_DIST; cfg->hold1_move1.hold = DEFAULT_HOLD1_MOVE1_HOLD; cfg->hold1_move1.drag_sens = DEFAULT_HOLD1_MOVE1_SENS; cfg->hold1_move1.up_btn = DEFAULT_HOLD1_MOVE1_BTN; cfg->hold1_move1.dn_btn = DEFAULT_HOLD1_MOVE1_BTN; cfg->hold1_move1.lt_btn = DEFAULT_HOLD1_MOVE1_BTN; cfg->hold1_move1.rt_btn = DEFAULT_HOLD1_MOVE1_BTN; #if 0 cfg->hold1_move2_stationary.max_move = DEFAULT_HOLD1_MOVE2_STATIONARY_MAX_MOVE; cfg->hold1_move2_stationary.button = DEFAULT_HOLD1_MOVE2_STATIONARY_BTN; cfg->hold1_move2.dist = DEFAULT_HOLD1_MOVE2_DIST; cfg->hold1_move2.hold = DEFAULT_HOLD1_MOVE2_HOLD; cfg->hold1_move2.drag_sens = DEFAULT_HOLD1_MOVE2_SENS; cfg->hold1_move2.up_btn = DEFAULT_HOLD1_MOVE2_BTN; cfg->hold1_move2.dn_btn = DEFAULT_HOLD1_MOVE2_BTN; cfg->hold1_move2.lt_btn = DEFAULT_HOLD1_MOVE2_BTN; cfg->hold1_move2.rt_btn = DEFAULT_HOLD1_MOVE2_BTN; cfg->hold1_move3_stationary.max_move = DEFAULT_HOLD1_MOVE3_STATIONARY_MAX_MOVE; cfg->hold1_move3_stationary.button = DEFAULT_HOLD1_MOVE3_STATIONARY_BTN; cfg->hold1_move3.dist = DEFAULT_HOLD1_MOVE3_DIST; cfg->hold1_move3.hold = DEFAULT_HOLD1_MOVE3_HOLD; cfg->hold1_move3.drag_sens = DEFAULT_HOLD1_MOVE3_SENS; cfg->hold1_move3.up_btn = DEFAULT_HOLD1_MOVE3_BTN; cfg->hold1_move3.dn_btn = DEFAULT_HOLD1_MOVE3_BTN; cfg->hold1_move3.lt_btn = DEFAULT_HOLD1_MOVE3_BTN; cfg->hold1_move3.rt_btn = DEFAULT_HOLD1_MOVE3_BTN; #endif cfg->drag_enable = DEFAULT_DRAG_ENABLE; cfg->drag_timeout = DEFAULT_DRAG_TIMEOUT; cfg->sensitivity = DEFAULT_SENSITIVITY; cfg->scroll_smooth = DEFAULT_SCROLL_HIGH_PREC; } 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; LOG_INFO("Touchpad supports regular and approaching touches.\n"); LOG_INFO(" touch_min = %d, touch_max = %d\n", cfg->touch_min, cfg->touch_max); } else if (caps->has_abs[MTDEV_TOUCH_MAJOR] && caps->has_abs[MTDEV_PRESSURE]) { cfg->touch_min = caps->abs[MTDEV_TOUCH_MAJOR].minimum; cfg->touch_max = caps->abs[MTDEV_TOUCH_MAJOR].maximum; cfg->pressure_min = caps->abs[MTDEV_PRESSURE].minimum; cfg->pressure_max = caps->abs[MTDEV_PRESSURE].maximum; /* select source of the events basing on its resolution */ if(cfg->pressure_max - cfg->pressure_min >= cfg->touch_max - cfg->touch_min) cfg->touch_type = MCFG_PRESSURE_SIZE; else cfg->touch_type = MCFG_SIZE_PRESSURE; LOG_INFO("Touchpad is pressure based, but supports regular touches also.\n"); LOG_INFO(" touch_min = %d, touch_max = %d\n", cfg->touch_min, cfg->touch_max); LOG_INFO(" pressure_min = %d, pressure_max = %d\n", cfg->pressure_min, cfg->pressure_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; LOG_INFO("Touchpad supports regular touches.\n"); LOG_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->pressure_min = caps->abs[MTDEV_PRESSURE].minimum; cfg->pressure_max = caps->abs[MTDEV_PRESSURE].maximum; LOG_INFO("Touchpad is pressure based.\n"); LOG_INFO(" pressure_min = %d, pressure_max = %d\n", cfg->pressure_min, cfg->pressure_max); } else { cfg->touch_type = MCFG_NONE; LOG_WARNING("Touchpad has minimal capabilities. Some features will be unavailable.\n"); } if (cfg->touch_minor) LOG_INFO("Touchpad supports minor touch widths.\n"); } void mconfig_configure(struct MConfig* cfg, pointer opts) { // 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->trackpad_disable = CLAMPVAL(xf86SetIntOption(opts, "TrackpadDisable", DEFAULT_TRACKPAD_DISABLE), 0, 3); cfg->button_enable = xf86SetBoolOption(opts, "ButtonEnable", DEFAULT_BUTTON_ENABLE); cfg->button_integrated = xf86SetBoolOption(opts, "ButtonIntegrated", DEFAULT_BUTTON_INTEGRATED); cfg->button_expire = MAXVAL(xf86SetIntOption(opts, "ButtonTouchExpire", DEFAULT_BUTTON_EXPIRE), 0); cfg->button_zones = xf86SetBoolOption(opts, "ButtonZonesEnable", DEFAULT_BUTTON_ZONES); cfg->is_button_zones_height_limited = xf86SetBoolOption(opts, "LimitButtonZonesToBottomEdge", DEFAULT_IS_BUTTON_ZONES_HEIGHT_LIMITED); cfg->button_first_zone = CLAMPVAL(xf86SetIntOption(opts, "FirstZoneButton", DEFAULT_BUTTON_FIRST_ZONE), 0, 32); cfg->button_second_zone = CLAMPVAL(xf86SetIntOption(opts, "SecondZoneButton", DEFAULT_BUTTON_SECOND_ZONE), 0, 32); cfg->button_third_zone = CLAMPVAL(xf86SetIntOption(opts, "ThirdZoneButton", DEFAULT_BUTTON_THIRD_ZONE), 0, 32); cfg->button_0touch = CLAMPVAL(xf86SetIntOption(opts, "ClickFinger0", DEFAULT_BUTTON_0TOUCH), 0, 32); cfg->button_1touch = CLAMPVAL(xf86SetIntOption(opts, "ClickFinger1", DEFAULT_BUTTON_1TOUCH), 0, 32); cfg->button_2touch = CLAMPVAL(xf86SetIntOption(opts, "ClickFinger2", DEFAULT_BUTTON_2TOUCH), 0, 32); cfg->button_3touch = CLAMPVAL(xf86SetIntOption(opts, "ClickFinger3", DEFAULT_BUTTON_3TOUCH), 0, 32); cfg->button_move = xf86SetBoolOption(opts, "ButtonMoveEmulate", DEFAULT_BUTTON_MOVE); cfg->tap_1touch = CLAMPVAL(xf86SetIntOption(opts, "TapButton1", DEFAULT_TAP_1TOUCH), 0, 32); cfg->tap_2touch = CLAMPVAL(xf86SetIntOption(opts, "TapButton2", DEFAULT_TAP_2TOUCH), 0, 32); cfg->tap_3touch = CLAMPVAL(xf86SetIntOption(opts, "TapButton3", DEFAULT_TAP_3TOUCH), 0, 32); cfg->tap_4touch = CLAMPVAL(xf86SetIntOption(opts, "TapButton4", DEFAULT_TAP_4TOUCH), 0, 32); 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 = 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_smooth = CLAMPVAL(xf86SetIntOption(opts, "ScrollSmooth", DEFAULT_SCROLL_HIGH_PREC), 0, 1); cfg->scroll.dist = MAXVAL(xf86SetIntOption(opts, "ScrollDistance", DEFAULT_SCROLL_DIST), 1); cfg->scroll.hold = MAXVAL(xf86SetIntOption(opts, "ScrollClickTime", DEFAULT_SCROLL_HOLD), 0); cfg->scroll.drag_sens = MAXVAL(xf86SetIntOption(opts, "ScrollSensitivity", DEFAULT_SWIPE_SENS), 0); cfg->scroll.up_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollUpButton", DEFAULT_SCROLL_UP_BTN), 0, 32); cfg->scroll.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollDownButton", DEFAULT_SCROLL_DN_BTN), 0, 32); cfg->scroll.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollLeftButton", DEFAULT_SCROLL_LT_BTN), 0, 32); cfg->scroll.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollRightButton", DEFAULT_SCROLL_RT_BTN), 0, 32); cfg->scroll_coast.min_speed = MAXVAL(xf86SetRealOption(opts, "ScrollCoastEnableSpeed", DEFAULT_SCROLL_COAST_MIN_SPEED), 0.0); cfg->scroll_coast.tick_ms = MAXVAL(DEFAULT_SCROLL_COAST_TICK_MS, 1); cfg->scroll_coast.duration = MAXVAL(xf86SetRealOption(opts, "ScrollCoastDuration", DEFAULT_SCROLL_COAST_DURATION), 0); cfg->scroll_coast.no_boost = xf86SetBoolOption(opts, "ScrollCoastNoBoost", DEFAULT_SCROLL_COAST_NO_BOOST); cfg->scroll_coast.ease = xf86SetBoolOption(opts, "ScrollCoastEase", DEFAULT_SCROLL_COAST_EASE); cfg->swipe3.dist = MAXVAL(xf86SetIntOption(opts, "SwipeDistance", DEFAULT_SWIPE_DIST), 1); cfg->swipe3.hold = MAXVAL(xf86SetIntOption(opts, "SwipeClickTime", DEFAULT_SWIPE_HOLD), 0); cfg->swipe3.drag_sens = MAXVAL(xf86SetIntOption(opts, "SwipeSensitivity", DEFAULT_SWIPE_SENS), 0); cfg->swipe3.up_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeUpButton", DEFAULT_SWIPE_UP_BTN), 0, 32); cfg->swipe3.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeDownButton", DEFAULT_SWIPE_DN_BTN), 0, 32); cfg->swipe3.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeLeftButton", DEFAULT_SWIPE_LT_BTN), 0, 32); cfg->swipe3.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeRightButton", DEFAULT_SWIPE_RT_BTN), 0, 32); cfg->swipe4.dist = MAXVAL(xf86SetIntOption(opts, "Swipe4Distance", DEFAULT_SWIPE4_DIST), 1); cfg->swipe4.hold = MAXVAL(xf86SetIntOption(opts, "Swipe4ClickTime", DEFAULT_SWIPE_HOLD), 0); cfg->swipe4.drag_sens = MAXVAL(xf86SetIntOption(opts, "Swipe4Sensitivity", DEFAULT_SWIPE_SENS), 0); cfg->swipe4.up_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4UpButton", DEFAULT_SWIPE4_UP_BTN), 0, 32); cfg->swipe4.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4DownButton", DEFAULT_SWIPE4_DN_BTN), 0, 32); cfg->swipe4.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4LeftButton", DEFAULT_SWIPE4_LT_BTN), 0, 32); cfg->swipe4.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4RightButton", DEFAULT_SWIPE4_RT_BTN), 0, 32); cfg->edge_scroll.dist = MAXVAL(xf86SetIntOption(opts, "EdgeScrollDist", DEFAULT_EDGE_SCROLL_DIST), 1); cfg->edge_scroll.hold = MAXVAL(xf86SetIntOption(opts, "EdgeScrollClickTime", DEFAULT_SCROLL_HOLD), 0); cfg->edge_scroll.drag_sens = MAXVAL(xf86SetIntOption(opts, "EdgeScrollSensitivity", DEFAULT_SWIPE_SENS), 0); cfg->edge_scroll.up_btn = CLAMPVAL(xf86SetIntOption(opts, "EdgeScrollUpButton", DEFAULT_SCROLL_UP_BTN), 0, 32); cfg->edge_scroll.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "EdgeScrollDownButton", DEFAULT_SCROLL_DN_BTN), 0, 32); cfg->edge_scroll.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "EdgeScrollLeftButton", DEFAULT_SCROLL_LT_BTN), 0, 32); cfg->edge_scroll.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "EdgeScrollRightButton", DEFAULT_SCROLL_RT_BTN), 0, 32); int edge_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeSize", 101), 0, 101); if(edge_size < 0 || edge_size > 100){ cfg->edge_top_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeTopSize", DEFAULT_EDGE_TOP_SIZE), 0, 100); cfg->edge_bottom_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeBottomSize", DEFAULT_EDGE_BOTTOM_SIZE), 0, 100); cfg->edge_left_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeLeftSize", DEFAULT_EDGE_LEFT_SIZE), 0, 100); cfg->edge_right_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeRightSize", DEFAULT_EDGE_RIGHT_SIZE), 0, 100); }else{ xf86Msg(X_WARNING, "mtrack %s:%i: %s", __FILE__, __LINE__, "You' re using DEPRECATED 'EdgeSize' option, it will be removed, plese switch to Edge{Right,Left,Top,Bottom}Size options.\n"); cfg->edge_top_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeTopSize", edge_size), 0, 100); cfg->edge_bottom_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeBottomSize", edge_size), 0, 100); cfg->edge_left_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeLeftSize", edge_size), 0, 100); cfg->edge_right_size = CLAMPVAL(xf86SetIntOption(opts, "EdgeRightSize", edge_size), 0, 100); } cfg->scale_dist = MAXVAL(xf86SetIntOption(opts, "ScaleDistance", DEFAULT_SCALE_DIST), 1); cfg->scale_up_btn = CLAMPVAL(xf86SetIntOption(opts, "ScaleUpButton", DEFAULT_SCALE_UP_BTN), 0, 32); cfg->scale_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "ScaleDownButton", DEFAULT_SCALE_DN_BTN), 0, 32); cfg->rotate_dist = MAXVAL(xf86SetIntOption(opts, "RotateDistance", DEFAULT_ROTATE_DIST), 1); cfg->rotate_lt_btn = CLAMPVAL(xf86SetIntOption(opts, "RotateLeftButton", DEFAULT_ROTATE_LT_BTN), 0, 32); cfg->rotate_rt_btn = CLAMPVAL(xf86SetIntOption(opts, "RotateRightButton", DEFAULT_ROTATE_RT_BTN), 0, 32); cfg->hold1_move1_stationary.button = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move1StationaryButton", DEFAULT_HOLD1_MOVE1_STATIONARY_BTN), 0, 32); cfg->hold1_move1_stationary.max_move = MAXVAL(xf86SetIntOption(opts, "Hold1Move1StationaryMaxMove", DEFAULT_HOLD1_MOVE1_STATIONARY_MAX_MOVE), 1); cfg->hold1_move1.dist = MAXVAL(xf86SetIntOption(opts, "Hold1Move1Distance", DEFAULT_HOLD1_MOVE1_DIST), 1); cfg->hold1_move1.hold = MAXVAL(xf86SetIntOption(opts, "Hold1Move1ClickTime", DEFAULT_HOLD1_MOVE1_HOLD), 0); cfg->hold1_move1.drag_sens = MAXVAL(xf86SetIntOption(opts, "Hold1Move1Sensitivity", DEFAULT_HOLD1_MOVE1_SENS), 0); cfg->hold1_move1.up_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move1UpButton", DEFAULT_HOLD1_MOVE1_BTN), 0, 32); cfg->hold1_move1.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move1DownButton", DEFAULT_HOLD1_MOVE1_BTN), 0, 32); cfg->hold1_move1.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move1LeftButton", DEFAULT_HOLD1_MOVE1_BTN), 0, 32); cfg->hold1_move1.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move1RightButton", DEFAULT_HOLD1_MOVE1_BTN), 0, 32); #if 0 cfg->hold1_move2_stationary.button = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move2StationaryButton", DEFAULT_HOLD1_MOVE2_STATIONARY_BTN), 0, 32); cfg->hold1_move2_stationary.max_move = MAXVAL(xf86SetIntOption(opts, "Hold1Move2StationaryMaxMove", DEFAULT_HOLD1_MOVE2_STATIONARY_MAX_MOVE), 1); cfg->hold1_move2.dist = MAXVAL(xf86SetIntOption(opts, "Hold1Move2Distance", DEFAULT_HOLD1_MOVE2_DIST), 1); cfg->hold1_move2.hold = MAXVAL(xf86SetIntOption(opts, "Hold1Move2ClickTime", DEFAULT_HOLD1_MOVE2_HOLD), 0); cfg->hold1_move2.drag_sens = MAXVAL(xf86SetIntOption(opts, "Hold1Move2Sensitivity", DEFAULT_HOLD1_MOVE2_SENS), 0); cfg->hold1_move2.up_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move2UpButton", DEFAULT_HOLD1_MOVE2_BTN), 0, 32); cfg->hold1_move2.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move2DownButton", DEFAULT_HOLD1_MOVE2_BTN), 0, 32); cfg->hold1_move2.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move2LeftButton", DEFAULT_HOLD1_MOVE2_BTN), 0, 32); cfg->hold1_move2.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move2RightButton", DEFAULT_HOLD1_MOVE2_BTN), 0, 32); cfg->hold1_move3_stationary.button = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move3StationaryButton", DEFAULT_HOLD1_MOVE3_STATIONARY_BTN), 0, 32); cfg->hold1_move3_stationary.max_move = MAXVAL(xf86SetIntOption(opts, "Hold1Move3StationaryMaxMove", DEFAULT_HOLD1_MOVE3_STATIONARY_MAX_MOVE), 1); cfg->hold1_move3.dist = MAXVAL(xf86SetIntOption(opts, "Hold1Move3Distance", DEFAULT_HOLD1_MOVE3_DIST), 1); cfg->hold1_move3.hold = MAXVAL(xf86SetIntOption(opts, "Hold1Move3ClickTime", DEFAULT_HOLD1_MOVE3_HOLD), 0); cfg->hold1_move3.drag_sens = MAXVAL(xf86SetIntOption(opts, "Hold1Move3Sensitivity", DEFAULT_HOLD1_MOVE3_SENS), 0); cfg->hold1_move3.up_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move3UpButton", DEFAULT_HOLD1_MOVE3_BTN), 0, 32); cfg->hold1_move3.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move3DownButton", DEFAULT_HOLD1_MOVE3_BTN), 0, 32); cfg->hold1_move3.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move3LeftButton", DEFAULT_HOLD1_MOVE3_BTN), 0, 32); cfg->hold1_move3.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "Hold1Move3RightButton", DEFAULT_HOLD1_MOVE3_BTN), 0, 32); #endif cfg->drag_enable = xf86SetBoolOption(opts, "TapDragEnable", DEFAULT_DRAG_ENABLE); cfg->drag_timeout = MAXVAL(xf86SetIntOption(opts, "TapDragTime", DEFAULT_DRAG_TIMEOUT), 1); cfg->drag_wait = MAXVAL(xf86SetIntOption(opts, "TapDragWait", DEFAULT_DRAG_WAIT), 0); cfg->drag_dist = MAXVAL(xf86SetIntOption(opts, "TapDragDist", DEFAULT_DRAG_DIST), 0); cfg->drag_lock_timeout = xf86SetIntOption(opts, "TapDragLockTimeout", DEFAULT_DRAG_LOCK_TIMEOUT); cfg->axis_x_invert = xf86SetBoolOption(opts, "AxisXInvert", DEFAULT_AXIS_X_INVERT); cfg->axis_y_invert = xf86SetBoolOption(opts, "AxisYInvert", DEFAULT_AXIS_Y_INVERT); cfg->sensitivity = MAXVAL(xf86SetRealOption(opts, "Sensitivity", DEFAULT_SENSITIVITY), 0); } ================================================ FILE: src/mtouch.c ================================================ /*************************************************************************** * * 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) { mt->fd = fd; int rc = read_capabilities(&mt->caps, mt->fd); if (rc < 0) return rc; output_capabilities(&mt->caps); return 0; } int mtouch_open(struct MTouch* mt, int fd) { int ret; mt->fd = fd; ret = mtdev_open(&mt->dev, mt->fd); if (ret) goto error; mconfig_init(&mt->cfg, &mt->caps); hwstate_init(&mt->hs, &mt->caps); mtstate_init(&mt->state); gestures_init(mt); 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 ret; if (use_grab) { SYSCALL(ret = ioctl(mt->fd, EVIOCGRAB, 0)); if (ret) LOG_WARNING("mtouch: ungrab failed\n"); } mtdev_close(&mt->dev); return 0; } int mtouch_read(struct MTouch* mt) { int ret = hwstate_modify(&mt->hs, &mt->dev, mt->fd, &mt->caps); if (ret <= 0) return ret; mtstate_extract(&mt->state, &mt->cfg, &mt->hs, &mt->caps); gestures_extract(mt); return 1; } int mtouch_delayed(struct MTouch* mt) { return gestures_delayed(mt); } ================================================ FILE: src/mtstate.c ================================================ /*************************************************************************** * * 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" #ifdef DEBUG_MTSTATE # define LOG_DEBUG_MTSTATE LOG_DEBUG #else # define LOG_DEBUG_MTSTATE LOG_DISABLED #endif 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; } static int inline pressure_range_ratio(const struct MConfig* cfg, int value) { return percentage(value - cfg->pressure_min, cfg->pressure_max - cfg->pressure_min); } static int finger_touch_ratio(const struct MConfig* cfg, const struct FingerState* hw) { switch(cfg->touch_type){ case MCFG_SCALE: return percentage(hw->touch_major, hw->width_major); /* = estimated pressure */ case MCFG_SIZE: case MCFG_SIZE_PRESSURE: return touch_range_ratio(cfg, hw->touch_major); case MCFG_PRESSURE_SIZE: case MCFG_PRESSURE: return pressure_range_ratio(cfg, hw->pressure); default: return 101; /* sholuld it be additional argument? or maybe it should return -1? */ } } /* Check if a finger is touching the trackpad. */ static int is_touch(const struct MConfig* cfg, const struct FingerState* hw) { return finger_touch_ratio(cfg, hw) > cfg->touch_down; } /* Check if a finger is released from the touchpad. */ static int is_release(const struct MConfig* cfg, const struct FingerState* hw) { return finger_touch_ratio(cfg, hw) < cfg->touch_up; } 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 (pct < cfg->thumb_ratio && size > cfg->thumb_size) { LOG_DEBUG_MTSTATE("is_thumb: yes %d > %d && %d > %d\n", pct, cfg->thumb_ratio, size, cfg->thumb_size); return 1; } else { LOG_DEBUG_MTSTATE("is_thumb: no %d > %d && %d > %d\n", pct, cfg->thumb_ratio, size, cfg->thumb_size); return 0; } } static int is_palm(const struct MConfig* cfg, const struct FingerState* hw) { int ratio; switch(cfg->touch_type){ case MCFG_SCALE: ratio = percentage(hw->touch_major, hw->width_major); break; case MCFG_SIZE: case MCFG_SIZE_PRESSURE: case MCFG_PRESSURE_SIZE: ratio = touch_range_ratio(cfg, hw->touch_major); break; case MCFG_PRESSURE: ratio = pressure_range_ratio(cfg, hw->pressure); break; default: return 0; } if (ratio > cfg->palm_size) { LOG_DEBUG_MTSTATE("is_palm: yes %d > %d\n", ratio, cfg->palm_size); return 1; } else { LOG_DEBUG_MTSTATE("is_palm: no %d > %d\n", ratio, cfg->palm_size); return 0; } } /* * |1| 2 |3| * |-|-------|-| * |4| 5 |6| * |-|-------|-| * |7| 8 |9| */ int touch_which_edge(const struct MConfig* cfg, const struct Touch* t) { typeof(cfg->pad_width) w = cfg->pad_width; typeof(cfg->pad_height) h = cfg->pad_height; /* Translate back origin from center of the device to top left. */ typeof(t->x) x = t->x + w/2; typeof(t->y) y = t->y + h/2; int out = 5; // see visualization above // y < (cfg->edge_top_size * cfg->pad_height)/100): if(y * 100 < cfg->edge_top_size * h) out = 2; // y > h - (cfg->edge_bottom_size * h)/100) : if((y - h) * 100 > -cfg->edge_bottom_size * h) out = 8; if(x * 100 < cfg->edge_left_size * w) out -= 1; // 1,4,7 // x > w - (cfg->edge_right_size * w)/100 if((x - w) * 100 > -cfg->edge_right_size * w) out += 1; // 3,6,9 LOG_INFO2(DISABLED, "x=%i y=%i out=%i w=%i h=%i\n", x,y,out,w,h); return out; } static int is_edge(const struct MConfig* cfg, const struct Touch* t) { return touch_which_edge(cfg, t) != 5; } /* 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 MConfig* cfg, const struct Capabilities* caps, const struct HWState* hs, int fn) { int x, y; int n = firstbit(~ms->touch_used); const struct FingerState* fs = &hs->data[fn]; if (n < 0) LOG_WARNING("Too many touches to track. Ignoring touch %d.\n", fs->tracking_id); else { /* Set origin (0,0) of 'struct Touch' coordinate system to central point of the device */ x = translate_cap_x(caps, fs->position_x); y = translate_cap_y(caps, fs->position_y); x = cfg->axis_x_invert ? -x : x; y = cfg->axis_y_invert ? -y : y; ms->touch[n].flags = 0U; timercp(&ms->touch[n].down, &hs->evtime); ms->touch[n].direction = TR_NONE; ms->touch[n].tracking_id = fs->tracking_id; ms->touch[n].x = x; ms->touch[n].y = 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].flags, MT_NEW); SETBIT(ms->touch_used, n); } return n; } /* Update a touch. */ static void touch_update(struct MTState* ms, const struct MConfig* cfg, const struct Capabilities* caps, const struct FingerState* fs, int touch) { int x, y; /* Translate origin of struct Touch coordinate system to middle point of device */ x = translate_cap_x(caps, fs->position_x); y = translate_cap_y(caps, fs->position_y); x = cfg->axis_x_invert ? -x : x; y = cfg->axis_y_invert ? -y : y; ms->touch[touch].dx = x - ms->touch[touch].x; ms->touch[touch].dy = 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 = x; ms->touch[touch].y = y; if (ms->touch[touch].dx != 0 || ms->touch[touch].dy != 0) { ms->touch[touch].direction = trig_direction(ms->touch[touch].dx, ms->touch[touch].dy); } CLEARBIT(ms->touch[touch].flags, 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].flags, MT_NEW); SETBIT(ms->touch[touch].flags, MT_RELEASED); } /* Invalidate all touches. */ static void touches_invalidate(struct MTState* ms) { int i; foreach_bit(i, ms->touch_used) SETBIT(ms->touch[i].flags, MT_INVALID); } /* Update all touches. */ static void touches_update(struct MTState* ms, const struct MConfig* cfg, const struct HWState* hs, const struct Capabilities* caps) { int i, n, disable = 0; // 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, cfg, caps, &hs->data[i], n); } else if (is_touch(cfg, &hs->data[i])) n = touch_append(ms, cfg, caps, hs, i); if (n >= 0) { // Track and invalidate thumb, palm, and edge touches. if (is_thumb(cfg, &hs->data[i])) SETBIT(ms->touch[n].flags, MT_THUMB); else CLEARBIT(ms->touch[n].flags, MT_THUMB); if (is_palm(cfg, &hs->data[i])) SETBIT(ms->touch[n].flags, MT_PALM); else CLEARBIT(ms->touch[n].flags, MT_PALM); if (is_edge(cfg, &ms->touch[n])) { if (GETBIT(ms->touch[n].flags, MT_NEW)) SETBIT(ms->touch[n].flags, MT_EDGE); } else CLEARBIT(ms->touch[n].flags, MT_EDGE); MODBIT(ms->touch[n].flags, MT_INVALID, (GETBIT(ms->touch[n].flags, MT_THUMB) && cfg->ignore_thumb) || (GETBIT(ms->touch[n].flags, MT_PALM) && cfg->ignore_palm) || GETBIT(ms->touch[n].flags, MT_EDGE)); disable |= cfg->disable_on_thumb && GETBIT(ms->touch[n].flags, MT_THUMB); disable |= cfg->disable_on_palm && GETBIT(ms->touch[n].flags, MT_PALM); } } if (disable) touches_invalidate(ms); } /* 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].flags, MT_RELEASED)) CLEARBIT(ms->touch_used, i); } } #if DEBUG_MTSTATE static void mtstate_output(const struct MTState* ms, const struct HWState* hs) { int i, n; struct timeval tv; n = bitcount(ms->touch_used); if (bitcount(ms->touch_used) > 0) { microtime(&tv); LOG_INFO("mtstate: %d touches at event time %llu (rt %llu)\n", n, timertoms(&hs->evtime), timertoms(&tv)); } foreach_bit(i, ms->touch_used) { if (GETBIT(ms->touch[i].flags, MT_RELEASED)) { timersub(&hs->evtime, &ms->touch[i].down, &tv); LOG_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, timertoms(&ms->touch[i].down), timertoms(&tv)); } else if (GETBIT(ms->touch[i].flags, MT_NEW)) { LOG_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, timertoms(&ms->touch[i].down)); } else if (GETBIT(ms->touch[i].flags, MT_INVALID)) { timersub(&hs->evtime, &ms->touch[i].down, &tv); LOG_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, timertoms(&ms->touch[i].down), timertoms(&tv)); } else { LOG_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, timertoms(&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, const struct Capabilities* caps) { touches_clean(ms); touches_update(ms, cfg, hs, caps); #if DEBUG_MTSTATE mtstate_output(ms, hs); #endif } ================================================ FILE: src/trig.c ================================================ /*************************************************************************** * * 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" #include /* Convert a radians value into an mtrack angle. */ static double trig_encode_radians(double radians) { double angle = (radians / M_PI) * 4.0; if (angle < 0.0) angle = angle + 8.0; return angle; } /* Convert an mtrack angle value into radians. */ static double trig_decode_radians(double angle) { if (angle < 4.0) return (angle * M_PI) / 4.0; else return ((8.0 - angle) * M_PI) / -4.0; } double trig_direction(double dx, double dy) { if (dx != 0 || dy != 0) return trig_encode_radians(atan2(dx, dy*-1)); return TR_NONE; } int trig_generalize(double dir) { if (dir == TR_NONE) return TR_NONE; else if (dir > 1.0 && dir <= 3.0) return TR_DIR_RT; else if (dir > 3.0 && dir <= 5.0) return TR_DIR_DN; else if (dir > 5.0 && dir <= 7.0) 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.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) { double angle; if (a1 > a2) angle = trig_angles_sub(a1, a2); else angle = trig_angles_sub(a2, a1); if (angle > 4.0) angle = 8.0 - angle; return angle; } double trig_angles_avg(double* angles, int len) { int i; double dx, dy, r; dx = dy = 0; for (i = 0; i < len; i++) { r = trig_decode_radians(angles[i]); dx += cos(r); dy += sin(r); } return trig_encode_radians(atan2(dy, dx)); } 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; } ================================================ FILE: tools/mtrack-test.c ================================================ /*************************************************************************** * * 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" #include #include #include void xf86Msg(int type, const char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) <= 13 typedef XF86OptionPtr pointer; #endif int xf86SetIntOption(XF86OptionPtr opts, const char *name, int deflt) { return deflt; } int xf86SetBoolOption(XF86OptionPtr opts, const char *name, int deflt) { return deflt; } double xf86SetRealOption(XF86OptionPtr opts, const char *name, double deflt) { return deflt; } 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.0 || gs->move_dy != 0.0) printf("moving (%lf, %lf)\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 (mtouch_read(&mt) > 0) print_gestures(&mt.gs); if (mtouch_delayed(&mt)) print_gestures(&mt.gs); } mtouch_close(&mt); } 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; } ================================================ FILE: tools/reconfigure-xinput.sh ================================================ #! /bin/bash # Script to parse configuration dumped by 'xinput --list-props ' # and reapply it on host machine. # It was created to help reconfigure mtrack switch easly between user's setup # and mine. logExec(){ echo $@ eval "$@" } # $1 - device id # $2 - file name to read new settings from main(){ local devId=$1 local fileToApply=$2 while read -r line || [[ -n "$line" ]]; do # echo line "$line" local propId=$(grep -Po '\(\d+\)' <<< $line) propId=$(grep -Po '\d+' <<< $propId) if [[ "$propId" == "" ]]; then continue fi local propName=$(grep -Po ".*\(" <<< $line) propName=${propName::-2} # echo $propName local values=$(grep -Po ':.*' <<< $line) values=${values:1} # skip colon values=${values//,/ } # remove commas # echo $propName $propId : ${values} logExec xinput --set-prop $devId \'$propName\' $values done < $fileToApply } # $1 - device id # $2 - file name to read new settings from main "$@"