ANTICHAT

ANTICHAT (https://forum.antichat.xyz/index.php)
-   Общие вопросы программирования (https://forum.antichat.xyz/forumdisplay.php?f=206)
-   -   Как поймать нажатие 3+ дополнительных клавиш мыши? (https://forum.antichat.xyz/showthread.php?t=1374080)

Ya Zaregalsya 28.12.2020 13:02

Приобрёл мышь с кучей кнопок (ЛКМ, ПКМ, СКМ и пять дополнительных). Так сложилось, что для нормального её использования мне необходимо переставить левый и правый клики на 3-ю и 4-ю доп. кнопки мыши. Так сейчас выглядят настройки драйвера:

Цитата:

Сообщение от Спойлер

Button 4-8 — это те самые доп. кнопки (далее будем называть их X/XBUTTON), всего их пять. Button 4 — это X1; Button 8 — это X5. Необходимо назначить клики именно на X3 и X4, ни одна другая для этой задачи не подходит. Собственно, в настройках это и сделано.

Но вот проблема. Как оказалось, Windows полноценно поддерживает не более двух дополнительных кнопок. Из-за этого X1 и X2 работают во всех приложениях, а X3, X4 и X5 работают только в половине, остальные их игнорируют.

Задача: Поймать нажатия этих кнопок в GTA SA. Хотелось бы конечно заставить их работать во всех приложениях, но если бы это было так легко, то это сделал бы производитель.

Препятствия: У меня почти нет опыта в работе с API Windows и драйверами, а решение проблемы находится за стенами интерфейсов, с которыми я имел дело. Вот есть список кодов клавиш, там есть X1 и X2, но нет X3, X4, X5. И всё, тут лоб врезается в стену.

Цитата:

Сообщение от Спойлер

Надежда: Как минимум в ГТА это точно возможно сделать. Очень много приложений работают с этими кнопками без нареканий. Далее несколько примеров.

Доп. кнопки поддерживаются: блокнот, раб. стол, Google Chrome, Visual Studio, Blender, NotePad++, MagicTXD.

Доп. кнопки не поддерживаются: диспетчер задач, редактор реестра, SciTe4AutoHotKey, клиент SAMP, GTA SA.

Код некоторых из них размещён в открытом доступе. Моих знаний, увы, пока недостаточно, чтобы быстро разобраться, за счёт чего они это делают. Но если более опытный разработчик взглянет на эти примеры, то наверно сможет выяснить ответ, MagicTXD — наиболее легковесный и близкий к ГТА образец. А пока что вместо мыши кирпич.

Ya Zaregalsya 29.12.2020 14:15

Прогресс:

• Граф. интерфейс пользователя в MagicTXD реализован не голыми руками через какой-нибудь OpenGL, а через всем известный фреймворк Qt.

• У Qt есть своя вики, находим там страницу, где описаны все идентификаторы основного пространства имён. Первым делом внимание падает на перечисление MouseButton. Там указаны заветные дополнительные кнопки. Здесь уже прослеживается описанная выше логика: из всех доп. кнопок первые две — особенные.

• Там же указаны ExtraButton3 и ExtraButton4. Пока оставим в стороне почему третья кнопка имеет дополнительное название, но это неожиданно.

• Тут вступает в дело нехватка моей квалификации. Я никогда не работал с указателями в виде литералов, только под видом переменных. Пока предположу, что они обозначают смещение от какого-то другого адреса: XAddress + 0x00000020 = ExtraButton 3. Дальше нужно искать XAddress.

Ya Zaregalsya 29.12.2020 15:44

Исходники Qt опубликованы на ГитХабе. Файлы по искомой теме:

qwindow.cpp/.h;

qeventloop.cpp/.h;

qabstracteventdistatcher.cpp/.h;

qcoreapplication.cpp/.h;

qguiapplication.cpp/.h;

qwindowsysteminterface.cpp/.h/
_p.h(судя по всему, здесь Qt захватывает события из Windows);

qcoreevent.cpp/.h (здесь реализован класс QEvent);

qevent.cpp/.h (QInputEvent, QMouseEvent);

qinputdevice.cpp/.h;

qwindowsmousehandler.cpp/.h;

https://code.woboq.org/qt5/qtbase/s...entLoop4execE6QFlagsINS_17ProcessEventsFlagEE

https://code.woboq.org/qt5/qtbase/src/gui/kernel/qguiapplication.cpp.html#1896(а здесь Qt уже разбирает события Windows);

https://code.woboq.org/qt5/qtbase/s...SystemInterfacePrivate::WindowSystemEventList

https://code.woboq.org/qt5/qtbase/s...dowSystemInterfacePrivate17WindowSystemEventE

Цитата:

Сообщение от Спойлер

QEvent

|

QInputEvent

|

QPointerEvent

|

QSinglePointEvent

|

QMouseEvent


Цитата:

Сообщение от Спойлер

Цитата:


/*!
\class QEvent
\inmodule QtCore
\brief The QEvent class is the base class of all
event classes. Event objects contain event parameters.

\ingroup events

Qt's main event loop (QCoreApplication::exec()) fetches native
window system events from the event queue, translates them into

QEvents, and sends the translated events to \l{QObject}s.

In general, events come from the underlying window system

(spontaneous() returns \c true), but it is also possible to manually

send events using QCoreApplication::sendEvent() and

QCoreApplication::postEvent() (spontaneous() returns \c false).

\l {QObject}{QObjects} receive events by having their QObject::event() function
called. The function can be reimplemented in subclasses to
customize event handling and add additional event types;
QWidget::event() is a notable example. By default, events are
dispatched to event handlers like QObject::timerEvent() and
QWidget::mouseMoveEvent(). QObject::installEventFilter() allows an
object to intercept events destined for another object.

The basic QEvent contains only an event type parameter and an
"accept" flag. The accept flag set with accept(), and cleared
with ignore(). It is set by default, but don't rely on this as
subclasses may choose to clear it in their constructor.

Subclasses of QEvent contain additional parameters that describe
the particular event.

\sa QObject::event(), QObject::installEventFilter(),
QCoreApplication::sendEvent(),
QCoreApplication::postEvent(), QCoreApplication::processEvents()
*/


Цитата:

Сообщение от Спойлер

Цитата:


Detailed Description​This class is used by non-GUI applications to provide their event loop. For non-GUI application that uses Qt, there should be exactly one QCoreApplication object. For GUI applications, seeQGuiApplication. For applications that use the Qt Widgets module, see QApplication.

QCoreApplication contains the main event loop, where all events from the operating system (e.g., timer and network events) and other sources are processed and dispatched. It also handles the application's initialization and finalization, as well as system-wide and application-wide settings.


The Event Loop and Event Handling​The event loop is started with a call to exec(). Long-running operations can call processEvents() to keep the application responsive.

In general, we recommend that you create a QCoreApplication, QGuiApplication or a QApplication object in your main() function as early as possible. exec() will not return until the event loop exits; e.g., when quit() is called.

Several static convenience functions are also provided. The QCoreApplication object is available from instance(). Events can be sent with sendEvent() or posted to an event queue with postEvent(). Pending events can be removed with removePostedEvents() or dispatched with sendPostedEvents().

The class provides a quit() slot and an aboutToQuit() signal.


Цитата:

Сообщение от Спойлер


C++:





Код:

void
QGuiApplicationPrivate
::
processWindowSystemEvent
(
QWindowSystemInterfacePrivate
::
WindowSystemEvent
*
e
)
{
Q_TRACE_SCOPE
(
QGuiApplicationPrivate_processWindowSystemEvent
,
e
->
type
)
;
switch
(
e
->
type
)
{
case
QWindowSystemInterfacePrivate
::
Mouse
:
QGuiApplicationPrivate
::
processMouseEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
Wheel
:
QGuiApplicationPrivate
::
processWheelEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
Key
:
QGuiApplicationPrivate
::
processKeyEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
Touch
:
QGuiApplicationPrivate
::
processTouchEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
GeometryChange
:
QGuiApplicationPrivate
::
processGeometryChangeEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
Enter
:
QGuiApplicationPrivate
::
processEnterEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
Leave
:
QGuiApplicationPrivate
::
processLeaveEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
ActivatedWindow
:
QGuiApplicationPrivate
::
processActivatedEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
WindowStateChanged
:
QGuiApplicationPrivate
::
processWindowStateChangedEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
WindowScreenChanged
:
QGuiApplicationPrivate
::
processWindowScreenChangedEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
SafeAreaMarginsChanged
:
QGuiApplicationPrivate
::
processSafeAreaMarginsChangedEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
ApplicationStateChanged
:
{
QWindowSystemInterfacePrivate
::
ApplicationStateChangedEvent
*
changeEvent
=
static_cast

(
e
)
;
QGuiApplicationPrivate
::
setApplicationState
(
changeEvent
->
newState
,
changeEvent
->
forcePropagate
)
;
}
break
;
case
QWindowSystemInterfacePrivate
::
FlushEvents
:
{
QWindowSystemInterfacePrivate
::
FlushEventsEvent
*
flushEventsEvent
=
static_cast

(
e
)
;
QWindowSystemInterface
::
deferredFlushWindowSystemEvents
(
flushEventsEvent
->
flags
)
;
}
break
;
case
QWindowSystemInterfacePrivate
::
Close
:
QGuiApplicationPrivate
::
processCloseEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
ScreenOrientation
:
QGuiApplicationPrivate
::
processScreenOrientationChange
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
ScreenGeometry
:
QGuiApplicationPrivate
::
processScreenGeometryChange
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
ScreenLogicalDotsPerInch
:
QGuiApplicationPrivate
::
processScreenLogicalDotsPerInchChange
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
ScreenRefreshRate
:
QGuiApplicationPrivate
::
processScreenRefreshRateChange
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
ThemeChange
:
QGuiApplicationPrivate
::
processThemeChanged
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
Expose
:
QGuiApplicationPrivate
::
processExposeEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
Tablet
:
QGuiApplicationPrivate
::
processTabletEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
TabletEnterProximity
:
QGuiApplicationPrivate
::
processTabletEnterProximityEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
TabletLeaveProximity
:
QGuiApplicationPrivate
::
processTabletLeaveProximityEvent
(
static_cast

(
e
)
)
;
break
;
#ifndef QT_NO_GESTURES
case
QWindowSystemInterfacePrivate
::
Gesture
:
QGuiApplicationPrivate
::
processGestureEvent
(
static_cast

(
e
)
)
;
break
;
#endif
case
QWindowSystemInterfacePrivate
::
PlatformPanel
:
QGuiApplicationPrivate
::
processPlatformPanelEvent
(
static_cast

(
e
)
)
;
break
;
case
QWindowSystemInterfacePrivate
::
FileOpen
:
QGuiApplicationPrivate
::
processFileOpenEvent
(
static_cast

(
e
)
)
;
break
;
#ifndef QT_NO_CONTEXTMENU
case
QWindowSystemInterfacePrivate
::
ContextMenu
:
QGuiApplicationPrivate
::
processContextMenuEvent
(
static_cast

(
e
)
)
;
break
;
#endif
case
QWindowSystemInterfacePrivate
::
EnterWhatsThisMode
:
QGuiApplication
::
postEvent
(
QGuiApplication
::
instance
(
)
,
new
QEvent
(
QEvent
::
EnterWhatsThisMode
)
)
;
break
;
default
:
qWarning
(
)

type
;
break
;
}
}



C++:





Код:

void
QGuiApplicationPrivate
::
processMouseEvent
(
QWindowSystemInterfacePrivate
::
MouseEvent
*
e
)
{
QEvent
::
Type type
=
QEvent
::
None
;
Qt
::
MouseButton button
=
Qt
::
NoButton
;
QWindow
*
window
=
e
->
window
.
data
(
)
;
bool
positionChanged
=
QGuiApplicationPrivate
::
lastCursorPosition
!=
e
->
globalPos
;
bool
mouseMove
=
false
;
bool
mousePress
=
false
;
if
(
e
->
enhancedMouseEvent
(
)
)
{
type
=
e
->
buttonType
;
button
=
e
->
button
;
if
(
type
==
QEvent
::
NonClientAreaMouseMove
||
type
==
QEvent
::
MouseMove
)
mouseMove
=
true
;
else
if
(
type
==
QEvent
::
NonClientAreaMouseButtonPress
||
type
==
QEvent
::
MouseButtonPress
)
mousePress
=
true
;
if
(
!
mouseMove
&&
positionChanged
)
{
QWindowSystemInterfacePrivate
::
MouseEvent
moveEvent
(
window
,
e
->
timestamp
,
e
->
localPos
,
e
->
globalPos
,
e
->
buttons
^
button
,
e
->
modifiers
,
Qt
::
NoButton
,
e
->
nonClientArea
?
QEvent
::
NonClientAreaMouseMove
:
QEvent
::
MouseMove
,
e
->
source
,
e
->
nonClientArea
)
;
if
(
e
->
synthetic
(
)
)
moveEvent
.
flags
|=
QWindowSystemInterfacePrivate
::
WindowSystemEvent
::
Synthetic
;
processMouseEvent
(
&
moveEvent
)
;
// mouse move excluding state change
processMouseEvent
(
e
)
;
// the original mouse event
return
;
}
}
else
{
Qt
::
MouseButtons stateChange
=
e
->
buttons
^
mouse_buttons
;
if
(
positionChanged
&&
(
stateChange
!=
Qt
::
NoButton
)
)
{
QWindowSystemInterfacePrivate
::
MouseEvent
moveEvent
(
window
,
e
->
timestamp
,
e
->
localPos
,
e
->
globalPos
,
mouse_buttons
,
e
->
modifiers
,
Qt
::
NoButton
,
QEvent
::
None
,
e
->
source
,
e
->
nonClientArea
)
;
if
(
e
->
synthetic
(
)
)
moveEvent
.
flags
|=
QWindowSystemInterfacePrivate
::
WindowSystemEvent
::
Synthetic
;
processMouseEvent
(
&
moveEvent
)
;
// mouse move excluding state change
processMouseEvent
(
e
)
;
// the original mouse event
return
;
}
// In the compatibility path we deduce event type and button that caused the event
if
(
positionChanged
)
{
mouseMove
=
true
;
type
=
e
->
nonClientArea
?
QEvent
::
NonClientAreaMouseMove
:
QEvent
::
MouseMove
;
}
else
{
// Check to see if a new button has been pressed/released.
for
(
uint mask
=
Qt
::
LeftButton
;
mask

buttons
)
{
mousePress
=
true
;
type
=
e
->
nonClientArea
?
QEvent
::
NonClientAreaMouseButtonPress
:
QEvent
::
MouseButtonPress
;
}
else
{
type
=
e
->
nonClientArea
?
QEvent
::
NonClientAreaMouseButtonRelease
:
QEvent
::
MouseButtonRelease
;
}
}
}
modifier_buttons
=
e
->
modifiers
;
QPointF localPoint
=
e
->
localPos
;
QPointF globalPoint
=
e
->
globalPos
;
bool
doubleClick
=
false
;
if
(
mouseMove
)
{
QGuiApplicationPrivate
::
lastCursorPosition
=
globalPoint
;
const
auto
doubleClickDistance
=
e
->
source
==
Qt
::
MouseEventNotSynthesized
?
mouseDoubleClickDistance
:
touchDoubleTapDistance
;
if
(
qAbs
(
globalPoint
.
x
(
)
-
mousePressX
)
>
doubleClickDistance
||
qAbs
(
globalPoint
.
y
(
)
-
mousePressY
)
>
doubleClickDistance
)
mousePressButton
=
Qt
::
NoButton
;
}
else
{
mouse_buttons
=
e
->
buttons
;
if
(
mousePress
)
{
ulong doubleClickInterval
=
static_cast

(
QGuiApplication
::
styleHints
(
)
->
mouseDoubleClickInterval
(
)
)
;
doubleClick
=
e
->
timestamp
-
mousePressTime

timestamp
;
mousePressButton
=
button
;
const
QPoint point
=
QGuiApplicationPrivate
::
lastCursorPosition
.
toPoint
(
)
;
mousePressX
=
point
.
x
(
)
;
mousePressY
=
point
.
y
(
)
;
}
}
if
(
e
->
nullWindow
(
)
)
{
window
=
QGuiApplication
::
topLevelAt
(
globalPoint
.
toPoint
(
)
)
;
if
(
window
)
{
// Moves and the release following a press must go to the same
// window, even if the cursor has moved on over another window.
if
(
e
->
buttons
!=
Qt
::
NoButton
)
{
if
(
!
currentMousePressWindow
)
currentMousePressWindow
=
window
;
else
window
=
currentMousePressWindow
;
}
else
if
(
currentMousePressWindow
)
{
window
=
currentMousePressWindow
;
currentMousePressWindow
=
0
;
}
QPointF delta
=
globalPoint
-
globalPoint
.
toPoint
(
)
;
localPoint
=
window
->
mapFromGlobal
(
globalPoint
.
toPoint
(
)
)
+
delta
;
}
}
if
(
!
window
)
return
;
#ifndef QT_NO_CURSOR
if
(
!
e
->
synthetic
(
)
)
{
if
(
const
QScreen
*
screen
=
window
->
screen
(
)
)
if
(
QPlatformCursor
*
cursor
=
screen
->
handle
(
)
->
cursor
(
)
)
{
const
QPointF nativeLocalPoint
=
QHighDpi
::
toNativePixels
(
localPoint
,
screen
)
;
const
QPointF nativeGlobalPoint
=
QHighDpi
::
toNativePixels
(
globalPoint
,
screen
)
;
QMouseEvent
ev
(
type
,
nativeLocalPoint
,
nativeLocalPoint
,
nativeGlobalPoint
,
button
,
e
->
buttons
,
e
->
modifiers
,
e
->
source
)
;
ev
.
setTimestamp
(
e
->
timestamp
)
;
cursor
->
pointerEvent
(
ev
)
;
}
}
#endif
QMouseEvent
ev
(
type
,
localPoint
,
localPoint
,
globalPoint
,
button
,
e
->
buttons
,
e
->
modifiers
,
e
->
source
)
;
ev
.
setTimestamp
(
e
->
timestamp
)
;
if
(
window
->
d_func
(
)
->
blockedByModalWindow
&&
!
qApp
->
d_func
(
)
->
popupActive
(
)
)
{
// a modal window is blocking this window, don't allow mouse events through
return
;
}
if
(
doubleClick
&&
(
ev
.
type
(
)
==
QEvent
::
MouseButtonPress
)
)
{
// QtBUG-25831, used to suppress delivery in qwidgetwindow.cpp
setMouseEventFlags
(
&
ev
,
ev
.
flags
(
)
|
Qt
::
MouseEventCreatedDoubleClick
)
;
}
QGuiApplication
::
sendSpontaneousEvent
(
window
,
&
ev
)
;
e
->
eventAccepted
=
ev
.
isAccepted
(
)
;
if
(
!
e
->
synthetic
(
)
&&
!
ev
.
isAccepted
(
)
&&
!
e
->
nonClientArea
&&
qApp
->
testAttribute
(
Qt
::
AA_SynthesizeTouchForUnhandledMouseEvents
)
)
{
if
(
!
m_fakeTouchDevice
)
{
m_fakeTouchDevice
=
new
QTouchDevice
;
QWindowSystemInterface
::
registerTouchDevice
(
m_fakeTouchDevice
)
;
}
QList

points
;
QWindowSystemInterface
::
TouchPoint point
;
point
.
id
=
1
;
point
.
area
=
QRectF
(
globalPoint
.
x
(
)
-
2
,
globalPoint
.
y
(
)
-
2
,
4
,
4
)
;
// only translate left button related events to
// avoid strange touch event sequences when several
// buttons are pressed
if
(
type
==
QEvent
::
MouseButtonPress
&&
button
==
Qt
::
LeftButton
)
{
point
.
state
=
Qt
::
TouchPointPressed
;
}
else
if
(
type
==
QEvent
::
MouseButtonRelease
&&
button
==
Qt
::
LeftButton
)
{
point
.
state
=
Qt
::
TouchPointReleased
;
}
else
if
(
type
==
QEvent
::
MouseMove
&&
(
e
->
buttons
&
Qt
::
LeftButton
)
)
{
point
.
state
=
Qt
::
TouchPointMoved
;
}
else
{
return
;
}
points

touchPoints
=
QWindowSystemInterfacePrivate
::
fromNativeTouchPoints
(
points
,
window
,
QTouchDevicePrivate
::
get
(
m_fakeTouchDevice
)
->
id
,
&
type
)
;
QWindowSystemInterfacePrivate
::
TouchEvent
fake
(
window
,
e
->
timestamp
,
type
,
m_fakeTouchDevice
,
touchPoints
,
e
->
modifiers
)
;
fake
.
flags
|=
QWindowSystemInterfacePrivate
::
WindowSystemEvent
::
Synthetic
;
processTouchEvent
(
&
fake
)
;
}
if
(
doubleClick
)
{
mousePressButton
=
Qt
::
NoButton
;
if
(
!
e
->
window
.
isNull
(
)
||
e
->
nullWindow
(
)
)
{
// QTBUG-36364, check if window closed in response to press
const
QEvent
::
Type doubleClickType
=
e
->
nonClientArea
?
QEvent
::
NonClientAreaMouseButtonDblClick
:
QEvent
::
MouseButtonDblClick
;
QMouseEvent
dblClickEvent
(
doubleClickType
,
localPoint
,
localPoint
,
globalPoint
,
button
,
e
->
buttons
,
e
->
modifiers
,
e
->
source
)
;
dblClickEvent
.
setTimestamp
(
e
->
timestamp
)
;
QGuiApplication
::
sendSpontaneousEvent
(
window
,
&
dblClickEvent
)
;
}
}
}





Ya Zaregalsya 23.01.2021 02:18

Цитата:

Сообщение от Ya Zaregalsya

Я провозился с этими кнопками месяц, потратил 60-70 часов времени, разочаровался в IT, в человечестве, проклял всю Кремневую долину и японскую нацию, готовился изучать микроконтроллеры и писать драйвер с нуля.

Оказывается, нужно было всего лишь запустить софт для мыши от имени администратора.

Спасибо всем специалистам Бластхака за неоценимую помощь!


Время: 03:50