diff --git a/README.md b/README.md
index 4b720c3..0cceb2d 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,9 @@ in Wayfire 0.11.0-r19 or newer. Unpatched upstream Wayfire is not supported.
| `panel-desktop` | Physical-monitor Show Desktop | Wayfire |
| `workspace-backgrounds` | Per-workspace wallpapers | gdk-pixbuf |
| `screensaver` | Per-monitor idle fade, black, power-off, keep-awake and auto-lock | Wayfire |
+| `find-pointer` | Press Ctrl twice to spotlight the pointer | Wayfire (GLES2 renderer) |
-All four build by default. Building a module does not enable it in Wayfire.
+All five build by default. Building a module does not enable it in Wayfire.
Window-actions owns the desktop's spatial transactions, previews and physical
capture identity; it has no storage and no options. It replaces the former
`window-state` plugin: in `wayfire.ini`, replace `window-state` with
@@ -36,6 +37,7 @@ The installed libraries:
- `libpanel-desktop.so`.
- `libworkspace-backgrounds.so`.
- `libscreensaver.so`.
+- `libfind-pointer.so`.
The package replaces the standalone implementations. Compatibility ebuilds for
`wayfire-window-state` and `wayfire-panel-desktop` depend
diff --git a/meson.build b/meson.build
index 4f2bea1..7eb9888 100644
--- a/meson.build
+++ b/meson.build
@@ -2,10 +2,10 @@ project('wayfire-plugins-custom', 'cpp', version: '0.1.0',
default_options: ['cpp_std=c++17', 'warning_level=3'])
wf = dependency('wayfire', version: '>=0.11.0')
-if not get_option('window-actions') and not get_option('panel-desktop') and not get_option('workspace-backgrounds') and not get_option('screensaver')
+if not get_option('window-actions') and not get_option('panel-desktop') and not get_option('workspace-backgrounds') and not get_option('screensaver') and not get_option('find-pointer')
error('Enable at least one plugin')
endif
-foreach plugin : ['window-actions', 'panel-desktop', 'workspace-backgrounds', 'screensaver']
+foreach plugin : ['window-actions', 'panel-desktop', 'workspace-backgrounds', 'screensaver', 'find-pointer']
if get_option(plugin)
subdir('plugins' / plugin)
endif
@@ -15,5 +15,6 @@ summary({
'panel-desktop': get_option('panel-desktop'),
'workspace-backgrounds': get_option('workspace-backgrounds'),
'screensaver': get_option('screensaver'),
+ 'find-pointer': get_option('find-pointer'),
'tests': get_option('tests'),
}, section: 'Custom Wayfire plugins')
diff --git a/meson_options.txt b/meson_options.txt
index a7b7823..11efc31 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -4,3 +4,4 @@ option('tests', type: 'boolean', value: false, description: 'Build unit tests')
option('workspace-backgrounds', type: 'boolean', value: true, description: 'Workspace wallpapers')
option('workspace-backgrounds-test-api', type: 'boolean', value: false, description: 'Private compositor test controls; do not enable in installed builds')
option('screensaver', type: 'boolean', value: true, description: 'Per-monitor idle fading, black, power-off, keep-awake and auto-lock')
+option('find-pointer', type: 'boolean', value: true, description: 'Press Ctrl twice to spotlight the pointer')
diff --git a/plugins/find-pointer/LICENSE b/plugins/find-pointer/LICENSE
new file mode 100644
index 0000000..51026fa
--- /dev/null
+++ b/plugins/find-pointer/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 find-pointer contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/plugins/find-pointer/README.md b/plugins/find-pointer/README.md
new file mode 100644
index 0000000..6f1b9e3
--- /dev/null
+++ b/plugins/find-pointer/README.md
@@ -0,0 +1,57 @@
+# find-pointer
+
+Press and release Ctrl twice quickly to find the pointer, like PowerToys'
+Find My Mouse. Every monitor dims, except for a circle that closes in on the
+pointer. The circle follows the pointer, stays for a second and then fades.
+
+Add `find-pointer` to `[core] plugins`. It needs the GLES2 renderer, which is
+Wayfire's default. Under another renderer it logs an error and does nothing.
+
+## Behaviour
+
+- **Double tap.** Press and release Ctrl (either one) twice. The second press
+ must come within 400 ms of the first, and each press must be released
+ within 400 ms. Ctrl must be pressed alone. Any other key, click or scroll in
+ between starts over, so Ctrl+C, Ctrl-click and Ctrl+scroll never trigger it.
+ A single tap does nothing.
+ - The plugin only watches keys. Clients receive every Ctrl event exactly as
+ before.
+ - A double tap is ignored while a fullscreen window has keyboard focus,
+ because double-tapping Ctrl is common in games. The `activate` shortcut
+ still works there.
+ - Nothing happens while the session is locked.
+- **Spotlight.** The screen outside the circle darkens to `dim` (default
+ half). The circle shrinks from nine times `radius` to `radius` (70 px) in
+ 250 ms, while the darkening fades in. It stays for `duration` (1000 ms) and
+ then fades out in 200 ms. A thin light ring marks its edge.
+ - The circle is placed in the global layout. Every monitor is darkened,
+ and the circle shows on the monitor under the pointer, across the edge
+ when the pointer is near one.
+ - Any key press, click, scroll or touch ends it early, with the same
+ 200 ms fade.
+ - Repeating the gesture while it is shown starts it again.
+- **Cost.** When idle the plugin adds no render hooks and does no per-frame
+ work. While shown, it adds one post-processing pass per monitor and
+ redraws every frame for the length of the effect.
+
+## Options
+
+| Option | Default | Meaning |
+| --- | --- | --- |
+| `double_tap_ctrl` | `true` | Find the pointer with a double tap of Ctrl. |
+| `activate` | `none` | Shortcut (activator) that does the same. |
+| `dim` | `0.5` | How much the screen darkens outside the circle (0.1–0.9). |
+| `radius` | `70` | Circle radius in logical pixels (20–400). |
+| `duration` | `1000` | Milliseconds the circle stays before fading (0–10000). |
+
+```ini
+[find-pointer]
+double_tap_ctrl = true
+activate = none
+```
+
+## Tests
+
+`meson test` runs `find-pointer-spotlight`. It covers the double-tap rules
+(timing, chords, other input, duplicate events) and the shrink, hold and
+fade timeline, including ending early.
diff --git a/plugins/find-pointer/find-pointer.xml b/plugins/find-pointer/find-pointer.xml
new file mode 100644
index 0000000..905c4d6
--- /dev/null
+++ b/plugins/find-pointer/find-pointer.xml
@@ -0,0 +1,38 @@
+
+
+
+ <_short>Find Pointer
+ <_long>Press and release Ctrl twice to dim the screen around a circle that closes in on the pointer.
+ Accessibility
+
+
+
+
+
+
+
diff --git a/plugins/find-pointer/meson.build b/plugins/find-pointer/meson.build
new file mode 100644
index 0000000..c391758
--- /dev/null
+++ b/plugins/find-pointer/meson.build
@@ -0,0 +1,7 @@
+shared_module('find-pointer', 'src/plugin.cpp', dependencies: wf,
+ install: true, install_dir: wf.get_variable(pkgconfig: 'plugindir'))
+install_data('find-pointer.xml', install_dir: wf.get_variable(pkgconfig: 'metadatadir'))
+if get_option('tests')
+ test('find-pointer-spotlight', executable('find-pointer-spotlight-test', 'tests/spotlight-test.cpp'),
+ timeout: 30)
+endif
diff --git a/plugins/find-pointer/src/plugin.cpp b/plugins/find-pointer/src/plugin.cpp
new file mode 100644
index 0000000..5cbcb6f
--- /dev/null
+++ b/plugins/find-pointer/src/plugin.cpp
@@ -0,0 +1,325 @@
+// find-pointer: double-tap Ctrl (or a shortcut) to dim every monitor except
+// a shrinking circle around the pointer, like PowerToys' Find My Mouse.
+// Idle, it only watches input: render hooks exist only while it is shown.
+#include "spotlight.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include