[
  {
    "path": ".gitignore",
    "content": "*.o\n*.lo\n*.la\n*.swp\n*.dirstamp\n.deps/\n.libs/\nMakefile\nMakefile.in\n\n/m4\n/missing\n/libtool\ninstall-sh\nltmain.sh\n/aclocal.m4\n/autom4te.cache/\n/build-aux/\n/config.*\n/configure\n/stamp-h1\n/depcomp\n/compile\n\ndebian/files\ndebian/xserver-xorg-input-mtrack.debhelper.log\ndebian/xserver-xorg-input-mtrack.substvars\ndebian/xserver-xorg-input-mtrack/\n\nmtrack-test\n"
  },
  {
    "path": "COPYING",
    "content": "\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\f\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\f\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\f\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\f\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\f\n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Library General\nPublic License instead of this License.\n"
  },
  {
    "path": "CREDITS",
    "content": "This is a copy of the CREDITS file from the xf86-input-multitouch driver.\n\n---\n\nMultitouch X driver (GPL license)\n\nCopyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n---\n\nThe Multitouch X driver extracts a lot of X knowledge from the\nsynaptics X driver (MIT license)\n\nCopyright (C) 1997 C. Scott Ananian\nCopyright (C) 1998-2000 Bruce Kalk\nCopyright (C) 1999 Henry Davies\nCopyright (C) 2001 Stefan Gmeiner\nCopyright (C) 2002 Linuxcare Inc. David Kennedy\nCopyright (C) 2003 Fred Hucht\nCopyright (C) 2003 Neil Brown\nCopyright (C) 2003 Jörg Bösner\nCopyright (C) 2003 Hartwig Felger\nCopyright (C) 2002-2007 Peter Osterlund\nCopyright (C) 2004 Arne Schwabe\nCopyright (C) 2004 Matthias Ihmig\nCopyright (C) 2004 Alexei Gilchrist\nCopyright (C) 2006-2007 Christian Thaeter\nCopyright (C) 2006 Stefan Bethge\nCopyright (C) 2007 Joseph P. Skudlarek\nCopyright (C) 2007 Florian Loitsch\nCopyright (C) 2008 Fedor P. Goncharov\nCopyright (C) 2008-2009 Red Hat, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n---\n"
  },
  {
    "path": "Makefile.am",
    "content": "SOURCES_COMMON = \\\n\tsrc/capabilities.c \\\n\tsrc/gestures.c \\\n\tsrc/hwstate.c \\\n\tsrc/mconfig.c \\\n\tsrc/mtouch.c \\\n\tsrc/mtstate.c \\\n\tsrc/trig.c\n\n@DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la\n@DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version\n@DRIVER_NAME@_drv_la_SOURCES = $(SOURCES_COMMON) \\\n\tdriver/mtrack.c \\\n\tdriver/mprops.c\n@DRIVER_NAME@_drv_ladir = @inputdir@\n\nnoinst_PROGRAMS = mtrack-test\nmtrack_test_SOURCES = $(SOURCES_COMMON) \\\n\ttools/mtrack-test.c\nmtrack_test_CFLAGS = $(AM_CFLAGS)\n\nACLOCAL_AMFLAGS = -I m4\nAM_CPPFLAGS = -I$(top_srcdir)/include/ \\\n\t-I/usr/include/xorg \\\n\t-I/usr/include/pixman-1\nAM_CFLAGS = $(XORG_CFLAGS)\n\n.PHONY: ChangeLog INSTALL\n\nINSTALL:\n\t$(INSTALL_CMD)\n\nChangeLog:\n\t$(CHANGELOG_CMD)\n\ndist-hook: ChangeLog INSTALL\n"
  },
  {
    "path": "README.md",
    "content": "xf86-input-mtrack\n=================\n###### v0.5.1\n\nAn Xorg driver for multitouch trackpads. Supports any trackpad whose kernel\ndriver uses the slotted multitouch protocol. For more information on the\nprotocol see the [kernel documentation][1].\n\nThis driver is compatible with Xorg server versions 1.7 to 1.19.0 It requires\nthe [mtdev][4] library to operate.\n\n\n## License\n\n* Copyright (C) 2015-2018 Paweł Turkowski <p2rkw0@gmail.com>\n* Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n* Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n\nThis software is licensed under the [GPLv2][2] and is a fork of the\n[xf86-input-multitouch][3] driver by Henrik Rydberg.\n\n\n## Building and Installing\nThis is a standard autoconf package. So:\n```\n    ./configure\n    make && make install\n```\nIt is likely that you will need to change system-dependent paths such as the\nxorg module directory. Otherwise mtrack may be not installed in xserver search\npath.\nSee `configure --help` for options.\n\nOn Debian systems (including Ubuntu) you may also need to install the `xorg-dev`\npackage prior to these commands.\n\nTo build deb package and install in system wide you will usually have to change\ninstallation prefix to /usr like so:\n```\n    ./configure --prefix=/usr\n    dpkg-buildpackage\n```\n## Bug Reporting\n\nIf you found a bug, and you are going to submit it, first run\n```\n    xinput --list\n```\nthen find your touchpad name/ID on the list and create report with output of\n```\n    xinput --list-props <your touchpad ID>\n```\nattached.\n\n## Configuration\n\n\nThe following is a minimal working InputClass section for xorg.conf:\n```\n    Section \"InputClass\"\n        MatchIsTouchpad \"on\"\n        Identifier      \"Touchpads\"\n        Driver          \"mtrack\"\n\n#       In case of problems enable/disable this line:\n        MatchDevicePath \"/dev/input/event*\"\n    EndSection\n```\n\n---\nTo apply changes made in xorg.conf (or related files) X server have to be restarted.\nChanges made with `xinput` are applied immedietly but they are not saved between sessions.\n\n### Options\nConfiguration options may be defined inside the InputClass section to configure\nthe driver. See examples/ directory for example configuration files. Feel free to submit\nyours, named as your laptop's model.\nAvailable options and their defaults are as follows.\n\n---\n#### Basic\n**[TrackpadDisable](#TrackpadDisable)**<a name=\"TrackpadDisable\"></a>\nDisables trackpad touch input. A value of 0 will enable the trackpad. A value\nof 1 will disable tapping and gestures but not movement. A value of 2 will\ndisable all input. A value of 3 will also disable physical buttons.  \nInteger. Default is 0.\n\n**[ButtonEnable](#ButtonEnable)**<a name=\"ButtonEnable\"></a>\nWhether or not to enable the physical buttons on or near the trackpad.  \nBoolean value. Defaults to true.\n\n**[ButtonIntegrated](#ButtonIntegrated)**<a name=\"ButtonIntegrated\"></a>\nWhether or not the physical buttons are integrated with the trackpad. If you\nhave a one-piece trackpad like on newer MacBooks, this should be set to true.\nButton emulation depends on this value being correct.  \nBoolean value. Defaults to true.\n\n---\n#### Responsiveness\n**[Sensitivity](#Sensitivity)**<a name=\"Sensitivity\"></a>\nAdjusts the sensitivity (movement speed) of the touchpad. This is a real number\ngreater than or equal to zero. A value of 0 will disable pointer movement.  \nFloat value. Default is 1.\n\n**[FingerHigh](#FingerHigh)**<a name=\"FingerHigh\"></a>\nDefines the pressure at which a finger is detected as a touch. This is a\npercentage represented as an integer.  \nInteger value. Default is 5.\n\n**[FingerLow](#FingerLow)**<a name=\"FingerLow\"></a>\nDefines the pressure at which a finger is detected as a release. This is a\npercentage represented as an integer.  \nInteger value. Default is 5.\n\n**[IgnoreThumb](#IgnoreThumb)**<a name=\"IgnoreThumb\"></a>\nWhether or not to ignore touches that are determined to be thumbs.  \nBoolean value. Defaults to false.\n\n**[IgnorePalm](#IgnorePalm)**<a name=\"IgnorePalm\"></a>\nWhether or not to ignore touches that are determined to be palms.  \nBoolean value. Defaults to false.\n\n**[DisableOnThumb](#DisableOnThumb)**<a name=\"DisableOnThumb\"></a>\nWhether or not to disable the entire trackpad when a thumb is touching.  \nBoolean value. Defaults to false.\n\n**[DisableOnPalm](#DisableOnPalm)**<a name=\"DisableOnPalm\"></a>\nWhether or not to disable the entire trackpad when a palm is touching.  \nBoolean value. Defaults to false.\n\n**[ThumbSize](#ThumbSize)**<a name=\"ThumbSize\"></a>\nThe minimum size of what's considered a thumb. It is expected that a thumb\nwill be larger than other fingers. This is represented as a percentage of the\nmaximum touch value and is dependent on the trackpad hardware.  \nInteger value. Defaults to 25.\n\n**[PalmSize](#PalmSize)**<a name=\"PalmSize\"></a>\nThe minimum size of what's considered a palm. Palms are expected to be very\nlarge on the trackpad. This is represented as a percentage of the maximum touch\nvalue and is dependent on the trackpad hardware.  \nInteger value. Defaults to 40.\n\n**[ThumbRatio](#ThumbRatio)**<a name=\"ThumbRatio\"></a>\nThe width/length ratio of what's considered a thumb. It is expected that a\nthumb is longer than it is wide. This tells the driver how much longer.  \nPercentage represented by an integer.  \nInteger value. Defaults to 70.\n\n---\n#### Zones<a name=\"Zones\"></a>\nDivide the touchpad into \"zones\". Clicking the integrated button in one of\nthese zones will send the button event configured for each {First, Second, Third}ZoneButton.\nThe driver will only add zones for the ZoneButton values that\nare enabled. The zone splitting start from the left to right using the first to third value.\nSo enabling only SecondZoneButton and ThirdZoneButton will create two\nzones, the left-middle part will fire SecondZoneButton and the middle-right part ThirdZoneButton.  \n\n**[ButtonZonesEnable](#ButtonZonesEnable)**<a name=\"ButtonZonesEnable\"></a>\nWhether or not to enable button zones. If button zones are enabled then the\ntrackpad will be split into one, two, or three vertical zones.\nBoolean value. Defaults to false.\n\n**[FirstZoneButton](#FirstZoneButton)**<a name=\"FirstZoneButton\"></a>\nThe button to emulate when the zone is pressed. This is the leftmost part of the pad.\nInteger value. A value of 0 disables this zone split.  \nInteger value. Defaults to 1.\n\n**[SecondZoneButton](#SecondZoneButton)**<a name=\"SecondZoneButton\"></a>\nThe button to emulate when the zone is pressed. This will float to the right of\nthe leftmost zone. Integer value. A value of 0 disables this zone split.  \nInteger value. Defaults to 2.\n\n**[ThirdZoneButton](#ThirdZoneButton)**<a name=\"ThirdZoneButton\"></a>\nThe button to emulate when the zone is pressed. This will float to the right of\nthe leftmost zone. Integer value. A value of 0 disables this zone split.  \nInteger value. Defaults to 0.\n\n**[LimitButtonZonesToBottomEdge](#LimitButtonZonesToBottomEdge)**<a name=\"LimitButtonZonesToBottomEdge\"></a>\nRestrict button zones inside the [EdgeBottom](#EdgeBottomSize) area. So instead of enabling zones\non the full pad height, the zone is limited to the percentage set for the [EdgeBottom](#EdgeBottomSize).  \nBoolean value. Default to false.\n\n---\n#### Physical click\n**[ClickFinger0](#ClickFinger0)**<a name=\"ClickFinger0\"></a>\nWhich button to emulate when no valid finger placement is touching the trackpad during a\nclick, as on \"EdgeBottom\".  \nInteger value. A value of 0 disables one-touch emulation. Defaults to 0.\n\n**[ClickFinger1](#ClickFinger1)**<a name=\"ClickFinger1\"></a>\nWhich button to emulate when one finger is touching the trackpad during a\nclick.  \nInteger value. A value of 0 disables one-touch emulation. Defaults to 1.\n\n**[ClickFinger2](#ClickFinger2)**<a name=\"ClickFinger2\"></a>\nWhich button to emulate when two fingers are touching the trackpad during a\nclick.  \nInteger value. A value of 0 disabled one-touch emulation. Defaults to 2.\n\n**[ClickFinger3](#ClickFinger3)**<a name=\"ClickFinger3\"></a>\nWhich button to emulate when three fingers are touching the trackpad during a\nclick.  \nInteger value. A value of 0 disabled one-touch emulation. Defaults to 3.\n\n**[ButtonMoveEmulate](#ButtonMoveEmulate)**<a name=\"ButtonMoveEmulate\"></a>\nWhether or not to count the moving finger when emulating button clicks.\nUseful to disable if you use two hands on trackpad.  \nBoolean value. Defaults to true.\n\n**[ButtonTouchExpire](#ButtonTouchExpire)**<a name=\"ButtonTouchExpire\"></a>\nHow long (in ms) to consider a touching finger as part of button emulation. A\nvalue of 0 will not expire touches.  \nInteger value. Defaults to 100.\n\n---\n#### Tap clicking\n**[TapButton1](#TapButton1)**<a name=\"TapButton1\"></a>\nWhich button to emulate for one-finger tapping. Integer value. A value of 0\ndisables one-finger tapping.  \nInteger value. Defaults to 1.\n\n**[TapButton2](#TapButton2)**<a name=\"TapButton2\"></a>\nWhich button to emulate for two-finger tapping. Integer value. A value of 0\ndisables two-finger tapping.  \nInteger value. Defaults to 3.\n\n**[TapButton3](#TapButton3)**<a name=\"TapButton3\"></a>\nWhich button to emulate for three-finger tapping. Integer value. A value of 0\ndisables three-finger tapping.  \nInteger value. Defaults to 2.\n\n**[TapButton4](#TapButton4)**<a name=\"TapButton4\"></a>\nWhich button to emulate for four-finger tapping. Integer value. A value of 0\ndisables three-finger tapping.  \nInteger value. Defaults to 0.\n\n**[ClickTime](#ClickTime)**<a name=\"ClickTime\"></a>\nWhen tapping, how much time to hold down the emulated button.  \nInteger value representing milliseconds. Defaults to 50.\n\n**[MaxTapTime](#MaxTapTime)**<a name=\"MaxTapTime\"></a>\nThe amount of time to wait for incoming touches after first one before counting\nit as emulated button click.  \nInteger value representing milliseconds. Defaults to 120.\n\n**[MaxTapMove](#MaxTapMove)**<a name=\"MaxTapMove\"></a>\nHow far a touch is allowed to move before counting it is no longer considered a\ntap.  \nInteger value. Defaults to 400.\n\n#### Gesture\n\n---\n##### Basic\n**[GestureClickTime](#GestureClickTime)**<a name=\"GestureClickTime\"></a>\nWhen a gesture triggers a click, how much time to hold down the emulated button.\nInteger value representing milliseconds. Defaults to 10.  \n\n**[GestureWaitTime](#GestureWaitTime)**<a name=\"GestureWaitTime\"></a>\nTouches are allowed to transition from one gesture to another. For example, you\nmay go from scrolling to swiping without releasing your fingers from the pad.\nThis value is the amount of time you must be performing the new gesture before\nit is triggered. This prevents accidental touches from triggering other\ngestures.   \nInteger value representing milliseconds. Defaults to 100.  \n\n---\n##### Two fingers Scrolling<a name=\"two-finger-scrolling\"></a>\n\n**[ScrollDistance](#ScrollDistance)**<a name=\"ScrollDistance\"></a>\nFor two finger scrolling. This sets the speed of your scrolling: the lower the\nnumber, the faster you scroll. \nTechnically, it is how far you must move your fingers before a button\nclick registering scrolling is triggered.\nInteger value. Defaults to 150.\n\n**[ScrollClickTime](#ScrollClickTime)**<a name=\"ScrollClickTime\"></a>\nFor two finger scrolling. How long button triggered by scrolling\nwill be hold down. A value of 0 will hold button down till end of gesture.\n0 - emit button click only once pre \"instance\" of gesture.\nInteger value representing milliseconds.  \nInteger value. Defaults to 20.\n\n**[ScrollSensitivity](#ScrollSensitivity)**<a name=\"ScrollSensitivity\"></a>\nFor two finger scrolling. Sensitivity (movement speed) of pointer during two\nfinger scrolling. A value of 0 disables pointer movement during gesture.\nInteger value expressed as parts per thousand of normal sensivity.\nA value of 1000 results with normal movement speed.\nInteger value. Defaults to 0.\n\n**[ScrollUpButton](#ScrollUpButton)**<a name=\"ScrollUpButton\"></a>\nFor two finger scrolling. The button that is triggered by scrolling up.  \nInteger value. A value of 0 disables scrolling up. Defaults to 4.\n\n**[ScrollDownButton](#ScrollDownButton)**<a name=\"ScrollDownButton\"></a>\nFor two finger scrolling. The button that is triggered by scrolling down.  \nInteger value. A value of 0 disables scrolling down. Defaults to 5.\n\n**[ScrollLeftButton](#ScrollLeftButton)**<a name=\"ScrollLeftButton\"></a>\nFor two finger scrolling. The button that is triggered by scrolling left.  \nInteger value. A value of 0 disables scrolling left. Defaults to 6.\n\n**[ScrollRightButton](#ScrollRightButton)**<a name=\"ScrollRightButton\"></a>\nFor two finger scrolling. The button that is triggered by scrolling right.  \nInteger value. A value of 0 disables scrolling right. Defaults to 7.\n\n**[ScrollSmooth](#ScrollSmooth)**<a name=\"ScrollSmooth\"></a>\nFor two finger scrolling. Whether to generate high precision scroll events.  \nBoolean value. Defaults to 1.\nProperty: \"Trackpad High Smooth Scroll\"\n\n**[ScrollCoastDuration](#ScrollCoastDuration)**<a name=\"ScrollCoastDuration\"></a>\nHow long after finished scrolling movement should be continued. Works only\nwith smooth scrolling enabled.  \nFloating value representing miliseconds. Defaults to 200.0.\nProperty: \"Trackpad Scroll Coasting\"\n\n**[ScrollCoastEnableSpeed](#ScrollCoastEnableSpeed)**<a name=\"ScrollCoastEnableSpeed\"></a>\nHow fast scroll should be to enable coasting feature.  \nFloating value. Defaults to 0.1.\nProperty: \"Trackpad Scroll Coasting\"\n\n**[ScrollCoastNoBoost](#ScrollCoastNoBoost)**<a name=\"ScrollCoastNoBoost\"></a>\nDisable boosting on second scroll gesture during coasting\nBoolean value. Defaults to false.\nProperty: \"Trackpad Scroll Coasting\"\n\n**[ScrollCoastEase](#ScrollCoastEase)**<a name=\"ScrollCoastEase\"></a>\nApply easing effect on coasting\nBoolean value. Defaults to false.\nProperty: \"Trackpad Scroll Coasting\"\n\n---\n##### Three fingers swipe<a name=\"three-finger-swipe\"></a>\n**[SwipeDistance](#SwipeDistance)**<a name=\"SwipeDistance\"></a>\nFor three finger swiping. How far you must move your fingers before a button\nclick is triggered.  \nInteger value. Defaults to 700.\n\n**[SwipeClickTime](#SwipeClickTime)**<a name=\"SwipeClickTime\"></a>\nFor three finger swiping. How long button triggered by swiping\nwill be hold down.  \nInteger value representing milliseconds. Defaults to 300.\n\n**[SwipeSensitivity](#SwipeSensitivity)**<a name=\"SwipeSensitivity\"></a>\nFor three finger scrolling. Sensitivity (movement speed) of pointer during three\nfinger scrolling. A value of 0 disables pointer movement during gesture.  \nInteger value expressed as parts per thousand of normal sensivity.  \nA value of 1000 results with normal movement speed. Defaults to 0.\n\n**[SwipeUpButton](#SwipeUpButton)**<a name=\"SwipeUpButton\"></a>\nFor three finger swiping. The button that is triggered by swiping up.  \nInteger value. A value of 0 disables swiping up. Defaults to 8.\n\n**[SwipeDownButton](#SwipeDownButton)**<a name=\"SwipeDownButton\"></a>\nFor three finger swiping. The button that is triggered by swiping down.  \nInteger value. A value of 0 disables swiping down. Defaults to 9.\n\n**[SwipeLeftButton](#SwipeLeftButton)**<a name=\"SwipeLeftButton\"></a>\nFor three finger swiping. The button that is triggered by swiping left.  \nInteger value. A value of 0 disables swiping left. Defaults to 10.\n\n**[SwipeRightButton](#SwipeRightButton)**<a name=\"SwipeRightButton\"></a>\nFor three finger swiping. The button that is triggered by swiping right.  \nInteger value. A value of 0 disables swiping right. Defaults to 11.\n\n---\n##### Four fingers swipe<a name=\"four-finger-swipe\"></a>\n**[Swipe4Distance](#Swipe4Distance)**<a name=\"Swipe4Distance\"></a>\nFor four finger swiping. How far you must move your fingers before a button\nclick is triggered.  \nInteger value. Defaults to 700.\n\n**[Swipe4ClickTime](#Swipe4ClickTime)**<a name=\"Swipe4ClickTime\"></a>\nFor four finger swiping. How long button triggered by swiping\nwill be hold down.  \nInteger value representing milliseconds. Defaults to 300.\n\n**[Swipe4Sensitivity](#Swipe4Sensitivity)**<a name=\"Swipe4Sensitivity\"></a>\nFor four finger scrolling. Sensitivity (movement speed) of pointer during four\nfinger scrolling. A value of 0 disables pointer movement during gesture.  \nInteger value expressed as parts per thousand of normal sensivity.  \nA value of 1000 results with normal movement speed. Defaults to 0.\n\n**[Swipe4UpButton](#Swipe4UpButton)**<a name=\"Swipe4UpButton\"></a>\nFor four finger swiping. The button that is triggered by swiping up.  \nInteger value. A value of 0 disables swiping up. Defaults to 8.\n\n**[Swipe4DownButton](#Swipe4DownButton)**<a name=\"Swipe4DownButton\"></a>\nFor four finger swiping. The button that is triggered by swiping down.  \nInteger value. A value of 0 disables swiping down. Defaults to 9.\n\n**[Swipe4LeftButton](#Swipe4LeftButton)**<a name=\"Swipe4LeftButton\"></a>\nFor four finger swiping. The button that is triggered by swiping left.  \nInteger value. A value of 0 disables swiping left. Defaults to 10.\n\n**[Swipe4RightButton](#Swipe4RightButton)**<a name=\"Swipe4RightButton\"></a>\nFor four finger swiping. The button that is triggered by swiping right.  \nInteger value. A value of 0 disables swiping right. Defaults to 11.\n\n---\n##### Edge Scrolling - One finger\nShould be used in conjuction with the [`Edge disabling`](#EdgeDisabling) options.\n\n**[EdgeScrollDist](#EdgeScrollDist)**<a name=\"EdgeScrollDist\"></a>\nFor one finger edge scrolling. How far you must move your finger on edge before\na button click is triggered.  \nInteger value. Defaults to 105.\n\n**[EdgeScrollClickTime](#EdgeScrollClickTime)**<a name=\"EdgeScrollClickTime\"></a>\nFor one finger edge scrolling. How long button triggered by edge scrolling\nwill be hold down. A value of 0 will hold button down till end of gesture.  \n0 - emit button click only once pre \"instance\" of gesture.  \nInteger value representing milliseconds. Defaults to 20.\n\n**[EdgeScrollSensitivity](#EdgeScrollSensitivity)**<a name=\"EdgeScrollSensitivity\"></a>\nFor one finger edge scrolling. Sensitivity (movement speed) of pointer during one\nfinger scrolling. A value of 0 disables pointer movement during gesture.  \nInteger value expressed as parts per thousand of normal sensivity.  \nA value of 1000 results with normal movement speed. Defaults to 0.\n\n**[EdgeScrollUpButton](#EdgeScrollUpButton)**<a name=\"EdgeScrollUpButton\"></a>\nFor one finger edge scrolling. The button that is triggered by edge scrolling up.  \nInteger value. A value of 0 disables scrolling up. Defaults to 4.\n\n**[EdgeScrollDownButton](#EdgeScrollDownButton)**<a name=\"EdgeScrollDownButton\"></a>\nFor one finger edge scrolling. The button that is triggered by edge scrolling down.  \nInteger value. A value of 0 disables scrolling down. Defaults to 5.\n\n**[EdgeScrollLeftButton](#EdgeScrollLeftButton)**<a name=\"EdgeScrollLeftButton\"></a>\nFor one finger edge scrolling. The button that is triggered by edge scrolling left.  \nInteger value. A value of 0 disables scrolling left. Defaults to 6.\n\n**[EdgeScrollRightButton](#EdgeScrollRightButton)**<a name=\"EdgeScrollRightButton\"></a>\nFor one finger edge scrolling. The button that is triggered by edge scrolling right.  \nInteger value. A value of 0 disables scrolling right. Defaults to 7.\n\n---\n##### Pinch scaling - Two fingers\n**[ScaleDistance](#ScaleDistance)**<a name=\"ScaleDistance\"></a>\nFor pinch scaling. How far you must move your fingers before a button click is\ntriggered.  \nInteger value. Defaults to 150.\n\n**[ScaleUpButton](#ScaleUpButton)**<a name=\"ScaleUpButton\"></a>\nFor pinch scaling. The button that is triggered by scaling up.  \nInteger value. A value of 0 disables scaling up. Defaults to 12.\n\n**[ScaleDownButton](#ScaleDownButton)**<a name=\"ScaleDownButton\"></a>\nFor pinch scaling. The button that is triggered by scaling down.  \nInteger value. A value of 0 disables scaling down. Defaults to 13.\n\n---\n##### Rotation - Two fingers\n**[RotateDistance](#RotateDistance)**<a name=\"RotateDistance\"></a>\nFor two finger rotation. How far you must move your fingers before a button\nclick is triggered.  \nInteger value. Defaults to 150.\n\n**[RotateLeftButton](#RotateLeftButton)**<a name=\"RotateLeftButton\"></a>\nFor two finger rotation. The button that is triggered by rotating left.  \nInteger value. A value of 0 disables rotation left. Defaults to 14.\n\n**[RotateRightButton](#RotateRightButton)**<a name=\"RotateRightButton\"></a>\nFor two finger rotation. The button that is triggered by rotating right.  \nInteger value. A value of 0 disables rotation right. Defaults to 15.\n\n---\n#### Edge disabling<a name=\"EdgeDisabling\"></a>\n~~[EdgeSize](#EdgeSize)~~<a name=\"EdgeSize\"></a>\nDEPRECATED.\nThe size of an area around the trackpad where new touches are ignored (fingers\ntraveling into this area from above will still be tracked).  \nThis is represented as a percentage of the total trackpad height.  \nInteger value. Defaults to 0.  \nValue set here is overwriten by EdgeLeftSize, EdgeRightSize, EdgeTopSize and\nEdgeBottomSize\n\n**[EdgeTopSize](#EdgeTopSize)**<a name=\"EdgeTopSize\"></a>\nThe size of an area at the top of the trackpad where new touches are ignored\n(fingers travelling into this area from the bottom will still be tracked).  \nInteger value representing a percentage of the total trackpad height. Defaults to 0.\n\n**[EdgeBottomSize](#EdgeBottomSize)**<a name=\"EdgeBottomSize\"></a>\nThe size of an area at the bottom of the trackpad where new touches are ignored\n(fingers travelling into this area from the top will still be tracked).  \nInteger value representing a percentage of the total trackpad height. Defaults to 10.\n\n**[EdgeLeftSize](#EdgeLeftSize)**<a name=\"EdgeLeftSize\"></a>\nThe size of an area at the left of the trackpad where new touches are ignored\n(fingers travelling into this area from the right will still be tracked).  \nInteger value representing a percentage of the total trackpad width. Defaults to 0.\n\n**[EdgeRightSize](#EdgeRightSize)**<a name=\"EdgeRightSize\"></a>\nThe size of an area at the right of the trackpad where new touches are ignored\n(fingers travelling into this area from the left will still be tracked).  \nInteger value representing a percentage of the total trackpad width. Defaults to 0.\n\n---\n#### Special features\n**[Hold1Move1StationaryButton](#Hold1Move1StationaryButton)**<a name=\"Hold1Move1StationaryButton\"></a>\nFor two finger hold-and-move functionality. The button that is triggered by\nholding one finger and moving another one.  \nInteger value. A value of 0 disables hold-and-move.  \nValue of 0 disables this functionality.  \nDefaults to 1.\n\n**[Hold1Move1StationaryMaxMove](#Hold1Move1StationaryMaxMove)**<a name=\"Hold1Move1StationaryMaxMove\"></a>\nFor two finger hold-and-move functionality. Fow far stationary finger can be\nmoved berfore gesture invalidation.  \nInteger value. Default to 20.\n\n**[TapDragEnable](#TapDragEnable)**<a name=\"TapDragEnable\"></a>\nWhether or not to enable tap-to-drag functionality.  \nBoolean value. Defaults to true.\n\n**[TapDragTime](#TapDragTime)**<a name=\"TapDragTime\"></a>\nThe tap-to-drag timeout. This is how long the driver will wait after a single\ntap for a movement event before sending the click.  \nInteger value representing milliseconds. Defaults to 350.\n\n**[TapDragWait](#TapDragWait)**<a name=\"TapDragWait\"></a>\nHow long after detecting movement to trigger a button down event. During this\ntime pointer movement will be disabled. Increase this value if you find you're\ndraggin when you don't wish it.  \nInteger value representing milliseconds. Defaults to 40.\n\n**[TapDragDist](#TapDragDist)**<a name=\"TapDragDist\"></a>\nHow far the finger is allowed to move during drag wait time. If the finger\nmoves farther than this distance during the wait time then dragging will be\ncanceled and pointer movement will resume.  \nInteger value. Defaults to 200.\n\n**[TapDragLockTimeout](#TapDragLockTimeout)**<a name=\"TapDragLockTimeout\"></a>\nThis is how long the driver will wait after initial drag in 'drag ready' state\nin which it will be able to resume previous drag without additional `up`, `down`\nsequence.  \nValue of 0 disables this functionality.  \nValues of less than zero will make mtrack require additional tap to finish drag\nby sending `button up`.  \nInteger value representing milliseconds. Defaults to 500.\n\n**[AxisXInvert](#AxisXInvert)**<a name=\"AxisXInvert\"></a>\nWhether or not to invert the X axis.  \nBoolean value. Defaults to false.\n\n**[AxisYInvert](#AxisYInvert)**<a name=\"AxisYInvert\"></a>\nWhether or not to invert the Y axis.  \nBoolean value. Defaults to false.\n\nTips\n-------------\n#### Swipe to drag\nTo setup swipe to drag functionality you have to choose which swipe gesture ([Scroll](two-finger-scrolling), [Swipe](#three-finger-swipe), [Swipe4](#four-finger-swipe))\nwill be used for dragging.  \nExample configuration for three finger drag:\n```\n    Option \"SwipeDistance\" \"1\"\n    Option \"SwipeLeftButton\" \"1\"\n    Option \"SwipeRightButton\" \"1\"\n    Option \"SwipeUpButton\" \"1\"\n    Option \"SwipeDownButton\" \"1\"\n    Option \"SwipeClickTime\" \"0\"\n    Option \"SwipeSensitivity\" \"1000\"\n```\nThis will enable dragging with three fingers. Change [sensitivity](#SwipeSensitivity) for faster/slower movements.\nScroll, and Swipe4 are also supported.\n\n#### Hold and move\nHold down one finger in place to initiate hold-and-move gesture.\nThen move another finger to drag configured button.\nGesture will last as long as fist finger (a.k.a. stationary finger) will\nbe held down in place.\n\nIncrease [TapDragDist](#TapDragDist) to give stationary finger more freedom.\nSet [Hold1Move1StationaryButton](#Hold1Move1StationaryButton) to 0 to disable, set to other value to send button other\nthan \"1\".\n\n#### Persistent dragging\nIf you're using lot of tools that require dragging you can make it a little bit\neasier by enabling [persistent tap-to-drag](#TapDragLockTimeout):\n```\n    Option \"TapDragLockTimeout\" \"-1\"\n```\nWith that change you will have to perform additional tap when dragging with\ntap-to-drag. Other positive values will let you continue yor drag within\nspecified time.\n\n#### Enabling soft button\n##### Basic\nIf 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.\n```\n    Option \"EdgeBottomSize\" \"20\"    # Disable tap and movement detection in the bottom 20% of the pad\n    Option \"ClickFinger0\" \"1\"       # Enable clicking action \"1\" when no finger is detected\n```\nYou could also use [ClickFinger0](#ClickFinger0) with [EdgeTop/Right/LeftSize](#EdgeDisabling).\n\n##### Advanced\nIf you want more than one button in the bottom edge, you need to use a more  \nadvanced configuration. It will enable you use up to [3 buttons](#Zones) inside that [edge](#EdgeBottomSize).  \n```\n    Option \"ButtonZonesEnable\" \"true\"               # Enable \"Zones\"\n    Option \"LimitButtonZonesToBottomEdge\" \"true\"    # Limit the zones to the bottom edge\n    Option \"EdgeBottomSize\" \"20\"                    # Disable tap and movement detection in the bottom 20% of the pad\n\n    # Zones stack from left to right inside the 20% height defined above\n    Option \"FirstZoneButton\" \"1\"   # Left part fire click 1\n    Option \"SecondZoneButton\" \"3\"   # Middle part fire click 3\n    Option \"ThirdZoneButton\" \"2\"   # Right part fire click 2\n```\n\n\n[1]: http://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt     \"Kernel Multitouch Protocol\"\n[2]: http://www.gnu.org/licenses/gpl-2.0.html                                   \"GNU General Public License, version 2\"\n[3]: http://bitmath.org/code/multitouch/                                        \"xf86-input-multitouch website\"\n[4]: http://bitmath.org/code/mtdev/                                             \"mtdev library website\"\n"
  },
  {
    "path": "RELEASE.md",
    "content": "## v0.5.1\n* Add button emulation when no finger placement is valid\n* Add dedicated zone button\n* Enable the restriction of the zone inside the bottom edge\n* Readme configuration options are grouped according to their relationship\n* Readme anchor has been moved to the options themselves\n\n## v0.5.0\n* Add tap-to-drag lock timeout\n* Make edge size configurable for each edge individually\n* Add edge scrolling\n* Fix movement truncation\n* Fix crash during suspend/wake up\n* Make movement speed resolution independent\n* Lock smooth scroll to one axis at time\n* Post button events before move events\n* New emulated buttons implementation\n* Reduce max recognized fingers/touch points from 32 to 16\n* New fancy readme with anchors\n* Fix warnings, improve logging\n* New examples\n* Helper script to parse output of xinput --list-props\n\n## v0.4.1\n* Smooth scroll: detect and handle flipped up&down, left&right buttons\n* Add examples with my current configuration as first example.\n* Proper initialization of scroll axes(2 and 3).\n* Setup scroll valuators also during device initialization.\n* Do not consider edge clicks when calculating emulated buttons\n* Change type of variables holding movement delta to double for better precision.\n* Rewrite tap implementation from scratch. Remove 'status' field from Touch structure.\n* Clear the MT_BUTTON bit of touches when an integrated button is released.\n* Scale gesture reimplementation.\n\n## v0.4.0\n* Add smooth scroll\n* Add scroll/swipe/swipe4 to drag functionality\n* Add scroll coasting\n* Add Hold And Move gesture\n* Replace EdgeSize with BottomEdge property\n* Replace busy waiting with timers\n* Fix button down -> up delays\n* Initial support for absolute mode devices\n* Improve support for pressure based devices\n* Many minor improvements/bugfixes\n\n## v0.3.1\n* Include a configure script.\n* Update the README.\n* Add the RELEASE file.\n"
  },
  {
    "path": "configure.ac",
    "content": "# Initialize Autoconf\nAC_PREREQ([2.60])\nAC_INIT([xf86-input-mtrack],\n\t[0.2.0],\n\t[https://bugs.freedesktop.org/enter_bug.cgi?product=xorg],\n\t[xf86-input-mtrack])\nAC_CONFIG_MACRO_DIR([m4])\nAC_CONFIG_SRCDIR([Makefile.am])\nAC_CONFIG_HEADERS([config.h])\nAC_CONFIG_AUX_DIR(.)\n\n# Initialize Automake\nAM_INIT_AUTOMAKE([foreign subdir-objects])\nAM_MAINTAINER_MODE\n\n# Initialize libtool\nAC_DISABLE_STATIC\nAC_PROG_LIBTOOL\n\n# Initialize X.Org macros 1.8 or later for MAN_SUBSTS set by XORG_MANPAGE_SECTIONS\nm4_ifndef([XORG_MACROS_VERSION],\n          [m4_fatal([must install xorg-macros 1.8 or later before running autoconf/autogen])])\nXORG_MACROS_VERSION(1.8)\nXORG_DEFAULT_OPTIONS\n\n# Checks for libraries.\nAC_CHECK_LIB([mtdev], [mtdev_open])\nAC_CHECK_LIB([m], [atan2])\n\n# Obtain compiler/linker options for the mtrack driver dependencies\nPKG_CHECK_MODULES(XORG, [xorg-server >= 1.7] xproto inputproto $REQUIRED_MODULES)\n\n# Set driver name\nDRIVER_NAME=mtrack\nAC_SUBST([DRIVER_NAME])\n\n# configure option for module install directory\nAC_ARG_WITH(xorg-module-dir, AC_HELP_STRING([--with-xorg-module-dir=DIR],\n\t[Default xorg module directory [[default=$libdir/xorg/modules]]]),\n\t[moduledir=\"$withval\"],\n\t[moduledir=\"$libdir/xorg/modules\"])\ninputdir=${moduledir}/input\nAC_SUBST(inputdir)\n\n# configure option to build extra tools\nAC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools],\n\t[Build extra tools (default: disabled)]),\n\t[ENABLE_TOOLS=yes],\n\t[ENABLE_TOOLS=no])\nAM_CONDITIONAL([BUILD_TOOLS], [test \"x$ENABLE_TOOLS\" = xyes])\n\n# configure option to enable gesture ate debugging\nAC_ARG_ENABLE(debug-gestures, AS_HELP_STRING([--enable-debug-gestures],\n                                    [Enable gesture debugging (default: disabled)]),\n                                    [DEBUG_GESTURES=yes],\n\t\t\t\t\t\t\t\t\t[DEBUG_GESTURES=no])\nif test \"x$DEBUG_GESTURES\" = xyes; then\n   AC_DEFINE(DEBUG_GESTURES, 1, [Enable gesture debugging.])\nfi\n\n# configure option to enable multitouch state debugging\nAC_ARG_ENABLE(debug-mtstate, AS_HELP_STRING([--enable-debug-mtstate],\n                                    [Enable multitouch state debugging (default: disabled)]),\n                                    [DEBUG_MTSTATE=yes],\n\t\t\t\t\t\t\t\t\t[DEBUG_MTSTATE=no])\nif test \"x$DEBUG_MTSTATE\" = xyes; then\n   AC_DEFINE(DEBUG_MTSTATE, 1, [Enable multitouch state debugging.])\nfi\n\n# configure option to enable property debugging\nAC_ARG_ENABLE(debug-props, AS_HELP_STRING([--enable-debug-props],\n                                    [Enable property debugging (default: disabled)]),\n                                    [DEBUG_PROPS=yes],\n\t\t\t\t\t\t\t\t\t[DEBUG_PROPS=no])\nif test \"x$DEBUG_PROPS\" = xyes; then\n   AC_DEFINE(DEBUG_PROPS, 1, [Enable property debugging.])\nfi\n\n# configure option to enable driver debugging\nAC_ARG_ENABLE(debug-driver, AS_HELP_STRING([--enable-debug-driver],\n                                    [Enable property debugging (default: disabled)]),\n                                    [DEBUG_DRIVER=yes],\n\t\t\t\t\t\t\t\t\t[DEBUG_DRIVER=no])\nif test \"x$DEBUG_DRIVER\" = xyes; then\n   AC_DEFINE(DEBUG_DRIVER, 1, [Enable property debugging.])\nfi\n\n\n# configure option to enable all debugging\nAC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],\n                                    [Enable all debugging (default: disabled)]),\n                                    [DEBUG_ALL=$enableval],\n\t\t\t\t\t\t\t\t\t[DEBUG_ALL=no])\nif test \"x$DEBUG_ALL\" = xyes; then\n   AC_DEFINE(DEBUG_GESTURES, 1, [Enable gesture debugging.])\n   AC_DEFINE(DEBUG_MTSTATE, 1, [Enable multitouch state debugging.])\n   AC_DEFINE(DEBUG_PROPS, 1, [Enable property debugging.])\n   AC_DEFINE(DEBUG_DRIVER, 1, [Enable driver debugging.])\nfi\n\n# Everything else\nAC_PROG_CC\nAC_PROG_INSTALL\nAC_CONFIG_FILES([Makefile])\nAC_OUTPUT\n\n"
  },
  {
    "path": "debian/README.Debian",
    "content": "Xorg Multitouch Trackpad Driver\n* Install the Debian package\n* Configure xorg.conf\n* Restart X\n\n"
  },
  {
    "path": "debian/changelog",
    "content": "xserver-xorg-input-mtrack (0.5.0) unstable; urgency=medium\n\n  * Add tap-to-drag lock timeout\n  * Make edge size configurable for each edge individually\n  * Add edge scrolling\n  * Fix movement truncation\n  * Fix crash during suspend/wake up\n  * Make movement speed resolution independent\n  * Lock smooth scroll to one axis at time\n  * Post button events before move events\n  * New emulated buttons implementation\n  * Reduce max recognized fingers/touch points from 32 to 16\n  * New fancy readme with anchors\n  * Fix warnings, improve logging\n  * New examples\n  * Helper script to parse output of xinput --list-props\n\n -- Paweł Turkowski <p2rkw0@gmail.com>  Sun, 28 Jan 2018 20:02:28 +0100\n\nxserver-xorg-input-mtrack (0.4.1) unstable; urgency=low\n\n  * Smooth scroll: detect and handle flipped up&down, left&right buttons\n  * Add examples with my current configuration as first example.\n  * Proper initialization of scroll axes(2 and 3).\n  * Setup scroll valuators also during device initialization.\n  * Do not consider edge clicks when calculating emulated buttons\n  * Change type of variables holding movement delta to double for better precision.\n  * Rewrite tap implementation from scratch. Remove 'status' field from Touch structure.\n  * Clear the MT_BUTTON bit of touches when an integrated button is released.\n  * Scale gesture reimplementation.\n\n -- Paweł Turkowski <p2rkw0@gmail.com>  Wed, 02 Nov 2016 00:13:04 +0100\n\nxserver-xorg-input-mtrack (0.4.0) unstable; urgency=low\n\n  * Add smooth scroll\n  * Add scroll/swipe/swipe4 to drag functionality\n  * Add scroll coasting\n  * Add scroll/swipe/swipe4 click time\n  * Add Hold And Move gesture\n  * Replace EdgeSize with BottomEdge property\n  * Replace busy waiting with timers\n  * Fix button down -> up delays\n  * Initial support for absolute mode devices\n  * Improve support for pressure based devices\n  * Many minor improvements/bugfixes\n\n -- Paweł Turkowski <p2rkw0@gmail.com>  Mon, 28 Sep 2015 01:20:38 +0200\n\nxserver-xorg-input-mtrack (0.2.0) unstable; urgency=low\n\n  * Now built via autoconf/automake.\n  * Fully configurable via XInput.\n  * Sensitivify now configurable.\n  * Trackpad can now be disabled on keystroke via an external daemon.\n  * Four finger tapping/swiping added.\n  * Button zones added, a new button emulation method.\n  * Tap-to-drag fixed.\n  * Three finger tapping responsiveness fixed.\n\n -- Ryan Bourgeois <bluedragonx@gmail.com>  Thu, 26 May 2011 09:44:43 -0500\n\nxserver-xorg-input-mtrack (0.1.1) unstable; urgency=low\n\n  * Bugfix release.\n  * Disabling tapping no longer (wrongly) disables pointer movement.\n  * Button emulation set to ignore \"old\" touches. What consitutes an old touch can be adjusted with the ButtonTouchExpire option.\n\n -- Ryan Bourgeois <bluedragonx@gmail.com>  Thu, 28 Apr 2011 01:14:58 -0500\n\nxserver-xorg-input-mtrack (0.1.0) unstable; urgency=low\n\n  * Initial release.\n\n -- Ryan Bourgeois <bluedragonx@gmail.com>  Fri, 15 Apr 2011 14:46:10 -0500\n"
  },
  {
    "path": "debian/compat",
    "content": "5\n"
  },
  {
    "path": "debian/control",
    "content": "Source: xserver-xorg-input-mtrack\nSection: misc\nPriority: optional\nMaintainer: Paweł Turkowski <p2rkw0@gmail.com>\nBuild-Depends:\n\tdebhelper (>= 5),\n\txserver-xorg-dev (>= 2:1.7.6),\n\tlibmtdev-dev (>= 1.0.10)\nStandards-Version: 3.7.2\nHomepage: https://github.com/p2rkw/xf86-input-mtrack\n\nPackage: xserver-xorg-input-mtrack\nArchitecture: any\nDepends: ${shlibs:Depends}, ${misc:Depends}, libmtdev1 (>= 1.0)\nDescription: Xorg Multitouch Trackpad Driver\n This X input driver provides gestures support for multitouch touchpads,\n with or without an integrated button.\n"
  },
  {
    "path": "debian/copyright",
    "content": "Source URL: https://github.com/p2rkw/xf86-input-mtrack\n\nUpstream Authors: Ryan Bourgeois <bluedragonx@gmail.com>\n                  Paweł Turkowski <p2rkw0@gmail.com>\n\nCopyright:\n\tCopyright (C) 2008\tHenrik Rydberg (rydberg@euromail.se)\n\tCopyright (C) 2011\tRyan Bourgeois (bluedragonx@gmail.com)\n\tCopyright (C) 2018\tPaweł Turkowski (p2rkw0@gmail.com)\n\nLicense:\n\n\tThis program is free software; you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation; either version 2 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\t See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program; if not, write to the Free Software\n\tFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\nPackaging:\n\tCopyright (C) 2008 by Henrik Rydberg <rydberg@euromail.se>\n\tCopyright (C) 2011 by Ryan Bourgeois <bluedragonx@gmail.com>\n\tCopyright (C) 2018 by Paweł Turkowski <p2rkw0@gmail.com>\n\treleased under GPL 2\n\n"
  },
  {
    "path": "debian/dirs",
    "content": "\n"
  },
  {
    "path": "debian/rules",
    "content": "#!/usr/bin/make -f\n# -*- makefile -*-\n\n# Uncomment this to turn on verbose mode.\n#export DH_VERBOSE=1\n\nbuild-arch:\n\tdh_testdir\n\t$(MAKE)\n\nbuild-indep:\n\nbuild: build-arch build-indep\n\ninstall:\n\tdh_testdir\n\tdh_testroot\n\tdh_clean -k\n\tdh_installdirs\n\t$(MAKE) DESTDIR=$(CURDIR)/debian/xserver-xorg-input-mtrack install\n\nbinary-arch: build-arch install\n\tdh_testdir\n\tdh_testroot\n\tdh_link\n\tdh_strip\n\tdh_compress\n\tdh_fixperms\n\tdh_installdeb\n\tdh_shlibdeps\n\tdh_gencontrol\n\tdh_md5sums\n\tdh_builddeb\n\nbinary-indep: build-indep\n\nbinary: binary-arch binary-indep\n\nclean:\n\tdh_testdir\n\tdh_testroot\n\t$(MAKE) clean\n\tdh_clean\n\n.PHONY: build-arch build-indep build install binary-arch binary-indep binary clean\n\n"
  },
  {
    "path": "driver/mprops.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"mprops.h\"\n#include \"common.h\"\n#include \"mtouch.h\"\n\n#ifdef DEBUG_PROPS\n# define LOG_DEBUG_PROPS LOG_DEBUG\n#else\n# define LOG_DEBUG_PROPS LOG_DISABLED\n#endif\n\n#define MAX_INT_VALUES 7\n#define MAX_FLOAT_VALUES 4\n#define MAX_BUTTON_VALUES 6\n\n#define VALID_BUTTON(x) (x >= 0 && x <= 32)\n#define VALID_BOOL(x) (x == 0 || x == 1)\n#define VALID_PCNT(x) (x >= 0 && x <= 100)\n\nstruct MProps mprops;\n\nAtom atom_init_integer(DeviceIntPtr dev, char* name, int nvalues, int* values, int size) {\n\tAtom atom;\n\tint i;\n\tuint8_t uvals8[MAX_INT_VALUES];\n\tuint16_t uvals16[MAX_INT_VALUES];\n\tuint32_t uvals32[MAX_INT_VALUES];\n\tpointer uvals;\n\tnvalues = MINVAL(MAX_INT_VALUES, nvalues);\n\n\tswitch(size) {\n\tcase 8:\n\t\tfor (i = 0; i < nvalues; i++) {\n\t\t\tuvals8[i] = values[i];\n\t\t}\n\t\tuvals = uvals8;\n\t\tbreak;\n\tcase 16:\n\t\tfor (i = 0; i < nvalues; i++) {\n\t\t\tuvals16[i] = values[i];\n\t\t}\n\t\tuvals = uvals16;\n\t\tbreak;\n\tdefault:\n\t\tfor (i = 0; i < nvalues; i++) {\n\t\t\tuvals32[i] = values[i];\n\t\t}\n\t\tuvals = uvals32;\n\t\tbreak;\n\t}\n\n\tatom = MakeAtom(name, strlen(name), TRUE);\n\tXIChangeDeviceProperty(dev, atom, XA_INTEGER, size, PropModeReplace, nvalues, uvals, FALSE);\n\tXISetDevicePropertyDeletable(dev, atom, FALSE);\n\treturn atom;\n}\n\nstatic\nAtom atom_init_integers32(DeviceIntPtr dev, char* name, int nvalues, int* values) {\n\tint size = 32;\n\tAtom atom;\n\n\tatom = MakeAtom(name, strlen(name), TRUE);\n\tXIChangeDeviceProperty(dev, atom, XA_INTEGER, size, PropModeReplace, nvalues, values, FALSE);\n\tXISetDevicePropertyDeletable(dev, atom, FALSE);\n\treturn atom;\n}\n\n\nAtom atom_init_float(DeviceIntPtr dev, char* name, int nvalues, float* values, Atom float_type) {\n\tAtom atom = MakeAtom(name, strlen(name), TRUE);\n\tXIChangeDeviceProperty(dev, atom, float_type, 32, PropModeReplace, nvalues, values, FALSE);\n\tXISetDevicePropertyDeletable(dev, atom, FALSE);\n\treturn atom;\n}\n\nstatic void init_swipe_props(DeviceIntPtr dev, struct MPropsSwipe* props_swipe,\n                             struct MConfigSwipe* cfg_swipe, char const* settings_prop_name,\n                             char const* buttons_prop_name){\n\tint ivals[MAX_INT_VALUES];\n\tivals[0] = cfg_swipe->dist;\n\tivals[1] = cfg_swipe->hold;\n\tivals[2] = cfg_swipe->drag_sens;\n\tprops_swipe->settings = atom_init_integer(dev, (char*)settings_prop_name, 3, ivals, 32);\n\n\tivals[0] = cfg_swipe->up_btn;\n\tivals[1] = cfg_swipe->dn_btn;\n\tivals[2] = cfg_swipe->lt_btn;\n\tivals[3] = cfg_swipe->rt_btn;\n\tprops_swipe->buttons = atom_init_integer(dev, (char*)buttons_prop_name, 4, ivals, 8);\n}\n\nstatic void init_edge_props(DeviceIntPtr dev, Atom* props_edge,\n                             struct MConfigSwipe* cfg_edge, char const* settings_prop_name){\n\tstatic const int nvalues = 7;\n\tint ivals[nvalues];\n\n\tivals[0] = cfg_edge->dist;\n\tivals[1] = cfg_edge->hold;\n\tivals[2] = cfg_edge->drag_sens;\n\tivals[3] = cfg_edge->up_btn;\n\tivals[4] = cfg_edge->dn_btn;\n\tivals[5] = cfg_edge->lt_btn;\n\tivals[6] = cfg_edge->rt_btn;\n\n\t*props_edge = atom_init_integers32(dev, (char*)settings_prop_name, nvalues, ivals);\n}\n\nvoid mprops_init(struct MConfig* cfg, InputInfoPtr local) {\n\tint ivals[MAX_INT_VALUES];\n\tfloat fvals[MAX_FLOAT_VALUES];\n\n\tmprops.float_type = XIGetKnownProperty(XATOM_FLOAT);\n\tif (!mprops.float_type) {\n\t\tmprops.float_type = MakeAtom(XATOM_FLOAT, strlen(XATOM_FLOAT), TRUE);\n\t\tif (!mprops.float_type) {\n\t\t\tLOG_ERROR(\"%s: Failed to init float atom. Property support is disabled.\\n\", local->name);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tivals[0] = cfg->trackpad_disable;\n\tmprops.trackpad_disable = atom_init_integer(local->dev, MTRACK_PROP_TRACKPAD_DISABLE, 1, ivals, 8);\n\n\tfvals[0] = (float)cfg->sensitivity;\n\tmprops.sensitivity = atom_init_float(local->dev, MTRACK_PROP_SENSITIVITY, 1, fvals, mprops.float_type);\n\n\tivals[0] = cfg->touch_down;\n\tivals[1] = cfg->touch_up;\n\tmprops.pressure = atom_init_integer(local->dev, MTRACK_PROP_PRESSURE, 2, ivals, 8);\n\n\tivals[0] = cfg->button_enable;\n\tivals[1] = cfg->button_integrated;\n\tmprops.button_settings = atom_init_integer(local->dev, MTRACK_PROP_BUTTON_SETTINGS, 2, ivals, 8);\n\n\tivals[0] = cfg->button_zones;\n\tivals[1] = cfg->button_move;\n\tivals[2] = cfg->button_expire;\n\tivals[3] = cfg->is_button_zones_height_limited;\n\tmprops.button_emulate_settings = atom_init_integer(local->dev, MTRACK_PROP_BUTTON_EMULATE_SETTINGS, 4, ivals, 16);\n\n\tivals[0] = cfg->button_0touch;\n\tivals[1] = cfg->button_1touch;\n\tivals[2] = cfg->button_2touch;\n\tivals[3] = cfg->button_3touch;\n\tivals[4] = cfg->button_first_zone;\n\tivals[5] = cfg->button_second_zone;\n\tivals[6] = cfg->button_third_zone;\n\tmprops.button_emulate_values = atom_init_integer(local->dev, MTRACK_PROP_BUTTON_EMULATE_VALUES, 7, ivals, 8);\n\n\tivals[0] = cfg->tap_hold;\n\tivals[1] = cfg->tap_timeout;\n\tivals[2] = cfg->tap_dist;\n\tmprops.tap_settings = atom_init_integer(local->dev, MTRACK_PROP_TAP_SETTINGS, 3, ivals, 32);\n\n\tivals[0] = cfg->tap_1touch;\n\tivals[1] = cfg->tap_2touch;\n\tivals[2] = cfg->tap_3touch;\n\tivals[3] = cfg->tap_4touch;\n\tmprops.tap_emulate = atom_init_integer(local->dev, MTRACK_PROP_TAP_EMULATE, 4, ivals, 8);\n\n\tivals[0] = cfg->ignore_thumb;\n\tivals[1] = cfg->disable_on_thumb;\n\tmprops.thumb_detect = atom_init_integer(local->dev, MTRACK_PROP_THUMB_DETECT, 2, ivals, 8);\n\n\tivals[0] = cfg->thumb_size;\n\tivals[1] = cfg->thumb_ratio;\n\tmprops.thumb_size = atom_init_integer(local->dev, MTRACK_PROP_THUMB_SIZE, 2, ivals, 32);\n\n\tivals[0] = cfg->ignore_palm;\n\tivals[1] = cfg->disable_on_palm;\n\tmprops.palm_detect = atom_init_integer(local->dev, MTRACK_PROP_PALM_DETECT, 2, ivals, 8);\n\n\tivals[0] = cfg->palm_size;\n\tmprops.palm_size = atom_init_integer(local->dev, MTRACK_PROP_PALM_SIZE, 1, ivals, 32);\n\n\tivals[0] = cfg->gesture_hold;\n\tivals[1] = cfg->gesture_wait;\n\tmprops.gesture_settings = atom_init_integer(local->dev, MTRACK_PROP_GESTURE_SETTINGS, 2, ivals, 16);\n\n\tivals[0] = cfg->scroll_smooth;\n\tmprops.scroll_smooth = atom_init_integer(local->dev, MTRACK_PROP_SMOOTH_SCROLL, 1, ivals, 8);\n\n\tinit_swipe_props(local->dev, &mprops.scroll, &cfg->scroll, MTRACK_PROP_SCROLL_SETTINGS, MTRACK_PROP_SCROLL_BUTTONS);\n\n\tinit_swipe_props(local->dev, &mprops.swipe3, &cfg->swipe3, MTRACK_PROP_SWIPE_SETTINGS, MTRACK_PROP_SWIPE_BUTTONS);\n\n\tinit_swipe_props(local->dev, &mprops.swipe4, &cfg->swipe4, MTRACK_PROP_SWIPE4_SETTINGS, MTRACK_PROP_SWIPE4_BUTTONS);\n\n\tfvals[0] = cfg->scroll_coast.min_speed;\n\tfvals[1] = cfg->scroll_coast.duration; /* = duration in miliseconds */\n\tmprops.scroll_coast = atom_init_float(local->dev, MTRACK_PROP_SCROLL_COAST, 2, fvals, mprops.float_type);\n\n\tinit_edge_props(local->dev, &mprops.edge_scroll, &cfg->edge_scroll, MTRACK_PROP_EDGE_SCROLL_SETTINGS);\n\n\tivals[0] = cfg->edge_top_size;\n\tivals[1] = cfg->edge_bottom_size;\n\tivals[2] = cfg->edge_left_size;\n\tivals[3] = cfg->edge_right_size;\n\tmprops.edge_sizes = atom_init_integer(local->dev, MTRACK_PROP_EDGE_SIZES, 4, ivals, 8);\n\n\tivals[0] = cfg->scale_dist;\n\tmprops.scale_dist = atom_init_integer(local->dev, MTRACK_PROP_SCALE_DIST, 1, ivals, 32);\n\n\tivals[0] = cfg->scale_up_btn;\n\tivals[1] = cfg->scale_dn_btn;\n\tmprops.scale_buttons = atom_init_integer(local->dev, MTRACK_PROP_SCALE_BUTTONS, 2, ivals, 8);\n\n\tivals[0] = cfg->rotate_dist;\n\tmprops.rotate_dist = atom_init_integer(local->dev, MTRACK_PROP_ROTATE_DIST, 1, ivals, 32);\n\n\tivals[0] = cfg->rotate_lt_btn;\n\tivals[1] = cfg->rotate_rt_btn;\n\tmprops.rotate_buttons = atom_init_integer(local->dev, MTRACK_PROP_ROTATE_BUTTONS, 2, ivals, 8);\n\n\tivals[0] = cfg->hold1_move1_stationary.max_move;\n\tivals[1] = cfg->hold1_move1_stationary.button;\n\tmprops.hold1_move1_stationary = atom_init_integer(local->dev, MTRACK_PROP_HOLD1_MOVE1_STATIONARY_SETTINGS, 2, ivals, 32);\n\n\tinit_swipe_props(local->dev, &mprops.hold1_move1, &cfg->hold1_move1, MTRACK_PROP_HOLD1_MOVE1_SETTINGS, MTRACK_PROP_HOLD1_MOVE1_BUTTONS);\n\n#if 0\n\tivals[0] = cfg->hold1_move2_stationary.max_move;\n\tivals[1] = cfg->hold1_move2_stationary.button;\n\tmprops.hold1_move2_stationary = atom_init_integer(local->dev, MTRACK_PROP_HOLD1_MOVE2_STATIONARY_SETTINGS, 2, ivals, 32);\n\n\tinit_swipe_props(local->dev, &mprops.hold1_move2, &cfg->hold1_move2, MTRACK_PROP_HOLD1_MOVE2_SETTINGS, MTRACK_PROP_HOLD1_MOVE2_BUTTONS);\n\n\n\tivals[0] = cfg->hold1_move3_stationary.max_move;\n\tivals[1] = cfg->hold1_move3_stationary.button;\n\tmprops.hold1_move3_stationary = atom_init_integer(local->dev, MTRACK_PROP_HOLD1_MOVE3_STATIONARY_SETTINGS, 2, ivals, 32);\n\n\tinit_swipe_props(local->dev, &mprops.hold1_move3, &cfg->hold1_move3, MTRACK_PROP_HOLD1_MOVE3_SETTINGS, MTRACK_PROP_HOLD1_MOVE3_BUTTONS);\n#endif\n\n\tivals[0] = cfg->drag_enable;\n\tivals[1] = cfg->drag_timeout;\n\tivals[2] = cfg->drag_wait;\n\tivals[3] = cfg->drag_dist;\n\tivals[4] = cfg->drag_lock_timeout;\n\tmprops.drag_settings = atom_init_integer(local->dev, MTRACK_PROP_DRAG_SETTINGS, 5, ivals, 32);\n\n\tivals[0] = cfg->axis_x_invert;\n\tivals[1] = cfg->axis_y_invert;\n\tmprops.axis_invert = atom_init_integer(local->dev, MTRACK_PROP_AXIS_INVERT, 2, ivals, 8);\n}\n\nint check_buttons_property(XIPropertyValuePtr prop, uint8_t** buttons_ret_arr, int buttons_count)\n{\n\tuint8_t* ivals8;\n\tint i;\n\n\tif (prop->size != buttons_count || prop->format != 8 || prop->type != XA_INTEGER)\n\t\treturn BadMatch;\n\n\tivals8 = (uint8_t*)prop->data;\n\tfor (i = 0; i < buttons_count; ++i) {\n\t\tif (!VALID_BUTTON(ivals8[i]))\n\t\t\treturn BadMatch;\n\t}\n\n\t*buttons_ret_arr = ivals8;\n\treturn Success;\n}\n\n/* Return:\n * 1 - property was recognized and handled with or without error, check error code for details\n * 0 - property not recognized, don't trust returned error code - it's invalid\n */\nstatic int set_swipe_properties(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,\n\t\t\t\t                       BOOL checkonly,\n                                struct MPropsSwipe* props_swipe,\n                                struct MConfigSwipe* cfg_swipe, int* error_code){\n\n\tuint8_t* ivals8;\n\tuint32_t* ivals32;\n\n\t*error_code = Success;\n\n\tif (property == props_swipe->settings) {\n\t\tif (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn *error_code = BadMatch, 1;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 1 || (int)(ivals32[1]) < 0)\n\t\t\treturn *error_code = BadMatch, 1;\n\n\t\tif (!checkonly) {\n\t\t\tcfg_swipe->dist = ivals32[0];\n\t\t\tcfg_swipe->hold = ivals32[1];\n\t\t\tcfg_swipe->drag_sens = ivals32[2];\n\t\t\tLOG_DEBUG_PROPS(\"set swipe settings: dist: %d hold: %d\\n\",\n\t\t\t\tcfg_swipe->dist, cfg_swipe->hold);\n\t\t}\n\t}\n\telse if (property == props_swipe->buttons) {\n\t\tif (check_buttons_property(prop, &ivals8, 4) == Success){\n\t\t\tif (!checkonly) {\n\t\t\t\tcfg_swipe->up_btn = ivals8[0];\n\t\t\t\tcfg_swipe->dn_btn = ivals8[1];\n\t\t\t\tcfg_swipe->lt_btn = ivals8[2];\n\t\t\t\tcfg_swipe->rt_btn = ivals8[3];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\treturn *error_code = BadMatch, 1;\n\t}\n\telse{\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\nint mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly) {\n\tInputInfoPtr local = dev->public.devicePrivate;\n\tstruct MConfig* cfg = &((struct MTouch*)local->private)->cfg;\n\n\tuint8_t* ivals8;\n\tuint16_t* ivals16;\n\tuint32_t* ivals32;\n\tfloat* fvals;\n\n\tint error_code;\n\n\tif (property == mprops.trackpad_disable) {\n\t\tif (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (ivals8[0] < 0 || ivals8[0] > 3)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->trackpad_disable = ivals8[0];\n\t\t\tif (cfg->trackpad_disable)\n\t\t\t\tLOG_DEBUG_PROPS(\"trackpad input disabled\\n\");\n\t\t\telse\n\t\t\t\tLOG_DEBUG_PROPS(\"trackpad input enabled\\n\");\n\t\t}\n\t}\n\telse if (property == mprops.sensitivity) {\n\t\tif (prop->size != 1 || prop->format != 32 || prop->type != mprops.float_type)\n\t\t\treturn BadMatch;\n\n\t\tfvals = (float*)prop->data;\n\t\tif (fvals[0] < 0)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->sensitivity = fvals[0];\n\t\t\tLOG_DEBUG_PROPS(\"set sensitivity to %f\\n\", cfg->sensitivity);\n\t\t}\n\t}\n\telse if (property == mprops.pressure) {\n\t\tif (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_PCNT(ivals8[0]) || !VALID_PCNT(ivals8[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->touch_down = ivals8[0];\n\t\t\tcfg->touch_up = ivals8[1];\n\t\t\tLOG_DEBUG_PROPS(\"set touch pressure to %d %d\\n\",\n\t\t\t\tcfg->touch_down, cfg->touch_up);\n\t\t}\n\t}\n\telse if (property == mprops.button_settings) {\n\t\tif (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->button_enable = ivals8[0];\n\t\t\tcfg->button_integrated = ivals8[1];\n\t\t\tLOG_DEBUG_PROPS(\"set button settings to %d %d\\n\",\n\t\t\t\tcfg->button_enable, cfg->button_integrated);\n\t\t}\n\t}\n\telse if (property == mprops.button_emulate_settings) {\n\t\tif (prop->size != 3 || prop->format != 16 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals16 = (uint16_t*)prop->data;\n\t\tif (!VALID_BOOL(ivals16[0]) || !VALID_BOOL(ivals16[1]) || ivals16[2] < 0)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->button_zones = ivals16[0];\n\t\t\tcfg->button_move = ivals16[1];\n\t\t\tcfg->button_expire = ivals16[2];\n\t\t\tLOG_DEBUG_PROPS(\"set button emulate settings to %d %d %d\\n\",\n\t\t\t\tcfg->button_zones, cfg->button_move, cfg->button_expire);\n\t\t}\n\t}\n\telse if (property == mprops.button_emulate_values) {\n\t\tif (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->button_0touch = ivals8[0];\n\t\t\tcfg->button_1touch = ivals8[1];\n\t\t\tcfg->button_2touch = ivals8[2];\n\t\t\tcfg->button_3touch = ivals8[3];\n\t\t\tLOG_DEBUG_PROPS(\"set button emulation to %d %d %d %d\\n\",\n\t\t\t\tcfg->button_0touch, cfg->button_1touch, cfg->button_2touch, cfg->button_3touch);\n\t\t}\n\t}\n\telse if (property == mprops.tap_settings) {\n\t\tif (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 1 || ivals32[1] < 1 || ivals32[2] < 1)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->tap_hold = ivals32[0];\n\t\t\tcfg->tap_timeout = ivals32[1];\n\t\t\tcfg->tap_dist = ivals32[2];\n\t\t\tLOG_DEBUG_PROPS(\"set tap settings to %d %d %d\\n\",\n\t\t\t\tcfg->tap_hold, cfg->tap_timeout, cfg->tap_dist);\n\t\t}\n\t}\n\telse if (property == mprops.tap_emulate) {\n\t\tif (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->tap_1touch = ivals8[0];\n\t\t\tcfg->tap_2touch = ivals8[1];\n\t\t\tcfg->tap_3touch = ivals8[2];\n\t\t\tcfg->tap_4touch = ivals8[3];\n\t\t\tLOG_DEBUG_PROPS(\"set tap emulation to %d %d %d %d\\n\",\n\t\t\t\tcfg->tap_1touch, cfg->tap_2touch, cfg->tap_3touch, cfg->tap_4touch);\n\t\t}\n\t}\n\telse if (property == mprops.thumb_detect) {\n\t\tif (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->ignore_thumb = ivals8[0];\n\t\t\tcfg->disable_on_thumb = ivals8[1];\n\t\t\tLOG_DEBUG_PROPS(\"set thumb detect to %d %d\\n\",\n\t\t\t\tcfg->ignore_thumb, cfg->disable_on_thumb);\n\t\t}\n\t}\n\telse if (property == mprops.thumb_size) {\n\t\tif (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 0 || !VALID_PCNT(ivals32[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->thumb_size = ivals32[0];\n\t\t\tcfg->thumb_ratio = ivals32[0];\n\t\t\tLOG_DEBUG_PROPS(\"set thumb size to %d %d\\n\",\n\t\t\t\tcfg->thumb_size, cfg->thumb_ratio);\n\t\t}\n\t}\n\telse if (property == mprops.palm_detect) {\n\t\tif (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->ignore_palm = ivals8[0];\n\t\t\tcfg->disable_on_palm = ivals8[1];\n\t\t\tLOG_DEBUG_PROPS(\"set palm detect to %d %d\\n\",\n\t\t\t\tcfg->ignore_palm, cfg->disable_on_palm);\n\t\t}\n\t}\n\telse if (property == mprops.palm_size) {\n\t\tif (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 0)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->palm_size = ivals32[0];\n\t\t\tLOG_DEBUG_PROPS(\"set palm size to %d\\n\",\n\t\t\t\tcfg->palm_size);\n\t\t}\n\t}\n\telse if (property == mprops.gesture_settings) {\n\t\tif (prop->size != 2 || prop->format != 16 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals16 = (uint16_t*)prop->data;\n\t\tif (ivals16[0] < 1 || ivals16[1] < 0)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->gesture_hold = ivals16[0];\n\t\t\tcfg->gesture_wait = ivals16[1];\n\t\t\tLOG_DEBUG_PROPS(\"set gesture settings to %d %d\\n\",\n\t\t\t\tcfg->gesture_hold, cfg->gesture_wait);\n\t\t}\n\t}\n\telse if (property == mprops.scroll_smooth) {\n\t\tif (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BOOL(ivals8[0]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->scroll_smooth = ivals8[0];\n\t\t\tLOG_DEBUG_PROPS(\"set high precision scrolling to %d\\n\",\n\t\t\t\tcfg->scroll_smooth);\n\t\t}\n\t}\n\telse if (set_swipe_properties(dev, property, prop, checkonly, &mprops.scroll, &cfg->scroll, &error_code)) {\n\t\treturn error_code;\n\t}\n\telse if (property == mprops.scroll_coast) {\n\t\tif (prop->size != 2 || prop->type != mprops.float_type)\n\t\t\treturn BadMatch;\n\n\t\tfvals = (float*)prop->data;\n\t\tif (fvals[0] < 0.0f || fvals[1] < 0.0f)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->scroll_coast.min_speed = fvals[0];\n\t\t\tcfg->scroll_coast.duration = fvals[1];\n\t\t\tLOG_DEBUG_PROPS(\"set scroll coasting to %f %f\\n\",\n\t\t\t\tcfg->scroll_coast.min_speed, cfg->scroll_coast.duration);\n\t\t}\n\t}\n\telse if (property == mprops.edge_scroll) {\n\t\tif (prop->size != 7 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\t\tivals32 = (uint32_t*)prop->data;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->edge_scroll.dist = ivals32[0];\n\t\t\tcfg->edge_scroll.hold = ivals32[1];\n\t\t\tcfg->edge_scroll.drag_sens = ivals32[2];\n\t\t\tcfg->edge_scroll.up_btn = ivals32[3];\n\t\t\tcfg->edge_scroll.dn_btn = ivals32[4];\n\t\t\tcfg->edge_scroll.lt_btn = ivals32[5];\n\t\t\tcfg->edge_scroll.rt_btn = ivals32[6];\n\t\t}\n\n\t}\n\telse if (property == mprops.scale_dist) {\n\t\tif (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 1)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->scale_dist = ivals32[0];\n\t\t\tLOG_DEBUG_PROPS(\"set scale distance to %d\\n\",\n\t\t\t\tcfg->scale_dist);\n\t\t}\n\t}\n\telse if (set_swipe_properties(dev, property, prop, checkonly, &mprops.swipe3, &cfg->swipe3, &error_code)) {\n\t\treturn error_code;\n\t}\n\telse if (set_swipe_properties(dev, property, prop, checkonly, &mprops.swipe4, &cfg->swipe4, &error_code)) {\n\t\treturn error_code;\n\t}\n\telse if (property == mprops.scale_buttons) {\n\t\tif (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->scale_up_btn = ivals8[0];\n\t\t\tcfg->scale_dn_btn = ivals8[1];\n\t\t\tLOG_DEBUG_PROPS(\"set scale buttons to %d %d\\n\",\n\t\t\t\tcfg->scale_up_btn, cfg->scale_dn_btn);\n\t\t}\n\t}\n\telse if (property == mprops.rotate_dist) {\n\t\tif (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 1)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->rotate_dist = ivals32[0];\n\t\t\tLOG_DEBUG_PROPS(\"set rotate distance to %d\\n\",\n\t\t\t\tcfg->rotate_dist);\n\t\t}\n\t}\n\telse if (property == mprops.rotate_buttons) {\n\t\tif (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->rotate_lt_btn = ivals8[0];\n\t\t\tcfg->rotate_rt_btn = ivals8[1];\n\t\t\tLOG_DEBUG_PROPS(\"set rotate buttons to %d %d\\n\",\n\t\t\t\tcfg->rotate_lt_btn, cfg->rotate_rt_btn);\n\t\t}\n\t}\n\telse if (property == mprops.hold1_move1_stationary){\n\t\tif (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 0|| !VALID_BUTTON(ivals32[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->hold1_move1_stationary.max_move = ivals32[0];\n\t\t\tcfg->hold1_move1_stationary.button = ivals32[1];\n\t\t\tLOG_DEBUG_PROPS(\"hold1_move1: max_move %d; button: %d\\n\",\n\t\t\t\tcfg->hold1_move1_stationary.max_move, cfg->hold1_move1_stationary.button);\n\t\t}\n\t}\n\telse if (set_swipe_properties(dev, property, prop, checkonly, &mprops.hold1_move1, &cfg->hold1_move1, &error_code)) {\n\t\treturn error_code;\n\t}\n#if 0\n\telse if (property == mprops.hold1_move2_stationary){\n\t\tif (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 0|| !VALID_BUTTON(ivals32[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->hold1_move2_stationary.max_move = ivals32[0];\n\t\t\tcfg->hold1_move2_stationary.button = ivals32[1];\n\t\t\tLOG_DEBUG_PROPS(\"hold1_move2: max_move %d; button: %d\\n\",\n\t\t\t\tcfg->hold1_move2_stationary.max_move, cfg->hold1_move2_stationary.button);\n\t\t}\n\t}\n\telse if (set_swipe_properties(dev, property, prop, checkonly, &mprops.hold1_move2, &cfg->hold1_move2, &error_code)) {\n\t\treturn error_code;\n\t}\n\telse if (property == mprops.hold1_move3_stationary){\n\t\tif (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (ivals32[0] < 0|| !VALID_BUTTON(ivals32[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->hold1_move3_stationary.max_move = ivals32[0];\n\t\t\tcfg->hold1_move3_stationary.button = ivals32[1];\n\t\t\tLOG_DEBUG_PROPS(\"hold1_move1: max_move %d; button: %d\\n\",\n\t\t\t\tcfg->hold1_move3_stationary.max_move, cfg->hold1_move3_stationary.button);\n\t\t}\n\t}\n\telse if (set_swipe_properties(dev, property, prop, checkonly, &mprops.hold1_move3, &cfg->hold1_move3, &error_code)) {\n\t\treturn error_code;\n\t}\n#endif\n\telse if (property == mprops.drag_settings) {\n\t\tif (prop->size != 5 || prop->format != 32 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals32 = (uint32_t*)prop->data;\n\t\tif (!VALID_BOOL(ivals32[0]) || ivals32[1] < 1 || (int)(ivals32[2]) < 0 || (int)(ivals32[3]) < 0)\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->drag_enable = ivals32[0];\n\t\t\tcfg->drag_timeout = ivals32[1];\n\t\t\tcfg->drag_wait = ivals32[2];\n\t\t\tcfg->drag_dist = ivals32[3];\n\t\t\tcfg->drag_lock_timeout = ivals32[4];\n\n\t\t\tLOG_DEBUG_PROPS(\"set drag settings to %d %d %d %d %d\\n\",\n\t\t\t\tcfg->drag_enable, cfg->drag_timeout, cfg->drag_wait, cfg->drag_dist, cfg->drag_lock_timeout);\n\t\t}\n\t}\n\telse if (property == mprops.axis_invert) {\n\t\tif (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_BOOL(ivals8[0]) || !VALID_BOOL(ivals8[1]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->axis_x_invert = ivals8[0];\n\t\t\tcfg->axis_y_invert = ivals8[1];\n\t\t\tLOG_DEBUG_PROPS(\"set axis inversion to %d %d\\n\",\n\t\t\t\tcfg->axis_x_invert, cfg->axis_y_invert);\n\t\t}\n\t}\n\telse if (property == mprops.edge_sizes) {\n\t\tif (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER)\n\t\t\treturn BadMatch;\n\n\t\tivals8 = (uint8_t*)prop->data;\n\t\tif (!VALID_PCNT(ivals8[0]) || !VALID_PCNT(ivals8[1]) || !VALID_PCNT(ivals8[2]) || !VALID_PCNT(ivals8[3]))\n\t\t\treturn BadMatch;\n\n\t\tif (!checkonly) {\n\t\t\tcfg->edge_top_size = ivals8[0];\n\t\t\tcfg->edge_bottom_size = ivals8[1];\n\t\t\tcfg->edge_left_size = ivals8[2];\n\t\t\tcfg->edge_right_size = ivals8[3];\n\t\t\tLOG_DEBUG_PROPS(\"set edge sizes to %d %d %d %d\\n\",\n\t\t\t\tcfg->edge_left_size, cfg->edge_right_size, cfg->edge_top_size, cfg->edge_bottom_size);\n\t\t}\n\t}\n\n\treturn Success;\n}\n"
  },
  {
    "path": "driver/mtrack.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n * Copyright (C) 2015-2018 Paweł Turkowski <p2rkw0@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"mtouch.h\"\n#include \"mprops.h\"\n#include \"capabilities.h\"\n#include \"os.h\"\n#include \"trig.h\" /* xorg/os.h for timers */\n\n#include <xf86Module.h>\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7\n#include <X11/Xatom.h>\n#include <xserver-properties.h>\n#endif\n#include <math.h> /* cos */\n\n#define TAP_HOLD 100\n#define TAP_TIMEOUT 200\n#define TAP_THRESHOLD 0.05\n#define TICK_TIMEOUT 50\n#define SCROLL_THRESHOLD 0.05\n#define SWIPE_THRESHOLD 0.15\n#define SCALE_THRESHOLD 0.15\n#define ROTATE_THRESHOLD 0.15\n#define PI 3.14159265\n\n#define NUM_AXES 4\n\n#ifdef DEBUG_DRIVER\n# define LOG_DEBUG_DRIVER LOG_DEBUG\n#else\n# define LOG_DEBUG_DRIVER LOG_DISABLED\n#endif\n\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12\ntypedef InputInfoPtr LocalDevicePtr;\n#endif\n\n/* button mapping simplified */\n#define PROPMAP(m, x, y) m[x] = XIGetKnownProperty(y)\n\nstatic void pointer_control(DeviceIntPtr dev, PtrCtrl *ctrl)\n{\n\tLOG_DEBUG_DRIVER(\"pointer_control\\n\");\n}\n\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7\nstatic void init_axes_labels(Atom map[NUM_AXES])\n{\n\tmemset(map, 0, NUM_AXES * sizeof(Atom));\n\tPROPMAP(map, 0, AXIS_LABEL_PROP_REL_X);\n\tPROPMAP(map, 1, AXIS_LABEL_PROP_REL_Y);\n\tPROPMAP(map, 2, AXIS_LABEL_PROP_REL_VSCROLL);\n\tPROPMAP(map, 3, AXIS_LABEL_PROP_REL_HSCROLL);\n}\n\nstatic void init_button_labels(Atom map[DIM_BUTTON])\n{\n\tmemset(map, 0, DIM_BUTTON * sizeof(Atom));\n\tPROPMAP(map, MT_BUTTON_LEFT, BTN_LABEL_PROP_BTN_LEFT);\n\tPROPMAP(map, MT_BUTTON_MIDDLE, BTN_LABEL_PROP_BTN_MIDDLE);\n\tPROPMAP(map, MT_BUTTON_RIGHT, BTN_LABEL_PROP_BTN_RIGHT);\n\tPROPMAP(map, MT_BUTTON_WHEEL_UP, BTN_LABEL_PROP_BTN_WHEEL_UP);\n\tPROPMAP(map, MT_BUTTON_WHEEL_DOWN, BTN_LABEL_PROP_BTN_WHEEL_DOWN);\n\tPROPMAP(map, MT_BUTTON_HWHEEL_LEFT, BTN_LABEL_PROP_BTN_HWHEEL_LEFT);\n\tPROPMAP(map, MT_BUTTON_HWHEEL_RIGHT, BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);\n\t/* how to map swipe buttons? */\n\tPROPMAP(map, MT_BUTTON_SWIPE_UP, BTN_LABEL_PROP_BTN_0);\n\tPROPMAP(map, MT_BUTTON_SWIPE_DOWN, BTN_LABEL_PROP_BTN_1);\n\tPROPMAP(map, MT_BUTTON_SWIPE_LEFT, BTN_LABEL_PROP_BTN_2);\n\tPROPMAP(map, MT_BUTTON_SWIPE_RIGHT, BTN_LABEL_PROP_BTN_3);\n\t/* how to map scale and rotate? */\n\tPROPMAP(map, MT_BUTTON_SCALE_DOWN, BTN_LABEL_PROP_BTN_4);\n\tPROPMAP(map, MT_BUTTON_SCALE_UP, BTN_LABEL_PROP_BTN_5);\n\tPROPMAP(map, MT_BUTTON_ROTATE_LEFT, BTN_LABEL_PROP_BTN_6);\n\tPROPMAP(map, MT_BUTTON_ROTATE_RIGHT, BTN_LABEL_PROP_BTN_7);\n}\n#endif\n\n/**\n * How to handle multitouch: http://www.x.org/wiki/Development/Documentation/Multitouch/\n * Howto about xinput: http://www.x.org/wiki/Development/Documentation/XorgInputHOWTO/\n * Example usage: https://gitlab.com/at-home-modifier/at-home-modifier-evdev/commit/d171b3d9194581cb6ed59dbe45d6cbf009dc0eaa?view=parallel\n * Patch were smooth scrolling were introduced: https://lists.x.org/archives/xorg-devel/2011-September/025835.html\n * @param dev\n * @param axnum\n * @param label\n * @param min\n * @param max\n * @param resolution\n */\nstatic void init_axle_absolute(DeviceIntPtr dev, int axnum, Atom* label)\n{\n\t/* Inform server about reported range of axis values. */\n\txf86InitValuatorAxisStruct(dev, axnum,\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7\n\t\t*label,\n#endif\n\t\t/* minval, maxval */ NO_AXIS_LIMITS, NO_AXIS_LIMITS,\n\t\t/*resolution*/ 1,\n\t\t/*min res*/ 0,\n\t\t/*max res*/ 1\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12\n\t\t/* mode */, Absolute\n#endif\n\t);\n\txf86InitValuatorDefaults(dev, axnum);\n}\n\nstatic void init_axle_relative(DeviceIntPtr dev, int axnum, Atom* label)\n{\n\txf86InitValuatorAxisStruct(dev, axnum, *label, NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative);\n\txf86InitValuatorDefaults(dev, axnum);\n}\n\nstatic int device_init(DeviceIntPtr dev, LocalDevicePtr local)\n{\n\tstruct MTouch *mt = local->private;\n\tunsigned char btmap[DIM_BUTTON + 1] = {\n\t\t0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15\n\t};\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7\n\tAtom axes_labels[NUM_AXES], btn_labels[DIM_BUTTON];\n\tinit_axes_labels(axes_labels);\n\tinit_button_labels(btn_labels);\n#endif\n\n\tlocal->fd = xf86OpenSerial(local->options);\n\tif (local->fd < 0) {\n\t\tLOG_ERROR(\"cannot open device %s\\n\", local->name);\n\t\treturn !Success;\n\t}\n\tif (mtouch_configure(mt, local->fd)) {\n\t\tLOG_ERROR(\"cannot configure device %s\\n\", local->name);\n\t\treturn !Success;\n\t}\n\txf86CloseSerial(local->fd);\n\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3\n\tInitPointerDeviceStruct((DevicePtr)dev,\n\t\t\t\tbtmap, DIM_BUTTON,\n\t\t\t\tGetMotionHistory,\n\t\t\t\tpointer_control,\n\t\t\t\tGetMotionHistorySize(),\n\t\t\t\t2);\n#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 7\n\tInitPointerDeviceStruct((DevicePtr)dev,\n\t\t\t\tbtmap, DIM_BUTTON,\n\t\t\t\tpointer_control,\n\t\t\t\tGetMotionHistorySize(),\n\t\t\t\t2);\n#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7\n\tInitPointerDeviceStruct((DevicePtr)dev,\n\t\t\t\tbtmap, DIM_BUTTON, btn_labels,\n\t\t\t\tpointer_control,\n\t\t\t\tGetMotionHistorySize(),\n\t\t\t\tNUM_AXES, axes_labels);\n#else\n#error \"Unsupported ABI_XINPUT_VERSION\"\n#endif\n\n\tinit_axle_absolute(dev, 0, &axes_labels[0]);\n\tinit_axle_absolute(dev, 1, &axes_labels[1]);\n\n\tinit_axle_relative(dev, 2, &axes_labels[2]);\n\tinit_axle_relative(dev, 3, &axes_labels[3]);\n\n\t/* Always set valuator distance to 1.0 because it reported values will be\n\t * adjusted accordingly by smooth scroll trigger.\n\t */\n\tSetScrollValuator(dev, 2, SCROLL_TYPE_VERTICAL, 1.0, SCROLL_FLAG_PREFERRED);\n\tSetScrollValuator(dev, 3, SCROLL_TYPE_HORIZONTAL, 1.0, SCROLL_FLAG_NONE);\n\n\tmprops_init(&mt->cfg, local);\n\n\tXIRegisterPropertyHandler(dev, mprops_set_property, NULL, NULL);\n\n\tmt->timer = NULL; /* allocated later */\n\tmt->timer_kind = 0;\n\tmt->absolute_mode = FALSE;\n\treturn Success;\n}\n\nstatic int device_on(LocalDevicePtr local)\n{\n\tstruct MTouch *mt = local->private;\n\tlocal->fd = xf86OpenSerial(local->options);\n\tif (local->fd < 0) {\n\t\tLOG_ERROR(\"cannot open device\\n\");\n\t\treturn !Success;\n\t}\n\tif (mtouch_open(mt, local->fd)) {\n\t\tLOG_ERROR(\"cannot grab device\\n\");\n\t\treturn !Success;\n\t}\n\t/*\n\t *  xf86AddEnabledDevice() will add our device's fd to the list of SIGIO handlers.\n\t *  When a SIGIO occurs, our read_input will get called.\n\t */\n\txf86AddEnabledDevice(local);\n\tif(mt->timer != NULL)\n\t\tTimerFree(mt->timer);\t// release any existing timer\n\tmt->timer = NULL;\n\tmt->timer_kind = 0;\n\treturn Success;\n}\n\nstatic int device_off(LocalDevicePtr local)\n{\n\tstruct MTouch *mt = local->private;\n\txf86RemoveEnabledDevice(local);\n\tif (mtouch_close(mt))\n\t\tLOG_WARNING(\"cannot ungrab device\\n\");\n\txf86CloseSerial(local->fd);\n\tif(mt->timer != NULL)\n\t\tTimerFree(mt->timer);\t// release any existing timer\n\tmt->timer = NULL;\n\tmt->timer_kind = 0;\n\treturn Success;\n}\n\nstatic int device_close(LocalDevicePtr local)\n{\n\treturn Success;\n}\n\nstatic void post_button(struct MTouch* mt, int button, int new_state);\n\nvoid mt_timer_start(struct MTouch *mt, int kind)\n{\n\tstruct Gestures* gs = &mt->gs;\n\tmstime_t timeout; /*< Set this variable to required timeout. */\n\n\tif(kind == mt->timer_kind){\n\t\tLOG_DEBUG_DRIVER(\"Timer %i already started\\n\", kind);\n\t\treturn;\n\t}\n\n\tmt_timer_stop(mt);\n\n\tswitch(kind){\n\tcase MT_TIMER_DELAYED_BUTTON:\n\t{\n\t\tstruct timeval delta;\n\n\t\ttimersub(&gs->button_delayed_time, &gs->time, &delta);\n\t\ttimeout = timertoms(&delta);\n\t\tbreak;\n\t}\n\n\tcase MT_TIMER_COASTING:\n\t\tgs->move_dx = gs->move_dy = 0.0;\n\t\tgs->move_type = GS_NONE;\n\t\tgs->coasting_duration_left = mt->cfg.scroll_coast.duration - 1;\n\t\ttimeout = mt->cfg.scroll_coast.tick_ms;\n\t\tbreak;\n\n\tcase MT_TIMER_ANY:\n\tcase MT_TIMER_NONE:\n\t\treturn; /* do nothing */\n\n\tdefault:\n\t\tLOG_INFO(\"Unimplemented timer, ID: %i\\n\", kind);\n\t\treturn;\n\t}\n\n\tLOG_DEBUG_DRIVER(\"Start timer, ID: %i with timeout: %llu\\n\", kind, timeout);\n\tmt->timer = TimerSet(mt->timer, 0, timeout, mt_timer_callback, mt);\n\tmt->timer_kind = kind;\n}\n\nvoid mt_timer_stop(struct MTouch *mt)\n{\n\tstruct Gestures* gs = &mt->gs;\n\n\tLOG_DEBUG_DRIVER(\"Stop timer, ID: %i\\n\", mt->timer_kind);\n\tTimerCancel(mt->timer);\n\n\tswitch(mt->timer_kind){\n\tcase MT_TIMER_DELAYED_BUTTON:\n\t{\n\t\tint button;\n\t\tbutton = trigger_delayed_button_uncond(gs);\n\t\tpost_button(mt, button, GETBIT(gs->buttons, button));\n\t\tgs->move_dx = gs->move_dy = 0.0;\n\t\tgs->move_type = GS_NONE;\n\t\tbreak;\n\t}\n\n\tcase MT_TIMER_COASTING:\n\t\tmt->gs.scroll_speed_x = mt->gs.scroll_speed_y = 0.0;\n\t\tbreak;\n\n\tcase MT_TIMER_ANY:\n\tcase MT_TIMER_NONE:\n\t\tbreak; /* do nothing */\n\n\tdefault:\n\t\tLOG_INFO(\"Unimplemented timer, ID: %i\\n\", mt->timer_kind);\n\t\tbreak;\n\t}\n\tmt->timer_kind = MT_TIMER_NONE;\n}\n\nCARD32 mt_timer_callback(OsTimerPtr timer, CARD32 time, void *arg)\n{\n\tstruct MTouch *mt = arg;\n\tstruct Gestures* gs = &mt->gs;\n\tValuatorMask* mask = mt->valuator_mask;\n\n\tswitch(mt->timer_kind){\n\tcase MT_TIMER_DELAYED_BUTTON:\n\t{\n\t\tint button;\n\t\tbutton = trigger_delayed_button_uncond(gs);\n\t\tpost_button(mt, button, GETBIT(gs->buttons, button));\n\t\tgs->move_dx = gs->move_dy = 0.0;\n\t\tgs->move_type = GS_NONE;\n\n\t\tmt_timer_stop(mt);\n\t\tbreak;\n\t}\n\n\tcase MT_TIMER_COASTING:\n\t{\n\t\tif (gs->coasting_duration_left <= 0)\n\t\t\treturn mt_timer_stop(mt), 0;\n\n\t\tmstime_t delta_ms;\n\t\tdouble coasting_progress;\n\n\t\tvaluator_mask_zero(mask);\n\t\tdelta_ms = mt->cfg.scroll_coast.tick_ms;\n\t\tif (mt->cfg.scroll_coast.ease) {\n\t\t\t/* Calculate easing effect */\n\t\t\tcoasting_progress = 1 - gs->coasting_duration_left / (double)mt->cfg.scroll_coast.duration;\n\t\t\tdelta_ms = delta_ms - (delta_ms * coasting_progress / 2);\n\t\t\tcoasting_progress = (cos(PI * coasting_progress) + 1) / 2;\n\t\t}\n\t\telse\n\t\t\tcoasting_progress = gs->coasting_duration_left / (double)mt->cfg.scroll_coast.duration;\n\n\t\tvaluator_mask_set_double(mask, 2, gs->scroll_speed_y * coasting_progress * delta_ms);\n\t\tvaluator_mask_set_double(mask, 3, gs->scroll_speed_x * coasting_progress * delta_ms);\n\t\tgs->coasting_duration_left -= delta_ms;\n\n\t\txf86PostMotionEventM(mt->local_dev, Relative, mask);\n\n\t\tmt_timer_continue(mt, mt->cfg.scroll_coast.tick_ms);\n\t\tbreak;\n\t}\n\n\tcase MT_TIMER_ANY:\n\tcase MT_TIMER_NONE:\n\t\treturn 0; /* do nothing */\n\n\tdefault:\n\t\tLOG_INFO(\"Unimplemented timer id: %i\\n\", mt->timer_kind);\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nvoid mt_timer_start_or_stop(struct MTouch *mt, int kind)\n{\n\tif(kind < 0)\n\t\tmt_timer_stop_if(mt, -kind);\n\telse if(kind > 0)\n\t\tmt_timer_start(mt, kind);\n}\n\nvoid mt_timer_stop_if(struct MTouch *mt, int kind)\n{\n\tif(mt->timer_kind == kind || kind == MT_TIMER_ANY || kind == -MT_TIMER_ANY)\n\t\tmt_timer_stop(mt);\n}\n\nvoid mt_timer_continue(struct MTouch *mt, mstime_t timeout)\n{\n\tLOG_DEBUG_DRIVER(\"Continue timer, ID: %i with timeout: %llu\\n\", mt->timer_kind, timeout);\n\tmt->timer = TimerSet(mt->timer, 0, timeout, mt_timer_callback, mt);\n}\n\n/**\n * @param mt\n * @return\n */\nstatic int can_start_coasting(struct MTouch *mt)\n{\n\tstruct Gestures* gs = &mt->gs;\n\tstruct MTState* ms = &mt->state;\n\tint i;\n\n\tif(mt->cfg.scroll_smooth && mt->cfg.scroll_coast.duration > 0 &&\n\t\t mt->cfg.scroll_coast.duration > 0 &&\n\t\t hypot_cmpf(gs->scroll_speed_x, gs->scroll_speed_y, mt->cfg.scroll_coast.min_speed) == 1)\n\t{\n\t\tforeach_bit(i, ms->touch_used) {\n\t\t\tif (GETBIT(ms->touch[i].flags, MT_INVALID) ||\n\t\t\t\t\tGETBIT(ms->touch[i].flags, MT_BUTTON) ||\n\t\t\t\t\tGETBIT(ms->touch[i].flags, MT_TAP)\n\t\t\t\t )\n\t\t\t\tcontinue;\n\t\t\tif(GETBIT(mt->state.touch[i].flags, MT_RELEASED))\n\t\t\t\treturn 1;\n\t\t} /* foreach */\n\t}\n\treturn 0;\n}\n\nstatic bitmask_t buttons_posted = 0U;\nstatic void post_gestures(struct MTouch *mt)\n{\n\tstruct Gestures* gs = &mt->gs;\n\tint i;\n\tconst double delta = timertoms(&gs->dt);\n\n\tif(mt->absolute_mode == FALSE){\n\t\tif (mt->cfg.scroll_smooth){\n\t\t\t/* Never post these buttons in smooth mode. */\n\t\t\tCLEARBIT(gs->buttons, MT_BUTTON_WHEEL_UP);\n\t\t\tCLEARBIT(gs->buttons, MT_BUTTON_WHEEL_DOWN);\n\t\t\tCLEARBIT(gs->buttons, MT_BUTTON_HWHEEL_LEFT);\n\t\t\tCLEARBIT(gs->buttons, MT_BUTTON_HWHEEL_RIGHT);\n\t\t}\n\t}\n\n\tfor (i = 0; i < 32; i++) {\n\t\tpost_button(mt, i, GETBIT(gs->buttons, i));\n\t}\n\n\tif(mt->absolute_mode == FALSE){\n\t\tif (mt->cfg.scroll_smooth){\n\t\t\tValuatorMask* mask;\tmask = mt->valuator_mask;\n\t\t\tvaluator_mask_zero(mask);\n\n\t\t\tif (gs->move_dx)\n\t\t\t\tvaluator_mask_set_double(mask, 0, gs->move_dx);\n\t\t\tif (gs->move_dy)\n\t\t\t\tvaluator_mask_set_double(mask, 1, gs->move_dy);\n\t\t\tgs->move_dx = gs->move_dy = 0.0;\n\n\t\t\t/* if is any swipe */\n\t\t\tif(gs->move_type == GS_SWIPE2 || gs->move_type == GS_SWIPE3 || gs->move_type == GS_SWIPE4){\n\t\t\t\tif(gs->scroll_speed_valid)\n\t\t\t\t{\n\t\t\t\t\t/* Delta encoded in scroll_speed. */\n\t\t\t\t\tvaluator_mask_set_double(mask, 2, gs->scroll_speed_y);\n\t\t\t\t\tvaluator_mask_set_double(mask, 3, gs->scroll_speed_x);\n\t\t\t\t}\n\t\t\t\tgs->scroll_speed_valid = 0;\n\n\t\t\t\t/* Start coasting here if needed. */\n\t\t\t\tif(delta != 0.0 && can_start_coasting(mt)){\n\t\t\t\t\t/* Remove delta component from scroll_speed. */\n\t\t\t\t\tgs->scroll_speed_x /= delta;\n\t\t\t\t\tgs->scroll_speed_y /= delta;\n\t\t\t\t\tmt_timer_start(mt, MT_TIMER_COASTING);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\txf86PostMotionEventM(mt->local_dev, Relative, mask);\n\t\t} /* if smooth scroll */\n\t\telse{\n\t\t\t// mt->absolute_mode == false\n\t\t\tif (gs->move_dx != 0.0 || gs->move_dy != 0.0)\n\t\t\t\txf86PostMotionEvent(mt->local_dev, 0, 0, 2, (int)gs->move_dx, (int)gs->move_dy);\n\t\t}\n\t}\n\telse{\n\t\t/* Give the HW coordinates to Xserver as absolute coordinates, these coordinates\n\t\t * are not scaled, this is oke if the touchscreen has the same resolution as the display.\n\t\t */\n\t\txf86PostMotionEvent(mt->local_dev, 1, 0, 2,\n\t\t\tmt->state.touch[0].x + get_cap_xmid(&mt->caps),\n\t\t\tmt->state.touch[0].y + get_cap_ymid(&mt->caps));\n\t}\n}\n\nstatic void post_button(struct MTouch* mt, int button, int new_state)\n{\n\tstruct Gestures* gs = &mt->gs;\n\n\tif (new_state == GETBIT(buttons_posted, button))\n\t\treturn;\n\tif (new_state) {\n\t\txf86PostButtonEvent(mt->local_dev, FALSE, button+1, 1, 0, 0);\n\t\tLOG_DEBUG_DRIVER(\"button %d down\\n\", button+1);\n\t}\n\telse {\n\t\txf86PostButtonEvent(mt->local_dev, FALSE, button+1, 0, 0, 0);\n\t\tLOG_DEBUG_DRIVER(\"button %d up\\n\", button+1);\n\t}\n\tMODBIT(gs->buttons, button, new_state);\n\tMODBIT(buttons_posted, button, new_state);\n}\n\n/*\n *  Called for each full received packet from the touchpad.\n * Any xf86 input event generated by int this callback function fill be queued by\n * xorg server, and fired when control return from this function.\n * So to fire event as early as possible this function should return quickly.\n * For delayed events we can't simply wait in this function, because it will delay\n * all events generated by 'post_gestures'.\n * Moreover we don't know when next input event will occur, so to guarantee proper\n * timing I have to use timer.\n *\n * More on input event processing:\n * http://www.x.org/wiki/Development/Documentation/InputEventProcessing/\n *\n * HowTo:\n * https://www.x.org/wiki/Development/Documentation/XorgInputHOWTO/\n */\nstatic void read_input(LocalDevicePtr local)\n{\n\tstruct MTouch *mt = local->private;\n\tint timer_kind;\n\n\tmt->local_dev = local->dev;\n\twhile (mtouch_read(mt) > 0){\n\t\ttimer_kind = mtouch_delayed(mt);\n\t\tmt_timer_start_or_stop(mt, timer_kind);\n\t\tpost_gestures(mt);\n\t}\n}\n\nstatic int switch_mode(ClientPtr client, DeviceIntPtr dev, int mode)\n{\n\tLocalDevicePtr local = dev->public.devicePrivate;\n\tstruct MTouch *mt = local->private;\n\n\tswitch (mode) {\n\tcase Absolute:\n\t\tmt->absolute_mode = TRUE;\n\t\tLOG_INFO(\"switch_mode: switing to absolute mode\\n\");\n\t\tbreak;\n\tcase Relative:\n\t\tmt->absolute_mode = FALSE;\n\t\tLOG_INFO(\"switch_mode: switing to relative mode\\n\");\n\t\tbreak;\n\tdefault:\n\t\treturn XI_BadMode;\n\t}\n\treturn Success;\n}\n\n\nstatic Bool device_control(DeviceIntPtr dev, int mode)\n{\n\tLocalDevicePtr local = dev->public.devicePrivate;\n\tswitch (mode) {\n\tcase DEVICE_INIT:\n\t\tLOG_INFO(\"device control: init\\n\");\n\t\treturn device_init(dev, local);\n\tcase DEVICE_ON:\n\t\tLOG_INFO(\"device control: on\\n\");\n\t\treturn device_on(local);\n\tcase DEVICE_OFF:\n\t\tLOG_INFO(\"device control: off\\n\");\n\t\treturn device_off(local);\n\tcase DEVICE_CLOSE:\n\t\tLOG_INFO(\"device control: close\\n\");\n\t\treturn device_close(local);\n\tdefault:\n\t\tLOG_INFO(\"device control: default\\n\");\n\t\treturn BadValue;\n\t}\n}\n\n\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12\nstatic int preinit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)\n{\n\tstruct MTouch *mt;\n\n\tmt = calloc(1, sizeof(*mt));\n\tif (!mt)\n\t\treturn BadAlloc;\n\n\tpInfo->private = mt;\n\tpInfo->type_name = XI_TOUCHPAD;\n\tpInfo->device_control = device_control;\n\tpInfo->read_input = read_input;\n\tpInfo->switch_mode = switch_mode;\n\n\txf86CollectInputOptions(pInfo, NULL);\n\txf86OptionListReport(pInfo->options);\n\txf86ProcessCommonOptions(pInfo, pInfo->options);\n\tmconfig_configure(&mt->cfg, pInfo->options); // set the defaults\n\tmt->valuator_mask = valuator_mask_new(4);\n\n\treturn Success;\n}\n#else\nstatic InputInfoPtr preinit(InputDriverPtr drv, IDevPtr dev, int flags)\n{\n\tstruct MTouch *mt;\n\tInputInfoPtr local = xf86AllocateInput(drv, 0);\n\tif (!local)\n\t\tgoto error;\n\tmt = calloc(1, sizeof(struct MTouch));\n\tif (!mt)\n\t\tgoto error;\n\n\tlocal->name = dev->identifier;\n\tlocal->type_name = XI_TOUCHPAD;\n\tlocal->device_control = device_control;\n\tlocal->read_input = read_input;\n\tlocal->switch_mode = switch_mode;\n\tlocal->private = mt;\n\tlocal->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS;\n\tlocal->conf_idev = dev;\n\n\txf86CollectInputOptions(local, NULL, NULL);\n\txf86OptionListReport(local->options);\n\txf86ProcessCommonOptions(local, local->options);\n\tmconfig_configure(&mt->cfg, local->options);\n\tmt->vm = NULL;\n\n\tlocal->flags |= XI86_CONFIGURED;\n error:\n\treturn local;\n}\n#endif\n\nstatic void uninit(InputDriverPtr drv, InputInfoPtr local, int flags)\n{\n\tstruct MTouch *mt = local->private;\n\n\tif (mt->valuator_mask)\n\t\tvaluator_mask_free(&mt->valuator_mask);\n\tfree(local->private);\n\tlocal->private = 0;\n\txf86DeleteInput(local, 0);\n}\n\n/* About xorg drivers, modules:\n * http://www.x.org/wiki/Development/Documentation/XorgInputHOWTO/\n */\nstatic InputDriverRec MTRACK = {\n\t1,\n\t\"mtrack\",\n\tNULL,\n\tpreinit,\n\tuninit,\n\tNULL,\n\t0\n};\n\nstatic XF86ModuleVersionInfo moduleVersion = {\n\t\"mtrack\",\n\tMODULEVENDORSTRING,\n\tMODINFOSTRING1,\n\tMODINFOSTRING2,\n\tXORG_VERSION_CURRENT,\n\tPACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,\n\tABI_CLASS_XINPUT,\n\tABI_XINPUT_VERSION,\n\tMOD_CLASS_XINPUT,\n\t{0, 0, 0, 0}\n};\n\nstatic pointer setup(pointer module, pointer options, int *errmaj, int *errmin)\n{\n\txf86AddInputDriver(&MTRACK, module, 0);\n\treturn module;\n}\n\n_X_EXPORT XF86ModuleData mtrackModuleData = {&moduleVersion, &setup, NULL };\n"
  },
  {
    "path": "examples/acer-aspire-s3.conf",
    "content": "Section \"InputClass\"\n        MatchIsTouchpad \"on\"\n        Identifier      \"Touchpads\"\n        Driver          \"mtrack\"\n        Option          \"Sensitivity\" \"0.60\"\n        Option          \"FingerHigh\" \"5\"\n        Option          \"FingerLow\" \"1\"\n        Option          \"IgnoreThumb\" \"true\"\n        Option          \"ThumbRatio\" \"70\"\n        Option          \"ThumbSize\" \"25\"\n        Option          \"IgnorePalm\" \"true\"\n        Option          \"TapButton1\" \"1\"\n        Option          \"TapButton2\" \"3\"\n        Option          \"TapButton3\" \"0\"\n        Option          \"TapButton4\" \"0\"\n        Option          \"ClickFinger1\" \"3\"\n        Option          \"ClickFinger2\" \"3\"\n        Option          \"ClickFinger3\" \"3\"\n        Option          \"ButtonMoveEmulate\" \"false\"\n        Option          \"ButtonIntegrated\" \"true\"\n        Option          \"ClickTime\" \"25\"\n        Option          \"SwipeLeftButton\" \"8\"\n        Option          \"SwipeRightButton\" \"9\"\n        Option          \"SwipeUpButton\" \"0\"\n        Option          \"SwipeDownButton\" \"0\"\n        Option          \"SwipeDistance\" \"700\"\n        Option          \"ScrollCoastDuration\" \"500\"\n        Option          \"ScrollCoastEnableSpeed\" \".8\"\n        Option          \"ScrollUpButton\" \"4\"\n        Option          \"ScrollDownButton\" \"5\"\n        Option          \"ScrollLeftButton\" \"6\"\n        Option          \"ScrollRightButton\" \"7\"\n        Option          \"ScrollDistance\" \"75\"\n        Option          \"ScrollSmooth\" \"1\"\n        Option          \"ScrollSensitivity\" \"0\"\n        Option          \"ScrollClickTime\" \"0\"\n\n        Option          \"EdgeSize\" \"20\"\nEndSection\n"
  },
  {
    "path": "examples/asus-zenbook-ux330ua-ah55",
    "content": "# mtrack config made for asus-zenbook-ux330ua-ah55 \n\n###--------------------------------------------------------------------------###\n# Properties in comments are \"xinput set-prop\" command to enable setting live (without restarting X)\n# Refer to source code to read more about those: xf86-input-mtrack/include/mprops.h\n###--------------------------------------------------------------------------###\n\n\nSection \"InputClass\"\n        MatchIsTouchpad \"on\"\n        Identifier \"Touchpads\"\n        Driver \"mtrack\"\n        Option \"DeviceEnabled\" \"1\"\n\n###--------------------------------------------------------------------------###\n###   http://www.x.org/wiki/Development/Documentation/PointerAcceleration/\n###   Base velocity setting\n###   Options for polynomial(\"2\") profile\n###--------------------------------------------------------------------------###\n        Option \"AccelerationProfile\" \"4\"\n        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.\n        Option \"AdaptiveDeceleration\" \"2.0\"\n        Option \"ConstantDeceleration\" \"1.02\"\n        Option \"VelocityScale\" \"1.0\"\n\n###---------------------------------------------------------------------------###\n###   Ignore certain touch type\n###---------------------------------------------------------------------------###\n        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.\n        Option \"IgnorePalm\" \"true\" # Whether or not to ignore touches that are determined to be palms. Boolean value. Defaults to false.\n        Option \"DisableOnPalm\" \"true\" # Whether or not to disable the entire trackpad when a palm is touching. Boolean value. Defaults to false.\n\n        Option \"IgnoreThumb\" \"true\" # Whether or not to ignore touches that are determined to be thumbs. Boolean value. Defaults to false.\n        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.\n        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.\n\n###--------------------------------------------------------------------------###\n###   Set click interactions\n###--------------------------------------------------------------------------###\n        Option \"ButtonEnable\" \"true\" # Whether or not to enable the physical buttons on or near the trackpad. Boolean value. Defaults to true.\n        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.\n        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.\n        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.\n        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.\n\n        # Zones stack from left to right inside the 20% height defined above (EdgeBottomSize)\n        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.\n        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.\n        # Next one is not needed and defaut to zero\n        #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.\n\n        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.\n        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.\n        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.\n        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.\n\n###---------------------- Gesture Settings ----------------------------------###\n### property: \"Trackpad Gesture Settings\"\n        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.\n        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.\n\n###--------------------------------------------------------------------------###\n        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.\n        \n        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.\n        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.\n\n###---------------------- Tap Settings --------------------------------------###\n### property \"Trackpad Tap Settings\"\n        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.\n        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.\n        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.\n\n###--------------------------------------------------------------------------###\n        Option \"TapButton1\" \"1\" # Which button to emulate for one-finger tapping. Integer value. A value of 0 disables one-finger tapping. Defaults to 1.\n        Option \"TapButton2\" \"3\" # Which button to emulate for two-finger tapping. Integer value. A value of 0 disables two-finger tapping. Defaults to 3.\n        Option \"TapButton3\" \"2\" # Which button to emulate for three-finger tapping. Integer value. A value of 0 disables three-finger tapping. Defaults to 2.\n        #Option \"TapButton4\" \"0\" # Which button to emulate for four-finger tapping. Integer value. A value of 0 disables three-finger tapping. Defaults to 0.\n\n###------------------------ Swipe2 as mouse wheel ---------------------------###\n        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.\n        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.\n        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.\n        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.\n        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.\n        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.\n        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.\n\n###------------------------ Swipe3 - act as mouse wheel ---------------------###\n        Option \"SwipeDistance\" \"22\"\n        Option \"SwipeClickTime\" \"12\"\n        \n### property: \"Trackpad Swipe Buttons\"\n        Option \"SwipeUpButton\" \"4\"\n        Option \"SwipeDownButton\" \"5\"\n        Option \"SwipeLeftButton\" \"6\"\n        Option \"SwipeRightButton\" \"7\"\n###--------------------------------------------------------------------------###\n\n###---------------------- Swipe4 to drag buttons 9 10 11 12 -----------------###\n        Option \"Swipe4Distance\" \"280\"\n        Option \"Swipe4ClickTime\" \"50\"\n        Option \"Swipe4Sensitivity\" \"0\"\n## property: \"Trackpad Swipe4 Buttons\"\n        Option \"Swipe4UpButton\" \"9\"\n        Option \"Swipe4DownButton\" \"10\"\n        Option \"Swipe4LeftButton\" \"11\"\n        Option \"Swipe4RightButton\" \"12\"\n###--------------------------------------------------------------------------###\n\n        Option \"ScaleDistance\" \"70\"\n        Option \"ScaleUpButton\" \"12\"\n        Option \"ScaleDownButton\" \"13\"\n\n        Option \"RotateDistance\" \"350\"\n        Option \"RotateLeftButton\" \"14\"\n        Option \"RotateRightButton\" \"15\"\n\n###--------------------------------------------------------------------------###\n### xinput set-prop 11 \"Trackpad Drag Settings\" 1 350 60 200\n        Option \"TapDragEnable\" \"true\"\n        Option \"TapDragTime\" \"350\"\n        Option \"TapDragWait\" \"40\"\n        Option \"TapDragDist\" \"150\"\n        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.\n###--------------------------------------------------------------------------###\n\n        Option \"EdgeBottomSize\" \"17\"  # Disable tap and movement detection in the bottom 17% of the pad\n        #Option \"EdgeTopSize\"   \"2\"\n        #Option \"EdgeLeftSize\"  \"4\"\n        #Option \"EdgeRightSize\" \"6\"\n        #Option \"EdgeScrollUpButton\"    \"4\"\n        #Option \"EdgeScrollDownButton\"  \"5\"\n\n        Option \"Hold1Move1StationaryMaxMove\" \"120\"\n        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.\nEndSection\n"
  },
  {
    "path": "examples/dell-precision-5520.conf",
    "content": "# Settings for Dell Precision 5520. Includes three-finger drag and reversed two finger scrolling.\nSection \"InputClass\"\n\tMatchIsTouchpad \"on\"\n\tIdentifier \"Touchpads\"\n\tDriver \"mtrack\"\n\tOption \"DeviceEnabled\" \"1\"\n\n\tOption \"AccelerationProfile\" \"1\"\n\tOption \"Sensitivity\" \"1.20\"\n\tOption \"ClickTime\" \"25\"\n\t# Tried these, didn't like them, YMMV.\n\t# Option \"AdaptiveDeceleration\" \"2.0\"\n\t# Option \"ConstantDeceleration\" \"1.2\"\n\t# Option \"VelocityScale\" \"3.0\"\n\n\tOption \"SwipeDistance\" \"1\"\n\tOption \"SwipeLeftButton\" \"1\"\n\tOption \"SwipeRightButton\" \"1\"\n\tOption \"SwipeUpButton\" \"1\"\n\tOption \"SwipeDownButton\" \"1\"\n\tOption \"SwipeClickTime\" \"0\"\n\tOption \"SwipeSensitivity\" \"1000\"\n\n\tOption \"ScrollDownButton\" \"4\"\n\tOption \"ScrollUpButton\" \"5\"\n\tOption \"ScrollDistance\" \"25\"\n\tOption \"ScrollCoastDuration\" \"500\"\n\n\tOption \"IgnorePalm\" \"true\"\n\tOption \"IgnoreThumb\" \"true\"\n\tOption \"ThumbSize\" \"25\"\n\tOption \"ThumbRatio\" \"70\"\n\tOption \"EdgeRightSize\" \"0\"\n\tOption \"TapDragEnable\" \"0\"\n\tOption \"Hold1Move1StationaryButton\" \"0\"\n\n\t# I hate edge scrolling.\n\tOption \"EdgeScrollDist\" \"2000000000\"\n\tOption \"FingerHigh\" \"5\"\n\tOption \"FingerLow\" \"1\"\nEndSection\n"
  },
  {
    "path": "examples/dell-xps13-9333.conf",
    "content": "#    MatchIsTouchpad \"on\"\n#Section \"InputClass\"\n#    Identifier      \"Touchpads\"\n#    Driver          \"mtrack\"\n#EndSection\n\nSection \"InputClass\"\n        MatchIsTouchpad \"on\"\n        Identifier \"Touchpads\"\n        Driver \"mtrack\"\n        Option \"DeviceEnabled\" \"1\"\n\n\n###--------------------------------------------------------------------------###\n# http://www.x.org/wiki/Development/Documentation/PointerAcceleration/\n###--------------------------------------------------------------------------###\n\n###--------------------------------------------------------------------------###\n### Options for acceleration profile \"0\" \n###--------------------------------------------------------------------------###\n#       some curved deceleration\n###         Option \"AccelerationProfile\" \"0\"\n###         Option \"AdaptiveDeceleration\" \"5.5\"\n#       linear deceleration (mouse speed reduction)\n###         Option \"ConstantDeceleration\" \"1.01\"\n###         Option \"Sensitivity\" \"0.49\"\n###         Option \"VelocityScale\" \"10.0\"\n###--------------------------------------------------------------------------###\n\n###--------------------------------------------------------------------------###\n###   Options for polynomial(\"2\") profile\n###--------------------------------------------------------------------------###\n        Option \"AccelerationProfile\" \"4\"\n        Option \"Sensitivity\" \"0.75\"\n        Option \"AdaptiveDeceleration\" \"2.0\"\n        Option \"ConstantDeceleration\" \"1.02\"\n        Option \"VelocityScale\" \"1.0\"\n#------------------------------------------------------------------------------#\n\n\n        Option \"PalmSize\" \"40\"\n        Option \"IgnorePalm\" \"true\"\n\n#        Option \"IgnoreThumb\" \"true\"\n#        Option \"ThumbRatio\" \"70\"\n#        Option \"ThumbSize\" \"35\"\n\n###---------------------- Gesture Settings ----------------------------------###\n### property: \"Trackpad Gesture Settings\"\n        Option \"GestureClickTime\" \"20\"\n        Option \"GestureWaitTime\" \"3\"\n###--------------------------------------------------------------------------###\n\n        Option \"ButtonTouchExpire\" \"120\"\n        \n        Option \"FingerLow\" \"0\"\n        Option \"FingerHigh\" \"0\"\n\n###---------------------- Tap Settings --------------------------------------###\n### property \"Trackpad Tap Settings\"\n        Option \"ClickTime\" \"35\"\n        Option \"MaxTapTime\" \"145\"\n        Option \"MaxTapMove\" \"85\"\n###--------------------------------------------------------------------------###\n\n        Option \"TapButton1\" \"1\"\n        Option \"TapButton2\" \"2\"\n        Option \"TapButton3\" \"3\"\n\n###------------------------ Swipe2 as mouse wheel ---------------------------###\n        Option \"ScrollDistance\" \"22\"\n        Option \"ScrollClickTime\" \"12\"\n        Option \"ScrollSensitivity\" \"0\"\n        Option \"ScrollUpButton\" \"4\"\n        Option \"ScrollDownButton\" \"5\"\n        Option \"ScrollLeftButton\" \"6\"\n        Option \"ScrollRightButton\" \"7\"\n###--------------------------------------------------------------------------###\n\n###------------------------ Swipe to drag button 8 --------------------------###\n        Option \"SwipeDistance\" \"1\"\n        Option \"SwipeClickTime\" \"0\"\n        Option \"SwipeSensitivity\" \"1100\"\n        Option \"SwipeUpButton\" \"8\"\n        Option \"SwipeDownButton\" \"8\"\n        Option \"SwipeLeftButton\" \"8\"\n        Option \"SwipeRightButton\" \"8\"\n###--------------------------------------------------------------------------###\n\n###------------------------ Scroll to drag button 8 -------------------------###\n        Option \"ScrollDistance\" \"1\"\n        Option \"ScrollClickTime\" \"0\"\n        Option \"ScrollSensitivity\" \"1100\"\n        Option \"ScrollUpButton\" \"8\"\n        Option \"ScrollDownButton\" \"8\"\n        Option \"ScrollLeftButton\" \"8\"\n        Option \"ScrollRightButton\" \"8\"\n###--------------------------------------------------------------------------###\n\n###------------------------ Swipe3 - act as mouse wheel ---------------------###\n        Option \"SwipeDistance\" \"22\"\n        Option \"SwipeClickTime\" \"12\"\n        Option \"ScrollSensitivity\" \"0\"\n### property: \"Trackpad Swipe Buttons\"\n        Option \"SwipeUpButton\" \"4\"\n        Option \"SwipeDownButton\" \"5\"\n        Option \"SwipeLeftButton\" \"6\"\n        Option \"SwipeRightButton\" \"7\"\n###--------------------------------------------------------------------------###\n\n###---------------------- Swipe4 to drag buttons 9 10 11 12 -----------------###\n        Option \"Swipe4Distance\" \"280\"\n        Option \"Swipe4ClickTime\" \"50\"\n        Option \"Swipe4Sensitivity\" \"0\"\n## property: \"Trackpad Swipe4 Buttons\"\n        Option \"Swipe4UpButton\" \"9\"\n        Option \"Swipe4DownButton\" \"10\"\n        Option \"Swipe4LeftButton\" \"11\"\n        Option \"Swipe4RightButton\" \"12\"\n###--------------------------------------------------------------------------###\n\n###---------------------------- Swipe4 to drag button 1 ---------------------###\n        Option \"Swipe4Distance\" \"1\"\n        Option \"Swipe4ClickTime\" \"0\"\n        Option \"Swipe4Sensitivity\" \"1350\"\n## property: \"Trackpad Swipe4 Buttons\"\n        Option \"Swipe4UpButton\" \"1\"\n        Option \"Swipe4DownButton\" \"1\"\n        Option \"Swipe4LeftButton\" \"1\"\n        Option \"Swipe4RightButton\" \"1\"\n###--------------------------------------------------------------------------###\n\n        Option \"ScaleDistance\" \"70\"\n        Option \"ScaleUpButton\" \"12\"\n        Option \"ScaleDownButton\" \"13\"\n\n        Option \"RotateDistance\" \"350\"\n        Option \"RotateLeftButton\" \"14\"\n        Option \"RotateRightButton\" \"15\"\n\n        Option \"ButtonEnable\" \"true\"\n        Option \"ButtonIntegrated\" \"true\"\n        Option \"ButtonZonesEnable\" \"true\"\n        Option \"ClickFinger1\" \"1\"\n        Option \"ClickFinger2\" \"2\"\n        Option \"ClickFinger3\" \"3\"\n\n\n###--------------------------------------------------------------------------###\n### xinput set-prop 11 \"Trackpad Drag Settings\" 1 350 60 200\n        Option \"TapDragEnable\" \"true\"\n        Option \"TapDragTime\" \"350\"\n        Option \"TapDragWait\" \"40\"\n        Option \"TapDragDist\" \"150\"\n###--------------------------------------------------------------------------###\n\n        Option \"EdgeBottomSize\"\t\"5\"  # dont count touches from bottom 5% of area\n        Option \"EdgeTopSize\"\t\t\"2\"\n        Option \"EdgeLeftSize\"\t\t\"4\"\n        Option \"EdgeRightSize\"\t\"6\"\n        Option \"EdgeScrollUpButton\"\t\t\"4\"\n        Option \"EdgeScrollDownButton\" \"5\"\n\n\n        Option \"Hold1Move1StationaryMaxMove\" \"90\"\n\nEndSection\n\n\n"
  },
  {
    "path": "examples/lenovo-710s.conf",
    "content": "Section \"InputClass\"\n  MatchIsTouchpad \"true\"\n  Identifier      \"Touchpads\"\n  Driver          \"mtrack\"\n  Option \"MaxTapTime\" \"400\"\n  Option \"MaxTapMove\" \"20\"\n  Option \"ScrollDistance\" \"55\"\n  Option \"ScrollSmooth\" \"1\"\n  Option \"ScrollCoastDuration\" \"360\"\n  Option \"ScrollCoastEnableSpeed\" \"0.3\"\n  Option \"AccelerationProfile\" \"3\"\n  Option \"VelocityScale\" \"1.3\"\n  Option \"Sensitivity\" \"0.8\"\n  Option \"ScrollUpButton\" \"5\"\n  Option \"ScrollDownButton\" \"4\"\n  Option \"SwipeDistance\" \"300\"\n  Option \"Hold1Move1StationaryMaxMove\" \"50\"\n  Option \"TapDragDist\" \"400\"\n  Option \"GestureWaitTime\" \"20\"\n  Option \"ButtonZonesEnable\" \"true\"\n  Option \"ClickFinger1\" \"1\"\n  Option \"ClickFinger2\" \"3\"\n  Option \"ClickFinger3\" \"0\"\n  Option \"SwipeClickTime\" \"60\"\n  Option \"SwipeDistance\" \"200\"\n  Option \"SwipeUpButton\" \"16\"\n  Option \"SwipeDownButton\" \"17\"\n  Option \"Swipe4UpButton\" \"18\"\n  Option \"Swipe4DownButton\" \"19\"\n  Option \"Swipe4LeftButton\" \"20\"\n  Option \"Swipe4RightButton\" \"21\"\n  Option \"Swipe4Distance\" \"300\"\n  Option \"EdgeLeftSize\" \"4\"\n  Option \"EdgeRightSize\" \"7\"\n  Option \"EdgeTopSize\" \"0\"\n  Option \"EdgeBottomSize\" \"15\"\nEndSection\n"
  },
  {
    "path": "examples/lenovo-yoga-2-pro.conf",
    "content": "## Works pretty well on Ubuntu 18.04 with deepin DE & Xfce\n\n########### 1. Install the mtrack driver ################\n#cd /tmp\n#git clone https://github.com/p2rkw/xf86-input-mtrack.git\n#cd xf86-input-mtrack\n#./configure --with-xorg-module-dir=/usr/lib/xorg/modules\n#make\n#sudo make install\n########### 2. Copy this file to /usr/share/X11/xorg.conf.d/99-mtrack.conf ################\n# precedence should be higher than synaptics and/or libinput (so not necessarily 99).\n\n\n########### 3. I recommend using this together with dispad, to disable the touchpad while typing #############\n## 1. install dependencies for dispad\n#sudo apt install libconfuse-dev libxi-dev\n#cd /tmp\n#git clone https://github.com/BlueDragonX/dispad.git #installation will fail if you have autocrlf=true in your gitconf\n#cd dispad\n#./configure\n#make\n#sudo make install\n\n## 2. launch dispad from terminal >dispad\n## 3. configure config file >gedit ~/.dispad like so:\n#\t# default dispad config file\n\n#\t# name of the property used to enable/disable the trackpad\n#\tproperty = \"Trackpad Disable Input\"\n\n#\t# the value used to enable the trackpad\n#\tenable = 0\n\n#\t# the value used to disable the trackpad\n#\tdisable = 3 \n\n#\t# whether or not modifier keys disable the trackpad\n#\tmodifiers = false\n\n#\t# how long (in ms) to sleep between keyboard polls\n#\tpoll = 44\n\n#\t# how long (in ms) to disable the trackpad after a keystroke\n#\tdelay = 500\n\n## 4. edit bashrc to launch dispad at terminal-startup\n# echo -e \"\\n#Turn off touchpad upon keypress\\ndispad\\n\" >> ~/.bashrc\n# alternatively, you can start the daemon via putting this line in ~/.profile\n# this profile option has the advantage of working straight from the beginning. However, it throws a warning (without consequences) after login.\n\n\nSection \"InputClass\"\n\tMatchIsTouchpad \"on\"\n\tIdentifier \"Touchpads\"\n\tDriver \"mtrack\"\n\tOption \"DeviceEnabled\" \"1\"\n\n\t# ----- Acceleration --- most important part! ---\n\tOption \"AccelerationProfile\" \"2\" # 2 is polynomial, recommended as \"very usable\"; see https://www.x.org/wiki/Development/Documentation/PointerAcceleration/#Introduction\n\tOption \"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)\n#\tOption \"ConstantDeceleration\" \"1.02\"\n\tOption \"VelocityScale\" \"4\"\n\n\t# ---- Basic setup\n#\tOption \"TrackPadDisable\" \"0\" # dont disable trackpad\n#\tOption \"ButtonEnable\" \"true\" # enable physical buttons\n#\tOption \"ButtonIntegrated\" \"true\" # Y2p has a 1-piece pad\n\n\t# ---- Responsiveness\n\tOption \"Sensitivity\" \"0.3\" # how fast does the mouse move\n\tOption \"FingerLow\" \"15\" # more than 10% pressure --> is a touch\n\tOption \"FingerHigh\" \"15\" # less than 10% pressure --> not a touch\n\tOption \"IgnoreThumb\" \"true\" # don't use thumb touches\n\tOption \"IgnorePalm\" \"true\" # don't use palm touches\n\tOption \"DisableOnThumb\" \"true\" # Disable complete pad once thumb detected\n\tOption \"DisableOnPalm\" \"true\" # ...same for palm\n\tOption \"PalmSize\" \"30\" # 30 is still a bit jumpy, but less results in bad detection of fingers\n#\tOption \"ThumbSize\" \"15\" # Thumb is 25% of trackpad-max\n#\tOption \"ThumbRatio\" \"40\" # thumb is 70% longer than wide\n\n\t# ---- Zones (I don't really understand zones)\n#\tOption \"ButtonZonesEnable\" \"true\" # Split pad in zones for clicks\n#\tOption \"FirstZoneButton\" \"1\"\n#\tOption \"SecondZoneButton\" \"0\"\n#\tOption \"ThirdZoneButton\" \"3\"\n#\tOption \"LimitButtonZonesToBottomEdge\" \"false\"\n\n\t# ---- Physical Clicks\n#\tOption \"ClickFinger0\" \"0\"\n#\tOption \"ClickFinger1\" \"1\"\n#\tOption \"ClickFinger2\" \"0\"\n#\tOption \"ClickFinger3\" \"0\"\n\n\t# ---- Tap-to-click\n#\tOption \"TapButton 1\" \"1\" #left click on 1-finger tap\n#\tOption \"TapButton 2\" \"3\" #right click on 2-finger tap\n#\tOption \"TapButton 3\" \"0\" #no click on 3-finger tap\n#\tOption \"TapButton 4\" \"0\" #no click on 4-finger tap\n\n\t# ---- 2-finger Scrolling\n#\tOption \"ScrollDistance\" \"150\" # how far to move fingers before scrolling starts (unit?)\n#\tOption \"ScrollClickTime\" \"0\" # how long (in ms) to press \"scroll\" - 0=only as long as fingers move\n#\tOption \"ScrollSensitivity\" \"0\" # how fast to move pointer during scrolling (0=not at all)\n#\tOption \"ScrollSmooth\" \"1\" # High precision smooth scrolling\n#\tOption \"ScrollCoastDuration\" \"100\" # how long to move screen after finger movement (in ms)\n#\tOption \"ScrollCoastEnableSpeed\" \".3\" # \n\n\t# ---- 3/4-finger Swiping\n\t# I never use that, so I set a threshold so high, it won't be used\n#\tOption \"SwipeDistance\" \"9999\"\n#\tOption \"SwipeUpButton\" \"0\"\n#\tOption \"SwipeDownButton\" \"0\"\n#\tOption \"SwipeLeftButton\" \"0\"\n#\tOption \"SwipeRightButton\" \"0\"\n#\tOption \"Swipe4Distance\" \"9999\"\n#\tOption \"Swipe4UpButton\" \"0\"\n#\tOption \"Swipe4DownButton\" \"0\"\n#\tOption \"Swipe4LeftButton\" \"0\"\n#\tOption \"Swipe4RightButton\" \"0\"\n\n\t# ---- Edge scrolling\n\t# I never use that, so I set a threshold so high, it won't be used\n#\tOption \"EdgeScrollDist\" \"9999\"\n#\tOption \"EdgeScrollUpButton\" \"0\"\n#\tOption \"EdgeScrollDownButton\" \"0\"\n#\tOption \"EdgeScrollLeftButton\" \"0\"\n#\tOption \"EdgeScrollRightButton\" \"0\"\n\n\t# ---- Pinch zoom\n#\tOption \"ScaleDistance\" \"100\" # how far to move fingers? (unit?)\n#\tOption \"ScaleUpButton\" \"12\"\n#\tOption \"ScaleDownButton\" \"13\"\n\n\t# ---- 2 finger rotation\n\t# I never use that, so I set a threshold so high, it won't be used\n#\tOption \"RotateDistance\" \"9999\" # how far to move fingers? (unit?)\n#\tOption \"RotateLeftButton\" \"0\"\n#\tOption \"RotateRightButton\" \"0\"\n\n\t# ---- highlight text with holding one and moving another finger\n#\tOption \"Hold1Move1StationaryButton\" \"1\" # left-mouse click = 1\n#\tOption \"Hold1Move1StationaryMaxMove\" \"90\" # threshold for how far 2nd finger can be moved (unit?)\n\n\t# ---- tap to drag\n#\tOption \"TapDragEnable\" \"true\"\n#\tOption \"TapDragWait\" \"60\"\n#\tOption \"TapDragTime\" \"300\" # how long after tap does the driver wait for drag (in ms)?\n#\tOption \"TapDragDist\" \"200\" # threshold, after this dist it's seen as mousemove not as drag (unit?)\n\n\t# ---- Invert axes for mousemovement (not scrolling!)\n#\tOption \"AxisXInvert\" \"false\"\n#\tOption \"AxisYInvert\" \"false\"\n\n\n\t# ---- Natural Scrolling (uncommenting will disable it, as default buttons are reversed)\n\tOption \"ScrollDownButton\"       \"4\"\n\tOption \"ScrollUpButton\"         \"5\"\n\tOption \"ScrollLeftButton\"       \"7\"\n\tOption \"ScrollRightButton\"      \"6\"\nEndSection\n\n"
  },
  {
    "path": "examples/macbook-pro-retina-2013.conf",
    "content": "Section \"InputClass\"\n        MatchIsTouchpad \"on\"\n        Identifier      \"Touchpads\"\n        MatchDevicePath \"/dev/input/event*\"\n        Driver          \"mtrack\"\n        # The faster you move, the more distance pointer will travel, using \"polynomial\" profile\n        Option          \"AccelerationProfile\" \"2\"\n        # Tweak cursor movement speed with this\n        Option          \"Sensitivity\" \"0.05\"\n        # Pressure at which a finger is detected as a touch\n        Option          \"FingerHigh\" \"1\"\n        # Pressure at which a finger is detected as a release\n        Option          \"FingerLow\" \"1\"\n        # I often use thumb to press down the physical button, so let's not ignore it\n        Option          \"IgnoreThumb\" \"false\"\n        Option          \"ThumbRatio\" \"70\"\n        Option          \"ThumbSize\" \"23\"\n        # Ignore palm, with palm takes up to 30% of your touch pad\n        Option          \"IgnorePalm\" \"true\"\n        Option          \"PalmSize\" \"30\"\n        # Trigger mouse button when tap: 1 finger - left click, 2 finger - right click, 3 - middle click\n        Option          \"TapButton1\" \"1\"\n        Option          \"TapButton2\" \"3\"\n        Option          \"TapButton3\" \"2\"\n        Option          \"TapButton4\" \"0\"\n        Option          \"ClickTime\" \"1\"\n        # Disable tap-to-drag, we're using three finger drag instead\n        Option          \"TapDragEnable\" \"false\"\n        # While touching the touch pad with # fingers, press the touchpad physical click button\n        Option          \"ClickFinger1\" \"1\"\n        Option          \"ClickFinger2\" \"3\"\n        Option          \"ClickFinger3\" \"2\"\n        Option          \"ButtonMoveEmulate\" \"false\"\n        Option          \"ButtonIntegrated\" \"true\"\n        # The momentum after scroll fingers released\n        Option          \"ScrollCoastDuration\" \"300\"\n        Option          \"ScrollCoastEnableSpeed\" \".1\"\n        # Natural scrolling with two fingers\n        Option          \"ScrollSmooth\" \"true\"\n        Option          \"ScrollUpButton\" \"5\"\n        Option          \"ScrollDownButton\" \"4\"\n        Option          \"ScrollLeftButton\" \"7\"\n        Option          \"ScrollRightButton\" \"6\"\n        # Tweak scroll sensitivity with ScrollDistance, don't touch ScrollSensitivity\n        Option          \"ScrollDistance\" \"270\"\n        Option          \"ScrollClickTime\" \"1\"\n        # Three finger drag\n        Option          \"SwipeDistance\" \"1\"\n        Option          \"SwipeLeftButton\" \"1\"\n        Option          \"SwipeRightButton\" \"1\"\n        Option          \"SwipeUpButton\" \"1\"\n        Option          \"SwipeDownButton\" \"1\"\n        Option          \"SwipeClickTime\" \"1\"\n        Option          \"SwipeSensitivity\" \"1500\"\n        # Four finger swipe, 8 & 9 are for browsers navigating back and forth respectively\n        Option          \"Swipe4LeftButton\" \"9\"\n        Option          \"Swipe4RightButton\" \"8\"\n        # Mouse button >= 10 are not used by Xorg, so we'll map them with xbindkeys and xdotool later\n        Option          \"Swipe4UpButton\" \"11\"\n        Option          \"Swipe4DownButton\" \"10\"\n        # Mouse buttons triggered by 2-finger pinching gesture\n        Option          \"ScaleDistance\" \"300\"\n        Option          \"ScaleUpButton\" \"12\"\n        Option          \"ScaleDownButton\" \"13\"\n        # Mouse buttons trigger by 2-finger rotating gesture, disabled to enhance the pinch gesture\n        Option          \"RotateLeftButton\" \"0\"\n        Option          \"RotateRightButton\" \"0\"\n        Option          \"TabDragEnable\" \"false\"\n        Option          \"GestureClickTime\" \"1\"\n        Option          \"GestureWaitTime\" \"0\"\nEndSection\n"
  },
  {
    "path": "examples/macbook.conf",
    "content": "# Taken from: https://howchoo.com/g/mdy0ngziogm/the-perfect-almost-touchpad-settings-on-linux-2\n\nSection \"InputClass\"\n        MatchIsTouchpad \"on\"\n        Identifier      \"Touchpads\"\n        Driver          \"mtrack\"\n        Option          \"Sensitivity\" \"0.60\"\n        Option          \"FingerHigh\" \"5\"\n        Option          \"FingerLow\" \"1\"\n        Option          \"IgnoreThumb\" \"true\"\n        Option          \"ThumbRatio\" \"70\"\n        Option          \"ThumbSize\" \"25\"\n        Option          \"IgnorePalm\" \"true\"\n        Option          \"TapButton1\" \"0\"\n        Option          \"TapButton2\" \"0\"\n        Option          \"TapButton3\" \"0\"\n        Option          \"TapButton4\" \"0\"\n        Option          \"ClickFinger1\" \"3\"\n        Option          \"ClickFinger2\" \"3\"\n        Option          \"ClickFinger3\" \"3\"\n        Option          \"ButtonMoveEmulate\" \"false\"\n        Option          \"ButtonIntegrated\" \"true\"\n        Option          \"ClickTime\" \"25\"\n        Option          \"BottomEdge\" \"30\"\n        Option          \"SwipeLeftButton\" \"8\"\n        Option          \"SwipeRightButton\" \"9\"\n        Option          \"SwipeUpButton\" \"0\"\n        Option          \"SwipeDownButton\" \"0\"\n        Option          \"SwipeDistance\" \"700\"\n        Option          \"ScrollCoastDuration\" \"500\"\n        Option          \"ScrollCoastEnableSpeed\" \".3\"\n        Option          \"ScrollUpButton\" \"5\"\n        Option          \"ScrollDownButton\" \"4\"\n        Option          \"ScrollLeftButton\" \"7\"\n        Option          \"ScrollRightButton\" \"6\"\n        Option          \"ScrollDistance\" \"350\"\n        Option          \"Hold1Move1StationaryButton\" \"0\"\nEndSection\n"
  },
  {
    "path": "include/button.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef BUTTON_H\n#define BUTTON_H\n\n#define DIM_BUTTON 15\n\n#define MT_BUTTON_LEFT 0\n#define MT_BUTTON_MIDDLE 1\n#define MT_BUTTON_RIGHT 2\n#define MT_BUTTON_WHEEL_UP 3\n#define MT_BUTTON_WHEEL_DOWN 4\n#define MT_BUTTON_HWHEEL_LEFT 5\n#define MT_BUTTON_HWHEEL_RIGHT 6\n#define MT_BUTTON_SWIPE_UP 7\n#define MT_BUTTON_SWIPE_DOWN 8\n#define MT_BUTTON_SWIPE_LEFT 9\n#define MT_BUTTON_SWIPE_RIGHT 10\n#define MT_BUTTON_SCALE_DOWN 11\n#define MT_BUTTON_SCALE_UP 12\n#define MT_BUTTON_ROTATE_LEFT 13\n#define MT_BUTTON_ROTATE_RIGHT 14\n\n#endif\n"
  },
  {
    "path": "include/capabilities.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef CAPABILITIES_H\n#define CAPABILITIES_H\n\n#include \"common.h\"\n#include \"button.h\"\n\nstruct Capabilities {\n\tstruct input_id devid;\n\tchar devname[128];\n\tint has_left, has_middle, has_right;\n\tint has_mtdata, has_ibt;\n\tint has_slot;\n\tint has_abs[MT_ABS_SIZE];\n\tstruct input_absinfo slot;\n\tstruct input_absinfo abs[MT_ABS_SIZE];\n};\n\nint read_capabilities(struct Capabilities *cap, int fd);\nint get_cap_xsize(const struct Capabilities *cap);\nint get_cap_ysize(const struct Capabilities *cap);\nint get_cap_wsize(const struct Capabilities *cap);\n\nint get_cap_xmid(const struct Capabilities *cap);\nint get_cap_ymid(const struct Capabilities *cap);\n\nint translate_cap_x(const struct Capabilities *cap, int x);\nint translate_cap_y(const struct Capabilities *cap, int y);\n\nint get_cap_xmin(const struct Capabilities *cap);\nint get_cap_ymin(const struct Capabilities *cap);\n\nvoid output_capabilities(const struct Capabilities *cap);\n\n#endif\n"
  },
  {
    "path": "include/common.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef COMMON_H\n#define COMMON_H\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include <xorg-server.h>\n#include <xf86.h>\n#include <xf86_OSproc.h>\n#include <xf86Xinput.h>\n#include <errno.h>\n#include <mtdev-mapping.h>\n#include <stdint.h>\n#include <sys/time.h>\n\n#define DIM_FINGER 16\n#define DIM_TOUCHES 16\n\n/* year-proof millisecond event time */\ntypedef __u64 mstime_t;\n\n/* all bit masks have this type */\ntypedef unsigned int bitmask_t;\n\n/**\n * m - bit set (integer)\n * x - modified bit\n * b - new value\n * @{\n */\n#define BITMASK(x) (1U << (x))\n#define BITONES(x) (BITMASK(x) - 1U)\n#define GETBIT(m, x) (((m) >> (x)) & 1U)\n#define SETBIT(m, x) (m |= BITMASK(x))\n#define CLEARBIT(m, x) (m &= ~BITMASK(x))\n#define MODBIT(m, x, b) ((b) ? SETBIT(m, x) : CLEARBIT(m, x))\n/** }@ */\n\n#define ABSVAL(x) ((x) < 0 ? -1*(x) : (x))\n#define MINVAL(x, y) ((x) < (y) ? (x) : (y))\n#define MAXVAL(x, y) ((x) > (y) ? (x) : (y))\n#define MODVAL(x, y) ((x) - ((int)((x) / (y))) * (y))\n#define SQRVAL(x) ((x) * (x))\n#define CLAMPVAL(x, min, max) MAXVAL(MINVAL(x, max), min)\n#define SGNVAL (x) ((x) < 0 ? -1 : (((x) > 0) ? 1 : 0))\n\n#define LOG_ERROR(...) \\\n\tdo{ \\\n\t\txf86Msg(X_ERROR, \"mtrack[%i] %s:%i: \", get_next_log_number(), __FILE__, __LINE__); \\\n\t\txf86Msg(X_ERROR, __VA_ARGS__); \\\n\t}while(0)\n\n#define LOG_WARNING(...) \\\n\tdo{ \\\n\t\txf86Msg(X_WARNING, \"mtrack[%i] %s:%i: \", get_next_log_number(), __FILE__, __LINE__); \\\n\t\txf86Msg(X_WARNING, __VA_ARGS__); \\\n\t}while(0)\n\n#define LOG_INFO(...) \\\n\tdo{ \\\n\t\txf86Msg(X_INFO, \"mtrack[%i] %s:%i: \", get_next_log_number(), __FILE__, __LINE__); \\\n\t\txf86Msg(X_INFO, __VA_ARGS__); \\\n\t}while(0)\n#define LOG_INFO_CONT(...) xf86Msg(X_INFO, \"mtrack[...]: \" __VA_ARGS__)\n\n#define LOG_DISABLED(...) do { } while(0)\n\n#define LOG_INFO_ENABLED(...) LOG_INFO(__VA_ARGS__)\n#define LOG_INFO_DISABLED(...)\n\n#define LOG_INFO2(ENABLED_or_DISABLED, ...) LOG_INFO_##ENABLED_or_DISABLED(__VA_ARGS__)\n\n#if defined(DEBUG_DRIVER) && (DEBUG_DRIVER != 0)\n# define LOG_DEBUG LOG_INFO\n#else\n# define LOG_DEBUG LOG_DISABLED\n#endif\n\nstatic inline int get_next_log_number(){\n\tstatic int last = 0;\n\treturn ++last;\n}\n\n/* Retrieve the current time and place it in tv.\n */\nstatic inline void microtime(struct timeval* tv)\n{\n\tgettimeofday(tv, NULL);\n}\n\n/* Copy one time value to another.\n */\nstatic inline void timercp(struct timeval* dest, const struct timeval* src)\n{\n\tmemcpy(dest, src, sizeof(struct timeval));\n}\n\n/* Convert a timeval to milliseconds since the epoch. Truncates additional\n * timer resolution effectively rounding down.\n */\nstatic inline mstime_t timertoms(const struct timeval* tv)\n{\n\treturn (mstime_t)(tv->tv_sec*1000) + (mstime_t)(tv->tv_usec/1000);\n}\n\n/* Convert a value in milliseconds to a timeval and place the value in tv.\n */\nstatic inline void timerfromms(struct timeval* tv, const mstime_t ms)\n{\n\ttv->tv_sec = (time_t)(ms/1000);\n\ttv->tv_usec = (suseconds_t)((ms%1000)*1000);\n}\n\n/* Convert a timeval to microseconds.\n */\nstatic inline suseconds_t timertomicro(const struct timeval* tv)\n{\n\treturn tv->tv_sec * 1000000 + tv->tv_usec;\n}\n\n/* Add milliseconds to a timeval and place the resulting value in dest.\n */\nstatic inline void timeraddms(const struct timeval* a, const mstime_t b, struct timeval* dest)\n{\n\tstruct timeval tv;\n\ttimerfromms(&tv, b);\n\ttimeradd(a, &tv, dest);\n}\n\n/* Check if given timeval a is set to epoch time.\n */\nstatic inline int isepochtime(const struct timeval* a)\n{\n\tstruct timeval epoch;\n\ttimerclear(&epoch);\n\n\treturn timercmp(a, &epoch, ==);\n}\n\n/* Clamp value to 15 bits.\n */\nstatic inline int clamp15(int x)\n{\n\treturn x < -32767 ? -32767 : x > 32767 ? 32767 : x;\n}\n\n/* Absolute scale is assumed to fit in 15 bits.\n */\nstatic inline int dist2(int dx, int dy)\n{\n\tdx = clamp15(dx);\n\tdy = clamp15(dy);\n\treturn dx * dx + dy * dy;\n}\n\n\n\n/* Count number of bits (Sean Eron Andersson's Bit Hacks).\n */\nstatic inline int bitcount(unsigned v)\n{\n\tv -= ((v>>1) & 0x55555555);\n\tv = (v&0x33333333) + ((v>>2) & 0x33333333);\n\treturn (((v + (v>>4)) & 0xF0F0F0F) * 0x1010101) >> 24;\n}\n\n/* Return index of first bit [0-31], -1 on zero\\\n */\n#define firstbit(v) (__builtin_ffs(v) - 1)\n\n/* Boost-style foreach bit.\n */\n#define foreach_bit(i, m)\t\t\t\t\t\t\\\n\tfor (i = firstbit(m); i >= 0; i = firstbit((m) & (~0U << (i + 1))))\n\n/* Robust system ioctl calls.\n */\n#define SYSCALL(call) while (((call) == -1) && (errno == EINTR))\n\n/**\n * \\defgroup Gestures.c Functions from gestures.c\n *\n *   These functions are implemented in gestures.c\n *\n * @{\n */\n\nstruct Gestures;\n\n/**\n *  Compute x*x+y*y and compare it with value*value.\n * It's equal to following comparision: sqrt(x*x+y*y) [?] value.\n * Implementation in gestures.c\n *\n * @param x\n * @param y\n * @param value\n * @return -1 when lhs is less than rhs, 0 when equal, 1 when greater\n */\nint hypot_cmpf(double x, double y, double value);\n\n/**\n *  It's called 'uncond' because caller have to check that all conditions required to\n * trigger delayed button were met.\n *\n * @return triggered button\n */\nint trigger_delayed_button_uncond(struct Gestures* gs);\n\n/** }@ */\n\n#endif\n"
  },
  {
    "path": "include/gestures.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n * Copyright (C) 2015-2018 Paweł Turkowski <p2rkw0@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef GESTURES_H\n#define GESTURES_H\n\n#include \"common.h\"\n#include \"mconfig.h\"\n#include \"hwstate.h\"\n#include \"mtstate.h\"\n\nstruct MTouch;\n\n#define GS_NONE 0\n#define GS_MOVE 1\n#define GS_SCROLL 2\n#define GS_SWIPE2 GS_SCROLL\n#define GS_SWIPE3 3\n#define GS_SWIPE4 4\n#define GS_SCALE 5\n#define GS_ROTATE 6\n#define GS_DRAG_READY 7\n#define GS_DRAG_WAIT 8\n#define GS_DRAG_ACTIVE 9\n#define GS_HOLD1_MOVE1 10\n#define GS_HOLD1_MOVE2 11\n#define GS_HOLD1_MOVE3 12\n\nstruct Gestures {\n\t/* Taps, physical buttons, and gestures will trigger\n\t * button events. If a bit is set, the button is down.\n\t * If a bit is not set, the button is up.\n\t * Bit 0 is button 1.\n\t */\n\tbitmask_t buttons;\n\n\t/* Pointer movement is tracked here.\n\t */\n\tdouble move_dx, move_dy;\n\n\t/* Current time and time delta. Updated after each event and after sleeping.\n\t */\n\tstruct timeval time;\n\tstruct timeval dt;\n\n\t/* Internal state tracking. Not for direct access.\n\t */\n\n\t/** Lastly emulated button, by pressing integrated button. */\n\tint button_emulate;\n\tint integrated_emulated_button;\n\t/* Invalid button_delayed means that there's no delayed button.\n\t */\n\tint button_delayed;\n\t/* If equals to epoch time then button is delayed till gesture end.\n\t */\n\tstruct timeval button_delayed_time;\n\n\tint tap_touching;\n\tint tap_released;\n\tstruct timeval tap_timeout;\n\n\tint move_type;\n\tdouble move_dist;\n\tint move_dir;\n\n\tint drag_state;\n\tint move_drag_dx;\n\tint move_drag_dy;\n\n\tstruct timeval move_wait;\n\tstruct timeval move_drag_wait;\n\tstruct timeval move_drag_expire;\n\tstruct timeval move_start;\n\n\t/* Scroll vertical, horizontal.\n\t */\n\tdouble scroll_speed_x, scroll_speed_y;\n\tint scroll_speed_valid;\n\n\t/* Duration left */\n\tint coasting_duration_left;\n};\n\n\nvoid gestures_init(struct MTouch* mt);\nvoid gestures_extract(struct MTouch* mt);\nint gestures_delayed(struct MTouch* mt);\n\nstatic int buttons_zone_update(\n\tstruct Gestures* gs,\n\tconst struct MConfig* cfg,\n\tconst struct MTState* ms,\n\tint lowest);\nstatic void touch_detect_update(\n\tstruct Gestures* gs,\n\tconst struct MConfig* cfg,\n\tconst struct MTState* ms,\n\tint latest);\n\n\n#endif\n\n"
  },
  {
    "path": "include/hwstate.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef HWSTATE_H\n#define HWSTATE_H\n\n#include \"common.h\"\n#include \"capabilities.h\"\n\nstruct FingerState {\n\t/* The size of the contact area */\n\tint touch_major, touch_minor;\n\t/* The size of the approaching tool */\n\tint width_major, width_minor;\n\tint orientation, pressure;\n\tint position_x, position_y;\n\tint tracking_id;\n};\n\nstruct HWState {\n\tstruct FingerState data[DIM_FINGER];\n\tbitmask_t used;\n\tbitmask_t slot;\n\tbitmask_t button;\n\tint max_x, max_y;\n\tstruct timeval evtime;\n};\n\nvoid hwstate_init(struct HWState *s,\n\t\t\tconst struct Capabilities *caps);\nint hwstate_modify(struct HWState *s,\n\t\t\tstruct mtdev *dev, int fd,\n\t\t\tconst struct Capabilities *caps);\nvoid hwstate_output(const struct HWState *s);\n\nint find_finger(const struct HWState *s, int tracking_id);\n\n#endif\n"
  },
  {
    "path": "include/mconfig.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef MCONFIG_H\n#define MCONFIG_H\n\n#include \"capabilities.h\"\n\n#define DEFAULT_TRACKPAD_DISABLE 0\n#define DEFAULT_TOUCH_DOWN 5\n#define DEFAULT_TOUCH_UP 5\n#define DEFAULT_IGNORE_THUMB 0\n#define DEFAULT_IGNORE_PALM 0\n#define DEFAULT_DISABLE_ON_THUMB 0\n#define DEFAULT_DISABLE_ON_PALM 0\n#define DEFAULT_THUMB_RATIO 70\n#define DEFAULT_THUMB_SIZE 25\n#define DEFAULT_PALM_SIZE 40\n#define DEFAULT_EDGE_LEFT_SIZE 0\n#define DEFAULT_EDGE_RIGHT_SIZE 0\n#define DEFAULT_EDGE_TOP_SIZE 0\n#define DEFAULT_EDGE_BOTTOM_SIZE 10\n#define DEFAULT_BUTTON_ENABLE 1\n#define DEFAULT_BUTTON_INTEGRATED 1\n#define DEFAULT_BUTTON_ZONES 0 /* Button zones are disabled */\n#define DEFAULT_IS_BUTTON_ZONES_HEIGHT_LIMITED 0 /* Button zones are emulated on the whole pad (Not limited to the bottom edge)*/\n#define DEFAULT_BUTTON_FIRST_ZONE 1\n#define DEFAULT_BUTTON_SECOND_ZONE 2\n#define DEFAULT_BUTTON_THIRD_ZONE 0\n#define DEFAULT_BUTTON_0TOUCH 0 /* \"invalid\" finger placement do not emulate any button */\n#define DEFAULT_BUTTON_1TOUCH 1\n#define DEFAULT_BUTTON_2TOUCH 2\n#define DEFAULT_BUTTON_3TOUCH 3\n#define DEFAULT_BUTTON_MOVE 1\n#define DEFAULT_BUTTON_EXPIRE 100\n#define DEFAULT_TAP_1TOUCH 1\n#define DEFAULT_TAP_2TOUCH 3\n#define DEFAULT_TAP_3TOUCH 2\n#define DEFAULT_TAP_4TOUCH 0\n#define DEFAULT_TAP_TIMEOUT 120\n#define DEFAULT_TAP_HOLD 50\n#define DEFAULT_TAP_DIST 400\n#define DEFAULT_GESTURE_HOLD 10\n#define DEFAULT_GESTURE_WAIT 100\n#define DEFAULT_SCROLL_DIST 150\n#define DEFAULT_SCROLL_UP_BTN 4\n#define DEFAULT_SCROLL_DN_BTN 5\n#define DEFAULT_SCROLL_LT_BTN 6\n#define DEFAULT_SCROLL_RT_BTN 7\n#define DEFAULT_SCROLL_HOLD 20\n#define DEFAULT_SCROLL_COAST_MIN_SPEED 0.1f\n#define DEFAULT_SCROLL_COAST_DURATION 200\n#define DEFAULT_SCROLL_COAST_TICK_MS 30 /* Schould be configurable? */\n#define DEFAULT_SCROLL_COAST_NO_BOOST 0\n#define DEFAULT_SCROLL_COAST_EASE 0\n#define DEFAULT_SWIPE_DIST 700\n#define DEFAULT_SWIPE_UP_BTN 8\n#define DEFAULT_SWIPE_DN_BTN 9\n#define DEFAULT_SWIPE_LT_BTN 10\n#define DEFAULT_SWIPE_RT_BTN 11\n#define DEFAULT_SWIPE_HOLD 300\n#define DEFAULT_SWIPE4_DIST 700\n#define DEFAULT_SWIPE4_UP_BTN 0\n#define DEFAULT_SWIPE4_DN_BTN 0\n#define DEFAULT_SWIPE4_LT_BTN 0\n#define DEFAULT_SWIPE4_RT_BTN 0\n#define DEFAULT_SWIPE_SENS 0\n#define DEFAULT_EDGE_SCROLL_DIST (DEFAULT_SCROLL_DIST*0.7)\n#define DEFAULT_SCALE_DIST 150\n#define DEFAULT_SCALE_UP_BTN 12\n#define DEFAULT_SCALE_DN_BTN 13\n#define DEFAULT_ROTATE_DIST 150\n#define DEFAULT_ROTATE_LT_BTN 14\n#define DEFAULT_ROTATE_RT_BTN 15\n#define DEFAULT_HOLD1_MOVE1_STATIONARY_BTN 1\n#define DEFAULT_HOLD1_MOVE1_STATIONARY_MAX_MOVE 20\n#define DEFAULT_HOLD1_MOVE1_DIST 1\n#define DEFAULT_HOLD1_MOVE1_HOLD 0\n#define DEFAULT_HOLD1_MOVE1_SENS 1000\n#define DEFAULT_HOLD1_MOVE1_BTN 1\n#define DEFAULT_HOLD1_MOVE2_STATIONARY_BTN 3\n#define DEFAULT_HOLD1_MOVE2_STATIONARY_MAX_MOVE 30\n#define DEFAULT_HOLD1_MOVE2_DIST 1\n#define DEFAULT_HOLD1_MOVE2_HOLD 0\n#define DEFAULT_HOLD1_MOVE2_SENS 1000\n#define DEFAULT_HOLD1_MOVE2_BTN 3\n#define DEFAULT_HOLD1_MOVE3_STATIONARY_BTN 10\n#define DEFAULT_HOLD1_MOVE3_STATIONARY_MAX_MOVE 60\n#define DEFAULT_HOLD1_MOVE3_DIST 100\n#define DEFAULT_HOLD1_MOVE3_HOLD 50\n#define DEFAULT_HOLD1_MOVE3_SENS 0\n#define DEFAULT_HOLD1_MOVE3_BTN 11\n#define DEFAULT_DRAG_ENABLE 1\n#define DEFAULT_DRAG_TIMEOUT 350\n#define DEFAULT_DRAG_WAIT 40\n#define DEFAULT_DRAG_DIST 200\n#define DEFAULT_DRAG_LOCK_TIMEOUT 500\n#define DEFAULT_AXIS_X_INVERT 0\n#define DEFAULT_AXIS_Y_INVERT 0\n#define DEFAULT_SENSITIVITY 1.0\n#define DEFAULT_SCROLL_HIGH_PREC 1\n\n#define MCFG_NONE 0\n#define MCFG_SCALE 1\n#define MCFG_SIZE 2\n#define MCFG_PRESSURE_SIZE 3\n#define MCFG_SIZE_PRESSURE 4 /* same capabilities as above, but with higher resolution of touches*/\n#define MCFG_PRESSURE 5\n\nstruct MConfig {\n\t/* Used by MTState */\n\n\t// Set by caps.\n\tint touch_type;\t\t// How to determine touch? 0 for none, 1 for scale, 2 for size, 3 for pressure\n\tint touch_minor;\t// Does the touchpad report touches as ellipses? 0 or 1\n\tint touch_min;\t\t// Minimum touch value.\n\tint touch_max;\t\t// Maximum touch value.\n\tint pressure_min;\t\t// Minimum pressure value.\n\tint pressure_max;\t\t// Maximum pressure value.\n\tint pad_width;\t\t// Width of the touchpad.\n\tint pad_height;\t\t// Height of the touchpad.\n\n\t// Set by config.\n\tint touch_down;\t\t// When is a finger touching? 0 - 100 (percentage)\n\tint touch_up;\t\t// When is a finger released? 0 - 100 (percentage)\n\tint ignore_thumb;\t// Whether or not to ignore thumbs. 0 or 1\n\tint ignore_palm;\t// Whether or not to ignore palms. 0 or 1\n\tint disable_on_thumb;\t// Disable the touchpad if thumb detected. 0 or 1\n\tint disable_on_palm;\t// Disable the touchpad if palm detected. 0 or 1\n\tint thumb_ratio;\t// Ratio of width to length that makes a touch a thumb. 0 - 100\n\tint thumb_size;\t\t// Minimum touch size for a thumb. 0 - 100\n\tint palm_size;\t\t// Minimum touch size for a palm. 0 - 100\n\tint edge_left_size;\t// Percent of left edge of trackpad to ignore for new touches. 0 - 100\n\tint edge_right_size;\t// Percent of right edge of trackpad to ignore for new touches. 0 - 100\n\tint edge_top_size;\t// Percent of top edge of trackpad to ignore for new touches. 0 - 100\n\tint edge_bottom_size;\t// Percent of bottom edge of trackpad to ignore for new touches. 0 - 100\n\tint axis_x_invert;\t// Whether or not to invert the x axis. 0 or 1.\n\tint axis_y_invert;\t// Whether or not to invert the y axis. 0 or 1.\n\n\t/* Used by Gestures */\n\n\t// Set by config.\n\tint trackpad_disable;\t// Disable the trackpad? 0 or 1\n\tint button_enable;\t\t// Enable physical buttons? 0 or 1\n\tint button_integrated;\t// Is the button under the touchpad? 0 or 1\n\tint button_expire;\t\t// How long to consider a touch for button emulation. >= 0\n\tint button_zones;\t\t// Use button zones for emulation?\n\tint is_button_zones_height_limited;\t// Limit the button zones to the height of the bottom edge?\n\tint button_first_zone;\t// Button to execute when clicking in the leftmost part of the zone (button_#_zones > 0)\n\tint button_second_zone;\t// Button to execute when clicking right of zone_button1 part of the zone (button_#_zones > 0)\n\tint button_third_zone;\t// Button to execute when clicking the rightmost part of the zone (button_#_zones > 0)\n\tint button_0touch;\t\t// What button to emulate when no finger is on the\n\t\t\t\t\t\t\t// pad and a click is registered? 0 to 32\n\tint button_1touch;\t\t// What button to emulate when one finger is on the\n\t\t\t\t\t\t\t// pad or the first zone is clicked? 0 to 32\n\tint button_2touch;\t\t// What button to emulate when two fingers are on the\n\t\t\t\t\t\t\t// pad or the second zone is clicked? 0 to 32\n\tint button_3touch;\t\t// What button to emulate when three fingers are on the\n\t\t\t\t\t\t\t// pad or the third zone is clicked? 0 to 32\n\tint button_move;\t\t// Whether or not to count the moving touch towards button\n\t\t\t\t\t\t\t// emulation.\n\tint tap_1touch;\t\t\t// What button to emulate for one touch taps? 0 to 32\n\tint tap_2touch;\t\t\t// What button to emulate for two touch taps? 0 to 32\n\tint tap_3touch;\t\t\t// What button to emulate for three touch taps? 0 to 32\n\tint tap_4touch;\t\t\t// What button to emulate for four touch taps? 0 to 32\n\tint tap_timeout;\t\t// Window for touches when counting for the button.\n\t\t\t\t\t\t\t// How long to wait for incoming touches after first one. > 0\n\tint tap_hold;\t\t\t// How long to \"hold down\" the emulated button on tap. > 0\n\tint tap_dist;\t\t\t// How far to allow a touch to move before it's a moving touch. > 0\n\tint gesture_hold;\t\t// How long to \"hold down\" the emulated button for gestures. > 0\n\tint gesture_wait;\t\t// How long after a gesture to wait before movement is allowed. >= 0\n\tstruct MConfigSwipe{\n\t\tint dist;\t\t// Distance needed to trigger a button. >= 0, 0 disables\n\t\tint hold;\t\t// How long to \"hold down\" the emulated button for swipe gesture. > 0\n\t\tint drag_sens;\t\t// Should this gesture emit movement events? 0 disables movement, 1000 set speed same as during normal movement\n\t\tint up_btn;\t\t// Button to use for swipe up. >= 0, 0 is none\n\t\tint dn_btn;\t\t// Button to use for swipe down. >= 0, 0 is none\n\t\tint lt_btn;\t\t// Button to use for swipe left. >= 0, 0 is none\n\t\tint rt_btn;\t\t// Button to use for swipe right. >= 0, 0 is none\n\t} scroll, swipe3, swipe4/*, swipe5*/;\n\tstruct MConfigSwipeCoasting{\n\t\tfloat min_speed;\t\t// What speed to start scroll coasting at. >= 0\n\t\tint tick_ms;\t\t// How fast events will be generated during coasting >= 1\n\t\tint duration;\t\t// How long coasting ticks will last >= 0, 0 disables coasting\n\t\tint no_boost;\t\t// Disable boosting on second scroll gesture during coasting. 0 or 1\n\t\tint ease;\t\t\t\t// Apply easing effect on coasting. 0 or 1\n\t} scroll_coast;\n\tstruct MConfigSwipe edge_scroll;\n\tint scale_dist;\t\t\t// Distance needed to trigger a button. >= 0, 0 disables\n\tint scale_up_btn;\t\t// Button to use for scale up. >= 0, 0 is none\n\tint scale_dn_btn;\t\t// Button to use for scale down. >= 0, 0 is none\n\tint rotate_dist;\t\t// Distance needed to trigger a button. >= 0, 0 disables\n\tint rotate_lt_btn;\t\t// Button to use for rotate left. >= 0, 0 is none\n\tint rotate_rt_btn;\t\t// Button to use for rotate right. >= 0, 0 is none\n\tstruct MConfigStationary{\n\t\tint max_move;\t\t\t// How far stationary finger can move, before interrupting the gesture. >= 0\n\t\tint button;\t\t// Button to be pressed/released when gesture starts/ends. >= 0, 0 disables\n\t} hold1_move1_stationary;\n#if 0\n\tstruct MConfigStationary hold1_move2_stationary, hold1_move3_stationary;\n#endif\n\tstruct MConfigSwipe\t\thold1_move1;\n#if 0\n\tstruct MConfigSwipe\t\thold1_move2, hold1_move3;\n#endif\n\tint drag_enable;\t\t// Enable tap-to-drag? 0 or 1\n\tint drag_timeout;\t\t// How long to wait for a move after tapping? > 0\n\tint drag_wait;\t\t\t// How long to wait before triggering button down? >= 0\n\tint drag_dist;\t\t\t// How far is the finger allowed to move during wait time? >= 0\n\tint drag_lock_timeout;  // How long to wait in 'drag ready' state after\n\t                        // dragging finger was released?\n\t                        // < 0 - wait of tap to break drag; 0 - disable; > 0 - time in ms\n\n\tdouble sensitivity;\t\t// Mouse movement multiplier. >= 0\n\tint scroll_smooth;\t\t// Enable high precision (smooth) scrolling. 0 or 1.\n};\n\n/* Load the MConfig struct with its defaults.\n */\nvoid mconfig_defaults(struct MConfig* cfg);\n\n/* Initialize the MConfig struct.\n */\nvoid mconfig_init(struct MConfig* cfg,\n\t\t\tconst struct Capabilities* caps);\n\nvoid mconfig_configure(struct MConfig* cfg,\n\t\t\tpointer opts);\n\n#endif\n"
  },
  {
    "path": "include/mprops.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef MTRACK_PROPS_H\n#define MTRACK_PROPS_H\n\n#include <xorg-server.h>\n#include <xf86Module.h>\n\n#include <X11/Xatom.h>\n#include <xf86.h>\n#include <xf86Xinput.h>\n#include <exevents.h>\n\n#include \"mconfig.h\"\n\n#ifndef XATOM_FLOAT\n#define XATOM_FLOAT \"FLOAT\"\n#endif\n\n// int, 1 value\n#define MTRACK_PROP_TRACKPAD_DISABLE \"Trackpad Disable Input\"\n// float, 1 value\n#define MTRACK_PROP_SENSITIVITY \"Trackpad Sensitivity\"\n// int, 2 values - finger low, finger high\n#define MTRACK_PROP_PRESSURE \"Trackpad Touch Pressure\"\n// int, 2 values - enable buttons, has integrated button\n#define MTRACK_PROP_BUTTON_SETTINGS \"Trackpad Button Settings\"\n// int, 4 values - enable button zones, button move emulation, emulation touch expiration, button zone in edge bottom\n#define MTRACK_PROP_BUTTON_EMULATE_SETTINGS \"Trackpad Button Emulation Settings\"\n// int, 7 values - button to emulate with 0 touch, 1 touch, 2 touches, 3 touches AND zone 1, zone 2, zone 3\n#define MTRACK_PROP_BUTTON_EMULATE_VALUES \"Trackpad Button Emulation Values\"\n// int, 3 values - click time, touch timeout, invalidate distance\n#define MTRACK_PROP_TAP_SETTINGS \"Trackpad Tap Settings\"\n// int, 4 values - 1 touch button, 2 touch button, 3 touch button, 4 touch button\n#define MTRACK_PROP_TAP_EMULATE \"Trackpad Tap Button Emulation\"\n// int, 2 values - ignore thumb touches, disable trackpad on thumb touches\n#define MTRACK_PROP_THUMB_DETECT \"Trackpad Thumb Detection\"\n// int, 2 values - size, width to length ratio\n#define MTRACK_PROP_THUMB_SIZE \"Trackpad Thumb Size\"\n// int, 2 values - ignore palm touches, disable trackpad on palm touches\n#define MTRACK_PROP_PALM_DETECT \"Trackpad Palm Detection\"\n// int, 1 value - size\n#define MTRACK_PROP_PALM_SIZE \"Trackpad Palm Size\"\n// int, 2 value - button hold, wait time\n#define MTRACK_PROP_GESTURE_SETTINGS \"Trackpad Gesture Settings\"\n// int, 1 value - enable high precision scrolling\n#define MTRACK_PROP_SMOOTH_SCROLL \"Trackpad Smooth Scroll\"\n// int, 3 values\n//   first: distance before a scroll event (two finger swipe) is triggered\n//   second: how much milliseconds button will be hold after {up,down,left,right} scroll\n//   third: sensitivity of the pointer during scroll gesture multiplied by 1000, value of 0 disables drag-to-move\n#define MTRACK_PROP_SCROLL_SETTINGS \"Trackpad Scroll Settings\"\n// int, 4 values - up button, down button, left button, right button\n#define MTRACK_PROP_SCROLL_BUTTONS \"Trackpad Scroll Buttons\"\n// int, 3 values - enable coasting, coasting enable speed, decelaration amount\n#define MTRACK_PROP_SCROLL_COAST \"Trackpad Scroll Coasting\"\n// int, 2 values\n//   first: distance before a three finger swipe event is triggered\n//   second: how much milliseconds button will be hold after {up,down,left,right} swipe\n//   third: sensitivity of the pointer during three finger swipe gesture multiplied by 1000, value of 0 disables drag-to-move\n#define MTRACK_PROP_SWIPE_SETTINGS \"Trackpad Swipe Settings\"\n// int, 4 values - up button, down button, left button, right button\n#define MTRACK_PROP_SWIPE_BUTTONS \"Trackpad Swipe Buttons\"\n// int, 3 values\n//   first: distance before a four finger swipe event is triggered\n//   second: how much milliseconds button will be hold after {up,down,left,right} swipe4\n//   third: sensitivity of the pointer during four finger swipe gesture multiplied by 1000, value of 0 disables drag-to-move\n#define MTRACK_PROP_SWIPE4_SETTINGS \"Trackpad Swipe4 Settings\"\n// int, 4 values - up button, down button, left button, right button\n#define MTRACK_PROP_SWIPE4_BUTTONS \"Trackpad Swipe4 Buttons\"\n// int, 7 values\n//    first: distance before event triggered (0 disable)\n//    second: how much miliseconds to hold button down\n//    third: sensitivity of the pointer during gesture\n//    rest: {up,down,left,right} buttons\n#define MTRACK_PROP_EDGE_SCROLL_SETTINGS \"Trackpad Edge Scroll Settings\"\n// int, 4 value - size of edge regions that are ignored: {up,down,left,right}\n#define MTRACK_PROP_EDGE_SIZES \"Trackpad Edge Sizes\"\n// int, 1 value - distance before a scale event is triggered\n#define MTRACK_PROP_SCALE_DIST \"Trackpad Scale Distance\"\n// int, 2 values - up button, down button\n#define MTRACK_PROP_SCALE_BUTTONS \"Trackpad Scale Buttons\"\n// int, 1 value - distance before a rotate event is triggered\n#define MTRACK_PROP_ROTATE_DIST \"Trackpad Rotate Distance\"\n// int, 2 values - left button, right button\n#define MTRACK_PROP_ROTATE_BUTTONS \"Trackpad Rotate Buttons\"\n// int, 2 values - hold&move with one stationary and one moving finger\n//   first: stationary finger max move distance\n//   second: button\n#define MTRACK_PROP_HOLD1_MOVE1_STATIONARY_SETTINGS \"Trackpad Hold1Move1 Stationary Settings\"\n// int, 2 values\n//   first: distance before a four swipe event is triggered\n//   second: how much milliseconds button will be hold after gesture\n#define MTRACK_PROP_HOLD1_MOVE1_SETTINGS \"Trackpad Hold1Move1 Settings\"\n// int, 4 values - up button, down button, left button, right button\n#define MTRACK_PROP_HOLD1_MOVE1_BUTTONS \"Trackpad Hold1Move1 Buttons\"\n\n// int, 2 values - hold&move with one stationary and two moving fingers\n//   first: stationary finger max move distance\n//   second: button\n#define MTRACK_PROP_HOLD1_MOVE2_STATIONARY_SETTINGS \"Trackpad Hold1Move2 Stationary Settings\"\n// int, 2 values\n//   first: distance before a four swipe event is triggered\n//   second: how much milliseconds button will be hold after gesture\n#define MTRACK_PROP_HOLD1_MOVE2_SETTINGS \"Trackpad Hold1Move2 Settings\"\n// int, 4 values - up button, down button, left button, right button\n#define MTRACK_PROP_HOLD1_MOVE2_BUTTONS \"Trackpad Hold1Move2 Buttons\"\n\n// int, 2 values - hold&move with one stationary and three moving finger\n//   first: stationary finger max move distance\n//   second: button\n#define MTRACK_PROP_HOLD1_MOVE3_STATIONARY_SETTINGS \"Trackpad Hold1Move3 Stationary Settings\"\n// int, 2 values\n//   first: distance before a four swipe event is triggered\n//   second: how much milliseconds button will be hold after gesture\n#define MTRACK_PROP_HOLD1_MOVE3_SETTINGS \"Trackpad Hold1Move3 Settings\"\n// int, 4 values - up button, down button, left button, right button\n#define MTRACK_PROP_HOLD1_MOVE3_BUTTONS \"Trackpad Hold1Move3 Buttons\"\n\n// int, 5 values - enable, timeout, wait, dist, lock timeout\n#define MTRACK_PROP_DRAG_SETTINGS \"Trackpad Drag Settings\"\n// int, 2 values - invert x axis, invert y axis\n#define MTRACK_PROP_AXIS_INVERT \"Trackpad Axis Inversion\"\n\nstruct MProps {\n\t// Properties Config\n\tAtom float_type;\n\n\t// Adjustable Properties\n\tAtom api;\n\tAtom trackpad_disable;\n\tAtom sensitivity;\n\tAtom pressure;\n\tAtom button_settings;\n\tAtom button_emulate_settings;\n\tAtom button_emulate_values;\n\tAtom tap_settings;\n\tAtom tap_emulate;\n\tAtom thumb_detect;\n\tAtom thumb_size;\n\tAtom palm_detect;\n\tAtom palm_size;\n\tAtom gesture_settings;\n\tAtom scroll_smooth;\n\tstruct MPropsSwipe{\n\t\tAtom settings;\n\t\tAtom buttons;\n\t} scroll, swipe3, swipe4/*, swipe5*/;\n\tAtom scroll_coast;\n\tAtom edge_scroll;\n\tAtom scale_dist;\n\tAtom scale_buttons;\n\tAtom rotate_dist;\n\tAtom rotate_buttons;\n\tAtom hold1_move1_stationary;\n\tstruct MPropsSwipe hold1_move1;\n#if 0\n\tAtom hold1_move2_stationary;\n\tstruct MPropsSwipe hold1_move2;\n\tAtom hold1_move3_stationary;\n\tstruct MPropsSwipe hold1_move3;\n#endif\n\tAtom drag_settings;\n\tAtom axis_invert;\n\tAtom edge_sizes;\n};\n\nvoid mprops_init(struct MConfig* cfg, InputInfoPtr local);\nint mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly);\n#endif\n"
  },
  {
    "path": "include/mtouch.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n * Copyright (C) 2015-2018 Paweł Turkowski <p2rkw0@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef MTOUCH_H\n#define MTOUCH_H\n\n#include \"common.h\"\n#include \"capabilities.h\"\n#include \"hwstate.h\"\n#include \"mtstate.h\"\n#include \"mconfig.h\"\n#include \"gestures.h\"\n#include \"os.h\" /* xorg/os.h for timers */\n\nstruct MTouch {\n\tint fd;\n\tstruct mtdev dev;\n\tstruct Capabilities caps;\n\tstruct HWState hs;\n\tstruct MTState state;\n\tstruct MConfig cfg;\n\tstruct Gestures gs;\n\tint timer_kind;   // to avoid setting timer multiple times\n\t/*\n\t * Timers documentation:\n\t * http://www.x.org/releases/X11R7.7/doc/xorg-server/Xserver-spec.html#id2536042\n\t */\n\tOsTimerPtr timer;\n\tint absolute_mode;\t\t// Should absolute mode be enabled ? 0 or 1\n\tValuatorMask* valuator_mask;\n\tDeviceIntPtr local_dev;\n};\n\nint mtouch_configure(struct MTouch* mt, int fd);\nint mtouch_open(struct MTouch* mt, int fd);\nint mtouch_close(struct MTouch* mt);\n\nint mtouch_read(struct MTouch* mt);\nint mtouch_delayed(struct MTouch* mt);\n\n\n/**\n * \\defgroup Timers Perform asynchronous tasks\n *\n *  Timers are defined by labels (IDs). To add new timer create new ID, and implement\n * appropriate section in mt_timer_start, mt_timer_stop, and mt_timer_callback.\n *\n *  All timers share same X resource, so generally you can't have more than one timer\n * ticking at time. For now it's fine.\n *\n *  Functions are implemented in mtrack.c\n *\n * @{\n */\n\n/** Will cancel any timer */\n#define MT_TIMER_ANY            -1\n#define MT_TIMER_NONE           0\n/* #define -MT_TIMER_ANY         1 - reserved */\n#define MT_TIMER_DELAYED_BUTTON 2\n#define MT_TIMER_COASTING       3\n\nvoid mt_timer_start(struct MTouch *mt, int kind);\n/**\n *  Executed on early stop.\n * For example when one timer is replaced by naother.\n * @param mt\n */\nvoid mt_timer_stop(struct MTouch *mt);\nCARD32 mt_timer_callback(OsTimerPtr timer, CARD32 time, void *arg);\nvoid mt_timer_stop_if(struct MTouch *mt, int kind);\n/**\n *  Restart timer with same kind.\n * Does not call start nor stop callbacks.\n * @param mt\n */\nvoid mt_timer_continue(struct MTouch *mt, mstime_t timeout);\n/**\n *  Kind > 0 starts timer, kind < 0 stops timer with ID == abs(kind).\n * MT_TIMER_ANY stops whatever timer is running.\n * @param mt\n * @param kind\n */\nvoid mt_timer_start_or_stop(struct MTouch *mt, int kind);\n\n/** }@ */\n\n#endif\n"
  },
  {
    "path": "include/mtstate.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#ifndef MTSTATE_H\n#define MTSTATE_H\n\n#include \"common.h\"\n#include \"mconfig.h\"\n#include \"hwstate.h\"\n#include \"capabilities.h\"\n\n#define MT_NEW 0\n#define MT_RELEASED 1\n#define MT_INVALID 2\n#define MT_THUMB 3\n#define MT_PALM 4\n#define MT_EDGE 5\n#define MT_TAP 6\n#define MT_BUTTON 7\n\nstruct Touch {\n\tbitmask_t flags;\n\tdouble direction;\n\tint tracking_id;\n\tint x, y, dx, dy;\n\tint total_dx, total_dy;\n\tstruct timeval down;\n};\n\nstruct MTState {\n\tbitmask_t touch_used;\n\tstruct Touch touch[DIM_TOUCHES];\n};\n\n/*\n * |1|   2   |3|\n * |-|-------|-|\n * |4|   5   |6|\n * |-|-------|-|\n * |7|   8   |9|\n */\nint touch_which_edge(const struct MConfig* cfg, const struct Touch* t);\n\n/* Initialize an MTState struct.\n */\nvoid mtstate_init(struct MTState* ms);\n\n/* Extract the MTState from the current hardware state.\n */\nvoid mtstate_extract(struct MTState* ms,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tconst struct HWState* hs,\n\t\t\tconst struct Capabilities* caps);\n\n#endif\n\n"
  },
  {
    "path": "include/trig.h",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n/* Some bastardized trig functions. These calculations flip the\n * Y axis since that axis on touchpads is opposite that of the\n * Cartesian system.\n */\n\n#ifndef MT_TRIG_H\n#define MT_TRIG_H\n\n#define TR_NONE -1\n#define TR_DIR_UP 0\n#define TR_DIR_RT 2\n#define TR_DIR_DN 4\n#define TR_DIR_LT 6\n\n/* Determine the direction of a vector. This uses the slope of the\n * vector to approximate the angle, as such it is only accurate at\n * increments of 45 degrees. This is sufficient for our uses.\n *\n * The returned value is 0 <= a < 8 such that the circle is split\n * into 45 degree sections. Each whole number lies 45 degrees apart\n * and so whole numbers are exact. All fractional parts are\n * aproximations.\n *\n * TR_NONE will be returned if the magnitude of the vector is zero.\n */\ndouble trig_direction(double dx, double dy);\n\n/* Generalize a direction.  Returns TR_NONE, TR_DIR_UP, TR_DIR_RT,\n * TR_DIR_DN, or TR_DIR_LT.\n */\nint trig_generalize(double dir);\n\n/* Add two angles.\n */\ndouble trig_angles_add(double a1, double a2);\n\n/* Subtract two angles.\n */\ndouble trig_angles_sub(double a1, double a2);\n\n/* Calculate the acute angle between two angles.\n */\ndouble trig_angles_acute(double a1, double a2);\n\n/* Average a collection of angles.\n */\ndouble trig_angles_avg(double* angles, int len);\n\n/* Compare two angles. Returns 0 if a1 == a2. Returns < 0 if a1 < a2.\n * Returns > 0 if a1 > a2.\n */\nint trig_angles_cmp(double a1, double a2);\n\n#endif\n\n"
  },
  {
    "path": "required_packages.txt",
    "content": "xserver-xorg-dev mtdev-dev libmtdev-dev xutils-dev\n"
  },
  {
    "path": "src/capabilities.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"capabilities.h\"\n\n#define SETABS(c, x, map, key, fd)\t\t\t\t\t\\\n\t(c->has_##x = getbit(map, key) && getabs(&c->x, key, fd))\n\n#define ADDCAP(s, c, x) strcat(s, c->has_##x ? \" \" #x : \"\")\n\nstatic const int SN_COORD = 250;\t/* coordinate signal-to-noise ratio */\nstatic const int SN_WIDTH = 100;\t/* width signal-to-noise ratio */\nstatic const int SN_ORIENT = 10;\t/* orientation signal-to-noise ratio */\n\nstatic const int bits_per_long = 8 * sizeof(long);\n\nstatic inline int nlongs(int nbit)\n{\n\treturn (nbit + bits_per_long - 1) / bits_per_long;\n}\n\nstatic inline int getbit(const unsigned long *map, int key)\n{\n\treturn (map[key / bits_per_long] >> (key % bits_per_long)) & 0x01;\n}\n\nstatic int getabs(struct input_absinfo *abs, int key, int fd)\n{\n\tint rc;\n\tSYSCALL(rc = ioctl(fd, EVIOCGABS(key), abs));\n\treturn rc >= 0;\n}\n\nstatic int has_mt_data(const struct Capabilities *cap)\n{\n\treturn cap->has_abs[MTDEV_POSITION_X] && cap->has_abs[MTDEV_POSITION_Y];\n}\n\nstatic int has_integrated_button(const struct Capabilities *cap)\n{\n\tstatic const int bcm5974_vmask_ibt = 1;\n\t/* magic trackpad */\n\tif (cap->devid.vendor == 0x05ac && cap->devid.product == 0x030e)\n\t\treturn 1;\n\t/* macbooks */\n\tif (strcmp(cap->devname, \"bcm5974\"))\n\t\treturn 0;\n\treturn cap->devid.version & bcm5974_vmask_ibt;\n}\n\nstatic void default_fuzz(struct Capabilities *cap, unsigned int code, int sn)\n{\n\tint bit = mtdev_abs2mt(code);\n\tif (cap->has_abs[bit] && cap->abs[bit].fuzz == 0)\n\t\tcap->abs[bit].fuzz =\n\t\t\t(cap->abs[bit].maximum - cap->abs[bit].minimum) / sn;\n}\n\nint read_capabilities(struct Capabilities *cap, int fd)\n{\n\tunsigned long evbits[nlongs(EV_MAX)];\n\tunsigned long absbits[nlongs(ABS_MAX)];\n\tunsigned long keybits[nlongs(KEY_MAX)];\n\tint rc, i;\n\n\tmemset(cap, 0, sizeof(struct Capabilities));\n\n\tSYSCALL(rc = ioctl(fd, EVIOCGID, &cap->devid));\n\tif (rc < 0)\n\t\treturn rc;\n\tSYSCALL(rc = ioctl(fd, EVIOCGNAME(sizeof(cap->devname) - 1), cap->devname));\n\tif (rc < 0)\n\t\treturn rc;\n\tSYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_SYN, sizeof(evbits)), evbits));\n\tif (rc < 0)\n\t\treturn rc;\n\tSYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits));\n\tif (rc < 0)\n\t\treturn rc;\n\tSYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits));\n\tif (rc < 0)\n\t\treturn rc;\n\n\tcap->has_left = getbit(keybits, BTN_LEFT);\n\tcap->has_middle = getbit(keybits, BTN_MIDDLE);\n\tcap->has_right = getbit(keybits, BTN_RIGHT);\n\n\tSETABS(cap, slot, absbits, ABS_MT_SLOT, fd);\n\tfor (i = 0; i < MT_ABS_SIZE; i++)\n\t\tSETABS(cap, abs[i], absbits, mtdev_mt2abs(i), fd);\n\n\tcap->has_mtdata = has_mt_data(cap);\n\tcap->has_ibt = has_integrated_button(cap);\n\n\tdefault_fuzz(cap, ABS_MT_POSITION_X, SN_COORD);\n\tdefault_fuzz(cap, ABS_MT_POSITION_Y, SN_COORD);\n\tdefault_fuzz(cap, ABS_MT_TOUCH_MAJOR, SN_WIDTH);\n\tdefault_fuzz(cap, ABS_MT_TOUCH_MINOR, SN_WIDTH);\n\tdefault_fuzz(cap, ABS_MT_WIDTH_MAJOR, SN_WIDTH);\n\tdefault_fuzz(cap, ABS_MT_WIDTH_MINOR, SN_WIDTH);\n\tdefault_fuzz(cap, ABS_MT_ORIENTATION, SN_ORIENT);\n\n//#define MIMIC_CUSTOM_CAPS\n#ifdef MIMIC_CUSTOM_CAPS\n\tcap->has_abs[MTDEV_POSITION_X] = 1;\n\tcap->has_abs[MTDEV_POSITION_Y] = 1;\n\tcap->has_abs[MTDEV_TRACKING_ID] = 1;\n\n\tcap->has_abs[MTDEV_TOUCH_MAJOR] = 0;\n\tcap->has_abs[MTDEV_TOUCH_MINOR] = 0;\n\tcap->has_abs[MTDEV_WIDTH_MAJOR] = 0;\n\tcap->has_abs[MTDEV_WIDTH_MINOR] = 0;\n\tcap->has_abs[MTDEV_ORIENTATION] = 0;\n\tcap->has_abs[MTDEV_PRESSURE] = 0;\n#endif\n\treturn 0;\n}\n\nint get_cap_xsize(const struct Capabilities *cap)\n{\n\tconst struct input_absinfo *x = &cap->abs[MTDEV_POSITION_X];\n\treturn x->maximum - x->minimum;\n}\n\nint get_cap_ysize(const struct Capabilities *cap)\n{\n\tconst struct input_absinfo *y = &cap->abs[MTDEV_POSITION_Y];\n\treturn y->maximum - y->minimum;\n}\n\nint get_cap_wsize(const struct Capabilities *cap)\n{\n\tconst struct input_absinfo *w = &cap->abs[MTDEV_TOUCH_MAJOR];\n\treturn w->maximum - w->minimum;\n}\n\nint get_cap_xmid(const struct Capabilities *cap)\n{\n\tconst struct input_absinfo *x = &cap->abs[MTDEV_POSITION_X];\n\treturn (x->maximum + x->minimum) >> 1;\n}\n\nint get_cap_ymid(const struct Capabilities *cap)\n{\n\tconst struct input_absinfo *y = &cap->abs[MTDEV_POSITION_Y];\n\treturn (y->maximum + y->minimum) >> 1;\n}\n\nint translate_cap_x(const struct Capabilities *cap, int x)\n{\n\tint mid = get_cap_xmid(cap);\n\treturn x - mid;\n}\n\nint translate_cap_y(const struct Capabilities *cap, int y)\n{\n\tint mid = get_cap_ymid(cap);\n\treturn y - mid;\n}\n\nint get_cap_xmin(const struct Capabilities *cap)\n{\n\tconst struct input_absinfo *x = &cap->abs[MTDEV_POSITION_X];\n\treturn x->minimum;\n}\n\nint get_cap_ymin(const struct Capabilities *cap)\n{\n\tconst struct input_absinfo *y = &cap->abs[MTDEV_POSITION_Y];\n\treturn y->minimum;\n}\n\nvoid output_capabilities(const struct Capabilities *cap)\n{\n\tchar line[1024];\n\tint i;\n\n\tLOG_INFO(\"Capabilities: \\n\");\n\tLOG_INFO_CONT(\"devname: %s\\n\", cap->devname);\n\tLOG_INFO_CONT(\"devid: %x %x %x\\n\", cap->devid.vendor, cap->devid.product, cap->devid.version);\n\n\tchar cap_names[MT_ABS_SIZE][24] = {\n\t\t\"ABS_MT_TOUCH_MAJOR\", \"ABS_MT_TOUCH_MINOR\",\n\t\t\"ABS_MT_WIDTH_MAJOR\",\t\"ABS_MT_WIDTH_MINOR\",\n\t\t\"ABS_MT_ORIENTATION\",\n\t\t\"ABS_MT_POSITION_X\", \"ABS_MT_POSITION_Y\",\n\t\t\"ABS_MT_TOOL_TYPE\",\n\t\t\"ABS_MT_BLOB_ID\",\n\t\t\"ABS_MT_TRACKING_ID\",\n\t\t\"ABS_MT_PRESSURE\"\n\t};\n\tfor (i = 0; i < MT_ABS_SIZE; i++) {\n\t\tif (cap->has_abs[i])\n\t\t\tLOG_INFO_CONT(\"%s: min: %d max: %d\\n\",\n\t\t\t\tcap_names[i],\n\t\t\t\tcap->abs[i].minimum,\n\t\t\t\tcap->abs[i].maximum);\n\t}\n\tif(cap->has_slot)\n\t\tLOG_INFO_CONT(\"ABS_MT_SLOT: min: %d max: %d\\n\", cap->slot.minimum, cap->slot.maximum);\n\n\tmemset(line, 0, sizeof(line));\n\tADDCAP(line, cap, left);\n\tADDCAP(line, cap, middle);\n\tADDCAP(line, cap, right);\n\tADDCAP(line, cap, mtdata);\n\tADDCAP(line, cap, ibt);\n\tADDCAP(line, cap, slot);\n\tLOG_INFO_CONT(\"enabled caps:%s\\n\", line);\n}\n"
  },
  {
    "path": "src/gestures.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n * Copyright (C) 2015-2018 Paweł Turkowski <p2rkw0@gmail.com>\n *\n * Gestures\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2010 Arturo Castro <mail@arturocastro.net>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n * Copyright (C) 2015-2018 Paweł Turkowski <p2rkw0@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"gestures.h\"\n#include \"mtouch.h\"\n#include \"trig.h\"\n//#define DEBUG_GESTURES 1\n#ifdef DEBUG_GESTURES\n# define LOG_DEBUG_GESTURES LOG_DEBUG\n# define LOG_EMULATED LOG_INFO\n#else\n# define LOG_DEBUG_GESTURES LOG_DISABLED\n# define LOG_EMULATED LOG_DISABLED\n#endif\n\n\n#define IS_VALID_BUTTON(x) (x >= 0 && x <= 31)\n\nstatic void break_coasting(struct Gestures* gs){\n\tgs->scroll_speed_x = gs->scroll_speed_y = 0.0f;\n}\n\nstatic void trigger_button_up(struct Gestures* gs, int button)\n{\n\tif (IS_VALID_BUTTON(button)) {\n\t\tif (button == 0 && gs->button_emulate > 0) {\n\t\t\tbutton = gs->button_emulate;\n\t\t\tgs->button_emulate = 0;\n\t\t}\n\t\tCLEARBIT(gs->buttons, button);\n\t\tLOG_DEBUG_GESTURES(\"trigger_button_up: %d up\\n\", button);\n\t}\n}\n\nstatic void trigger_button_down(struct Gestures* gs, int button)\n{\n\tstruct timeval epoch;\n\ttimerclear(&epoch);\n\n\tif (IS_VALID_BUTTON(button) &&\n\t\t\t(button != gs->button_delayed || !IS_VALID_BUTTON(gs->button_delayed))\n\t) {\n\t\tSETBIT(gs->buttons, button);\n\n\t\tLOG_DEBUG_GESTURES(\"trigger_button_down: %d down\\n\", button);\n\t}\n\telse if (IS_VALID_BUTTON(button))\n\t\tLOG_DEBUG_GESTURES(\"trigger_button_down: %d down ignored, in delayed mode\\n\", button);\n}\n\nstatic void trigger_button_emulation(struct Gestures* gs, int button)\n{\n#if 0\n\tif (IS_VALID_BUTTON(button) && GETBIT(gs->buttons, 0)) {\n\t\tCLEARBIT(gs->buttons, 0);\n\t\tSETBIT(gs->buttons, button);\n\t\tgs->button_emulate = button;\n\t\tLOG_DEBUG_GESTURES(\"trigger_button_emulation: %d emulated\\n\", button);\n\t}\n#else\n\ttrigger_button_down(gs, button);\n\tgs->integrated_emulated_button = button + 1;\n#endif\n}\n\nstatic void trigger_button_emulation_end(struct Gestures* gs)\n{\n\tif(gs->integrated_emulated_button > 0){\n\t\ttrigger_button_up(gs, gs->integrated_emulated_button - 1);\n\t\tgs->integrated_emulated_button = 0;\n\t}\n}\n\nint trigger_delayed_button_uncond(struct Gestures* gs)\n{\n\tint button;\n\n\t// clear button before timer (unless compiler decide otherwise)\n\tbutton = gs->button_delayed;\n\n\tgs->button_delayed = -1;\n\ttimerclear(&gs->button_delayed_time);\n\tgs->move_dist = 0; /* don't count movement from delayed button phase in next stroke */\n\n\tLOG_DEBUG_GESTURES(\"trigger_delayed_button: %d up, timer expired\\n\", button);\n\ttrigger_button_up(gs, button);\n\n\treturn button;\n}\n\n/*\n * If trigger_up_time is NULL or epoch time it will set timer to infinity - button up will\n * be send when user finish gesture.\n */\nstatic void trigger_button_click(struct Gestures* gs,\n\t\t\tint button, struct timeval* trigger_up_time)\n{\n#ifdef DEBUG_GESTURES\n\tstruct timeval delta;\n#endif\n\n\tif (!IS_VALID_BUTTON(button))\n\t\treturn;\n\n\tif (!IS_VALID_BUTTON(gs->button_delayed)) {\n\t\ttrigger_button_down(gs, button);\n\t\tgs->button_delayed = button;\n\t\tif(trigger_up_time == NULL)\n\t\t\ttimerclear(&gs->button_delayed_time);\t\t// \"infinite timer\", wait for gesture end\n\t\telse\n\t\t\ttimercp(&gs->button_delayed_time, trigger_up_time);\t// may be also \"infinite\"\n\n#ifdef DEBUG_GESTURES\n\t\ttimersub(&gs->button_delayed_time, &gs->time, &delta);\n\t\tLOG_DEBUG_GESTURES(\"trigger_button_click: %d placed in delayed mode; delta: %lld ms\\n\", button, timertoms(&delta));\n#endif\n\t}\n#ifdef DEBUG_GESTURES\n\telse\n\t\tLOG_DEBUG_GESTURES(\"trigger_button_click: %d ignored, in delayed mode\\n\", button);\n#endif\n}\n\nstatic void reset_drag(struct Gestures* gs){\n\tif (gs->drag_state == GS_DRAG_ACTIVE) {\n\t\tgs->drag_state = GS_NONE;\n\t\ttimerclear(&gs->move_drag_expire);\n\t\ttrigger_button_up(gs, 0);\n\t\tLOG_DEBUG_GESTURES(\"reset_drag: drag stopped\\n\");\n\t}\n}\n\nstatic void trigger_drag_ready(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg)\n{\n\tgs->drag_state = GS_DRAG_READY;\n\ttimeraddms(&gs->time, cfg->drag_timeout, &gs->move_drag_expire);\n\tLOG_DEBUG_GESTURES(\"trigger_drag_ready: drag is ready\\n\");\n\t/* Break coasting */\n\tbreak_coasting(gs);\n}\n\nstatic int trigger_drag_start(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tint dx, int dy)\n{\n\tif (gs->drag_state == GS_DRAG_READY) {\n\t\ttimerclear(&gs->move_drag_expire);\n\t\tif (cfg->drag_wait == 0) {\n \t\t\tgs->drag_state = GS_DRAG_ACTIVE;\n\t\t\ttrigger_button_down(gs, 0);\n\t\t\tLOG_DEBUG_GESTURES(\"trigger_drag_start: drag is active\\n\");\n\t\t}\n\t\telse {\n\t\t\tgs->drag_state = GS_DRAG_WAIT;\n\t\t\tgs->move_drag_dx = dx;\n\t\t\tgs->move_drag_dy = dy;\n\t\t\ttimeraddms(&gs->time, cfg->drag_wait, &gs->move_drag_wait);\n\t\t\tLOG_DEBUG_GESTURES(\"trigger_drag_start: drag in wait\\n\");\n\t\t}\n\t}\n\telse if (gs->drag_state == GS_DRAG_WAIT) {\n\t\tgs->move_drag_dx += dx;\n\t\tgs->move_drag_dy += dy;\n\t\tif (!timercmp(&gs->time, &gs->move_drag_wait, <)) {\n\t\t\tgs->drag_state = GS_DRAG_ACTIVE;\n\t\t\ttrigger_button_down(gs, 0);\n\t\t\tLOG_DEBUG_GESTURES(\"trigger_drag_start: drag is active\\n\");\n\t\t}\n\t\telse if (dist2(gs->move_drag_dx, gs->move_drag_dy) > SQRVAL(cfg->drag_dist)) {\n\t\t\tgs->drag_state = GS_NONE;\n\t\t\tLOG_DEBUG_GESTURES(\"trigger_drag_start: drag canceled, moved too far\\n\");\n\t\t}\n\t}\n\treturn gs->drag_state != GS_DRAG_WAIT;\n}\n\nstatic void trigger_drag_stop(struct Gestures* gs, const struct MConfig* cfg)\n{\n\tint lock_timeout;\n\n\tlock_timeout = cfg->drag_lock_timeout;\n\n\tif (gs->drag_state == GS_DRAG_READY) {\n\t\t/* if timeout < 0 then tap-dragging will require another tap to break the drag */\n\t\tif (lock_timeout >= 0 && timercmp(&gs->time, &gs->move_drag_expire, >=)) {\n\t\t\tLOG_DEBUG_GESTURES(\"trigger_drag_stop: locked drag expored\\n\");\n\t\t\ttrigger_button_up(gs, 0);\n\t\t}\n\t\tgs->drag_state = GS_NONE;\n\t\ttimerclear(&gs->move_drag_expire);\n\t\tLOG_DEBUG_GESTURES(\"trigger_drag_stop: drag canceled\\n\");\n\t}\n\telse if (gs->drag_state == GS_DRAG_ACTIVE) {\n\t\tif(lock_timeout == 0){\n\t\t\treset_drag(gs);\n\t\t}\n\t\telse{\n\t\t\t/* Tap to drag lock timeout implementaion:\n\t\t\t * Instead of breaking dragging completely, just take one step back\n\t\t\t * to ready state and start timer waiting for future dragging */\n\t\t\tgs->drag_state = GS_DRAG_READY;\n\t\t\ttimeraddms(&gs->time, lock_timeout, &gs->move_drag_expire);\n\t\t\tLOG_DEBUG_GESTURES(\"trigger_drag_stop: drag in wait lock\\n\");\n\t\t}\n   }\n}\n\n/* Return 0 if current time stamp is greater than move_wait time stamp. */\nstatic int can_change_gesture_type(struct Gestures* gs, int desired_gesture){\n\tif (gs->move_type == desired_gesture)\n\t\treturn 1;\n\tif(gs->move_type == GS_NONE || gs->move_type == GS_MOVE)\n\t\treturn 1;\n\treturn timercmp(&gs->time, &gs->move_wait, >);\n}\n\nstatic void buttons_update(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tconst struct HWState* hs,\n\t\t\tstruct MTState* ms)\n{\n\tif (!cfg->button_enable || cfg->trackpad_disable >= 3)\n\t\treturn;\n\n\tstatic bitmask_t button_prev = 0U;\n\tint i, integrated_down, integrated_up;\n\tint integrated_just_clicked, integrated_just_released;\n\n\tintegrated_down = 0;\n\tintegrated_up = 0;\n\tintegrated_just_clicked = GETBIT(hs->button, 0) && !GETBIT(button_prev, 0);\n\tintegrated_just_released = !GETBIT(hs->button, 0) && GETBIT(button_prev, 0);\n\n\tfor (i = 0; i < 32; i++) {\n\t\tif (GETBIT(hs->button, i) == GETBIT(button_prev, i))\n\t\t\tcontinue;\n\t\tif (GETBIT(hs->button, i)) {\n\t\t\tintegrated_down++;\n\t\t}\n\t\telse {\n\t\t\tintegrated_up++;\n\t\t}\n\t}\n\tbutton_prev = hs->button;\n\n\tif (integrated_down) {\n\t\tint earliest, latest, lowest;\n\t\tgs->move_type = GS_NONE;\n\t\ttimeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);\n\t\tearliest = -1;\n\t\tlatest = -1;\n\t\tlowest = -1;\n\t\tforeach_bit(i, ms->touch_used) {\n\t\t\tif (!cfg->button_zones && GETBIT(ms->touch[i].flags, MT_INVALID))\n\t\t\t\tcontinue;\n\t\t\tif (cfg->button_integrated)\n\t\t\t\tSETBIT(ms->touch[i].flags, MT_BUTTON); /* Mark all existing touches as physical button press */\n\t\t\tif (lowest == -1 || ms->touch[i].y > ms->touch[lowest].y) /* The logic/naming seems to be inverted here */\n\t\t\t\tlowest = i;\n\t\t\tif (earliest == -1 || timercmp(&ms->touch[i].down, &ms->touch[earliest].down, <))\n\t\t\t\tearliest = i;\n\t\t\tif (latest == -1 || timercmp(&ms->touch[i].down, &ms->touch[latest].down, >))\n\t\t\t\tlatest = i;\n\t\t}\n\n\t\tif (integrated_just_clicked) {\n\t\t\tLOG_EMULATED(\"buttons_update: integrated_just_clicked=true\\n\");\n\t\t\tint is_emulation_triggered = 0;\n\t\t\tif (cfg->button_zones && lowest >= 0) {\n\t\t\t\tis_emulation_triggered = buttons_zone_update(gs, cfg, ms, lowest);\n\t\t\t}\n\t\t\tif (!is_emulation_triggered) {\n\t\t\t\ttouch_detect_update(gs, cfg, ms, latest);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (integrated_just_released) {\n\t\tforeach_bit(i, ms->touch_used) {\n\t\t\tif (cfg->button_integrated) { /* <-- I know it can be outide loop, but this is less error prone */\n\t\t\t\t/* Physical button is no longer down, so unmark touches */\n\t\t\t\tCLEARBIT(ms->touch[i].flags, MT_BUTTON);\n\t\t\t}\n\t\t}\n\t\ttrigger_button_emulation_end(gs);\n\t}\n}\n\n/*\n * Handle the physical button click in relation to touch properties.\n * It count the number of valid finger touching the pad while clicking it to\n * emulate the action in the user config. See \"ClickFinger#\" configuration parameter.\n*/\nstatic void touch_detect_update(\n\tstruct Gestures* gs,\n\tconst struct MConfig* cfg,\n\tconst struct MTState* ms,\n\tint latest)\n{\n\tint i = 0,\n\ttouching = 0;\n\tstruct timeval expire;\n\n\t/*If latest is not set, the finger placement is not valid*/\n\tif (latest >= 0) {\n\t\tforeach_bit(i, ms->touch_used) {\n\t\t\tmicrotime(&expire);\n\t\t\ttimeraddms(&ms->touch[i].down, cfg->button_expire, &expire);\n\t\t\tif ((cfg->button_move || cfg->button_expire == 0 || timercmp(&ms->touch[latest].down, &expire, <)) &&\n\t\t\t\t!(cfg->ignore_thumb && GETBIT(ms->touch[i].flags, MT_THUMB)) &&\n\t\t\t\t!(cfg->ignore_palm && GETBIT(ms->touch[i].flags, MT_PALM)) &&\n\t\t\t\t!(GETBIT(ms->touch[i].flags, MT_EDGE))) {\n\t\t\t\ttouching++;\n\t\t\t\tLOG_EMULATED(\"touch_detect_udpate: latest >=0: touching++ =%d\\n\", touching);\n\t\t\t}\n\t\t}\n\t}\n\tLOG_EMULATED(\"touch_detect_udpate: latest=%d: touching=%d\\n\", latest, touching);\n\tif (touching == 0 && cfg->button_0touch > 0) {\n\t\t/* The integrated physical button have been pressed but no finger are valid\n\t\t * This code path can be reached by enabling the ClickFinger0 in the config file. */\n\t\ttrigger_button_emulation(gs, cfg->button_0touch - 1);\n\t}\n\telse if (touching == 1 && cfg->button_1touch > 0)\n\t\ttrigger_button_emulation(gs, cfg->button_1touch - 1);\n\telse if (touching == 2 && cfg->button_2touch > 0)\n\t\ttrigger_button_emulation(gs, cfg->button_2touch - 1);\n\telse if (touching == 3 && cfg->button_3touch > 0)\n\t\ttrigger_button_emulation(gs, cfg->button_3touch - 1);\n}\n\n/*\n * Handle the button zone options for triggering button emulation\n * Up to three zones can be setup, one for each button_touch type.\n * This function take into account a maximum height for zone action.\n *\n * Return: \"true\" if a button_emulation was triggered, else false\n*/\nstatic int buttons_zone_update(\n\tstruct Gestures* gs,\n\tconst struct MConfig* cfg,\n\tconst struct MTState* ms,\n\tint lowest)\n{\n\tint i, zones, left, right, pos_x, pos_y;\n\tdouble width;\n\tdouble limit_height;\n\n\tzones = 0;\n\tif (cfg->button_first_zone > 0)\n\t\tzones++;\n\tif (cfg->button_second_zone > 0)\n\t\tzones++;\n\tif (cfg->button_third_zone > 0)\n\t\tzones++;\n\n\tif (zones > 0) {\n\t\tlimit_height = -1;\n\t\tpos_y = 0;\n\n\t\t/* Check if the zone need to be limited in height */\n\t\tif(cfg->is_button_zones_height_limited != 0 && cfg->edge_bottom_size != 0) {\n\t\t\tlimit_height = cfg->pad_height - cfg->pad_height * ((double)cfg->edge_bottom_size / 100.0);\n\t\t\tpos_y = cfg->pad_height / 2 + ms->touch[lowest].y;\n\t\t\tLOG_EMULATED(\"button_zone_update: limit_height %f, pos_y %d, pad_height %d, edge_bottom_size %d\\n\",\n\t\t\t\tlimit_height, pos_y, cfg->pad_height, cfg->edge_bottom_size);\n\t\t}\n\t\t/* If no height limit is set in the config, it will always be true */\n\t\tif(pos_y >= limit_height) {\n\t\t\twidth = ((double)cfg->pad_width) / ((double)zones);\n\t\t\tpos_x = cfg->pad_width / 2 + ms->touch[lowest].x; /* Why not mean x of all touches? */\n\t\t\tLOG_EMULATED(\"button_zone_update: pad width %d, zones %d, zone width %f, zone height %f, x %d\\n\",\n\t\t\t\tcfg->pad_width, zones, width, limit_height, pos_x);\n\n\t\t\tfor (i = 0; i < zones; i++) {\n\t\t\t\tleft = width*i;\n\t\t\t\tright = width*(i+1);\n\t\t\t\tif ((i == 0 || pos_x >= left) && (i == zones - 1 || pos_x <= right)) {\n\t\t\t\t\tLOG_EMULATED(\"button_zone_update: button %d, left %d, right %d (found)\\n\", i, left, right);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tLOG_EMULATED(\"button_zone_update: button %d, left %d, right %d\\n\", i, left, right);\n\t\t\t}\n\t\t\tif (i == 0)\n\t\t\t\ttrigger_button_emulation(gs, cfg->button_first_zone - 1);\n\t\t\telse if (i == 1)\n\t\t\t\ttrigger_button_emulation(gs, cfg->button_second_zone - 1);\n\t\t\telse if (i == 2)\n\t\t\t\ttrigger_button_emulation(gs, cfg->button_third_zone - 1);\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n}\n\n/*\n * So, tapping. Tap begins with 0 fingers on trackpad,\n * then one or more are coming down, stay down for a moment,\n * and then all of them are released more or less simultaneously\n *\n * These 3 steps must be completed in relative short time.\n * Another requirement is that no other gesture can be made duting tapping.\n *\n * What can break taps:\n *  too much time passed by\n *  one of the fingers moved too far\n */\nstatic void abort_tapping(struct Gestures* gs, struct MTState* ms){\n\tint i;\n\n\tgs->tap_touching = 0;\n\tgs->tap_released = 0;\n\ttimerclear(&gs->tap_timeout);\n\n\tforeach_bit(i, ms->touch_used) {\n\t\tCLEARBIT(ms->touch[i].flags, MT_TAP);\n\t}\n}\n\n#ifdef DEBUG_GESTURES\n#define LOG_TAP LOG_INFO\n#else\n#define LOG_TAP(...)\n#endif\n\nstatic void tapping_update(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tstruct MTState* ms)\n{\n\tint i, dist;\n\tstruct timeval tv_tmp;\n\tstruct Touch* iTouch;\n\tint final_touch_count;\n\tint button;\n\n\tfinal_touch_count = 0;\n\n\tif (cfg->trackpad_disable >= 1)\n\t\treturn;\n\n\t/* Check conditions for early exit - posibilities to break active tap */\n\tif (!isepochtime(&gs->tap_timeout)){ /* Tap was started, check exit conditions */\n\t\tif (timercmp(&gs->time, &gs->tap_timeout, >=)) {\n\t\t\t/* too much time passed by from first touch, stop waiting for incoming touches */\n\t\t\tabort_tapping(gs, ms);\n\t\t\tLOG_TAP(\"tapping_update: break: too slow; !isepoch:%d, timercmp:%d\\n\", !isepochtime(&gs->tap_timeout), timercmp(&gs->time, &gs->tap_timeout, >=));\n\t\t\treturn;\n\t\t}\n\t\tforeach_bit(i, ms->touch_used) {\n\t\t\tiTouch = ms->touch + i;\n\t\t\tif (GETBIT(iTouch->flags, MT_TAP)) {\n\t\t\t\t/* Finger moved too far. */\n\t\t\t\tdist = dist2(iTouch->total_dx, iTouch->total_dy);\n\t\t\t\tif (dist >= SQRVAL(cfg->tap_dist)) {\n\t\t\t\t\tabort_tapping(gs, ms);\n\t\t\t\t\tLOG_TAP(\"tapping_update: break: too far\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (GETBIT(iTouch->flags, MT_INVALID) ||\n\t\t\t\t\tGETBIT(iTouch->flags, MT_BUTTON)){\n\t\t\t\t/* Invalid or physical button */\n\t\t\t\tabort_tapping(gs, ms);\n\t\t\t\tLOG_TAP(\"tapping_update: break: invalid or button\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/* If there is touch with any other flag than NEW, TAP, or RELEASED,\n\t\t\t * tap will be discontinued\n\t\t\t */\n\t\t\tif (!(GETBIT(iTouch->flags, MT_NEW)||\n\t\t\t\t\t\tGETBIT(iTouch->flags, MT_TAP)||\n\t\t\t\t\t\tGETBIT(iTouch->flags, MT_RELEASED)\n\t\t\t\t\t\t)\n\t\t\t){\n\t\t\t\tabort_tapping(gs, ms);\n\t\t\t\tLOG_TAP(\"tapping_update: break: not NEW, TAP, or RELEASED\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Conditions satisfied, check for new touches/releases\n\t * All touches are either in NEW, TAP, or RELEASED state.\n\t */\n\tforeach_bit(i, ms->touch_used) {\n\t\tiTouch = ms->touch + i;\n\n\t\tif (GETBIT(iTouch->flags, MT_NEW)) { /* New touch is coming */\n\t\t\tgs->tap_touching += 1;\n\t\t\tSETBIT(iTouch->flags, MT_TAP);\n\t\t\tif(gs->tap_touching == 1){ /* That was first touch, start timer */\n\t\t\t\tLOG_TAP(\"tapping_update: start new tap; timeout=%d\\n\", cfg->tap_timeout);\n\t\t\t\ttimeraddms(&gs->time, cfg->tap_timeout, &gs->tap_timeout);\n\t\t\t}\n\t\t}\n\n\t\tif (GETBIT(iTouch->flags, MT_RELEASED)) {\n\t\t\tgs->tap_touching -= 1;\n\t\t\tif(gs->tap_touching == 0){ /* Last finger released, time for decision */\n\t\t\t\tfinal_touch_count = MAXVAL(gs->tap_released, gs->tap_touching + 1); /* At least one */\n\t\t\t}else if(gs->tap_touching > 0){\n\t\t\t\t/* Store how many fingers where down at tap's peak */\n\t\t\t\tgs->tap_released = MAXVAL(gs->tap_released, gs->tap_touching + 1);\n\t\t\t}\n\t\t\telse{ /* gs->tap_touching is < 0 */\n\t\t\t\t/* That means finges(s) were down, while tap wasn't active and finger was released */\n\t\t\t\tgs->tap_touching = 0;\n\t\t\t\ttrigger_drag_stop(gs, cfg);\n\t\t\t\treturn; /* Pretty common situation; do nothing */\n\t\t\t}\n\t\t\tLOG_TAP(\"tapping_update: touch released; gs->tap_touching=%d, gs->tap_released=%d\\n\", gs->tap_touching, gs->tap_released);\n\t\t}\n\t}\n\n\tif(final_touch_count == 0){ /* Tap is still posible, it's just not finished yet */\n\t\treturn;\n\t}\n\n\tswitch(final_touch_count){\n\tcase 1: button = cfg->tap_1touch - 1; break;\n\tcase 2: button = cfg->tap_2touch - 1; break;\n\tcase 3: button = cfg->tap_3touch - 1; break;\n\tcase 4: button = cfg->tap_4touch - 1; break;\n\tdefault:\n\t\tLOG_TAP(\"tapping_update: Something went really bad; final_touch_count=%d\\n\", final_touch_count);\n\t\tbutton = cfg->tap_4touch - 1;\n\t}\n\n\ttimeraddms(&gs->time, cfg->tap_hold, &tv_tmp); /* How long button should be hold down */\n\ttrigger_button_click(gs, button, &tv_tmp);\n\tif (cfg->drag_enable && button == 0)\n\t\ttrigger_drag_ready(gs, cfg);\n\n\tgs->move_type = GS_NONE;\n\ttimeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);\n\tabort_tapping(gs, ms);\n}\n#undef LOG_TAP\n\nstatic void trigger_move(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tint dx, int dy)\n{\n\tif ((gs->move_type == GS_MOVE || timercmp(&gs->time, &gs->move_wait, >=)) && (dx != 0 || dy != 0)) {\n\t\tif (trigger_drag_start(gs, cfg, dx, dy)) {\n\t\t\tgs->move_dx = dx*cfg->sensitivity;\n\t\t\tgs->move_dy = dy*cfg->sensitivity;\n\t\t\tbreak_coasting(gs);\n\t\t\tgs->move_type = GS_MOVE;\n\t\t\tgs->move_dist = 0;\n\t\t\tgs->move_dir = TR_NONE;\n\t\t\ttimerclear(&gs->move_wait);\n\t\t\tLOG_DEBUG_GESTURES(\"trigger_move: %.2f, %.2f\\n\", gs->move_dx, gs->move_dy);\n\t\t}\n\t}\n}\n\nstatic double get_swipe_dir_n(const struct Touch* touches[DIM_TOUCHES], int count)\n{\n\tif(count > DIM_TOUCHES || count <= 0)\n\t\treturn TR_NONE;\n\tif(count == 1)\n\t\treturn touches[0]->direction;\n\n\tint i;\n\tdouble avg_dir;\n\tdouble angles[DIM_TOUCHES];\n\n\t/* Find average direction */\n\tfor (i = 0; i < count; ++i) {\n\t\tif (touches[i]->direction == TR_NONE)\n\t\t\treturn TR_NONE;\n\t\tangles[i] = touches[i]->direction;\n\t}\n\tavg_dir = trig_angles_avg(angles, count);\n\n\t/* Check if all touches have (almost) same direction */\n\tfor (i = 0; i < count; ++i) {\n\t\tif (trig_angles_acute(avg_dir, touches[i]->direction) > 0.5)\n\t\t\treturn TR_NONE;\n\t}\n\treturn avg_dir;\n}\n\nstatic void get_swipe_avg_xy(const struct Touch* touches[DIM_TOUCHES], int count, double* out_x, double* out_y){\n\tdouble x, y;\n\tx = y = 0.0;\n\tint i;\n\tfor (i = 0; i < count; ++i) {\n\t\tx += touches[i]->dx;\n\t\ty += touches[i]->dy;\n\t}\n\t*out_x = x/(double)count;\n\t*out_y = y/(double)count;\n}\n\n/* To avoid users' confusion accept diagonal direction only if same button was\n * bound to aligned directions.\n * If different buttons were used and it's diagonal movement we can't decide\n * which button schould be generated.\n * It's better to ignore one gesture than confuse user.\n */\nint get_button_for_dir(const struct MConfigSwipe* cfg_swipe, int dir){\n\tswitch (dir){\n\tcase TR_NONE:\n\t\tif(cfg_swipe->up_btn == cfg_swipe->lt_btn && cfg_swipe->lt_btn == cfg_swipe->dn_btn && cfg_swipe->dn_btn == cfg_swipe->rt_btn)\n\t\t\treturn cfg_swipe->up_btn;\n\t\treturn -1;\n\tcase TR_DIR_UP:\n\t\treturn cfg_swipe->up_btn;\n\tcase TR_DIR_DN:\n\t\treturn cfg_swipe->dn_btn;\n\tcase TR_DIR_LT:\n\t\treturn cfg_swipe->lt_btn;\n\tcase TR_DIR_RT:\n\t\treturn cfg_swipe->rt_btn;\n\tcase (8 + TR_DIR_UP + TR_DIR_LT) / 2:\n\t\tif(cfg_swipe->up_btn != cfg_swipe->lt_btn)\n\t\t\treturn -1;\n\t\treturn cfg_swipe->up_btn;\n\tcase (TR_DIR_LT + TR_DIR_DN) / 2:\n\t\tif(cfg_swipe->lt_btn != cfg_swipe->dn_btn)\n\t\t\treturn -1;\n\t\treturn cfg_swipe->lt_btn;\n\tcase (TR_DIR_DN + TR_DIR_RT) / 2:\n\t\tif(cfg_swipe->dn_btn != cfg_swipe->rt_btn)\n\t\t\treturn -1;\n\t\treturn cfg_swipe->dn_btn;\n\tcase (TR_DIR_RT + TR_DIR_UP) / 2:\n\t\tif(cfg_swipe->rt_btn != cfg_swipe->up_btn)\n\t\t\treturn -1;\n\t\treturn cfg_swipe->rt_btn;\n\tdefault:\n\t\treturn -1;\n\t}\n}\n\n/* It's called 'unsafe' because caller have to check that all conditions required to\n * trigger swipe were met.\n */\nstatic int trigger_swipe_unsafe(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg, const struct MConfigSwipe* cfg_swipe,\n\t\t\tconst struct Touch* touches[4], int touches_count,\n\t\t\tint move_type_to_trigger)\n{\n\tdouble avg_move_x, avg_move_y;\n\tint button;\n\tdouble dist;\n\tint dir;\n\tstruct timeval tv_tmp;\n\n\tif (touches_count <= 0)\n\t\treturn 0;\n\t/** Is that kind of scroll enabled? */\n\tif (!cfg->scroll_smooth && cfg_swipe->dist <= 0)\n\t\treturn 0;\n\n\tdir = trig_generalize(get_swipe_dir_n(touches, touches_count));\n\tbutton = get_button_for_dir(cfg_swipe, dir);\n\tif(button == -1){\n\t\t/* No button? Probably fingers were still down,\n\t\t * but without movement.\n\t\t */\n\t\treturn 0;\n\t}\n\n\ttrigger_drag_stop(gs, cfg);\n\tget_swipe_avg_xy(touches, touches_count, &avg_move_x, &avg_move_y);\n\t// hypot(1/n * (x0 + ... + xn); 1/n * (y0 + ... + yn)) <=> 1/n * hypot(x0 + ... + xn; y0 + ... + yn)\n\tdist = hypot(avg_move_x, avg_move_y);\n\tif(cfg_swipe->drag_sens){\n\t\tgs->move_dx = cfg->sensitivity * avg_move_x * cfg_swipe->drag_sens * 0.001;\n\t\tgs->move_dy = cfg->sensitivity * avg_move_y * cfg_swipe->drag_sens * 0.001;\n\t} else{\n\t\tgs->move_dx = gs->move_dy = 0.0;\n\t}\n\tif (gs->move_type != move_type_to_trigger){\n\t\ttrigger_delayed_button_uncond(gs);\n\t\tgs->move_dist = 0;\n\t}\n\telse if (gs->move_dir != dir){\n\t\tgs->move_dist = 0;\n\t}\n\tgs->move_type = move_type_to_trigger;\n\tgs->move_dist += ABSVAL(dist);\n\tgs->move_dir = dir;\n\ttimeraddms(&gs->time, cfg->gesture_wait + 5 /*bonus from me*/, &gs->move_wait);\n\n\t/* Special case for smooth scrolling */\n\tif(cfg->scroll_smooth && button >= 4 && button <= 7){\n\t\t/* Calculate speed vector */\n\n\t\t/* Forbid perpendicular movement: this pre-set will disable h-scroll\n\t\t * while v-scroll is active. See Github #19 */\n\t\tgs->scroll_speed_x = 0.0;\n\t\tgs->scroll_speed_y = 0.0;\n\t\t/* Map selected _button_ (instead of dir) to scroll axis */\n\t\t/* Detect 'natural scrolling' - situation when user fipped scroll up and\n\t\t * down buttons. Sign of speed vactor have to match selected button.\n\t\t */\n\t\tswitch(button){\n\t\t\tcase 4: gs->scroll_speed_y = -ABSVAL(avg_move_y); break;  /* scroll up - always negative */\n\t\t\tcase 5: gs->scroll_speed_y = ABSVAL(avg_move_y); break;   /* scroll down - always positve */\n\t\t\tcase 6: gs->scroll_speed_x = -ABSVAL(avg_move_x); break;  /* scroll left */\n\t\t\tcase 7: gs->scroll_speed_x = ABSVAL(avg_move_x); break;   /* scroll right */\n\t\t}\n\t\t/* Valuator increment=1.0 so I can scale by dist manually here. */\n\t\t/* Delta component will be kept in scroll_speed. */\n\t\tgs->scroll_speed_x = gs->scroll_speed_x / (double)(cfg_swipe->dist /* * timertoms(&gs->dt) */);\n\t\tgs->scroll_speed_y = gs->scroll_speed_y / (double)(cfg_swipe->dist /* * timertoms(&gs->dt) */);\n\n\t\tgs->scroll_speed_valid = 1;\n\t\tLOG_INFO2(DISABLED, \"smooth scrolling: speed: x: %lf, y: %lf\\n\", gs->scroll_speed_x, gs->scroll_speed_y);\n\n\t\tif (cfg->scroll_coast.no_boost)\n\t\t\t/* Prevent scroll coasting boost */\n\t\t\tgs->coasting_duration_left = 0;\n\t\telse\n\t\t\t/* Reset coasting duration 'to go' ticks. */\n\t\t\tgs->coasting_duration_left = cfg->scroll_coast.duration - 1;\n\n\t\t/* Don't modulo move_dist */\n\t}\n\telse if (gs->move_dist >= cfg_swipe->dist) {\n\t\tif(cfg_swipe->hold != 0)\n\t\t\ttimeraddms(&gs->time, cfg_swipe->hold, &tv_tmp);\n\t\telse\n\t\t\ttimerclear(&tv_tmp); // wait for gesture end\n\t\tgs->move_dist = MODVAL(gs->move_dist, cfg_swipe->dist);\n\t\ttrigger_button_click(gs, button - 1, &tv_tmp);\n\t}\n\tLOG_DEBUG_GESTURES(\"trigger_swipe_button: swiping %f in direction %d (at %f of %d)\\n\",\n\t\tdist, dir, gs->move_dist, cfg_swipe->dist);\n\treturn 1;\n}\n\n/* Return:\n *  0 - it wasn't swipe\n *  1 - it was swipe and was executed\n *  other value - it was swipe, but couldn't be executed\n */\nstatic int trigger_swipe(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg, const struct Touch* touches[4], int touches_count)\n{\n\tint move_type_to_trigger;\n\tconst struct MConfigSwipe* cfg_swipe;\n\t/* Feature: allow transition from low order swipes into higher ones.\n\t * Motivation: it's extremly hard to start fast swipe3 or swipe4 without triggering\n\t * swipe2 earlier.\n\t */\n\tint can_transit_swipe;\n\n\tcan_transit_swipe = FALSE;\n\n\tswitch(touches_count){\n\tcase 2:\n\t\tcfg_swipe = &cfg->scroll;\n\t\tmove_type_to_trigger = GS_SCROLL;\n\t\tbreak;\n\tcase 3:\n\t\tcfg_swipe = &cfg->swipe3;\n\t\tmove_type_to_trigger = GS_SWIPE3;\n\t\tcan_transit_swipe = gs->move_type == GS_SCROLL;\n\t\tbreak;\n\tcase 4:\n\t\tcfg_swipe = &cfg->swipe4;\n\t\tmove_type_to_trigger = GS_SWIPE4;\n\t\tcan_transit_swipe = gs->move_type == GS_SCROLL || gs->move_type == GS_SWIPE3;\n\t\tbreak;\n\tdefault:\n\t\tgoto not_a_swipe;\n\t}\n\n\tif (can_change_gesture_type(gs, move_type_to_trigger) || can_transit_swipe != FALSE){\n\t\tif (trigger_swipe_unsafe(gs, cfg, cfg_swipe, touches, touches_count, move_type_to_trigger))\n\t\t\treturn 1;\n\t\tgoto not_a_swipe;\n\t}\n\n\tnot_a_swipe:{\n\t\t//gs->scroll_speed_x = gs->scroll_speed_y = 0.0;\n\t\t//if(is_any_swipe(gs->move_type)){\n\t\t//\tgs->move_type = GS_NONE;\n\t\t//}\n\t\treturn 0;\n\t}\n}\n\n/* Return:\n *  0 - it wasn't swipe\n *  1 - it was swipe and was executed\n *  other value - it was swipe, but couldn't be executed\n */\nstatic int trigger_edge(struct Gestures* gs, const struct MConfig* cfg,\n                        const struct Touch* touch)\n{\n\tint dir = TR_NONE;\n\tint edge = touch_which_edge(cfg, touch);\n\n\tswitch (edge){\n\t\tcase 5:\n\t\tcase 7: case 8: case 9: /* Always disable scrolling for bottom edge. */\n\t\t\treturn 0;\n\t}\n\n\tif (GETBIT(touch->flags, MT_EDGE) == 0)\n\t\treturn 0;\n\n\tdir = trig_generalize(touch->direction);\n\tif (dir == TR_NONE)\n\t\treturn 0;\n\treturn trigger_swipe_unsafe(gs, cfg, &cfg->edge_scroll, &touch, 1, GS_SCROLL);\n}\n\n/* Compute hypot from x, y and compare it with given value\n */\nstatic int hypot_cmp(int x, int y, int value)\n{\n\tint lhs, rhs;\n\tlhs = x * x + y * y;\n\trhs = value * value;\n\n\tif(lhs == rhs)\n\t\treturn 0;\n\tif(lhs < rhs)\n\t\treturn -1;\n\treturn 1;\n}\n\n/* Compute hypot from x, y and compare it with given value\n */\nint hypot_cmpf(double x, double y, double value)\n{\n\tdouble lhs, rhs;\n\tlhs = x * x + y * y;\n\trhs = value * value;\n\n\tif(lhs == rhs)\n\t\treturn 0;\n\tif(lhs < rhs)\n\t\treturn -1;\n\treturn 1;\n}\n\nstatic int is_touch_stationary(const struct Touch* touch, int max_movement)\n{\n\treturn touch->direction == TR_NONE ||\n\t\t\t\t(hypot_cmp(touch->total_dx, touch->total_dy, max_movement) <= 0);\n}\n\nstatic int can_trigger_hold_move(const struct Gestures* gs,\n\t\t\t\tconst struct Touch* touches[DIM_TOUCHES], int touches_count,\n\t\t\t\tconst struct MConfig* cfg, int max_move)\n{\n\tstruct timeval tv_tmp;\n\n\tif (touches_count <= 1)\n\t\treturn 0;\n\n\t/* Condition: allow only translation from 'neutral' move type. */\n\tif(gs->move_type != GS_NONE && gs->move_type != GS_MOVE)\n\t\treturn 0;\n\n\t/* Conditions: was first finger hold in place for some time */\n\tif (!is_touch_stationary(touches[0], max_move))\n\t\treturn 0;\n\t/* todo: make this time configurable */\n\ttimeraddms(&touches[0]->down, MAXVAL(cfg->tap_timeout * 1.2, 350), &tv_tmp);\n\tif (timercmp(&gs->time, &tv_tmp, <)) /* time < down + wait ?*/\n\t\treturn 0;\n\n\tif (timercmp(&gs->time, &gs->move_wait, <))\n\t\treturn 0;\n\n\t/* Condition: are other fingers making swipe gesture */\n\tif (get_swipe_dir_n(touches+1, touches_count-1) == TR_NONE)\n\t\treturn 0;\n\n\treturn 1;\n}\n\n/* Map hold-and-move gesture type to touches */\nstatic int hold_move_gesture_to_touches(int move_type, int real_touches_count){\n\tswitch (move_type){\n\tcase GS_HOLD1_MOVE1:\n\t\treturn 2;\n\tcase GS_HOLD1_MOVE2:\n\t\treturn 3;\n\tcase GS_HOLD1_MOVE3:\n\t\treturn 4;\n\t}\n\treturn real_touches_count;\n}\n\nstatic int is_hold_move(struct Gestures* gs)\n{\n\tstatic int invalid_mark = -1;\n\treturn hold_move_gesture_to_touches(gs->move_type, invalid_mark) != invalid_mark;\n}\n\n/* Right now only gesture with one stationary finger and one moving finger is supported.\n * Gestures with two or more stationary fingers or two or more moving fingers\n * are not implemented, but I guess it will be easy to extend this function to support\n * them.\n */\nstatic int trigger_hold_move(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg, const struct Touch* touches[DIM_TOUCHES], int touches_count)\n{\n\tint move_type_to_trigger;\n\tconst struct MConfigSwipe* cfg_swipe;\n\tint stationary_max_move, stationary_btn;\n\n\t/* At the moment there's only one Hold1Move* gesture, so this line is fine,\n\t * but after addition of other similar gestures it will become invalid, since we\n\t * can't initialize stationary button yet (wrom which cfg it should be read?)\n\t */\n\tstationary_btn = cfg->hold1_move1_stationary.button - 1;\n\tmove_type_to_trigger = -1;\n\n\t/* Additional code for future features like hold1move{2,3} */\n\tswitch(hold_move_gesture_to_touches(gs->move_type, touches_count)){\n\t\tcase 0:\n\t\tcase 1:\n\t\t\t/* Process them later. */\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tmove_type_to_trigger = GS_HOLD1_MOVE1;\n\t\t\tstationary_btn = cfg->hold1_move1_stationary.button - 1;\n\t\t\tstationary_max_move = cfg->hold1_move1_stationary.max_move;\n\t\t\tcfg_swipe = &cfg->hold1_move1;\n\t\t\tbreak;\n#if 0\n\t\tcase 3:\n\t\t\tmove_type_to_trigger = GS_HOLD1_MOVE2;\n\t\t\tstationary_btn = cfg->hold1_move2_stationary.button - 1;\n\t\t\tstationary_max_move = cfg->hold1_move2_stationary.max_move;\n\t\t\tcfg_swipe = &cfg->hold1_move2;\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tmove_type_to_trigger = GS_HOLD1_MOVE3;\n\t\t\tstationary_btn = cfg->hold1_move3_stationary.button - 1;\n\t\t\tstationary_max_move = cfg->hold1_move3_stationary.max_move;\n\t\t\tcfg_swipe = &cfg->hold1_move3;\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\tif (stationary_btn < 0){ /* feature disabled, reurn immediately */\n\t\treturn 0;\n\t}\n\tif (is_hold_move(gs)){\n\t\t/* Condition: no fingers or stationary just released or stationary moved */\n\t\tif (touches_count == 0 ||\n\t\t\t\tGETBIT(touches[0]->flags, MT_RELEASED) ||\n\t\t\t\t!is_touch_stationary(touches[0], stationary_max_move)){\n\t\t\t/* Stationary finger released or moved too far */\n\t\t\tgs->move_type = GS_NONE;\n\t\t\ttrigger_delayed_button_uncond(gs);\n\t\t\ttrigger_button_up(gs, stationary_btn);\n\t\t\treturn 1;\n\t\t}\n\t\telse if (touches_count == 1){\n\t\t\t/* Only one finger is touching, it's stationary.\n\t\t\t * The gesture was initiated earlier.\n\t\t\t * Block other actions/movements.\n\t\t\t */\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tif (touches_count <= 1 || move_type_to_trigger == -1)\n\t\treturn 0;\n\n\tif (gs->move_type == move_type_to_trigger){\n\t\treturn trigger_swipe_unsafe(gs, cfg, cfg_swipe, touches + 1, touches_count - 1,\n\t\t\t\t\t\tmove_type_to_trigger);\n\t}\n\telse if (can_trigger_hold_move(gs, touches, touches_count, cfg, stationary_max_move)){\n\t\ttrigger_button_down(gs, stationary_btn);\n\t\treturn trigger_swipe_unsafe(gs, cfg, cfg_swipe, touches + 1, touches_count - 1,\n\t\t\t\t\t\tmove_type_to_trigger);\n\t}\n\n\treturn 0;\n}\n\n#if defined(DEBUG_GESTURES)\n#define LOG_SCALE LOG_INFO\n#else\n#define LOG_SCALE(...)\n#endif\n\n\n#define ZOOM_IN TR_DIR_UP\n#define ZOOM_OUT TR_DIR_DN\n#define ZOOM_UNKNOWN TR_NONE\nstatic int calc_scale_dir(const struct Touch* t0, const struct Touch* t1){\n\tint dist_sq, dist_prev_sq;\n\tint x0_prev, y0_prev;\n\tint x1_prev, y1_prev;\n\n\tx0_prev = t0->x - t0->dx;\n\ty0_prev = t0->y - t0->dy;\n\tx1_prev = t1->x - t1->dx;\n\ty1_prev = t1->y - t1->dy;\n\n\tdist_sq = dist2(t0->x - t1->x, t0->y - t1->y);\n\tdist_prev_sq = dist2(x0_prev - x1_prev, y0_prev - y1_prev);\n\n\n\tif(dist_prev_sq == dist_sq)\n\t\treturn ZOOM_UNKNOWN;\n\treturn dist_sq > dist_prev_sq ? ZOOM_IN : ZOOM_OUT;\n}\n\nstatic int trigger_scale(struct Gestures* gs, const struct MConfig* cfg,\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst struct Touch* t0, const struct Touch* t1){\n\tdouble d0, d1, angles_diff, max_error;\n\tint dir;\n\tint dist;\n\n\tmax_error = 45; /* = sum of difference of directions, in degrees  */\n\tmax_error = max_error * 8.0/360.0; /* Convert degrees to our from 0 to 8 angle system */\n\tdir = TR_NONE;\n\n\tif (gs->move_type != GS_SCALE && timercmp(&gs->time, &gs->move_wait, <)) {\n\t\treturn 0;\n\t}\n\n\td0 = t0->direction;\n\td1 = t1->direction;\n\n\tif(d0 == TR_NONE && d1 == TR_NONE){\n\t\tLOG_SCALE(\"trigger_scale: no directions\\n\");\n\t\treturn 0;\n\t}\n\t/* Handle scaling with one finger */\n\tif(d0 == TR_NONE || d1 == TR_NONE){\n\t\tLOG_SCALE(\"trigger_scale: scaling with one finger\\n\");\n\t\treturn 0;\n\t}\n\n\t/* Check if both vectors are on more or less same axis, but with different directions: */\n\tangles_diff = trig_angles_acute(d0, d1); /* less eq 4.0 */\n\tangles_diff = trig_angles_sub(4.0, angles_diff);\n\tif(angles_diff > max_error){\n\t\tLOG_SCALE(\"trigger_scale: movement foo tar from scaling axis, diff: %lf, max_error: %lf\\n\", angles_diff, max_error);\n\t\treturn 0;\n\t}\n\n\tdist = dist2(t0->dx,t0->dy) + dist2(t1->dx, t1->dy);\n\t/* Determinate is it zoom in or zoom out */\n\tdir = calc_scale_dir(t0, t1);\n\n\tstruct timeval tv_tmp;\n\ttrigger_drag_stop(gs, cfg);\n\tif (gs->move_type != GS_SCALE || gs->move_dir != dir)\n\t\tgs->move_dist = 0;\n\tgs->move_dx = gs->move_dy = 0.0;\n\tgs->move_type = GS_SCALE;\n\tgs->move_dist += ABSVAL(dist);\n\tgs->move_dir = dir;\n\ttimeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);\n\tif (gs->move_dist >= cfg->scale_dist) {\n\t\tgs->move_dist = MODVAL(gs->move_dist, cfg->scale_dist);\n\t\ttimeraddms(&gs->time, cfg->gesture_hold, &tv_tmp);\n\t\tif (dir == ZOOM_IN)\n\t\t\ttrigger_button_click(gs, cfg->scale_up_btn - 1, &tv_tmp);\n\t\telse if (dir == ZOOM_OUT)\n\t\t\ttrigger_button_click(gs, cfg->scale_dn_btn - 1, &tv_tmp);\n\t}\n\n\treturn 1;\n}\n#undef ZOOM_IN\n#undef ZOOM_OUT\n#undef ZOOM_UNKNOWN\n\nstatic void trigger_rotate(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tdouble dist, int dir)\n{\n\tif (gs->move_type == GS_ROTATE || !timercmp(&gs->time, &gs->move_wait, <)) {\n\t\tstruct timeval tv_tmp;\n\t\ttrigger_drag_stop(gs, cfg);\n\t\tif (gs->move_type != GS_ROTATE || gs->move_dir != dir)\n\t\t\tgs->move_dist = 0;\n\t\tgs->move_dx = 0.0;\n\t\tgs->move_dy = 0.0;\n\t\tgs->move_type = GS_ROTATE;\n\t\tgs->move_dist += ABSVAL(dist);\n\t\tgs->move_dir = dir;\n\t\ttimeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);\n\t\tif (gs->move_dist >= cfg->rotate_dist) {\n\t\t\tgs->move_dist = MODVAL(gs->move_dist, cfg->rotate_dist);\n\t\t\ttimeraddms(&gs->time, cfg->gesture_hold, &tv_tmp);\n\t\t\tif (dir == TR_DIR_LT)\n\t\t\t\ttrigger_button_click(gs, cfg->rotate_lt_btn - 1, &tv_tmp);\n\t\t\telse if (dir == TR_DIR_RT)\n\t\t\t\ttrigger_button_click(gs, cfg->rotate_rt_btn - 1, &tv_tmp);\n\t\t}\n\t\tLOG_DEBUG_GESTURES(\"trigger_rotate: rotating %f in direction %d (at %f of %d)\\n\",\n\t\t\tdist, dir, gs->move_dist, cfg->rotate_dist);\n\t}\n}\n\nstatic void trigger_reset(struct Gestures* gs)\n{\n\treset_drag(gs);\n\tgs->move_dx = gs->move_dy = 0.0;\n\t/* Don't\treset scroll speed cuz it may break things. */\n\tgs->move_type = GS_NONE;\n\tgs->move_dist = 0;\n\tgs->move_dir = TR_NONE;\n\ttimerclear(&gs->move_wait);\n}\n\nstatic int get_rotate_dir(const struct Touch* t1,\n\t\t\tconst struct Touch* t2)\n{\n\tdouble v, d1, d2;\n\tv = trig_direction(t2->x - t1->x, t2->y - t1->y);\n\td1 = trig_angles_add(v, 2);\n\td2 = trig_angles_sub(v, 2);\n\tif (trig_angles_acute(t1->direction, d1) < 2 && trig_angles_acute(t2->direction, d2) < 2)\n\t\treturn TR_DIR_RT;\n\telse if (trig_angles_acute(t1->direction, d2) < 2 && trig_angles_acute(t2->direction, d1) < 2)\n\t\treturn TR_DIR_LT;\n\treturn TR_NONE;\n}\n\nstatic void moving_update(struct Gestures* gs,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tstruct MTState* ms)\n{\n\tint i, count, btn_count, dx, dy, dir;\n\tdouble dist;\n\tconst struct Touch* touches[DIM_TOUCHES];\n\tconst struct Touch* edge_touch;\n\n\tedge_touch = NULL;\n\tcount = btn_count = 0;\n\tdx = dy = 0;\n\tdir = 0;\n\n\t// Reset movement.\n\tgs->move_dx = gs->move_dy = 0.0;\n\n\t// Count touches and aggregate touch movements.\n\tforeach_bit(i, ms->touch_used) {\n\t\tif (GETBIT(ms->touch[i].flags, MT_INVALID)){\n\t\t\tif (GETBIT(ms->touch[i].flags, MT_EDGE))\n\t\t\t\tedge_touch = &ms->touch[i]; // only valid when count == 1, so could be overwritten here\n\t\t\tcontinue;\n\t\t}\n\t\telse if (GETBIT(ms->touch[i].flags, MT_BUTTON)) {\n\t\t\tbtn_count++;\n\t\t\tdx += ms->touch[i].dx;\n\t\t\tdy += ms->touch[i].dy;\n\t\t}\n\t\telse if (!GETBIT(ms->touch[i].flags, MT_TAP)) {\n\t\t\tif (count < DIM_TOUCHES)\n\t\t\t\ttouches[count++] = &ms->touch[i];\n\t\t}\n\t}\n\n\t// Determine gesture type.\n\tif (cfg->trackpad_disable < 1 && trigger_hold_move(gs, cfg, touches, count)){\n\t\t/* nothing to do */\n\t}\n\telse if (count == 0) {\n\t\tif (btn_count >= 1 && cfg->trackpad_disable < 2)\n\t\t\ttrigger_move(gs, cfg, dx, dy);\n\t\t// one touch down, but count == 0 because it was not counted there\n\t\telse if(edge_touch != NULL && trigger_edge(gs, cfg, edge_touch) != 0){ }\n\t\telse if (btn_count < 1)\n\t\t\ttrigger_reset(gs);\n\t}\n\telse if (count == 1 && cfg->trackpad_disable < 2) {\n\t\tdx += touches[0]->dx;\n\t\tdy += touches[0]->dy;\n\t\ttrigger_move(gs, cfg, dx, dy);\n\t}\n\telse if (count == 2 && cfg->trackpad_disable < 1) {\n\t\t// scroll, scale, or rotate\n\t\tif (trigger_swipe(gs, cfg, touches, count)) {\n\t\t\t/* nothing to do */\n\t\t}\n\t\telse if ((dir = get_rotate_dir(touches[0], touches[1])) != TR_NONE) {\n\t\t\tdist = ABSVAL(hypot(touches[0]->dx, touches[0]->dy)) +\n\t\t\t\tABSVAL(hypot(touches[1]->dx, touches[1]->dy));\n\t\t\ttrigger_rotate(gs, cfg, dist/2, dir);\n\t\t}\n\t\telse if(trigger_scale(gs,cfg, touches[0], touches[1])){\n\t\t\t/* nothing to do */\n\t\t}\n\t}\n\telse if ((count == 3 || count == 4) && cfg->trackpad_disable < 1) {\n\t\tif (trigger_swipe(gs, cfg, touches, count)) {\n\t\t\t/* nothing to do */\n\t\t}\n\t}\n}\n\nstatic void dragging_update(struct Gestures* gs, const struct MConfig* cfg)\n{\n\tif (gs->drag_state == GS_DRAG_READY && timercmp(&gs->time, &gs->move_drag_expire, >)) {\n\t\tLOG_DEBUG_GESTURES(\"dragging_update: drag expired\\n\");\n\t\ttrigger_drag_stop(gs, cfg);\n\t}\n}\n\nstatic int is_timer_infinite(struct Gestures* gs){\n\treturn isepochtime(&gs->button_delayed_time);\n}\n\nstatic void delayed_update(struct Gestures* gs)\n{\n\t// if there's no delayed button - return\n\tif(!IS_VALID_BUTTON(gs->button_delayed))\n\t\treturn;\n\n\tif (!is_timer_infinite(gs) && timercmp(&gs->time, &gs->button_delayed_time, >=)) {\n\t\tLOG_DEBUG_GESTURES(\"delayed_update: %d delay expired, triggering up\\n\", gs->button_delayed);\n\t\ttrigger_delayed_button_uncond(gs);\n\t}\n\telse {\n#ifdef DEBUG_GESTURES\n\t\tstruct timeval delta;\n\t\ttimersub(&gs->button_delayed_time, &gs->time, &delta);\n\t\tLOG_DEBUG_GESTURES(\"delayed_update: %d still waiting, new delta %lld ms\\n\", gs->button_delayed, timertoms(&delta));\n#endif\n\t}\n}\n\nvoid gestures_init(struct MTouch* mt)\n{\n\tmemset(&mt->gs, 0, sizeof(struct Gestures));\n\ttimerclear(&mt->gs.tap_timeout);\n}\n\nvoid gestures_extract(struct MTouch* mt)\n{\n\ttimersub(&mt->hs.evtime, &mt->gs.time, &mt->gs.dt);\n\ttimercp(&mt->gs.time, &mt->hs.evtime);\n\n\tdragging_update(&mt->gs, &mt->cfg);\n\tbuttons_update(&mt->gs, &mt->cfg, &mt->hs, &mt->state);\n\ttapping_update(&mt->gs, &mt->cfg, &mt->state);\n\tmoving_update(&mt->gs, &mt->cfg, &mt->state);\n\tdelayed_update(&mt->gs);\n}\n\n/**\n *  Executed every input time frame, at least once. First time from 'read_input' to check if\n * timer is needed.\n * This function returns timer ID which should be installed/disabled(if negative).\n *\n * Return vale meaning:\n *  - 0 - no delay to handle, don't install timer, do nothing\n *  - MT_TIMER_* - install timer\n *  - -MT_TIMER_* - remove timer\n *  - MT_TIMER_ANY - remove any timer\n */\nint gestures_delayed(struct MTouch* mt)\n{\n\tstruct Gestures* gs = &mt->gs;\n\tstruct MTState* ms = &mt->state;\n\tstruct timeval now, delta;\n\tint i, fingers_released, fingers_down;\n\n\t// count released fingers\n\tfingers_released = fingers_down = 0;\n\tforeach_bit(i, ms->touch_used) {\n\t\tif (GETBIT(ms->touch[i].flags, MT_RELEASED))\n\t\t\t++fingers_released;\n\t\telse if (!GETBIT(ms->touch[i].flags, MT_INVALID))\n\t\t\t++fingers_down;\n\t}\n\n\t// if there's no delayed button - do nothing\n\tif(!IS_VALID_BUTTON(gs->button_delayed))\n\t\treturn MT_TIMER_NONE;\n\n\t/* Condition: was finger released and gesture is 'infinite' and it's not hold&move */\n\tif(fingers_released != 0 && is_timer_infinite(gs) && !is_hold_move(gs)){\n\t\t/* Gesture finished - it's time to send \"button up\" event immediately without\n\t\t * checking for delivery time.\n\t\t */\n\n\t\ttrigger_delayed_button_uncond(gs);\n\t\tgs->move_dx = gs->move_dy = 0.0;\n\t\tgs->move_type = GS_NONE;\n\t\treturn -MT_TIMER_DELAYED_BUTTON; /* remove delayed button timer */\n\t}\n\n\tif(is_timer_infinite(gs))\n\t\treturn MT_TIMER_NONE;\n\n\tmicrotime(&now);\n\t//timersub(&now, &mt->gs.time, &mt->gs.dt);\n\t//timercp(&mt->gs.time, &now);\n\n\tif(timercmp(&gs->button_delayed_time, &now, >)){\n\t\t timersub(&gs->button_delayed_time, &now, &delta);\n\t\t /* That second check may seem unnecessary, but it is not.\n\t\t  * Even if button delayed time is > than now time, timertoms may still return 0\n\t\t  * because it truncates time to miliseconds. It's important because truncated time\n\t\t  * is used to setup timer.\n\t\t  */\n\t\t if(timertoms(&delta) > 1){\n\t\t\tLOG_DEBUG_GESTURES(\"gestures_delayed: %d delayed, new delta: %lld ms\\n\", gs->button_delayed, timertoms(&delta));\n\n\t\t\treturn MT_TIMER_DELAYED_BUTTON;\n\t\t }\n\t}\n\n\ttrigger_delayed_button_uncond(gs);\n\tgs->move_dx = gs->move_dy = 0.0;\n\treturn -MT_TIMER_DELAYED_BUTTON; /* remove delayed button timer */\n}\n"
  },
  {
    "path": "src/hwstate.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"hwstate.h\"\n\nvoid hwstate_init(struct HWState *s, const struct Capabilities *caps)\n{\n\tint i;\n\tmemset(s, 0, sizeof(struct HWState));\n\tfor (i = 0; i < DIM_FINGER; i++)\n\t\ts->data[i].tracking_id = MT_ID_NULL;\n\ts->max_x = get_cap_xsize(caps);\n\ts->max_y = get_cap_ysize(caps);\n}\n\nstatic void finish_packet(struct HWState *s, const struct Capabilities *caps,\n\t\t\t  const struct input_event *syn)\n{\n\tint i;\n\tforeach_bit(i, s->used) {\n\t\tif (!caps->has_abs[MTDEV_TOUCH_MINOR])\n\t\t\ts->data[i].touch_minor = s->data[i].touch_major;\n\t\tif (!caps->has_abs[MTDEV_WIDTH_MINOR])\n\t\t\ts->data[i].width_minor = s->data[i].width_major;\n\t}\n\ttimercp(&s->evtime, &syn->time);\n}\n\n/*\n * This function will return:\n *  1 - when SYN_REPORT received\n *  0 - otherwise\n *\n * More on kernel input events:\n * https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt\n * https://www.kernel.org/doc/Documentation/input/event-codes.txt\n * http://linuxwacom.sourceforge.net/wiki/index.php/Kernel_Input_Event_Overview#Touchpad_Overview\n */\nstatic int read_event(struct HWState *s, const struct Capabilities *caps,\n                      const struct input_event *ev)\n{\n\tswitch (ev->type) {\n\tcase EV_SYN:\n\t\tswitch (ev->code) {\n\t\tcase SYN_REPORT:\n\t\t\tfinish_packet(s, caps, ev);\n\t\t\treturn 1;\n\t\t}\n\t\tbreak;\n\tcase EV_KEY:\n\t\tswitch (ev->code) {\n\t\tcase BTN_LEFT:\n\t\t\tMODBIT(s->button, MT_BUTTON_LEFT, ev->value);\n\t\t\tbreak;\n\t\tcase BTN_MIDDLE:\n\t\t\tMODBIT(s->button, MT_BUTTON_MIDDLE, ev->value);\n\t\t\tbreak;\n\t\tcase BTN_RIGHT:\n\t\t\tMODBIT(s->button, MT_BUTTON_RIGHT, ev->value);\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase EV_ABS:\n\t\tswitch (ev->code) {\n\t\tcase ABS_MT_SLOT:\n\t\t\tif (ev->value >= 0 && ev->value < DIM_FINGER)\n\t\t\t\ts->slot = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_TOUCH_MAJOR:\n\t\t\ts->data[s->slot].touch_major = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_TOUCH_MINOR:\n\t\t\ts->data[s->slot].touch_minor = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_WIDTH_MAJOR:\n\t\t\ts->data[s->slot].width_major = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_WIDTH_MINOR:\n\t\t\ts->data[s->slot].width_minor = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_ORIENTATION:\n\t\t\ts->data[s->slot].orientation = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_PRESSURE:\n\t\t\ts->data[s->slot].pressure = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_POSITION_X:\n\t\t\ts->data[s->slot].position_x = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_POSITION_Y:\n\t\t\ts->data[s->slot].position_y = ev->value;\n\t\t\tbreak;\n\t\tcase ABS_MT_TRACKING_ID:\n\t\t\ts->data[s->slot].tracking_id = ev->value;\n\t\t\tMODBIT(s->used, s->slot, ev->value != MT_ID_NULL);\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nint hwstate_modify(struct HWState *s, struct mtdev *dev, int fd,\n\t\t   const struct Capabilities *caps)\n{\n\tstruct input_event ev;\n\tint ret;\n\twhile ((ret = mtdev_get(dev, fd, &ev, 1)) > 0) {\n\t\tif (read_event(s, caps, &ev))\n\t\t\treturn 1;\n\t}\n\treturn ret;\n}\n\nint find_finger(const struct HWState *s, int tracking_id) {\n\tint i;\n\tforeach_bit(i, s->used) {\n\t\tif (s->data[i].tracking_id == tracking_id)\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\n"
  },
  {
    "path": "src/mconfig.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"mconfig.h\"\n\nvoid mconfig_defaults(struct MConfig* cfg)\n{\n\t// Configure MTState\n\tcfg->touch_down = DEFAULT_TOUCH_DOWN;\n\tcfg->touch_up = DEFAULT_TOUCH_UP;\n\tcfg->ignore_thumb = DEFAULT_IGNORE_THUMB;\n\tcfg->ignore_palm = DEFAULT_IGNORE_PALM;\n\tcfg->disable_on_palm = DEFAULT_DISABLE_ON_PALM;\n\tcfg->disable_on_thumb = DEFAULT_DISABLE_ON_THUMB;\n\tcfg->thumb_ratio = DEFAULT_THUMB_RATIO;\n\tcfg->thumb_size = DEFAULT_THUMB_SIZE;\n\tcfg->palm_size = DEFAULT_PALM_SIZE;\n\tcfg->edge_top_size = DEFAULT_EDGE_TOP_SIZE;\n\tcfg->edge_bottom_size = DEFAULT_EDGE_BOTTOM_SIZE;\n\tcfg->edge_left_size = DEFAULT_EDGE_LEFT_SIZE;\n\tcfg->edge_right_size = DEFAULT_EDGE_RIGHT_SIZE;\n\n\t// Configure Gestures\n\tcfg->trackpad_disable = DEFAULT_TRACKPAD_DISABLE;\n\tcfg->button_enable = DEFAULT_BUTTON_ENABLE;\n\tcfg->button_integrated = DEFAULT_BUTTON_INTEGRATED;\n\tcfg->button_expire = DEFAULT_BUTTON_EXPIRE;\n\tcfg->button_zones = DEFAULT_BUTTON_ZONES;\n\tcfg->is_button_zones_height_limited = DEFAULT_IS_BUTTON_ZONES_HEIGHT_LIMITED;\n\tcfg->button_first_zone = DEFAULT_BUTTON_FIRST_ZONE;\n\tcfg->button_second_zone = DEFAULT_BUTTON_SECOND_ZONE;\n\tcfg->button_third_zone = DEFAULT_BUTTON_THIRD_ZONE;\n\tcfg->button_0touch = DEFAULT_BUTTON_0TOUCH;\n\tcfg->button_1touch = DEFAULT_BUTTON_1TOUCH;\n\tcfg->button_2touch = DEFAULT_BUTTON_2TOUCH;\n\tcfg->button_3touch = DEFAULT_BUTTON_3TOUCH;\n\tcfg->tap_1touch = DEFAULT_TAP_1TOUCH;\n\tcfg->tap_2touch = DEFAULT_TAP_2TOUCH;\n\tcfg->tap_3touch = DEFAULT_TAP_3TOUCH;\n\tcfg->tap_4touch = DEFAULT_TAP_4TOUCH;\n\tcfg->tap_timeout = DEFAULT_TAP_TIMEOUT;\n\tcfg->tap_hold = DEFAULT_TAP_HOLD;\n\tcfg->tap_dist = DEFAULT_TAP_DIST;\n\tcfg->gesture_hold = DEFAULT_GESTURE_HOLD;\n\tcfg->gesture_wait = DEFAULT_GESTURE_WAIT;\n\tcfg->scroll.dist = DEFAULT_SCROLL_DIST;\n\tcfg->scroll.hold = DEFAULT_SCROLL_HOLD;\n\tcfg->scroll.drag_sens = DEFAULT_SWIPE_SENS;\n\tcfg->scroll.up_btn = DEFAULT_SCROLL_UP_BTN;\n\tcfg->scroll.dn_btn = DEFAULT_SCROLL_DN_BTN;\n\tcfg->scroll.lt_btn = DEFAULT_SCROLL_LT_BTN;\n\tcfg->scroll.rt_btn = DEFAULT_SCROLL_RT_BTN;\n\tcfg->scroll_coast.min_speed = DEFAULT_SCROLL_COAST_MIN_SPEED;\n\tcfg->scroll_coast.tick_ms = DEFAULT_SCROLL_COAST_TICK_MS;\n\tcfg->scroll_coast.duration = DEFAULT_SCROLL_COAST_DURATION;\n\tcfg->scroll_coast.no_boost = DEFAULT_SCROLL_COAST_NO_BOOST;\n\tcfg->scroll_coast.ease = DEFAULT_SCROLL_COAST_EASE;\n\tcfg->swipe3.dist = DEFAULT_SWIPE_DIST;\n\tcfg->swipe3.hold = DEFAULT_SWIPE_HOLD;\n\tcfg->swipe3.drag_sens = DEFAULT_SWIPE_SENS;\n\tcfg->swipe3.up_btn = DEFAULT_SWIPE_UP_BTN;\n\tcfg->swipe3.dn_btn = DEFAULT_SWIPE_DN_BTN;\n\tcfg->swipe3.lt_btn = DEFAULT_SWIPE_LT_BTN;\n\tcfg->swipe3.rt_btn = DEFAULT_SWIPE_RT_BTN;\n\tcfg->swipe4.dist = DEFAULT_SWIPE4_DIST;\n\tcfg->swipe4.hold = DEFAULT_SWIPE_HOLD;\n\tcfg->swipe4.drag_sens = DEFAULT_SWIPE_SENS;\n\tcfg->swipe4.up_btn = DEFAULT_SWIPE4_UP_BTN;\n\tcfg->swipe4.dn_btn = DEFAULT_SWIPE4_DN_BTN;\n\tcfg->swipe4.lt_btn = DEFAULT_SWIPE4_LT_BTN;\n\tcfg->swipe4.rt_btn = DEFAULT_SWIPE4_RT_BTN;\n\tcfg->edge_scroll.dist = DEFAULT_EDGE_SCROLL_DIST;\n\tcfg->edge_scroll.hold = DEFAULT_SCROLL_HOLD;\n\tcfg->edge_scroll.drag_sens = DEFAULT_SWIPE_SENS;\n\tcfg->edge_scroll.up_btn = DEFAULT_SCROLL_UP_BTN;\n\tcfg->edge_scroll.dn_btn = DEFAULT_SCROLL_DN_BTN;\n\tcfg->edge_scroll.lt_btn = DEFAULT_SCROLL_LT_BTN;\n\tcfg->edge_scroll.rt_btn = DEFAULT_SCROLL_RT_BTN;\n\tcfg->scale_dist = DEFAULT_SCALE_DIST;\n\tcfg->scale_up_btn = DEFAULT_SCALE_UP_BTN;\n\tcfg->scale_dn_btn = DEFAULT_SCALE_DN_BTN;\n\tcfg->rotate_dist = SQRVAL(DEFAULT_ROTATE_DIST);\n\tcfg->rotate_lt_btn = DEFAULT_ROTATE_LT_BTN;\n\tcfg->rotate_rt_btn = DEFAULT_ROTATE_RT_BTN;\n\n\tcfg->hold1_move1_stationary.max_move = DEFAULT_HOLD1_MOVE1_STATIONARY_MAX_MOVE;\n\tcfg->hold1_move1_stationary.button = DEFAULT_HOLD1_MOVE1_STATIONARY_BTN;\n\tcfg->hold1_move1.dist = DEFAULT_HOLD1_MOVE1_DIST;\n\tcfg->hold1_move1.hold = DEFAULT_HOLD1_MOVE1_HOLD;\n\tcfg->hold1_move1.drag_sens = DEFAULT_HOLD1_MOVE1_SENS;\n\tcfg->hold1_move1.up_btn = DEFAULT_HOLD1_MOVE1_BTN;\n\tcfg->hold1_move1.dn_btn = DEFAULT_HOLD1_MOVE1_BTN;\n\tcfg->hold1_move1.lt_btn = DEFAULT_HOLD1_MOVE1_BTN;\n\tcfg->hold1_move1.rt_btn = DEFAULT_HOLD1_MOVE1_BTN;\n\n#if 0\n\tcfg->hold1_move2_stationary.max_move = DEFAULT_HOLD1_MOVE2_STATIONARY_MAX_MOVE;\n\tcfg->hold1_move2_stationary.button = DEFAULT_HOLD1_MOVE2_STATIONARY_BTN;\n\tcfg->hold1_move2.dist = DEFAULT_HOLD1_MOVE2_DIST;\n\tcfg->hold1_move2.hold = DEFAULT_HOLD1_MOVE2_HOLD;\n\tcfg->hold1_move2.drag_sens = DEFAULT_HOLD1_MOVE2_SENS;\n\tcfg->hold1_move2.up_btn = DEFAULT_HOLD1_MOVE2_BTN;\n\tcfg->hold1_move2.dn_btn = DEFAULT_HOLD1_MOVE2_BTN;\n\tcfg->hold1_move2.lt_btn = DEFAULT_HOLD1_MOVE2_BTN;\n\tcfg->hold1_move2.rt_btn = DEFAULT_HOLD1_MOVE2_BTN;\n\n\tcfg->hold1_move3_stationary.max_move = DEFAULT_HOLD1_MOVE3_STATIONARY_MAX_MOVE;\n\tcfg->hold1_move3_stationary.button = DEFAULT_HOLD1_MOVE3_STATIONARY_BTN;\n\tcfg->hold1_move3.dist = DEFAULT_HOLD1_MOVE3_DIST;\n\tcfg->hold1_move3.hold = DEFAULT_HOLD1_MOVE3_HOLD;\n\tcfg->hold1_move3.drag_sens = DEFAULT_HOLD1_MOVE3_SENS;\n\tcfg->hold1_move3.up_btn = DEFAULT_HOLD1_MOVE3_BTN;\n\tcfg->hold1_move3.dn_btn = DEFAULT_HOLD1_MOVE3_BTN;\n\tcfg->hold1_move3.lt_btn = DEFAULT_HOLD1_MOVE3_BTN;\n\tcfg->hold1_move3.rt_btn = DEFAULT_HOLD1_MOVE3_BTN;\n#endif\n\n\tcfg->drag_enable = DEFAULT_DRAG_ENABLE;\n\tcfg->drag_timeout = DEFAULT_DRAG_TIMEOUT;\n\tcfg->sensitivity = DEFAULT_SENSITIVITY;\n\tcfg->scroll_smooth = DEFAULT_SCROLL_HIGH_PREC;\n}\n\nvoid mconfig_init(struct MConfig* cfg,\n\t\t\tconst struct Capabilities* caps)\n{\n\tcfg->touch_minor = caps->has_abs[MTDEV_TOUCH_MINOR];\n\tcfg->pad_width = get_cap_xsize(caps);\n\tcfg->pad_height = get_cap_ysize(caps);\n\n\tif (caps->has_abs[MTDEV_TOUCH_MAJOR] && caps->has_abs[MTDEV_WIDTH_MAJOR]) {\n\t\tcfg->touch_type = MCFG_SCALE;\n\t\tcfg->touch_min = caps->abs[MTDEV_TOUCH_MAJOR].minimum;\n\t\tcfg->touch_max = caps->abs[MTDEV_TOUCH_MAJOR].maximum;\n\t\tLOG_INFO(\"Touchpad supports regular and approaching touches.\\n\");\n\t\tLOG_INFO(\"  touch_min = %d, touch_max = %d\\n\", cfg->touch_min, cfg->touch_max);\n\t}\n\telse if (caps->has_abs[MTDEV_TOUCH_MAJOR] && caps->has_abs[MTDEV_PRESSURE]) {\n\t\tcfg->touch_min = caps->abs[MTDEV_TOUCH_MAJOR].minimum;\n\t\tcfg->touch_max = caps->abs[MTDEV_TOUCH_MAJOR].maximum;\n\t\tcfg->pressure_min = caps->abs[MTDEV_PRESSURE].minimum;\n\t\tcfg->pressure_max = caps->abs[MTDEV_PRESSURE].maximum;\n\t\t/* select source of the events basing on its resolution */\n\t\tif(cfg->pressure_max - cfg->pressure_min >= cfg->touch_max - cfg->touch_min)\n\t\t\tcfg->touch_type = MCFG_PRESSURE_SIZE;\n\t\telse\n\t\t\tcfg->touch_type = MCFG_SIZE_PRESSURE;\n\t\tLOG_INFO(\"Touchpad is pressure based, but supports regular touches also.\\n\");\n\t\tLOG_INFO(\"  touch_min = %d, touch_max = %d\\n\", cfg->touch_min, cfg->touch_max);\n\t\tLOG_INFO(\"  pressure_min = %d, pressure_max = %d\\n\", cfg->pressure_min, cfg->pressure_max);\n\t}\n\telse if (caps->has_abs[MTDEV_TOUCH_MAJOR]) {\n\t\tcfg->touch_type = MCFG_SIZE;\n\t\tcfg->touch_min = caps->abs[MTDEV_TOUCH_MAJOR].minimum;\n\t\tcfg->touch_max = caps->abs[MTDEV_TOUCH_MAJOR].maximum;\n\t\tLOG_INFO(\"Touchpad supports regular touches.\\n\");\n\t\tLOG_INFO(\"  touch_min = %d, touch_max = %d\\n\", cfg->touch_min, cfg->touch_max);\n\t}\n\telse if (caps->has_abs[MTDEV_PRESSURE]) {\n\t\tcfg->touch_type = MCFG_PRESSURE;\n\t\tcfg->pressure_min = caps->abs[MTDEV_PRESSURE].minimum;\n\t\tcfg->pressure_max = caps->abs[MTDEV_PRESSURE].maximum;\n\t\tLOG_INFO(\"Touchpad is pressure based.\\n\");\n\t\tLOG_INFO(\"  pressure_min = %d, pressure_max = %d\\n\", cfg->pressure_min, cfg->pressure_max);\n\t}\n\telse {\n\t\tcfg->touch_type = MCFG_NONE;\n\t\tLOG_WARNING(\"Touchpad has minimal capabilities. Some features will be unavailable.\\n\");\n\t}\n\n\tif (cfg->touch_minor)\n\t\tLOG_INFO(\"Touchpad supports minor touch widths.\\n\");\n}\n\nvoid mconfig_configure(struct MConfig* cfg,\n\t\t\tpointer opts)\n{\n\t// Configure MTState\n\tcfg->touch_down = CLAMPVAL(xf86SetIntOption(opts, \"FingerHigh\", DEFAULT_TOUCH_DOWN), 0, 100);\n\tcfg->touch_up = CLAMPVAL(xf86SetIntOption(opts, \"FingerLow\", DEFAULT_TOUCH_UP), 0, 100);\n\tcfg->ignore_thumb = xf86SetBoolOption(opts, \"IgnoreThumb\", DEFAULT_IGNORE_THUMB);\n\tcfg->ignore_palm = xf86SetBoolOption(opts, \"IgnorePalm\", DEFAULT_IGNORE_PALM);\n\tcfg->disable_on_thumb = xf86SetBoolOption(opts, \"DisableOnThumb\", DEFAULT_DISABLE_ON_THUMB);\n\tcfg->disable_on_palm = xf86SetBoolOption(opts, \"DisableOnPalm\", DEFAULT_DISABLE_ON_PALM);\n\tcfg->thumb_ratio = CLAMPVAL(xf86SetIntOption(opts, \"ThumbRatio\", DEFAULT_THUMB_RATIO), 0, 100);\n\tcfg->thumb_size = CLAMPVAL(xf86SetIntOption(opts, \"ThumbSize\", DEFAULT_THUMB_SIZE), 0, 100);\n\tcfg->palm_size = CLAMPVAL(xf86SetIntOption(opts, \"PalmSize\", DEFAULT_PALM_SIZE), 0, 100);\n\n\t// Configure Gestures\n\tcfg->trackpad_disable = CLAMPVAL(xf86SetIntOption(opts, \"TrackpadDisable\", DEFAULT_TRACKPAD_DISABLE), 0, 3);\n\tcfg->button_enable = xf86SetBoolOption(opts, \"ButtonEnable\", DEFAULT_BUTTON_ENABLE);\n\tcfg->button_integrated = xf86SetBoolOption(opts, \"ButtonIntegrated\", DEFAULT_BUTTON_INTEGRATED);\n\tcfg->button_expire = MAXVAL(xf86SetIntOption(opts, \"ButtonTouchExpire\", DEFAULT_BUTTON_EXPIRE), 0);\n\tcfg->button_zones = xf86SetBoolOption(opts, \"ButtonZonesEnable\", DEFAULT_BUTTON_ZONES);\n\tcfg->is_button_zones_height_limited = xf86SetBoolOption(opts, \"LimitButtonZonesToBottomEdge\", DEFAULT_IS_BUTTON_ZONES_HEIGHT_LIMITED);\n\tcfg->button_first_zone = CLAMPVAL(xf86SetIntOption(opts, \"FirstZoneButton\", DEFAULT_BUTTON_FIRST_ZONE), 0, 32);\n\tcfg->button_second_zone = CLAMPVAL(xf86SetIntOption(opts, \"SecondZoneButton\", DEFAULT_BUTTON_SECOND_ZONE), 0, 32);\n\tcfg->button_third_zone = CLAMPVAL(xf86SetIntOption(opts, \"ThirdZoneButton\", DEFAULT_BUTTON_THIRD_ZONE), 0, 32);\n\tcfg->button_0touch = CLAMPVAL(xf86SetIntOption(opts, \"ClickFinger0\", DEFAULT_BUTTON_0TOUCH), 0, 32);\n\tcfg->button_1touch = CLAMPVAL(xf86SetIntOption(opts, \"ClickFinger1\", DEFAULT_BUTTON_1TOUCH), 0, 32);\n\tcfg->button_2touch = CLAMPVAL(xf86SetIntOption(opts, \"ClickFinger2\", DEFAULT_BUTTON_2TOUCH), 0, 32);\n\tcfg->button_3touch = CLAMPVAL(xf86SetIntOption(opts, \"ClickFinger3\", DEFAULT_BUTTON_3TOUCH), 0, 32);\n\tcfg->button_move = xf86SetBoolOption(opts, \"ButtonMoveEmulate\", DEFAULT_BUTTON_MOVE);\n\tcfg->tap_1touch = CLAMPVAL(xf86SetIntOption(opts, \"TapButton1\", DEFAULT_TAP_1TOUCH), 0, 32);\n\tcfg->tap_2touch = CLAMPVAL(xf86SetIntOption(opts, \"TapButton2\", DEFAULT_TAP_2TOUCH), 0, 32);\n\tcfg->tap_3touch = CLAMPVAL(xf86SetIntOption(opts, \"TapButton3\", DEFAULT_TAP_3TOUCH), 0, 32);\n\tcfg->tap_4touch = CLAMPVAL(xf86SetIntOption(opts, \"TapButton4\", DEFAULT_TAP_4TOUCH), 0, 32);\n\tcfg->tap_hold = MAXVAL(xf86SetIntOption(opts, \"ClickTime\", DEFAULT_TAP_HOLD), 1);\n\tcfg->tap_timeout = MAXVAL(xf86SetIntOption(opts, \"MaxTapTime\", DEFAULT_TAP_TIMEOUT), 1);\n\tcfg->tap_dist = MAXVAL(xf86SetIntOption(opts, \"MaxTapMove\", DEFAULT_TAP_DIST), 1);\n\tcfg->gesture_hold = MAXVAL(xf86SetIntOption(opts, \"GestureClickTime\", DEFAULT_GESTURE_HOLD), 1);\n\tcfg->gesture_wait = MAXVAL(xf86SetIntOption(opts, \"GestureWaitTime\", DEFAULT_GESTURE_WAIT), 0);\n\tcfg->scroll_smooth = CLAMPVAL(xf86SetIntOption(opts, \"ScrollSmooth\", DEFAULT_SCROLL_HIGH_PREC), 0, 1);\n\tcfg->scroll.dist = MAXVAL(xf86SetIntOption(opts, \"ScrollDistance\", DEFAULT_SCROLL_DIST), 1);\n\tcfg->scroll.hold = MAXVAL(xf86SetIntOption(opts, \"ScrollClickTime\", DEFAULT_SCROLL_HOLD), 0);\n\tcfg->scroll.drag_sens = MAXVAL(xf86SetIntOption(opts, \"ScrollSensitivity\", DEFAULT_SWIPE_SENS), 0);\n\tcfg->scroll.up_btn = CLAMPVAL(xf86SetIntOption(opts, \"ScrollUpButton\", DEFAULT_SCROLL_UP_BTN), 0, 32);\n\tcfg->scroll.dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"ScrollDownButton\", DEFAULT_SCROLL_DN_BTN), 0, 32);\n\tcfg->scroll.lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"ScrollLeftButton\", DEFAULT_SCROLL_LT_BTN), 0, 32);\n\tcfg->scroll.rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"ScrollRightButton\", DEFAULT_SCROLL_RT_BTN), 0, 32);\n\tcfg->scroll_coast.min_speed = MAXVAL(xf86SetRealOption(opts, \"ScrollCoastEnableSpeed\", DEFAULT_SCROLL_COAST_MIN_SPEED), 0.0);\n\tcfg->scroll_coast.tick_ms = MAXVAL(DEFAULT_SCROLL_COAST_TICK_MS, 1);\n\tcfg->scroll_coast.duration = MAXVAL(xf86SetRealOption(opts, \"ScrollCoastDuration\", DEFAULT_SCROLL_COAST_DURATION), 0);\n\tcfg->scroll_coast.no_boost = xf86SetBoolOption(opts, \"ScrollCoastNoBoost\", DEFAULT_SCROLL_COAST_NO_BOOST);\n\tcfg->scroll_coast.ease = xf86SetBoolOption(opts, \"ScrollCoastEase\", DEFAULT_SCROLL_COAST_EASE);\n\tcfg->swipe3.dist = MAXVAL(xf86SetIntOption(opts, \"SwipeDistance\", DEFAULT_SWIPE_DIST), 1);\n\tcfg->swipe3.hold = MAXVAL(xf86SetIntOption(opts, \"SwipeClickTime\", DEFAULT_SWIPE_HOLD), 0);\n\tcfg->swipe3.drag_sens = MAXVAL(xf86SetIntOption(opts, \"SwipeSensitivity\", DEFAULT_SWIPE_SENS), 0);\n\tcfg->swipe3.up_btn = CLAMPVAL(xf86SetIntOption(opts, \"SwipeUpButton\", DEFAULT_SWIPE_UP_BTN), 0, 32);\n\tcfg->swipe3.dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"SwipeDownButton\", DEFAULT_SWIPE_DN_BTN), 0, 32);\n\tcfg->swipe3.lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"SwipeLeftButton\", DEFAULT_SWIPE_LT_BTN), 0, 32);\n\tcfg->swipe3.rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"SwipeRightButton\", DEFAULT_SWIPE_RT_BTN), 0, 32);\n\tcfg->swipe4.dist = MAXVAL(xf86SetIntOption(opts, \"Swipe4Distance\", DEFAULT_SWIPE4_DIST), 1);\n\tcfg->swipe4.hold = MAXVAL(xf86SetIntOption(opts, \"Swipe4ClickTime\", DEFAULT_SWIPE_HOLD), 0);\n\tcfg->swipe4.drag_sens = MAXVAL(xf86SetIntOption(opts, \"Swipe4Sensitivity\", DEFAULT_SWIPE_SENS), 0);\n\tcfg->swipe4.up_btn = CLAMPVAL(xf86SetIntOption(opts, \"Swipe4UpButton\", DEFAULT_SWIPE4_UP_BTN), 0, 32);\n\tcfg->swipe4.dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"Swipe4DownButton\", DEFAULT_SWIPE4_DN_BTN), 0, 32);\n\tcfg->swipe4.lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Swipe4LeftButton\", DEFAULT_SWIPE4_LT_BTN), 0, 32);\n\tcfg->swipe4.rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Swipe4RightButton\", DEFAULT_SWIPE4_RT_BTN), 0, 32);\n\tcfg->edge_scroll.dist = MAXVAL(xf86SetIntOption(opts, \"EdgeScrollDist\", DEFAULT_EDGE_SCROLL_DIST), 1);\n\tcfg->edge_scroll.hold = MAXVAL(xf86SetIntOption(opts, \"EdgeScrollClickTime\", DEFAULT_SCROLL_HOLD), 0);\n\tcfg->edge_scroll.drag_sens = MAXVAL(xf86SetIntOption(opts, \"EdgeScrollSensitivity\", DEFAULT_SWIPE_SENS), 0);\n\tcfg->edge_scroll.up_btn = CLAMPVAL(xf86SetIntOption(opts, \"EdgeScrollUpButton\", DEFAULT_SCROLL_UP_BTN), 0, 32);\n\tcfg->edge_scroll.dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"EdgeScrollDownButton\", DEFAULT_SCROLL_DN_BTN), 0, 32);\n\tcfg->edge_scroll.lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"EdgeScrollLeftButton\", DEFAULT_SCROLL_LT_BTN), 0, 32);\n\tcfg->edge_scroll.rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"EdgeScrollRightButton\", DEFAULT_SCROLL_RT_BTN), 0, 32);\n\tint edge_size =\tCLAMPVAL(xf86SetIntOption(opts, \"EdgeSize\", 101), 0, 101);\n\tif(edge_size < 0 || edge_size > 100){\n\t\tcfg->edge_top_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeTopSize\", DEFAULT_EDGE_TOP_SIZE), 0, 100);\n\t\tcfg->edge_bottom_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeBottomSize\", DEFAULT_EDGE_BOTTOM_SIZE), 0, 100);\n\t\tcfg->edge_left_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeLeftSize\", DEFAULT_EDGE_LEFT_SIZE), 0, 100);\n\t\tcfg->edge_right_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeRightSize\", DEFAULT_EDGE_RIGHT_SIZE), 0, 100);\n\t}else{\n\t\txf86Msg(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\");\n\t\tcfg->edge_top_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeTopSize\", edge_size), 0, 100);\n\t\tcfg->edge_bottom_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeBottomSize\", edge_size), 0, 100);\n\t\tcfg->edge_left_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeLeftSize\", edge_size), 0, 100);\n\t\tcfg->edge_right_size = CLAMPVAL(xf86SetIntOption(opts, \"EdgeRightSize\", edge_size), 0, 100);\n\t}\n\n\tcfg->scale_dist = MAXVAL(xf86SetIntOption(opts, \"ScaleDistance\", DEFAULT_SCALE_DIST), 1);\n\tcfg->scale_up_btn = CLAMPVAL(xf86SetIntOption(opts, \"ScaleUpButton\", DEFAULT_SCALE_UP_BTN), 0, 32);\n\tcfg->scale_dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"ScaleDownButton\", DEFAULT_SCALE_DN_BTN), 0, 32);\n\tcfg->rotate_dist = MAXVAL(xf86SetIntOption(opts, \"RotateDistance\", DEFAULT_ROTATE_DIST), 1);\n\tcfg->rotate_lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"RotateLeftButton\", DEFAULT_ROTATE_LT_BTN), 0, 32);\n\tcfg->rotate_rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"RotateRightButton\", DEFAULT_ROTATE_RT_BTN), 0, 32);\n\n\tcfg->hold1_move1_stationary.button = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move1StationaryButton\", DEFAULT_HOLD1_MOVE1_STATIONARY_BTN), 0, 32);\n\tcfg->hold1_move1_stationary.max_move = MAXVAL(xf86SetIntOption(opts, \"Hold1Move1StationaryMaxMove\", DEFAULT_HOLD1_MOVE1_STATIONARY_MAX_MOVE), 1);\n\tcfg->hold1_move1.dist = MAXVAL(xf86SetIntOption(opts, \"Hold1Move1Distance\", DEFAULT_HOLD1_MOVE1_DIST), 1);\n\tcfg->hold1_move1.hold = MAXVAL(xf86SetIntOption(opts, \"Hold1Move1ClickTime\", DEFAULT_HOLD1_MOVE1_HOLD), 0);\n\tcfg->hold1_move1.drag_sens = MAXVAL(xf86SetIntOption(opts, \"Hold1Move1Sensitivity\", DEFAULT_HOLD1_MOVE1_SENS), 0);\n\tcfg->hold1_move1.up_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move1UpButton\", DEFAULT_HOLD1_MOVE1_BTN), 0, 32);\n\tcfg->hold1_move1.dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move1DownButton\", DEFAULT_HOLD1_MOVE1_BTN), 0, 32);\n\tcfg->hold1_move1.lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move1LeftButton\", DEFAULT_HOLD1_MOVE1_BTN), 0, 32);\n\tcfg->hold1_move1.rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move1RightButton\", DEFAULT_HOLD1_MOVE1_BTN), 0, 32);\n\n#if 0\n\tcfg->hold1_move2_stationary.button = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move2StationaryButton\", DEFAULT_HOLD1_MOVE2_STATIONARY_BTN), 0, 32);\n\tcfg->hold1_move2_stationary.max_move = MAXVAL(xf86SetIntOption(opts, \"Hold1Move2StationaryMaxMove\", DEFAULT_HOLD1_MOVE2_STATIONARY_MAX_MOVE), 1);\n\tcfg->hold1_move2.dist = MAXVAL(xf86SetIntOption(opts, \"Hold1Move2Distance\", DEFAULT_HOLD1_MOVE2_DIST), 1);\n\tcfg->hold1_move2.hold = MAXVAL(xf86SetIntOption(opts, \"Hold1Move2ClickTime\", DEFAULT_HOLD1_MOVE2_HOLD), 0);\n\tcfg->hold1_move2.drag_sens = MAXVAL(xf86SetIntOption(opts, \"Hold1Move2Sensitivity\", DEFAULT_HOLD1_MOVE2_SENS), 0);\n\tcfg->hold1_move2.up_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move2UpButton\", DEFAULT_HOLD1_MOVE2_BTN), 0, 32);\n\tcfg->hold1_move2.dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move2DownButton\", DEFAULT_HOLD1_MOVE2_BTN), 0, 32);\n\tcfg->hold1_move2.lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move2LeftButton\", DEFAULT_HOLD1_MOVE2_BTN), 0, 32);\n\tcfg->hold1_move2.rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move2RightButton\", DEFAULT_HOLD1_MOVE2_BTN), 0, 32);\n\n\tcfg->hold1_move3_stationary.button = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move3StationaryButton\", DEFAULT_HOLD1_MOVE3_STATIONARY_BTN), 0, 32);\n\tcfg->hold1_move3_stationary.max_move = MAXVAL(xf86SetIntOption(opts, \"Hold1Move3StationaryMaxMove\", DEFAULT_HOLD1_MOVE3_STATIONARY_MAX_MOVE), 1);\n\tcfg->hold1_move3.dist = MAXVAL(xf86SetIntOption(opts, \"Hold1Move3Distance\", DEFAULT_HOLD1_MOVE3_DIST), 1);\n\tcfg->hold1_move3.hold = MAXVAL(xf86SetIntOption(opts, \"Hold1Move3ClickTime\", DEFAULT_HOLD1_MOVE3_HOLD), 0);\n\tcfg->hold1_move3.drag_sens = MAXVAL(xf86SetIntOption(opts, \"Hold1Move3Sensitivity\", DEFAULT_HOLD1_MOVE3_SENS), 0);\n\tcfg->hold1_move3.up_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move3UpButton\", DEFAULT_HOLD1_MOVE3_BTN), 0, 32);\n\tcfg->hold1_move3.dn_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move3DownButton\", DEFAULT_HOLD1_MOVE3_BTN), 0, 32);\n\tcfg->hold1_move3.lt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move3LeftButton\", DEFAULT_HOLD1_MOVE3_BTN), 0, 32);\n\tcfg->hold1_move3.rt_btn = CLAMPVAL(xf86SetIntOption(opts, \"Hold1Move3RightButton\", DEFAULT_HOLD1_MOVE3_BTN), 0, 32);\n#endif\n\n\tcfg->drag_enable = xf86SetBoolOption(opts, \"TapDragEnable\", DEFAULT_DRAG_ENABLE);\n\tcfg->drag_timeout = MAXVAL(xf86SetIntOption(opts, \"TapDragTime\", DEFAULT_DRAG_TIMEOUT), 1);\n\tcfg->drag_wait = MAXVAL(xf86SetIntOption(opts, \"TapDragWait\", DEFAULT_DRAG_WAIT), 0);\n\tcfg->drag_dist = MAXVAL(xf86SetIntOption(opts, \"TapDragDist\", DEFAULT_DRAG_DIST), 0);\n\tcfg->drag_lock_timeout = xf86SetIntOption(opts, \"TapDragLockTimeout\", DEFAULT_DRAG_LOCK_TIMEOUT);\n\n\tcfg->axis_x_invert = xf86SetBoolOption(opts, \"AxisXInvert\", DEFAULT_AXIS_X_INVERT);\n\tcfg->axis_y_invert = xf86SetBoolOption(opts, \"AxisYInvert\", DEFAULT_AXIS_Y_INVERT);\n\tcfg->sensitivity = MAXVAL(xf86SetRealOption(opts, \"Sensitivity\", DEFAULT_SENSITIVITY), 0);\n}\n"
  },
  {
    "path": "src/mtouch.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"mtouch.h\"\n\nstatic const int use_grab = 0;\n\nint mtouch_configure(struct MTouch* mt, int fd)\n{\n\tmt->fd = fd;\n\tint rc = read_capabilities(&mt->caps, mt->fd);\n\tif (rc < 0)\n\t\treturn rc;\n\toutput_capabilities(&mt->caps);\n\treturn 0;\n}\n\n\nint mtouch_open(struct MTouch* mt, int fd)\n{\n\tint ret;\n\tmt->fd = fd;\n\tret = mtdev_open(&mt->dev, mt->fd);\n\tif (ret)\n\t\tgoto error;\n\tmconfig_init(&mt->cfg, &mt->caps);\n\thwstate_init(&mt->hs, &mt->caps);\n\tmtstate_init(&mt->state);\n\tgestures_init(mt);\n\tif (use_grab) {\n\t\tSYSCALL(ret = ioctl(fd, EVIOCGRAB, 1));\n\t\tif (ret)\n\t\t\tgoto close;\n\t}\n\treturn 0;\n close:\n\tmtdev_close(&mt->dev);\n error:\n\treturn ret;\n}\n\n\nint mtouch_close(struct MTouch* mt)\n{\n\tint ret;\n\tif (use_grab) {\n\t\tSYSCALL(ret = ioctl(mt->fd, EVIOCGRAB, 0));\n\t\tif (ret)\n\t\t\tLOG_WARNING(\"mtouch: ungrab failed\\n\");\n\t}\n\tmtdev_close(&mt->dev);\n\treturn 0;\n}\n\nint mtouch_read(struct MTouch* mt)\n{\n\tint ret = hwstate_modify(&mt->hs, &mt->dev, mt->fd, &mt->caps);\n\tif (ret <= 0)\n\t\treturn ret;\n\tmtstate_extract(&mt->state, &mt->cfg, &mt->hs, &mt->caps);\n\tgestures_extract(mt);\n\treturn 1;\n}\n\nint mtouch_delayed(struct MTouch* mt)\n{\n\treturn gestures_delayed(mt);\n}\n\n"
  },
  {
    "path": "src/mtstate.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"mtstate.h\"\n#include \"trig.h\"\n\n#ifdef DEBUG_MTSTATE\n# define LOG_DEBUG_MTSTATE LOG_DEBUG\n#else\n# define LOG_DEBUG_MTSTATE LOG_DISABLED\n#endif\n\nstatic int inline percentage(int dividend, int divisor)\n{\n\treturn (double)dividend / (double)divisor * 100;\n}\n\nstatic int inline touch_range_ratio(const struct MConfig* cfg, int value)\n{\n\treturn (double)(value - cfg->touch_min) / (double)(cfg->touch_max - cfg->touch_min) * 100;\n}\n\nstatic int inline pressure_range_ratio(const struct MConfig* cfg, int value)\n{\n\treturn percentage(value - cfg->pressure_min, cfg->pressure_max - cfg->pressure_min);\n}\n\nstatic int finger_touch_ratio(const struct MConfig* cfg, const struct FingerState* hw)\n{\n\tswitch(cfg->touch_type){\n\tcase MCFG_SCALE:\n\t\treturn percentage(hw->touch_major, hw->width_major); /* = estimated pressure */\n\tcase MCFG_SIZE:\n\tcase MCFG_SIZE_PRESSURE:\n\t\treturn touch_range_ratio(cfg, hw->touch_major);\n\tcase MCFG_PRESSURE_SIZE:\n\tcase MCFG_PRESSURE:\n\t\treturn pressure_range_ratio(cfg, hw->pressure);\n\tdefault:\n\t\treturn 101; /* sholuld it be additional argument? or maybe it should return -1? */\n\t}\n}\n\n/* Check if a finger is touching the trackpad.\n */\nstatic int is_touch(const struct MConfig* cfg,\n\t\t\tconst struct FingerState* hw)\n{\n\treturn finger_touch_ratio(cfg, hw) > cfg->touch_down;\n}\n\n/* Check if a finger is released from the touchpad.\n */\nstatic int is_release(const struct MConfig* cfg,\n\t\t\tconst struct FingerState* hw)\n{\n\treturn finger_touch_ratio(cfg, hw) < cfg->touch_up;\n}\n\nstatic int is_thumb(const struct MConfig* cfg,\n\t\t\tconst struct FingerState* hw)\n{\n\tif (!cfg->touch_minor)\n\t\treturn 0;\n\n\tint min = MINVAL(hw->touch_minor, hw->touch_major);\n\tint max = MAXVAL(hw->touch_minor, hw->touch_major);\n\tint pct = percentage(min, max);\n\tint size = touch_range_ratio(cfg, hw->touch_major);\n\n\tif (pct < cfg->thumb_ratio && size > cfg->thumb_size) {\n\t\tLOG_DEBUG_MTSTATE(\"is_thumb: yes %d > %d && %d > %d\\n\",\n\t\t\tpct, cfg->thumb_ratio, size, cfg->thumb_size);\n\t\treturn 1;\n\t}\n\telse {\n\t\tLOG_DEBUG_MTSTATE(\"is_thumb: no  %d > %d && %d > %d\\n\",\n\t\t\tpct, cfg->thumb_ratio, size, cfg->thumb_size);\n\t\treturn 0;\n\t}\n}\n\nstatic int is_palm(const struct MConfig* cfg,\n\t\t\tconst struct FingerState* hw)\n{\n\tint ratio;\n\tswitch(cfg->touch_type){\n\tcase MCFG_SCALE:\n\t\tratio = percentage(hw->touch_major, hw->width_major);\n\t\tbreak;\n\tcase MCFG_SIZE:\n\tcase MCFG_SIZE_PRESSURE:\n\tcase MCFG_PRESSURE_SIZE:\n\t\tratio = touch_range_ratio(cfg, hw->touch_major);\n\t\tbreak;\n\tcase MCFG_PRESSURE:\n\t\tratio =  pressure_range_ratio(cfg, hw->pressure);\n\t\tbreak;\n\tdefault:\n\t\treturn 0;\n\t}\n\n\tif (ratio > cfg->palm_size) {\n\t\tLOG_DEBUG_MTSTATE(\"is_palm: yes %d > %d\\n\", ratio, cfg->palm_size);\n\t\treturn 1;\n\t}\n\telse {\n\t\tLOG_DEBUG_MTSTATE(\"is_palm: no  %d > %d\\n\", ratio, cfg->palm_size);\n\t\treturn 0;\n\t}\n}\n\n/*\n * |1|   2   |3|\n * |-|-------|-|\n * |4|   5   |6|\n * |-|-------|-|\n * |7|   8   |9|\n */\nint touch_which_edge(const struct MConfig* cfg, const struct Touch* t)\n{\n\ttypeof(cfg->pad_width) w = cfg->pad_width;\n\ttypeof(cfg->pad_height) h = cfg->pad_height;\n\n\t/* Translate back origin from center of the device to top left. */\n\ttypeof(t->x) x = t->x + w/2;\n\ttypeof(t->y) y = t->y + h/2;\n\n\tint out = 5; // see visualization above\n\n\t// y < (cfg->edge_top_size * cfg->pad_height)/100):\n\tif(y * 100 < cfg->edge_top_size * h) out = 2;\n\n\t// y > h - (cfg->edge_bottom_size * h)/100) :\n\tif((y - h) * 100 > -cfg->edge_bottom_size * h) out = 8;\n\n\tif(x * 100 < cfg->edge_left_size * w) out -= 1; // 1,4,7\n\n\t// x > w - (cfg->edge_right_size * w)/100\n\tif((x - w) * 100 > -cfg->edge_right_size * w) out += 1; // 3,6,9\n\n\tLOG_INFO2(DISABLED, \"x=%i y=%i out=%i w=%i h=%i\\n\", x,y,out,w,h);\n\treturn out;\n}\n\nstatic int is_edge(const struct MConfig* cfg, const struct Touch* t)\n{\n\treturn touch_which_edge(cfg, t) != 5;\n}\n\n/* Find a touch by its tracking ID.  Return -1 if not found.\n */\nstatic int find_touch(struct MTState* ms,\n\t\t\tint tracking_id)\n{\n\tint i;\n\tforeach_bit(i, ms->touch_used) {\n\t\tif (ms->touch[i].tracking_id == tracking_id)\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\n\n/* Add a touch to the MTState.  Return the new index of the touch.\n */\nstatic int touch_append(struct MTState* ms,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tconst struct Capabilities* caps,\n\t\t\tconst struct HWState* hs,\n\t\t\tint fn)\n{\n\tint x, y;\n\tint n = firstbit(~ms->touch_used);\n\tconst struct FingerState* fs = &hs->data[fn];\n\tif (n < 0)\n\t\tLOG_WARNING(\"Too many touches to track. Ignoring touch %d.\\n\", fs->tracking_id);\n\telse {\n\t\t/* Set origin (0,0) of 'struct Touch' coordinate system to central point of the device */\n\t\tx = translate_cap_x(caps, fs->position_x);\n\t\ty = translate_cap_y(caps, fs->position_y);\n\t\tx = cfg->axis_x_invert ? -x : x;\n\t\ty = cfg->axis_y_invert ? -y : y;\n\t\tms->touch[n].flags = 0U;\n\t\ttimercp(&ms->touch[n].down, &hs->evtime);\n\t\tms->touch[n].direction = TR_NONE;\n\t\tms->touch[n].tracking_id = fs->tracking_id;\n\t\tms->touch[n].x = x;\n\t\tms->touch[n].y = y;\n\t\tms->touch[n].dx = 0;\n\t\tms->touch[n].dy = 0;\n\t\tms->touch[n].total_dx = 0;\n\t\tms->touch[n].total_dy = 0;\n\t\tSETBIT(ms->touch[n].flags, MT_NEW);\n\t\tSETBIT(ms->touch_used, n);\n\t}\n\treturn n;\n}\n\n/* Update a touch.\n */\nstatic void touch_update(struct MTState* ms,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tconst struct Capabilities* caps,\n\t\t\tconst struct FingerState* fs,\n\t\t\tint touch)\n{\n\tint x, y;\n\t/* Translate origin of struct Touch coordinate system to middle point of device */\n\tx = translate_cap_x(caps, fs->position_x);\n\ty = translate_cap_y(caps, fs->position_y);\n\n\tx = cfg->axis_x_invert ? -x : x;\n\ty = cfg->axis_y_invert ? -y : y;\n\tms->touch[touch].dx = x - ms->touch[touch].x;\n\tms->touch[touch].dy = y - ms->touch[touch].y;\n\tms->touch[touch].total_dx += ms->touch[touch].dx;\n\tms->touch[touch].total_dy += ms->touch[touch].dy;\n\tms->touch[touch].x = x;\n\tms->touch[touch].y = y;\n\tif (ms->touch[touch].dx != 0 || ms->touch[touch].dy != 0) {\n\t\tms->touch[touch].direction = trig_direction(ms->touch[touch].dx, ms->touch[touch].dy);\n\t}\n\tCLEARBIT(ms->touch[touch].flags, MT_NEW);\n}\n\n/* Release a touch.\n */\nstatic void touch_release(struct MTState* ms,\n\t\t\tint touch)\n{\n\tms->touch[touch].dx = 0;\n\tms->touch[touch].dy = 0;\n\tms->touch[touch].direction = TR_NONE;\n\tCLEARBIT(ms->touch[touch].flags, MT_NEW);\n\tSETBIT(ms->touch[touch].flags, MT_RELEASED);\n}\n\n/* Invalidate all touches.\n */\nstatic void touches_invalidate(struct MTState* ms)\n{\n\tint i;\n\tforeach_bit(i, ms->touch_used)\n\t\tSETBIT(ms->touch[i].flags, MT_INVALID);\n}\n\n/* Update all touches.\n */\nstatic void touches_update(struct MTState* ms,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tconst struct HWState* hs,\n\t\t\tconst struct Capabilities* caps)\n{\n\tint i, n, disable = 0;\n\t// Release missing touches.\n\tforeach_bit(i, ms->touch_used) {\n\t\tif (find_finger(hs, ms->touch[i].tracking_id) == -1)\n\t\t\ttouch_release(ms, i);\n\t}\n\n\t// Add and update touches.\n\tforeach_bit(i, hs->used) {\n\t\tn = find_touch(ms, hs->data[i].tracking_id);\n\t\tif (n >= 0) {\n\t\t\tif (is_release(cfg, &hs->data[i]))\n\t\t\t\ttouch_release(ms, n);\n\t\t\telse\n\t\t\t\ttouch_update(ms, cfg, caps, &hs->data[i], n);\n\t\t}\n\t\telse if (is_touch(cfg, &hs->data[i]))\n\t\t\tn = touch_append(ms, cfg, caps, hs, i);\n\n\t\tif (n >= 0) {\n\t\t\t// Track and invalidate thumb, palm, and edge touches.\n\t\t\tif (is_thumb(cfg, &hs->data[i]))\n\t\t\t\tSETBIT(ms->touch[n].flags, MT_THUMB);\n\t\t\telse\n\t\t\t\tCLEARBIT(ms->touch[n].flags, MT_THUMB);\n\n\t\t\tif (is_palm(cfg, &hs->data[i]))\n\t\t\t\tSETBIT(ms->touch[n].flags, MT_PALM);\n\t\t\telse\n\t\t\t\tCLEARBIT(ms->touch[n].flags, MT_PALM);\n\n\t\t\tif (is_edge(cfg, &ms->touch[n])) {\n\t\t\t\tif (GETBIT(ms->touch[n].flags, MT_NEW))\n\t\t\t\t\tSETBIT(ms->touch[n].flags, MT_EDGE);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCLEARBIT(ms->touch[n].flags, MT_EDGE);\n\n\t\t\tMODBIT(ms->touch[n].flags, MT_INVALID,\n\t\t\t\t(GETBIT(ms->touch[n].flags, MT_THUMB) && cfg->ignore_thumb) ||\n\t\t\t\t(GETBIT(ms->touch[n].flags, MT_PALM) && cfg->ignore_palm) ||\n\t\t\t\tGETBIT(ms->touch[n].flags, MT_EDGE));\n\n\t\t\tdisable |= cfg->disable_on_thumb && GETBIT(ms->touch[n].flags, MT_THUMB);\n\t\t\tdisable |= cfg->disable_on_palm && GETBIT(ms->touch[n].flags, MT_PALM);\n\t\t}\n\t}\n\n\tif (disable)\n\t\ttouches_invalidate(ms);\n}\n\n/* Remove released touches.\n */\nstatic void touches_clean(struct MTState* ms)\n{\n\tint i, used;\n\tused = ms->touch_used;\n\tforeach_bit(i, used) {\n\t\tif (GETBIT(ms->touch[i].flags, MT_RELEASED))\n\t\t\tCLEARBIT(ms->touch_used, i);\n\t}\n}\n\n#if DEBUG_MTSTATE\nstatic void mtstate_output(const struct MTState* ms,\n\t\t\tconst struct HWState* hs)\n{\n\tint i, n;\n\tstruct timeval tv;\n\n\tn = bitcount(ms->touch_used);\n\tif (bitcount(ms->touch_used) > 0) {\n\t\tmicrotime(&tv);\n\t\tLOG_INFO(\"mtstate: %d touches at event time %llu (rt %llu)\\n\",\n\t\t\tn, timertoms(&hs->evtime), timertoms(&tv));\n\t}\n\tforeach_bit(i, ms->touch_used) {\n\t\tif (GETBIT(ms->touch[i].flags, MT_RELEASED)) {\n\t\t\ttimersub(&hs->evtime, &ms->touch[i].down, &tv);\n\t\t\tLOG_INFO(\"  released p(%d, %d) d(%+d, %+d) dir(%f) down(%llu) time(%lld)\\n\",\n\t\t\t\t\t\tms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,\n\t\t\t\t\t\tms->touch[i].direction, timertoms(&ms->touch[i].down), timertoms(&tv));\n\t\t}\n\t\telse if (GETBIT(ms->touch[i].flags, MT_NEW)) {\n\t\t\tLOG_INFO(\"  new      p(%d, %d) d(%+d, %+d) dir(%f) down(%llu)\\n\",\n\t\t\t\t\t\tms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,\n\t\t\t\t\t\tms->touch[i].direction, timertoms(&ms->touch[i].down));\n\t\t}\n\t\telse if (GETBIT(ms->touch[i].flags, MT_INVALID)) {\n\t\t\ttimersub(&hs->evtime, &ms->touch[i].down, &tv);\n\t\t\tLOG_INFO(\"  invalid  p(%d, %d) d(%+d, %+d) dir(%f) down(%llu) time(%lld)\\n\",\n\t\t\t\t\t\tms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,\n\t\t\t\t\t\tms->touch[i].direction, timertoms(&ms->touch[i].down), timertoms(&tv));\n\t\t}\n\t\telse {\n\t\t\tLOG_INFO(\"  touching p(%d, %d) d(%+d, %+d) dir(%f) down(%llu)\\n\",\n\t\t\t\t\t\tms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,\n\t\t\t\t\t\tms->touch[i].direction, timertoms(&ms->touch[i].down));\n\t\t}\n\t}\n}\n#endif\n\nvoid mtstate_init(struct MTState* ms)\n{\n\tmemset(ms, 0, sizeof(struct MTState));\n}\n\n// Process changes in touch state.\nvoid mtstate_extract(struct MTState* ms,\n\t\t\tconst struct MConfig* cfg,\n\t\t\tconst struct HWState* hs,\n\t\t\tconst struct Capabilities* caps)\n{\n\ttouches_clean(ms);\n\ttouches_update(ms, cfg, hs, caps);\n\n#if DEBUG_MTSTATE\n\tmtstate_output(ms, hs);\n#endif\n}\n\n"
  },
  {
    "path": "src/trig.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"trig.h\"\n#include \"common.h\"\n#include <math.h>\n\n/* Convert a radians value into an mtrack angle.\n */\nstatic double trig_encode_radians(double radians) {\n\tdouble angle = (radians / M_PI) * 4.0;\n\tif (angle < 0.0)\n\t\tangle = angle + 8.0;\n\treturn angle;\n}\n\n/* Convert an mtrack angle value into radians.\n */\nstatic double trig_decode_radians(double angle) {\n\tif (angle < 4.0)\n\t\treturn (angle * M_PI) / 4.0;\n\telse\n\t\treturn ((8.0 - angle) * M_PI) / -4.0;\n}\n\ndouble trig_direction(double dx, double dy) {\n\tif (dx != 0 || dy != 0)\n\t\treturn trig_encode_radians(atan2(dx, dy*-1));\n\treturn TR_NONE;\n}\n\nint trig_generalize(double dir)\n{\n\tif (dir == TR_NONE)\n\t\treturn TR_NONE;\n\telse if (dir > 1.0 && dir <= 3.0)\n\t\treturn TR_DIR_RT;\n\telse if (dir > 3.0 && dir <= 5.0)\n\t\treturn TR_DIR_DN;\n\telse if (dir > 5.0 && dir <= 7.0)\n\t\treturn TR_DIR_LT;\n\telse\n\t\treturn TR_DIR_UP;\n}\n\ndouble trig_angles_add(double a1, double a2)\n{\n\tdouble a = MODVAL(a1 + a2, 8.0);\n\tif (a < 0.0)\n\t\ta = a + 8.0;\n\treturn a;\n}\n\ndouble trig_angles_sub(double a1, double a2)\n{\n\treturn trig_angles_add(a1, -1.0*a2);\n}\n\ndouble trig_angles_acute(double a1, double a2)\n{\n\tdouble angle;\n\tif (a1 > a2)\n\t\tangle = trig_angles_sub(a1, a2);\n\telse\n\t\tangle = trig_angles_sub(a2, a1);\n\tif (angle > 4.0)\n\t\tangle = 8.0 - angle;\n\treturn angle;\n}\n\ndouble trig_angles_avg(double* angles, int len)\n{\n\tint i;\n\tdouble dx, dy, r;\n\tdx = dy = 0;\n\tfor (i = 0; i < len; i++) {\n\t\tr = trig_decode_radians(angles[i]);\n\t\tdx += cos(r);\n\t\tdy += sin(r);\n\t}\n\treturn trig_encode_radians(atan2(dy, dx));\n}\n\nint trig_angles_cmp(double a1, double a2)\n{\n\tdouble m1, m2;\n\tm1 = MODVAL(a1, 8);\n\tm2 = MODVAL(a2, 8);\n\tif (m1 == m2)\n\t\treturn 0;\n\telse if (m1 > m2)\n\t\treturn 1;\n\telse\n\t\treturn -1;\n}\n"
  },
  {
    "path": "tools/mtrack-test.c",
    "content": "/***************************************************************************\n *\n * Multitouch X driver\n * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>\n * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n *\n **************************************************************************/\n\n#include \"mtouch.h\"\n#include <fcntl.h>\n#include <stdarg.h>\n#include <unistd.h>\n\nvoid xf86Msg(int type, const char *format, ...)\n{\n\tva_list args;\n\tva_start(args, format);\n\tvfprintf(stderr, format, args);\n\tva_end(args);\n}\n\n#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) <= 13\ntypedef XF86OptionPtr pointer;\n#endif\n\nint xf86SetIntOption(XF86OptionPtr opts, const char *name, int deflt)\n{\n\treturn deflt;\n}\n\nint xf86SetBoolOption(XF86OptionPtr opts, const char *name, int deflt)\n{\n\treturn deflt;\n}\n\ndouble xf86SetRealOption(XF86OptionPtr opts, const char *name, double deflt)\n{\n\treturn deflt;\n}\n\nstatic void print_gestures(const struct Gestures* gs)\n{\n\tint i;\n\tstatic bitmask_t buttons_prev = 0U;\n\tfor (i = 0; i < 32; i++) {\n\t\tif (GETBIT(gs->buttons, i) == GETBIT(buttons_prev, i))\n\t\t\tcontinue;\n\n\t\tif (GETBIT(gs->buttons, i))\n\t\t\tprintf(\"button %d down\\n\", i+1);\n\t\telse\n\t\t\tprintf(\"button %d up\\n\", i+1);\n\t}\n\n\tif (gs->move_dx != 0.0 || gs->move_dy != 0.0)\n\t\tprintf(\"moving (%lf, %lf)\\n\", gs->move_dx, gs->move_dy);\n\n\tbuttons_prev = gs->buttons;\n}\n\nstatic void loop_device(int fd)\n{\n\tstruct MTouch mt;\n\tif (mtouch_configure(&mt, fd)) {\n\t\tfprintf(stderr, \"error: could not configure device\\n\");\n\t\treturn;\n\t}\n\tif (mtouch_open(&mt, fd)) {\n\t\tfprintf(stderr, \"error: could not open device\\n\");\n\t\treturn;\n\t}\n\n\tmconfig_defaults(&mt.cfg);\n\tprintf(\"width:  %d\\n\", mt.hs.max_x);\n\tprintf(\"height: %d\\n\", mt.hs.max_y);\n\n\t//while (!mtdev_idle(&mt.dev, fd, 5000)) {\n\twhile (1) {\n\t\twhile (mtouch_read(&mt) > 0)\n\t\t\tprint_gestures(&mt.gs);\n\t\tif (mtouch_delayed(&mt))\n\t\t\tprint_gestures(&mt.gs);\n\t}\n\tmtouch_close(&mt);\n}\n\nint main(int argc, char *argv[])\n{\n\tif (argc < 2) {\n\t\tfprintf(stderr, \"Usage: test <mtdev>\\n\");\n\t\treturn -1;\n\t}\n\tint fd = open(argv[1], O_RDONLY | O_NONBLOCK);\n\tif (fd < 0) {\n\t\tfprintf(stderr, \"error: could not open file\\n\");\n\t\treturn -1;\n\t}\n\tloop_device(fd);\n\tclose(fd);\n\treturn 0;\n}\n"
  },
  {
    "path": "tools/reconfigure-xinput.sh",
    "content": "#! /bin/bash\n\n# Script to parse configuration dumped by 'xinput --list-props <dev id>'\n# and reapply it on host machine.\n# It was created to help reconfigure mtrack switch easly between user's setup\n# and mine. \n\nlogExec(){\n  echo $@\n  eval \"$@\"\n}\n\n# $1 - device id\n# $2 - file name to read new settings from\nmain(){\n  local devId=$1\n  local fileToApply=$2\n  while read -r line || [[ -n \"$line\" ]]; do\n#    echo line \"$line\"\n    local propId=$(grep -Po '\\(\\d+\\)' <<< $line)\n    propId=$(grep -Po '\\d+' <<< $propId)\n    if [[ \"$propId\" == \"\" ]]; then\n      continue\n    fi\n    \n    local propName=$(grep -Po \".*\\(\" <<< $line)\n    propName=${propName::-2}\n#    echo $propName\n    \n    local values=$(grep -Po ':.*' <<< $line)\n    values=${values:1} # skip colon\n    values=${values//,/ } # remove commas\n    \n#    echo $propName $propId : ${values}\n    logExec xinput --set-prop $devId \\'$propName\\' $values   \n  done < $fileToApply\n}\n\n# $1 - device id\n# $2 - file name to read new settings from\nmain \"$@\""
  }
]