 |

28.12.2020, 13:02
|
|
Постоянный
Регистрация: 29.05.2017
Сообщений: 394
С нами:
4714653
Репутация:
98
|
|
Приобрёл мышь с кучей кнопок (ЛКМ, ПКМ, СКМ и пять дополнительных). Так сложилось, что для нормального её использования мне необходимо переставить левый и правый клики на 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 — наиболее легковесный и близкий к ГТА образец. А пока что вместо мыши кирпич.
|
|
|

29.12.2020, 14:15
|
|
Постоянный
Регистрация: 29.05.2017
Сообщений: 394
С нами:
4714653
Репутация:
98
|
|
Прогресс:
• Граф. интерфейс пользователя в MagicTXD реализован не голыми руками через какой-нибудь OpenGL, а через всем известный фреймворк Qt.
• У Qt есть своя вики, находим там страницу, где описаны все идентификаторы основного пространства имён. Первым делом внимание падает на перечисление MouseButton. Там указаны заветные дополнительные кнопки. Здесь уже прослеживается описанная выше логика: из всех доп. кнопок первые две — особенные.
• Там же указаны ExtraButton3 и ExtraButton4. Пока оставим в стороне почему третья кнопка имеет дополнительное название, но это неожиданно.
• Тут вступает в дело нехватка моей квалификации. Я никогда не работал с указателями в виде литералов, только под видом переменных. Пока предположу, что они обозначают смещение от какого-то другого адреса: XAddress + 0x00000020 = ExtraButton 3. Дальше нужно искать XAddress.
|
|
|

29.12.2020, 15:44
|
|
Постоянный
Регистрация: 29.05.2017
Сообщений: 394
С нами:
4714653
Репутация:
98
|
|
• Исходники 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: ostEvent() (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:  ostEvent(), QCoreApplication:  rocessEvents()
*/
Сообщение от Спойлер
Detailed DescriptionThis 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 HandlingThe 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
)
;
}
}
}
|
|
|

23.01.2021, 02:18
|
|
Постоянный
Регистрация: 29.05.2017
Сообщений: 394
С нами:
4714653
Репутация:
98
|
|
Сообщение от Ya Zaregalsya
Я провозился с этими кнопками месяц, потратил 60-70 часов времени, разочаровался в IT, в человечестве, проклял всю Кремневую долину и японскую нацию, готовился изучать микроконтроллеры и писать драйвер с нуля.
Оказывается, нужно было всего лишь запустить софт для мыши от имени администратора.
Спасибо всем специалистам Бластхака за неоценимую помощь!
|
|
|
|
 |
Предыдущая тема
Следующая тема
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|