-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclickindicator.cpp
More file actions
97 lines (88 loc) · 4.14 KB
/
Copy pathclickindicator.cpp
File metadata and controls
97 lines (88 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "clickindicator.h"
#include <QDebug>
#include <QGuiApplication>
#include <QPainter>
#include <QScreen>
ClickIndicatorOverlay::ClickIndicatorOverlay()
: QWidget(nullptr,
Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool
| Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput)
{
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_ShowWithoutActivating);
// Belt-and-braces click-through: the overlay spans the whole screen, so it
// must never intercept input meant for windows beneath it.
setAttribute(Qt::WA_TransparentForMouseEvents);
m_timer.setInterval(STEP_MS);
connect(&m_timer, &QTimer::timeout, this, [this]{
if (++m_step >= STEPS) { m_timer.stop(); hide(); }
else update();
});
}
void ClickIndicatorOverlay::flash(QPoint gp)
{
// Draw the ring inside a click-through overlay that spans the ENTIRE virtual
// desktop (all monitors), anchored at the desktop origin, and paint the ring
// at the cursor's offset within it.
//
// Spanning everything — rather than sizing the overlay to the cursor's
// monitor — is the key to landing on the right screen. Moving a top-level
// window onto a specific, non-primary monitor is unreliable: under
// Wayland/XWayland the compositor controls placement and routinely drops the
// window back onto the primary monitor, so the ring showed up on the wrong
// screen no matter how correctly its coordinates were computed. A single
// window covering the whole desktop has only one possible position (the
// desktop origin), so there is no placement decision left for the compositor
// to get wrong; the ring then appears wherever the cursor is, on any monitor.
QScreen* scr = QGuiApplication::screenAt(gp);
if (!scr) scr = QGuiApplication::primaryScreen();
if (!scr) return;
const QRect vg = scr->virtualGeometry(); // union of all monitors (logical)
setGeometry(vg);
m_center = gp - vg.topLeft(); // ring centre, overlay-local coords
if (qEnvironmentVariableIntValue("TRACKCLICK_CLICK_DEBUG"))
qWarning() << "TrackClick click-ring: cursor" << gp
<< "| screen" << scr->name() << scr->geometry()
<< "| virtual" << vg << "| center" << m_center;
m_step = 0;
update();
show();
raise();
m_timer.start();
}
void ClickIndicatorOverlay::paintEvent(QPaintEvent*)
{
// Leave the window fully transparent (WA_TranslucentBackground clears it
// each frame); only the ring + dot are painted, so nothing else is visible
// and the whole surface stays click-through.
// One-off diagnostic (first frame only): where did the overlay window
// ACTUALLY land? If mapToGlobal(0,0) is not the virtual-desktop origin, the
// compositor/WM has repositioned the window and the ring is offset by that
// amount regardless of how correct m_center is. devicePixelRatio surfaces
// any fractional-scaling mismatch between the injected position (physical)
// and the overlay's logical paint coordinates.
if (m_step == 0 && qEnvironmentVariableIntValue("TRACKCLICK_CLICK_DEBUG"))
qWarning() << "TrackClick click-ring PAINT: window origin (global)"
<< mapToGlobal(QPoint(0, 0))
<< "| geometry" << geometry()
<< "| frameGeometry" << frameGeometry()
<< "| dpr" << devicePixelRatioF()
<< "| m_center" << m_center;
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
const float t = float(m_step) / float(STEPS);
const int radius = int(MIN_R + t * (MAX_R - MIN_R));
const int alpha = int(255 * (1.0f - t));
// Outer expanding ring
QPen pen(QColor(0xFF, 0xA6, 0x00, alpha), 3);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawEllipse(m_center, radius, radius);
// Small solid centre dot that fades quickly
const int dotAlpha = int(255 * qMax(0.0f, 1.0f - t * 2.5f));
if (dotAlpha > 0) {
p.setPen(Qt::NoPen);
p.setBrush(QColor(0xFF, 0xA6, 0x00, dotAlpha));
p.drawEllipse(m_center, 4, 4);
}
}