mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-22 20:43:22 +08:00
[fix] support selective mouse button release across desktop platforms
This commit is contained in:
@@ -32,6 +32,9 @@ class MouseController : public DeviceController {
|
||||
virtual int Destroy() = 0;
|
||||
virtual int SendMouseCommand(RemoteAction remote_action,
|
||||
int display_index) = 0;
|
||||
// Bits 0/1/2 are left/right/middle. Return the subset confirmed released,
|
||||
// including buttons already up. Must not require valid display geometry.
|
||||
virtual unsigned ReleasePressedButtons(unsigned mask = 7) = 0;
|
||||
virtual void UpdateDisplayInfoList(
|
||||
const std::vector<DisplayInfo>&) {}
|
||||
};
|
||||
|
||||
@@ -67,6 +67,7 @@ void PlatformMouseController::UpdateDisplayInfoList(
|
||||
}
|
||||
|
||||
int PlatformMouseController::Destroy() {
|
||||
ReleasePressedButtons();
|
||||
CleanupWaylandPortal();
|
||||
|
||||
if (display_) {
|
||||
@@ -77,6 +78,28 @@ int PlatformMouseController::Destroy() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned PlatformMouseController::ReleasePressedButtons(unsigned mask) {
|
||||
mask &= 7;
|
||||
const unsigned pressed = pressed_buttons_ & mask;
|
||||
for (unsigned i = 0; i < 3; ++i) {
|
||||
const unsigned bit = 1u << i;
|
||||
if (!(pressed & bit)) continue;
|
||||
bool released = false;
|
||||
if (use_wayland_portal_) {
|
||||
RemoteAction up{};
|
||||
up.type = ControlType::mouse;
|
||||
up.m.flag = static_cast<MouseFlag>(2 + i * 2);
|
||||
released = SendWaylandMouseCommand(up, 0) == 0;
|
||||
} else if (display_) {
|
||||
const unsigned buttons[] = {1, 3, 2};
|
||||
released = XTestFakeButtonEvent(display_, buttons[i], False, CurrentTime) != 0;
|
||||
XFlush(display_);
|
||||
}
|
||||
if (released) pressed_buttons_ &= ~bit;
|
||||
}
|
||||
return mask & ~pressed_buttons_;
|
||||
}
|
||||
|
||||
int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
int display_index) {
|
||||
if (remote_action.type != ControlType::mouse) {
|
||||
@@ -84,7 +107,16 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
}
|
||||
|
||||
if (use_wayland_portal_) {
|
||||
return SendWaylandMouseCommand(remote_action, display_index);
|
||||
const int ret = SendWaylandMouseCommand(remote_action, display_index);
|
||||
if (ret == 0) {
|
||||
const int flag = static_cast<int>(remote_action.m.flag);
|
||||
if (flag >= 1 && flag <= 6) {
|
||||
const unsigned bit = 1u << ((flag - 1) / 2);
|
||||
if (flag & 1) pressed_buttons_ |= bit;
|
||||
else pressed_buttons_ &= ~bit;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!display_) {
|
||||
@@ -112,27 +144,27 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
break;
|
||||
}
|
||||
case MouseFlag::left_down:
|
||||
XTestFakeButtonEvent(display_, 1, True, CurrentTime);
|
||||
if (!XTestFakeButtonEvent(display_, 1, True, CurrentTime)) return -1;
|
||||
XFlush(display_);
|
||||
break;
|
||||
case MouseFlag::left_up:
|
||||
XTestFakeButtonEvent(display_, 1, False, CurrentTime);
|
||||
if (!XTestFakeButtonEvent(display_, 1, False, CurrentTime)) return -1;
|
||||
XFlush(display_);
|
||||
break;
|
||||
case MouseFlag::right_down:
|
||||
XTestFakeButtonEvent(display_, 3, True, CurrentTime);
|
||||
if (!XTestFakeButtonEvent(display_, 3, True, CurrentTime)) return -1;
|
||||
XFlush(display_);
|
||||
break;
|
||||
case MouseFlag::right_up:
|
||||
XTestFakeButtonEvent(display_, 3, False, CurrentTime);
|
||||
if (!XTestFakeButtonEvent(display_, 3, False, CurrentTime)) return -1;
|
||||
XFlush(display_);
|
||||
break;
|
||||
case MouseFlag::middle_down:
|
||||
XTestFakeButtonEvent(display_, 2, True, CurrentTime);
|
||||
if (!XTestFakeButtonEvent(display_, 2, True, CurrentTime)) return -1;
|
||||
XFlush(display_);
|
||||
break;
|
||||
case MouseFlag::middle_up:
|
||||
XTestFakeButtonEvent(display_, 2, False, CurrentTime);
|
||||
if (!XTestFakeButtonEvent(display_, 2, False, CurrentTime)) return -1;
|
||||
XFlush(display_);
|
||||
break;
|
||||
case MouseFlag::wheel_vertical: {
|
||||
@@ -157,6 +189,12 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
break;
|
||||
}
|
||||
|
||||
const int flag = static_cast<int>(remote_action.m.flag);
|
||||
if (flag >= 1 && flag <= 6) {
|
||||
const unsigned bit = 1u << ((flag - 1) / 2);
|
||||
if (flag & 1) pressed_buttons_ |= bit;
|
||||
else pressed_buttons_ &= ~bit;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class PlatformMouseController final : public MouseController {
|
||||
virtual int Init(std::vector<DisplayInfo> display_info_list);
|
||||
virtual int Destroy();
|
||||
virtual int SendMouseCommand(RemoteAction remote_action, int display_index);
|
||||
unsigned ReleasePressedButtons(unsigned mask = 7) override;
|
||||
void UpdateDisplayInfoList(const std::vector<DisplayInfo>& display_info_list);
|
||||
|
||||
private:
|
||||
@@ -58,6 +59,7 @@ class PlatformMouseController final : public MouseController {
|
||||
int screen_width_ = 0;
|
||||
int screen_height_ = 0;
|
||||
bool use_wayland_portal_ = false;
|
||||
unsigned pressed_buttons_ = 0;
|
||||
|
||||
DBusConnection* dbus_connection_ = nullptr;
|
||||
std::string wayland_session_handle_;
|
||||
|
||||
@@ -46,7 +46,37 @@ int PlatformMouseController::Init(std::vector<DisplayInfo> display_info_list) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PlatformMouseController::Destroy() { return 0; }
|
||||
int PlatformMouseController::Destroy() {
|
||||
return ReleasePressedButtons() == 7 ? 0 : -1;
|
||||
}
|
||||
|
||||
unsigned PlatformMouseController::ReleasePressedButtons(unsigned mask) {
|
||||
mask &= 7;
|
||||
const unsigned pressed = pressed_buttons_ & mask;
|
||||
if (!pressed) return mask;
|
||||
const auto cursor = CGEventCreate(nullptr);
|
||||
if (!cursor) return mask & ~pressed;
|
||||
const auto point = CGEventGetLocation(cursor);
|
||||
CFRelease(cursor);
|
||||
const CGEventType types[] = {kCGEventLeftMouseUp, kCGEventRightMouseUp,
|
||||
kCGEventOtherMouseUp};
|
||||
const CGMouseButton buttons[] = {kCGMouseButtonLeft, kCGMouseButtonRight,
|
||||
kCGMouseButtonCenter};
|
||||
for (unsigned i = 0; i < 3; ++i) {
|
||||
const unsigned bit = 1u << i;
|
||||
if (!(pressed & bit)) continue;
|
||||
const auto event = CGEventCreateMouseEvent(nullptr, types[i], point, buttons[i]);
|
||||
if (!event) continue;
|
||||
CGEventSetIntegerValueField(event, kCGEventSourceUserData, kCrossDeskInjectedEvent);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
pressed_buttons_ &= ~bit;
|
||||
if (i == 0) { left_dragging_ = false; left_click_tracker_ = {}; }
|
||||
if (i == 1) { right_dragging_ = false; right_click_tracker_ = {}; }
|
||||
if (i == 2) middle_click_tracker_ = {};
|
||||
}
|
||||
return mask & ~pressed_buttons_;
|
||||
}
|
||||
|
||||
void PlatformMouseController::UpdateDisplayInfoList(
|
||||
const std::vector<DisplayInfo>& display_info_list) {
|
||||
@@ -135,7 +165,6 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
switch (remote_action.m.flag) {
|
||||
case MouseFlag::left_down:
|
||||
mouse_type = kCGEventLeftMouseDown;
|
||||
left_dragging_ = true;
|
||||
click_state = BeginClick(left_click_tracker_, tracked_mouse_pos_x,
|
||||
tracked_mouse_pos_y);
|
||||
mouse_event = CGEventCreateMouseEvent(NULL, mouse_type, mouse_point,
|
||||
@@ -144,7 +173,6 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
break;
|
||||
case MouseFlag::left_up:
|
||||
mouse_type = kCGEventLeftMouseUp;
|
||||
left_dragging_ = false;
|
||||
click_state = EndClick(left_click_tracker_, tracked_mouse_pos_x,
|
||||
tracked_mouse_pos_y);
|
||||
mouse_event = CGEventCreateMouseEvent(NULL, mouse_type, mouse_point,
|
||||
@@ -153,7 +181,6 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
break;
|
||||
case MouseFlag::right_down:
|
||||
mouse_type = kCGEventRightMouseDown;
|
||||
right_dragging_ = true;
|
||||
click_state = BeginClick(right_click_tracker_, tracked_mouse_pos_x,
|
||||
tracked_mouse_pos_y);
|
||||
mouse_event = CGEventCreateMouseEvent(NULL, mouse_type, mouse_point,
|
||||
@@ -162,7 +189,6 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
break;
|
||||
case MouseFlag::right_up:
|
||||
mouse_type = kCGEventRightMouseUp;
|
||||
right_dragging_ = false;
|
||||
click_state = EndClick(right_click_tracker_, tracked_mouse_pos_x,
|
||||
tracked_mouse_pos_y);
|
||||
mouse_event = CGEventCreateMouseEvent(NULL, mouse_type, mouse_point,
|
||||
@@ -215,6 +241,16 @@ int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
kCrossDeskInjectedEvent);
|
||||
CGEventPost(kCGHIDEventTap, mouse_event);
|
||||
CFRelease(mouse_event);
|
||||
const int flag = static_cast<int>(remote_action.m.flag);
|
||||
if (flag >= 1 && flag <= 6) {
|
||||
const unsigned bit = 1u << ((flag - 1) / 2);
|
||||
if (flag & 1) pressed_buttons_ |= bit;
|
||||
else pressed_buttons_ &= ~bit;
|
||||
left_dragging_ = (pressed_buttons_ & 1) != 0;
|
||||
right_dragging_ = (pressed_buttons_ & 2) != 0;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -26,6 +26,7 @@ class PlatformMouseController final : public MouseController {
|
||||
virtual int Init(std::vector<DisplayInfo> display_info_list);
|
||||
virtual int Destroy();
|
||||
virtual int SendMouseCommand(RemoteAction remote_action, int display_index);
|
||||
unsigned ReleasePressedButtons(unsigned mask = 7) override;
|
||||
void UpdateDisplayInfoList(
|
||||
const std::vector<DisplayInfo>& display_info_list);
|
||||
|
||||
@@ -45,6 +46,7 @@ class PlatformMouseController final : public MouseController {
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
bool left_dragging_ = false;
|
||||
bool right_dragging_ = false;
|
||||
unsigned pressed_buttons_ = 0;
|
||||
ClickTracker left_click_tracker_;
|
||||
ClickTracker right_click_tracker_;
|
||||
ClickTracker middle_click_tracker_;
|
||||
|
||||
@@ -40,11 +40,14 @@ void PlatformMouseController::UpdateDisplayInfoList(
|
||||
display_info_list_.size());
|
||||
}
|
||||
|
||||
int PlatformMouseController::Destroy() { return ReleasePressedButtons(); }
|
||||
int PlatformMouseController::Destroy() {
|
||||
return ReleasePressedButtons() == 7 ? 0 : -1;
|
||||
}
|
||||
|
||||
int PlatformMouseController::ReleasePressedButtons() {
|
||||
const unsigned pressed = pressed_buttons_.load();
|
||||
if (!pressed) return 0;
|
||||
unsigned PlatformMouseController::ReleasePressedButtons(unsigned mask) {
|
||||
mask &= 7;
|
||||
const unsigned pressed = pressed_buttons_.load() & mask;
|
||||
if (!pressed) return mask;
|
||||
INPUT input{};
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.dwExtraInfo = kInjectedMouseInputMarker;
|
||||
@@ -53,10 +56,10 @@ int PlatformMouseController::ReleasePressedButtons() {
|
||||
if (pressed & 4) input.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
|
||||
if (SendInputOnUserDesktop(input) != 1) {
|
||||
LOG_WARN("Release remote mouse buttons failed, error={}", GetLastError());
|
||||
return -1;
|
||||
return mask & ~pressed;
|
||||
}
|
||||
pressed_buttons_.fetch_and(~pressed);
|
||||
return 0;
|
||||
return mask;
|
||||
}
|
||||
|
||||
int PlatformMouseController::SendMouseCommand(RemoteAction remote_action,
|
||||
|
||||
@@ -29,7 +29,7 @@ class PlatformMouseController final : public MouseController {
|
||||
virtual int SendMouseCommand(RemoteAction remote_action, int display_index);
|
||||
void UpdateDisplayInfoList(
|
||||
const std::vector<DisplayInfo>& display_info_list) override;
|
||||
int ReleasePressedButtons();
|
||||
unsigned ReleasePressedButtons(unsigned mask = 7) override;
|
||||
|
||||
private:
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
|
||||
@@ -244,6 +244,10 @@ class HarmonyMouse final : public MouseController {
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
unsigned ReleasePressedButtons(unsigned mask = 7) override {
|
||||
std::lock_guard lock(input_mutex);
|
||||
return input.ReleaseMouseButtons(mask);
|
||||
}
|
||||
int Init(std::vector<DisplayInfo> displays) override {
|
||||
if (displays.empty()) return -1;
|
||||
std::lock_guard lock(input_mutex);
|
||||
|
||||
@@ -111,12 +111,17 @@ bool Input::Apply(const RemoteAction& action) {
|
||||
bool Input::Release() {
|
||||
for (const auto& change : ReconcileKeyboardState(keys_, {}))
|
||||
Key({change.key_code, change.scan_code, change.extended}, false);
|
||||
for (int button = 0; button < 3; ++button)
|
||||
if (buttons_ & (1u << button)) Pointer(2 + button * 2, x_, y_, 0);
|
||||
ReleaseMouseButtons(7);
|
||||
// Failed releases remain pending so a later cleanup can retry them.
|
||||
have_key_sequence_ = false;
|
||||
return keys_.empty() && buttons_ == 0;
|
||||
}
|
||||
unsigned Input::ReleaseMouseButtons(unsigned mask) {
|
||||
mask &= 7;
|
||||
for (int button = 0; button < 3; ++button)
|
||||
if (buttons_ & mask & (1u << button)) Pointer(2 + button * 2, x_, y_, 0);
|
||||
return mask & ~buttons_;
|
||||
}
|
||||
bool Input::LockWorkstation() {
|
||||
// The public Harmony shortcut is Meta+L. Never carry a prior remote drag or
|
||||
// modifier into it, and retain failed releases for the normal cleanup path.
|
||||
|
||||
@@ -16,6 +16,7 @@ class Input {
|
||||
bool Configure(int width, int height, int display);
|
||||
bool Apply(const RemoteAction& action);
|
||||
bool Release();
|
||||
unsigned ReleaseMouseButtons(unsigned mask);
|
||||
bool LockWorkstation();
|
||||
private:
|
||||
bool Key(const KeyboardKey& key, bool down);
|
||||
|
||||
Reference in New Issue
Block a user