update:mac限制鼠标移动

This commit is contained in:
rankun 2019-02-10 20:22:29 +08:00
parent be616458a2
commit 4e9b7c3aa1
8 changed files with 292 additions and 1 deletions

View file

@ -54,6 +54,7 @@ include ($$PWD/uibase/uibase.pri)
include ($$PWD/fontawesome/fontawesome.pri)
include ($$PWD/filehandler/filehandler.pri)
include ($$PWD/recorder/recorder.pri)
include ($$PWD/util/util.pri)
# 附加包含路径
INCLUDEPATH += \
@ -68,6 +69,7 @@ INCLUDEPATH += \
$$PWD/uibase \
$$PWD/filehandler \
$$PWD/recorder \
$$PWD/util \
$$PWD/fontawesome

View file

@ -6,6 +6,9 @@
#include "dialog.h"
#include "decoder.h"
#ifdef Q_OS_OSX
#include "cocoamousetap.h"
#endif
Dialog* g_mainDlg = Q_NULLPTR;
@ -25,6 +28,10 @@ int main(int argc, char *argv[])
installTranslator();
#ifdef Q_OS_OSX
CocoaMouseTap::getInstance()->initMouseEventTap();
#endif
#ifdef Q_OS_WIN32
qputenv("QTSCRCPY_ADB_PATH", "../../../third_party/adb/win/adb.exe");
qputenv("QTSCRCPY_SERVER_PATH", "../../../third_party/scrcpy-server.jar");
@ -50,6 +57,9 @@ int main(int argc, char *argv[])
int ret = a.exec();
#ifdef Q_OS_OSX
CocoaMouseTap::getInstance()->quitMouseEventTap();
#endif
Decoder::deInit();
return ret;
}

BIN
QtScrcpy/util/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,29 @@
#ifndef COCOAMOUSETAP_H
#define COCOAMOUSETAP_H
#include <QThread>
#include <QSemaphore>
struct MouseEventTapData;
class QWidget;
class CocoaMouseTap : public QThread
{
private:
CocoaMouseTap(QObject *parent = Q_NULLPTR);
~CocoaMouseTap();
public:
static CocoaMouseTap* getInstance();
void initMouseEventTap();
void quitMouseEventTap();
void enableMouseEventTap(QWidget* widget, bool enabled);
protected:
void run() override;
private:
MouseEventTapData *m_tapData = Q_NULLPTR;
QSemaphore m_runloopStartedSemaphore;
static CocoaMouseTap *s_instance;
};
#endif // COCOAMOUSETAP_H

View file

@ -0,0 +1,237 @@
#import <Cocoa/Cocoa.h>
#include <QDebug>
#include <QWidget>
#include "cocoamousetap.h"
static const CGEventMask movementEventsMask =
CGEventMaskBit(kCGEventLeftMouseDragged)
| CGEventMaskBit(kCGEventRightMouseDragged)
| CGEventMaskBit(kCGEventMouseMoved);
static const CGEventMask allGrabbedEventsMask =
CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventLeftMouseUp)
| CGEventMaskBit(kCGEventRightMouseDown) | CGEventMaskBit(kCGEventRightMouseUp)
| CGEventMaskBit(kCGEventOtherMouseDown) | CGEventMaskBit(kCGEventOtherMouseUp)
| CGEventMaskBit(kCGEventLeftMouseDragged) | CGEventMaskBit(kCGEventRightMouseDragged)
| CGEventMaskBit(kCGEventMouseMoved);
typedef struct MouseEventTapData{
CFMachPortRef tap = Q_NULLPTR;
CFRunLoopRef runloop = Q_NULLPTR;
CFRunLoopSourceRef runloopSource = Q_NULLPTR;
QWidget* widget = Q_NULLPTR;
} MouseEventTapData;
static CGEventRef Cocoa_MouseTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
Q_UNUSED(proxy);
MouseEventTapData *tapdata = (MouseEventTapData*)refcon;
NSView *nsview;
NSWindow *nswindow;
NSRect windowRect;
NSRect newWindowRect;
CGPoint eventLocation;
switch (type) {
case kCGEventTapDisabledByTimeout:
{
CGEventTapEnable(tapdata->tap, true);
return NULL;
}
case kCGEventTapDisabledByUserInput:
{
return NULL;
}
default:
break;
}
if (!tapdata->widget) {
return event;
}
// get nswindow from qt widget
nsview = (NSView *)tapdata->widget->window()->winId();
if (!nsview) {
return event;
}
nswindow = [nsview window];
eventLocation = CGEventGetUnflippedLocation(event);
windowRect = [nswindow contentRectForFrameRect:[nswindow frame]];
newWindowRect = NSMakeRect(windowRect.origin.x, windowRect.origin.y,
windowRect.size.width - 10, windowRect.size.height - 10);
qDebug() << newWindowRect.origin.x << newWindowRect.origin.y
<< newWindowRect.size.width << newWindowRect.size.height;
if (!NSMouseInRect(NSPointFromCGPoint(eventLocation), newWindowRect, NO)) {
/* This is in CGs global screenspace coordinate system, which has a
* flipped Y.
*/
CGPoint newLocation = CGEventGetLocation(event);
if (eventLocation.x < NSMinX(windowRect)) {
newLocation.x = NSMinX(windowRect);
} else if (eventLocation.x >= NSMaxX(windowRect)) {
newLocation.x = NSMaxX(windowRect) - 1.0;
}
if (eventLocation.y <= NSMinY(windowRect)) {
newLocation.y -= (NSMinY(windowRect) - eventLocation.y + 1);
} else if (eventLocation.y > NSMaxY(windowRect)) {
newLocation.y += (eventLocation.y - NSMaxY(windowRect));
}
CGWarpMouseCursorPosition(newLocation);
CGAssociateMouseAndMouseCursorPosition(YES);
if ((CGEventMaskBit(type) & movementEventsMask) == 0) {
/* For click events, we just constrain the event to the window, so
* no other app receives the click event. We can't due the same to
* movement events, since they mean that our warp cursor above
* behaves strangely.
*/
CGEventSetLocation(event, newLocation);
}
}
return event;
}
static void SemaphorePostCallback(CFRunLoopTimerRef timer, void *info)
{
Q_UNUSED(timer);
QSemaphore *runloopStartedSemaphore = (QSemaphore *)info;
if (runloopStartedSemaphore) {
runloopStartedSemaphore->release();
}
}
CocoaMouseTap * CocoaMouseTap::s_instance = Q_NULLPTR;
CocoaMouseTap::CocoaMouseTap(QObject *parent)
: QThread(parent)
{
m_tapData = new MouseEventTapData;
}
CocoaMouseTap::~CocoaMouseTap()
{
if (m_tapData) {
delete m_tapData;
m_tapData = Q_NULLPTR;
}
}
CocoaMouseTap *CocoaMouseTap::getInstance()
{
if (s_instance == Q_NULLPTR) {
s_instance = new CocoaMouseTap();
}
return s_instance;
}
void CocoaMouseTap::initMouseEventTap()
{
if (!m_tapData) {
return;
}
m_tapData->tap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
kCGEventTapOptionDefault, allGrabbedEventsMask,
&Cocoa_MouseTapCallback, m_tapData);
if (!m_tapData->tap) {
return;
}
/* Tap starts disabled, until app requests mouse grab */
CGEventTapEnable(m_tapData->tap, false);
start();
}
void CocoaMouseTap::quitMouseEventTap()
{
bool status;
if (m_tapData == Q_NULLPTR || m_tapData->tap == Q_NULLPTR) {
/* event tap was already cleaned up (possibly due to CGEventTapCreate
* returning null.)
*/
return;
}
/* Ensure that the runloop has been started first.
* TODO: Move this to InitMouseEventTap, check for error conditions that can
* happen in Cocoa_MouseTapThread, and fall back to the non-EventTap way of
* grabbing the mouse if it fails to Init.
*/
status = m_runloopStartedSemaphore.tryAcquire(1, 5000);
if (status) {
/* Then stop it, which will cause Cocoa_MouseTapThread to return. */
CFRunLoopStop(m_tapData->runloop);
/* And then wait for Cocoa_MouseTapThread to finish cleaning up. It
* releases some of the pointers in tapdata. */
wait();
}
}
void CocoaMouseTap::enableMouseEventTap(QWidget* widget, bool enabled)
{
if (m_tapData && m_tapData->tap)
{
enabled ? m_tapData->widget = widget : m_tapData->widget = Q_NULLPTR;
CGEventTapEnable(m_tapData->tap, enabled);
}
}
void CocoaMouseTap::run()
{
/* Tap was created on main thread but we own it now. */
CFMachPortRef eventTap = m_tapData->tap;
if (eventTap) {
/* Try to create a runloop source we can schedule. */
CFRunLoopSourceRef runloopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
if (runloopSource) {
m_tapData->runloopSource = runloopSource;
} else {
CFRelease(eventTap);
m_runloopStartedSemaphore.release();
/* TODO: Both here and in the return below, set some state in
* tapdata to indicate that initialization failed, which we should
* check in InitMouseEventTap, after we move the semaphore check
* from Quit to Init.
*/
return;
}
} else {
m_runloopStartedSemaphore.release();
return;
}
m_tapData->runloop = CFRunLoopGetCurrent();
CFRunLoopAddSource(m_tapData->runloop, m_tapData->runloopSource, kCFRunLoopCommonModes);
CFRunLoopTimerContext context = {.info = &m_runloopStartedSemaphore};
/* We signal the runloop started semaphore *after* the run loop has started, indicating it's safe to CFRunLoopStop it. */
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0, 0, 0, &SemaphorePostCallback, &context);
CFRunLoopAddTimer(m_tapData->runloop, timer, kCFRunLoopCommonModes);
CFRelease(timer);
/* Run the event loop to handle events in the event tap. */
CFRunLoopRun();
/* Make sure this is signaled so that SDL_QuitMouseEventTap knows it can safely SDL_WaitThread for us. */
if (m_runloopStartedSemaphore.available() < 1) {
m_runloopStartedSemaphore.release();
}
CFRunLoopRemoveSource(m_tapData->runloop, m_tapData->runloopSource, kCFRunLoopCommonModes);
/* Clean up. */
CGEventTapEnable(m_tapData->tap, false);
CFRelease(m_tapData->runloopSource);
CFRelease(m_tapData->tap);
m_tapData->runloopSource = Q_NULLPTR;
m_tapData->tap = Q_NULLPTR;
return;
}

6
QtScrcpy/util/util.pri Normal file
View file

@ -0,0 +1,6 @@
mac {
HEADERS += $$PWD/cocoamousetap.h
OBJECTIVE_SOURCES += $$PWD/cocoamousetap.mm
LIBS += -framework Appkit
QMAKE_CFLAGS += -mmacosx-version-min=10.6
}

View file

@ -18,6 +18,9 @@
#include "toolform.h"
#include "controlevent.h"
#include "recorder.h"
#ifdef Q_OS_OSX
#include "cocoamousetap.h"
#endif
VideoForm::VideoForm(const QString& serial, quint16 maxSize, quint32 bitRate, const QString& fileName, QWidget *parent) :
QWidget(parent),
@ -126,6 +129,9 @@ void VideoForm::initSignals()
} else {
ClipCursor(Q_NULLPTR);
}
#endif
#ifdef Q_OS_OSX
CocoaMouseTap::getInstance()->enableMouseEventTap(ui->videoWidget, grab);
#endif
});
connect(m_server, &Server::serverStartResult, this, [this](bool success){

View file

@ -1,5 +1,6 @@
// TODO mac: Qt::FramelessWindowHit full screen is abnormal
限制鼠标移动抽象跨平台接口
Mac游戏时向右移动不流畅问题。
工具栏扩展(模拟点击指定次数等)
模拟点击改用手指(注意:辅助按键就都没了)