<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Linux Experience on Code is cheap, let&#39;s talk</title>
    <link>https://blog.ferstar.org/en/series/linux-experience/</link>
    <description>Code is cheap, let&#39;s talk</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <copyright>Copyright 2026 ferstar</copyright>
    <lastBuildDate>Sun, 29 Jan 2023 02:08:34 +0800</lastBuildDate>
    <ttl>60</ttl><atom:link href="https://blog.ferstar.org/en/series/linux-experience/index.xml" rel="self" type="application/rss+xml" /><image>
      <url>https://blog.ferstar.org/site-logo.png</url>
      <title>Code is cheap, let&#39;s talk</title>
      <link>https://blog.ferstar.org/</link>
    </image>
    
    <item>
      <title>Enhancing Linux Touchpad Gestures: Implementing &#39;Three-Finger Drag&#39;</title>
      <link>https://blog.ferstar.org/en/posts/linux-touchpad-gestures-drag/</link>
      <pubDate>Sun, 29 Jan 2023 02:08:34 +0800</pubDate>
      
      <guid isPermaLink="true">https://blog.ferstar.org/en/posts/linux-touchpad-gestures-drag/</guid>
      <description>Miss the smooth macOS three-finger drag on Linux? This high-performance Rust-based solution for libinput gestures supports both X11 and Wayland with minimal CPU overhead.</description><content:encoded><![CDATA[<blockquote><p>I am not a native English speaker; this article was translated by AI.</p>
</blockquote><p>After switching to Linux, I’ve been missing the smooth <strong>three-finger drag</strong> experience from macOS. Given that recent Windows laptops have significantly improved touchpad size and responsiveness, I decided it was time to tackle <strong>touchpad gestures</strong> on Linux.</p>
<hr>

<h2 class="relative group">Research and Selection
    <div id="research-and-selection" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#research-and-selection" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are basically two approaches to implementation:</p>
<ol>
<li>Parsing <code>libinput debug-events</code> output and using tools like <code>xdotool</code> to send keystrokes or mouse clicks.</li>
<li>Calling the <code>libinput</code> API directly, which offers the best performance.</li>
</ol>
<hr>

<h2 class="relative group">Implementation
    <div id="implementation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#implementation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I chose to fork and merge two Rust projects to create a unified solution. One project handles general <a href="https://github.com/riley-martin/gestures"  target="_blank" rel="noreferrer">gestures</a>, and the other focuses on the <a href="https://github.com/marsqing/libinput-three-finger-drag"  target="_blank" rel="noreferrer">three-finger drag</a> effect at the API level.</p>

<h3 class="relative group">Architecture
    <div id="architecture" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#architecture" aria-label="Anchor">#</a>
    </span>
    
</h3>
<pre class="not-prose mermaid">graph TD
    A[Touchpad Event] --> B{libinput}
    B --> C[ferstar/gestures <br/>Rust Engine]
    C --> D{Display Server}
    D -- X11 --> E[libxdo API]
    D -- Wayland --> F[ydotool daemon]
    E --> G[Smooth Drag / Key Stroke]
    F --> G
    
    style C fill:#f96,stroke:#333,stroke-width:2px
    style G fill:#4ecdc4,stroke:#333,stroke-width:2px</pre>
<p>The project is now at <strong>v0.8.1</strong>, with major improvements including:</p>
<ul>
<li><strong>Dual Platform Support</strong>: Works on both X11 and Wayland (auto-detection).</li>
<li><strong>Performance Optimization</strong>:
<ul>
<li>Direct <code>libxdo</code> API calls for X11 (minimum latency).</li>
<li>Optimized <code>ydotool</code> integration for Wayland with 60 FPS throttling.</li>
<li>Thread pooling to prevent PID exhaustion.</li>
<li>Regex and config caching for speed.</li>
</ul>
</li>
</ul>
<hr>

<h2 class="relative group">Performance Metrics
    <div id="performance-metrics" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#performance-metrics" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ol>
<li>
<p><strong>Low CPU Usage</strong></p>
<ul>
<li>Even under intense three-finger dragging, this implementation uses less than 1% CPU.</li>
<li>Competing Python or Ruby implementations often exceed 20%.</li>
</ul>
</li>
<li>
<p><strong>Resource Efficiency</strong></p>
<ul>
<li>Memory usage: < 5MB.</li>
<li>Binary size: < 2MB.</li>
<li>Zero unnecessary dependencies.</li>
</ul>
</li>
</ol>
<hr>

<h2 class="relative group">Installation & Usage
    <div id="installation--usage" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#installation--usage" aria-label="Anchor">#</a>
    </span>
    
</h2>

<h3 class="relative group">Dependencies
    <div id="dependencies" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dependencies" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><strong>Ubuntu/Debian:</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install libudev-dev libinput-dev libxdo-dev xdotool
</span></span><span class="line"><span class="cl"><span class="c1"># For Wayland</span>
</span></span><span class="line"><span class="cl">sudo apt install ydotool</span></span></code></pre></div></div>
<p><strong>Arch Linux:</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S libinput xdotool
</span></span><span class="line"><span class="cl"><span class="c1"># Wayland</span>
</span></span><span class="line"><span class="cl">yay -S ydotool</span></span></code></pre></div></div>

<h3 class="relative group">Install Binary
    <div id="install-binary" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-binary" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p><strong>Option 1: Pre-compiled Binary (Recommended)</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget https://github.com/ferstar/gestures/releases/latest/download/gestures
</span></span><span class="line"><span class="cl">chmod +x gestures
</span></span><span class="line"><span class="cl">sudo mv gestures /usr/local/bin/</span></span></code></pre></div></div>
<p><strong>Option 2: Install via Cargo</strong></p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo install --git https://github.com/ferstar/gestures.git</span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Configuration
    <div id="configuration" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configuration" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The config uses the KDL format. Example:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-kdl" data-lang="kdl">// Three-finger drag (X11 & Wayland)
gesture "drag" swipe any {
    fingers 3
    acceleration 1.0      // Drag speed
    mouse_up_delay 500    // Delay after lifting fingers (ms)
}

// Four-finger swipe up to switch workspace
gesture "switch-workspace-up" swipe up {
    fingers 4
    exec "xdotool" "key" "super+Page_Up"
}</code></pre></div>
<hr>

<h2 class="relative group">Running the Program
    <div id="running-the-program" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#running-the-program" aria-label="Anchor">#</a>
    </span>
    
</h2>

<h3 class="relative group">Systemd Service
    <div id="systemd-service" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#systemd-service" aria-label="Anchor">#</a>
    </span>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Install service file</span>
</span></span><span class="line"><span class="cl">gestures install-service
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Enable and start</span>
</span></span><span class="line"><span class="cl">systemctl --user <span class="nb">enable</span> --now gestures</span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Common Issues
    <div id="common-issues" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#common-issues" aria-label="Anchor">#</a>
    </span>
    
</h2>

<h3 class="relative group">Permission Denied
    <div id="permission-denied" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#permission-denied" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>You need to add your user to the <code>input</code> group:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo usermod -aG input <span class="nv">$USER</span></span></span></code></pre></div></div>
<hr>

<h2 class="relative group">Links
    <div id="links" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#links" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li><strong>GitHub</strong>: <a href="https://github.com/ferstar/gestures"  target="_blank" rel="noreferrer">https://github.com/ferstar/gestures</a></li>
<li><strong>Issues</strong>: <a href="https://github.com/ferstar/gestures/issues"  target="_blank" rel="noreferrer">https://github.com/ferstar/gestures/issues</a></li>
</ul>
<hr>
<p><strong>Enjoy!</strong></p>
]]></content:encoded>
      
    </item>
    
  </channel>
</rss>
