mirror of
https://github.com/4yn/slidershim.git
synced 2024-11-12 00:40:49 +01:00
use statically linked interception library for directinput emulation
This commit is contained in:
parent
0e75256520
commit
571eec24d3
1
src-interception/.gitignore
vendored
Normal file
1
src-interception/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/
|
140
src-interception/Cargo.lock
generated
Normal file
140
src-interception/Cargo.lock
generated
Normal file
@ -0,0 +1,140 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "interception"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"num_enum",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
|
13
src-interception/Cargo.toml
Normal file
13
src-interception/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "interception"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
num_enum = "0.5.0"
|
||||
serde = { version = "1.0.114", features = ["derive"] }
|
173
src-interception/README.md
Normal file
173
src-interception/README.md
Normal file
@ -0,0 +1,173 @@
|
||||
Derived from https://github.com/bozbez/interception-sys and https://github.com/bozbez/interception-rs
|
||||
|
||||
Repackaged to link statically with https://github.com/oblitum/Interception 1.0.1 instead of using dynamic library.
|
||||
|
||||
Interception is licensed under LGPL 3.0 for non-commercial use. Interception-sys and interception-rs according to its Cargo.toml are licensed under LGPL 3.0.
|
||||
|
||||
```
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
```
|
11
src-interception/build.rs
Normal file
11
src-interception/build.rs
Normal file
@ -0,0 +1,11 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=include/interception.h");
|
||||
println!("cargo:rerun-if-changed=src/interception.c");
|
||||
cc::Build::new()
|
||||
.define("INTERCEPTION_STATIC", None)
|
||||
.file("src/interception.c")
|
||||
.include("include/")
|
||||
.compile("interception");
|
||||
|
||||
println!("cargo:rustc-link-lib=static=interception");
|
||||
}
|
194
src-interception/include/interception.h
Normal file
194
src-interception/include/interception.h
Normal file
@ -0,0 +1,194 @@
|
||||
#ifndef _INTERCEPTION_H_
|
||||
#define _INTERCEPTION_H_
|
||||
|
||||
#ifdef INTERCEPTION_STATIC
|
||||
#define INTERCEPTION_API
|
||||
#else
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#ifdef INTERCEPTION_EXPORT
|
||||
#ifdef __GNUC__
|
||||
#define INTERCEPTION_API __attribute__((dllexport))
|
||||
#else
|
||||
#define INTERCEPTION_API __declspec(dllexport)
|
||||
#endif
|
||||
#else
|
||||
#ifdef __GNUC__
|
||||
#define INTERCEPTION_API __attribute__((dllimport))
|
||||
#else
|
||||
#define INTERCEPTION_API __declspec(dllimport)
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#if __GNUC__ >= 4
|
||||
#define INTERCEPTION_API __attribute__ ((visibility("default")))
|
||||
#else
|
||||
#define INTERCEPTION_API
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define INTERCEPTION_MAX_KEYBOARD 10
|
||||
|
||||
#define INTERCEPTION_MAX_MOUSE 10
|
||||
|
||||
#define INTERCEPTION_MAX_DEVICE ((INTERCEPTION_MAX_KEYBOARD) + (INTERCEPTION_MAX_MOUSE))
|
||||
|
||||
#define INTERCEPTION_KEYBOARD(index) ((index) + 1)
|
||||
|
||||
#define INTERCEPTION_MOUSE(index) ((INTERCEPTION_MAX_KEYBOARD) + (index) + 1)
|
||||
|
||||
typedef void *InterceptionContext;
|
||||
|
||||
typedef int InterceptionDevice;
|
||||
|
||||
typedef int InterceptionPrecedence;
|
||||
|
||||
typedef unsigned short InterceptionFilter;
|
||||
|
||||
typedef int (*InterceptionPredicate)(InterceptionDevice device);
|
||||
|
||||
enum InterceptionKeyState
|
||||
{
|
||||
INTERCEPTION_KEY_DOWN = 0x00,
|
||||
INTERCEPTION_KEY_UP = 0x01,
|
||||
INTERCEPTION_KEY_E0 = 0x02,
|
||||
INTERCEPTION_KEY_E1 = 0x04,
|
||||
INTERCEPTION_KEY_TERMSRV_SET_LED = 0x08,
|
||||
INTERCEPTION_KEY_TERMSRV_SHADOW = 0x10,
|
||||
INTERCEPTION_KEY_TERMSRV_VKPACKET = 0x20
|
||||
};
|
||||
|
||||
enum InterceptionFilterKeyState
|
||||
{
|
||||
INTERCEPTION_FILTER_KEY_NONE = 0x0000,
|
||||
INTERCEPTION_FILTER_KEY_ALL = 0xFFFF,
|
||||
INTERCEPTION_FILTER_KEY_DOWN = INTERCEPTION_KEY_UP,
|
||||
INTERCEPTION_FILTER_KEY_UP = INTERCEPTION_KEY_UP << 1,
|
||||
INTERCEPTION_FILTER_KEY_E0 = INTERCEPTION_KEY_E0 << 1,
|
||||
INTERCEPTION_FILTER_KEY_E1 = INTERCEPTION_KEY_E1 << 1,
|
||||
INTERCEPTION_FILTER_KEY_TERMSRV_SET_LED = INTERCEPTION_KEY_TERMSRV_SET_LED << 1,
|
||||
INTERCEPTION_FILTER_KEY_TERMSRV_SHADOW = INTERCEPTION_KEY_TERMSRV_SHADOW << 1,
|
||||
INTERCEPTION_FILTER_KEY_TERMSRV_VKPACKET = INTERCEPTION_KEY_TERMSRV_VKPACKET << 1
|
||||
};
|
||||
|
||||
enum InterceptionMouseState
|
||||
{
|
||||
INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN = 0x001,
|
||||
INTERCEPTION_MOUSE_LEFT_BUTTON_UP = 0x002,
|
||||
INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN = 0x004,
|
||||
INTERCEPTION_MOUSE_RIGHT_BUTTON_UP = 0x008,
|
||||
INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN = 0x010,
|
||||
INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP = 0x020,
|
||||
|
||||
INTERCEPTION_MOUSE_BUTTON_1_DOWN = INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN,
|
||||
INTERCEPTION_MOUSE_BUTTON_1_UP = INTERCEPTION_MOUSE_LEFT_BUTTON_UP,
|
||||
INTERCEPTION_MOUSE_BUTTON_2_DOWN = INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN,
|
||||
INTERCEPTION_MOUSE_BUTTON_2_UP = INTERCEPTION_MOUSE_RIGHT_BUTTON_UP,
|
||||
INTERCEPTION_MOUSE_BUTTON_3_DOWN = INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN,
|
||||
INTERCEPTION_MOUSE_BUTTON_3_UP = INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP,
|
||||
|
||||
INTERCEPTION_MOUSE_BUTTON_4_DOWN = 0x040,
|
||||
INTERCEPTION_MOUSE_BUTTON_4_UP = 0x080,
|
||||
INTERCEPTION_MOUSE_BUTTON_5_DOWN = 0x100,
|
||||
INTERCEPTION_MOUSE_BUTTON_5_UP = 0x200,
|
||||
|
||||
INTERCEPTION_MOUSE_WHEEL = 0x400,
|
||||
INTERCEPTION_MOUSE_HWHEEL = 0x800
|
||||
};
|
||||
|
||||
enum InterceptionFilterMouseState
|
||||
{
|
||||
INTERCEPTION_FILTER_MOUSE_NONE = 0x0000,
|
||||
INTERCEPTION_FILTER_MOUSE_ALL = 0xFFFF,
|
||||
|
||||
INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_DOWN = INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_UP = INTERCEPTION_MOUSE_LEFT_BUTTON_UP,
|
||||
INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_DOWN = INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_UP = INTERCEPTION_MOUSE_RIGHT_BUTTON_UP,
|
||||
INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_DOWN = INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_UP = INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP,
|
||||
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_1_DOWN = INTERCEPTION_MOUSE_BUTTON_1_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_1_UP = INTERCEPTION_MOUSE_BUTTON_1_UP,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_2_DOWN = INTERCEPTION_MOUSE_BUTTON_2_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_2_UP = INTERCEPTION_MOUSE_BUTTON_2_UP,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_3_DOWN = INTERCEPTION_MOUSE_BUTTON_3_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_3_UP = INTERCEPTION_MOUSE_BUTTON_3_UP,
|
||||
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_4_DOWN = INTERCEPTION_MOUSE_BUTTON_4_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_4_UP = INTERCEPTION_MOUSE_BUTTON_4_UP,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_5_DOWN = INTERCEPTION_MOUSE_BUTTON_5_DOWN,
|
||||
INTERCEPTION_FILTER_MOUSE_BUTTON_5_UP = INTERCEPTION_MOUSE_BUTTON_5_UP,
|
||||
|
||||
INTERCEPTION_FILTER_MOUSE_WHEEL = INTERCEPTION_MOUSE_WHEEL,
|
||||
INTERCEPTION_FILTER_MOUSE_HWHEEL = INTERCEPTION_MOUSE_HWHEEL,
|
||||
|
||||
INTERCEPTION_FILTER_MOUSE_MOVE = 0x1000
|
||||
};
|
||||
|
||||
enum InterceptionMouseFlag
|
||||
{
|
||||
INTERCEPTION_MOUSE_MOVE_RELATIVE = 0x000,
|
||||
INTERCEPTION_MOUSE_MOVE_ABSOLUTE = 0x001,
|
||||
INTERCEPTION_MOUSE_VIRTUAL_DESKTOP = 0x002,
|
||||
INTERCEPTION_MOUSE_ATTRIBUTES_CHANGED = 0x004,
|
||||
INTERCEPTION_MOUSE_MOVE_NOCOALESCE = 0x008,
|
||||
INTERCEPTION_MOUSE_TERMSRV_SRC_SHADOW = 0x100
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short state;
|
||||
unsigned short flags;
|
||||
short rolling;
|
||||
int x;
|
||||
int y;
|
||||
unsigned int information;
|
||||
} InterceptionMouseStroke;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short code;
|
||||
unsigned short state;
|
||||
unsigned int information;
|
||||
} InterceptionKeyStroke;
|
||||
|
||||
typedef char InterceptionStroke[sizeof(InterceptionMouseStroke)];
|
||||
|
||||
InterceptionContext INTERCEPTION_API interception_create_context(void);
|
||||
|
||||
void INTERCEPTION_API interception_destroy_context(InterceptionContext context);
|
||||
|
||||
InterceptionPrecedence INTERCEPTION_API interception_get_precedence(InterceptionContext context, InterceptionDevice device);
|
||||
|
||||
void INTERCEPTION_API interception_set_precedence(InterceptionContext context, InterceptionDevice device, InterceptionPrecedence precedence);
|
||||
|
||||
InterceptionFilter INTERCEPTION_API interception_get_filter(InterceptionContext context, InterceptionDevice device);
|
||||
|
||||
void INTERCEPTION_API interception_set_filter(InterceptionContext context, InterceptionPredicate predicate, InterceptionFilter filter);
|
||||
|
||||
InterceptionDevice INTERCEPTION_API interception_wait(InterceptionContext context);
|
||||
|
||||
InterceptionDevice INTERCEPTION_API interception_wait_with_timeout(InterceptionContext context, unsigned long milliseconds);
|
||||
|
||||
int INTERCEPTION_API interception_send(InterceptionContext context, InterceptionDevice device, const InterceptionStroke *stroke, unsigned int nstroke);
|
||||
|
||||
int INTERCEPTION_API interception_receive(InterceptionContext context, InterceptionDevice device, InterceptionStroke *stroke, unsigned int nstroke);
|
||||
|
||||
unsigned int INTERCEPTION_API interception_get_hardware_id(InterceptionContext context, InterceptionDevice device, void *hardware_id_buffer, unsigned int buffer_size);
|
||||
|
||||
int INTERCEPTION_API interception_is_invalid(InterceptionDevice device);
|
||||
|
||||
int INTERCEPTION_API interception_is_keyboard(InterceptionDevice device);
|
||||
|
||||
int INTERCEPTION_API interception_is_mouse(InterceptionDevice device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
315
src-interception/src/bindings.rs
Normal file
315
src-interception/src/bindings.rs
Normal file
@ -0,0 +1,315 @@
|
||||
/* automatically generated by rust-bindgen 0.54.1 */
|
||||
|
||||
pub const INTERCEPTION_MAX_KEYBOARD: u32 = 10;
|
||||
pub const INTERCEPTION_MAX_MOUSE: u32 = 10;
|
||||
pub const INTERCEPTION_MAX_DEVICE: u32 = 20;
|
||||
pub type InterceptionContext = *mut ::std::os::raw::c_void;
|
||||
pub type InterceptionDevice = ::std::os::raw::c_int;
|
||||
pub type InterceptionPrecedence = ::std::os::raw::c_int;
|
||||
pub type InterceptionFilter = ::std::os::raw::c_ushort;
|
||||
pub type InterceptionPredicate = ::std::option::Option<
|
||||
unsafe extern "C" fn(device: InterceptionDevice) -> ::std::os::raw::c_int,
|
||||
>;
|
||||
pub const InterceptionKeyState_INTERCEPTION_KEY_DOWN: InterceptionKeyState = 0;
|
||||
pub const InterceptionKeyState_INTERCEPTION_KEY_UP: InterceptionKeyState = 1;
|
||||
pub const InterceptionKeyState_INTERCEPTION_KEY_E0: InterceptionKeyState = 2;
|
||||
pub const InterceptionKeyState_INTERCEPTION_KEY_E1: InterceptionKeyState = 4;
|
||||
pub const InterceptionKeyState_INTERCEPTION_KEY_TERMSRV_SET_LED: InterceptionKeyState = 8;
|
||||
pub const InterceptionKeyState_INTERCEPTION_KEY_TERMSRV_SHADOW: InterceptionKeyState = 16;
|
||||
pub const InterceptionKeyState_INTERCEPTION_KEY_TERMSRV_VKPACKET: InterceptionKeyState = 32;
|
||||
pub type InterceptionKeyState = u32;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_NONE: InterceptionFilterKeyState = 0;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_ALL: InterceptionFilterKeyState =
|
||||
65535;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_DOWN: InterceptionFilterKeyState = 1;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_UP: InterceptionFilterKeyState = 2;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_E0: InterceptionFilterKeyState = 4;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_E1: InterceptionFilterKeyState = 8;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_TERMSRV_SET_LED:
|
||||
InterceptionFilterKeyState = 16;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_TERMSRV_SHADOW:
|
||||
InterceptionFilterKeyState = 32;
|
||||
pub const InterceptionFilterKeyState_INTERCEPTION_FILTER_KEY_TERMSRV_VKPACKET:
|
||||
InterceptionFilterKeyState = 64;
|
||||
pub type InterceptionFilterKeyState = u32;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN: InterceptionMouseState = 1;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_LEFT_BUTTON_UP: InterceptionMouseState = 2;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN: InterceptionMouseState = 4;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_RIGHT_BUTTON_UP: InterceptionMouseState = 8;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN: InterceptionMouseState = 16;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP: InterceptionMouseState = 32;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_1_DOWN: InterceptionMouseState = 1;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_1_UP: InterceptionMouseState = 2;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_2_DOWN: InterceptionMouseState = 4;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_2_UP: InterceptionMouseState = 8;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_3_DOWN: InterceptionMouseState = 16;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_3_UP: InterceptionMouseState = 32;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_4_DOWN: InterceptionMouseState = 64;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_4_UP: InterceptionMouseState = 128;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_5_DOWN: InterceptionMouseState = 256;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_BUTTON_5_UP: InterceptionMouseState = 512;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_WHEEL: InterceptionMouseState = 1024;
|
||||
pub const InterceptionMouseState_INTERCEPTION_MOUSE_HWHEEL: InterceptionMouseState = 2048;
|
||||
pub type InterceptionMouseState = u32;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_NONE:
|
||||
InterceptionFilterMouseState = 0;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_ALL: InterceptionFilterMouseState =
|
||||
65535;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_DOWN:
|
||||
InterceptionFilterMouseState = 1;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_UP:
|
||||
InterceptionFilterMouseState = 2;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_DOWN:
|
||||
InterceptionFilterMouseState = 4;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_UP:
|
||||
InterceptionFilterMouseState = 8;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_DOWN:
|
||||
InterceptionFilterMouseState = 16;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_UP:
|
||||
InterceptionFilterMouseState = 32;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_1_DOWN:
|
||||
InterceptionFilterMouseState = 1;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_1_UP:
|
||||
InterceptionFilterMouseState = 2;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_2_DOWN:
|
||||
InterceptionFilterMouseState = 4;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_2_UP:
|
||||
InterceptionFilterMouseState = 8;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_3_DOWN:
|
||||
InterceptionFilterMouseState = 16;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_3_UP:
|
||||
InterceptionFilterMouseState = 32;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_4_DOWN:
|
||||
InterceptionFilterMouseState = 64;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_4_UP:
|
||||
InterceptionFilterMouseState = 128;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_5_DOWN:
|
||||
InterceptionFilterMouseState = 256;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_BUTTON_5_UP:
|
||||
InterceptionFilterMouseState = 512;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_WHEEL:
|
||||
InterceptionFilterMouseState = 1024;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_HWHEEL:
|
||||
InterceptionFilterMouseState = 2048;
|
||||
pub const InterceptionFilterMouseState_INTERCEPTION_FILTER_MOUSE_MOVE:
|
||||
InterceptionFilterMouseState = 4096;
|
||||
pub type InterceptionFilterMouseState = u32;
|
||||
pub const InterceptionMouseFlag_INTERCEPTION_MOUSE_MOVE_RELATIVE: InterceptionMouseFlag = 0;
|
||||
pub const InterceptionMouseFlag_INTERCEPTION_MOUSE_MOVE_ABSOLUTE: InterceptionMouseFlag = 1;
|
||||
pub const InterceptionMouseFlag_INTERCEPTION_MOUSE_VIRTUAL_DESKTOP: InterceptionMouseFlag = 2;
|
||||
pub const InterceptionMouseFlag_INTERCEPTION_MOUSE_ATTRIBUTES_CHANGED: InterceptionMouseFlag = 4;
|
||||
pub const InterceptionMouseFlag_INTERCEPTION_MOUSE_MOVE_NOCOALESCE: InterceptionMouseFlag = 8;
|
||||
pub const InterceptionMouseFlag_INTERCEPTION_MOUSE_TERMSRV_SRC_SHADOW: InterceptionMouseFlag = 256;
|
||||
pub type InterceptionMouseFlag = u32;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct InterceptionMouseStroke {
|
||||
pub state: ::std::os::raw::c_ushort,
|
||||
pub flags: ::std::os::raw::c_ushort,
|
||||
pub rolling: ::std::os::raw::c_short,
|
||||
pub x: ::std::os::raw::c_int,
|
||||
pub y: ::std::os::raw::c_int,
|
||||
pub information: ::std::os::raw::c_uint,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_InterceptionMouseStroke() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<InterceptionMouseStroke>(),
|
||||
20usize,
|
||||
concat!("Size of: ", stringify!(InterceptionMouseStroke))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<InterceptionMouseStroke>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(InterceptionMouseStroke))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<InterceptionMouseStroke>())).state as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionMouseStroke),
|
||||
"::",
|
||||
stringify!(state)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<InterceptionMouseStroke>())).flags as *const _ as usize },
|
||||
2usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionMouseStroke),
|
||||
"::",
|
||||
stringify!(flags)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<InterceptionMouseStroke>())).rolling as *const _ as usize },
|
||||
4usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionMouseStroke),
|
||||
"::",
|
||||
stringify!(rolling)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<InterceptionMouseStroke>())).x as *const _ as usize },
|
||||
8usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionMouseStroke),
|
||||
"::",
|
||||
stringify!(x)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<InterceptionMouseStroke>())).y as *const _ as usize },
|
||||
12usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionMouseStroke),
|
||||
"::",
|
||||
stringify!(y)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
&(*(::std::ptr::null::<InterceptionMouseStroke>())).information as *const _ as usize
|
||||
},
|
||||
16usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionMouseStroke),
|
||||
"::",
|
||||
stringify!(information)
|
||||
)
|
||||
);
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct InterceptionKeyStroke {
|
||||
pub code: ::std::os::raw::c_ushort,
|
||||
pub state: ::std::os::raw::c_ushort,
|
||||
pub information: ::std::os::raw::c_uint,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_InterceptionKeyStroke() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<InterceptionKeyStroke>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(InterceptionKeyStroke))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<InterceptionKeyStroke>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(InterceptionKeyStroke))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<InterceptionKeyStroke>())).code as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionKeyStroke),
|
||||
"::",
|
||||
stringify!(code)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<InterceptionKeyStroke>())).state as *const _ as usize },
|
||||
2usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionKeyStroke),
|
||||
"::",
|
||||
stringify!(state)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
&(*(::std::ptr::null::<InterceptionKeyStroke>())).information as *const _ as usize
|
||||
},
|
||||
4usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(InterceptionKeyStroke),
|
||||
"::",
|
||||
stringify!(information)
|
||||
)
|
||||
);
|
||||
}
|
||||
pub type InterceptionStroke = [::std::os::raw::c_char; 20usize];
|
||||
extern "C" {
|
||||
pub fn interception_create_context() -> InterceptionContext;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_destroy_context(context: InterceptionContext);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_get_precedence(
|
||||
context: InterceptionContext,
|
||||
device: InterceptionDevice,
|
||||
) -> InterceptionPrecedence;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_set_precedence(
|
||||
context: InterceptionContext,
|
||||
device: InterceptionDevice,
|
||||
precedence: InterceptionPrecedence,
|
||||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_get_filter(
|
||||
context: InterceptionContext,
|
||||
device: InterceptionDevice,
|
||||
) -> InterceptionFilter;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_set_filter(
|
||||
context: InterceptionContext,
|
||||
predicate: InterceptionPredicate,
|
||||
filter: InterceptionFilter,
|
||||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_wait(context: InterceptionContext) -> InterceptionDevice;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_wait_with_timeout(
|
||||
context: InterceptionContext,
|
||||
milliseconds: ::std::os::raw::c_ulong,
|
||||
) -> InterceptionDevice;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_send(
|
||||
context: InterceptionContext,
|
||||
device: InterceptionDevice,
|
||||
stroke: *const InterceptionStroke,
|
||||
nstroke: ::std::os::raw::c_uint,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_receive(
|
||||
context: InterceptionContext,
|
||||
device: InterceptionDevice,
|
||||
stroke: *mut InterceptionStroke,
|
||||
nstroke: ::std::os::raw::c_uint,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_get_hardware_id(
|
||||
context: InterceptionContext,
|
||||
device: InterceptionDevice,
|
||||
hardware_id_buffer: *mut ::std::os::raw::c_void,
|
||||
buffer_size: ::std::os::raw::c_uint,
|
||||
) -> ::std::os::raw::c_uint;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_is_invalid(device: InterceptionDevice) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_is_keyboard(device: InterceptionDevice) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn interception_is_mouse(device: InterceptionDevice) -> ::std::os::raw::c_int;
|
||||
}
|
328
src-interception/src/interception.c
Normal file
328
src-interception/src/interception.c
Normal file
@ -0,0 +1,328 @@
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include "interception.h"
|
||||
|
||||
#define IOCTL_SET_PRECEDENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_GET_PRECEDENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_SET_FILTER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_GET_FILTER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_SET_EVENT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x820, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_READ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x840, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_GET_HARDWARE_ID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x880, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
typedef struct _KEYBOARD_INPUT_DATA
|
||||
{
|
||||
USHORT UnitId;
|
||||
USHORT MakeCode;
|
||||
USHORT Flags;
|
||||
USHORT Reserved;
|
||||
ULONG ExtraInformation;
|
||||
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;
|
||||
|
||||
typedef struct _MOUSE_INPUT_DATA
|
||||
{
|
||||
USHORT UnitId;
|
||||
USHORT Flags;
|
||||
USHORT ButtonFlags;
|
||||
USHORT ButtonData;
|
||||
ULONG RawButtons;
|
||||
LONG LastX;
|
||||
LONG LastY;
|
||||
ULONG ExtraInformation;
|
||||
} MOUSE_INPUT_DATA, *PMOUSE_INPUT_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *handle;
|
||||
void *unempty;
|
||||
} *InterceptionDeviceArray;
|
||||
|
||||
InterceptionContext interception_create_context(void)
|
||||
{
|
||||
InterceptionDeviceArray device_array = 0;
|
||||
char device_name[] = "\\\\.\\interception00";
|
||||
DWORD bytes_returned;
|
||||
InterceptionDevice i;
|
||||
|
||||
device_array = (InterceptionDeviceArray)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERCEPTION_MAX_DEVICE * sizeof(*((InterceptionDeviceArray) 0)));
|
||||
if(!device_array) return 0;
|
||||
|
||||
for(i = 0; i < INTERCEPTION_MAX_DEVICE; ++i)
|
||||
{
|
||||
HANDLE zero_padded_handle[2] = {0};
|
||||
|
||||
sprintf(&device_name[sizeof(device_name) - 3], "%02d", i);
|
||||
|
||||
device_array[i].handle = CreateFileA(device_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (device_array[i].handle == INVALID_HANDLE_VALUE) {
|
||||
interception_destroy_context(device_array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_array[i].unempty = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if(device_array[i].unempty == NULL)
|
||||
{
|
||||
interception_destroy_context(device_array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
zero_padded_handle[0] = device_array[i].unempty;
|
||||
|
||||
if(!DeviceIoControl(device_array[i].handle, IOCTL_SET_EVENT, zero_padded_handle, sizeof(zero_padded_handle), NULL, 0, &bytes_returned, NULL))
|
||||
{
|
||||
interception_destroy_context(device_array);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return device_array;
|
||||
}
|
||||
|
||||
void interception_destroy_context(InterceptionContext context)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
unsigned int i;
|
||||
|
||||
if(!context) return;
|
||||
|
||||
for(i = 0; i < INTERCEPTION_MAX_DEVICE; ++i)
|
||||
{
|
||||
if(device_array[i].handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(device_array[i].handle);
|
||||
|
||||
if(device_array[i].unempty != NULL)
|
||||
CloseHandle(device_array[i].unempty);
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, context);
|
||||
}
|
||||
|
||||
InterceptionPrecedence interception_get_precedence(InterceptionContext context, InterceptionDevice device)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
InterceptionPrecedence precedence = 0;
|
||||
DWORD bytes_returned;
|
||||
|
||||
if(context && device_array[device - 1].handle)
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_GET_PRECEDENCE, NULL, 0, (LPVOID)&precedence, sizeof(InterceptionPrecedence), &bytes_returned, NULL);
|
||||
|
||||
return precedence;
|
||||
}
|
||||
|
||||
void interception_set_precedence(InterceptionContext context, InterceptionDevice device, InterceptionPrecedence precedence)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
DWORD bytes_returned;
|
||||
|
||||
if(context && device_array[device - 1].handle)
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_SET_PRECEDENCE, (LPVOID)&precedence, sizeof(InterceptionPrecedence), NULL, 0, &bytes_returned, NULL);
|
||||
}
|
||||
|
||||
InterceptionFilter interception_get_filter(InterceptionContext context, InterceptionDevice device)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
InterceptionFilter filter = 0;
|
||||
DWORD bytes_returned;
|
||||
|
||||
if(context && device_array[device - 1].handle)
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_GET_FILTER, NULL, 0, (LPVOID)&filter, sizeof(InterceptionFilter), &bytes_returned, NULL);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
void interception_set_filter(InterceptionContext context, InterceptionPredicate interception_predicate, InterceptionFilter filter)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
InterceptionDevice i;
|
||||
DWORD bytes_returned;
|
||||
|
||||
if(context)
|
||||
for(i = 0; i < INTERCEPTION_MAX_DEVICE; ++i)
|
||||
if(device_array[i].handle && interception_predicate(i + 1))
|
||||
DeviceIoControl(device_array[i].handle, IOCTL_SET_FILTER, (LPVOID)&filter, sizeof(InterceptionFilter), NULL, 0, &bytes_returned, NULL);
|
||||
}
|
||||
|
||||
InterceptionDevice interception_wait(InterceptionContext context)
|
||||
{
|
||||
return interception_wait_with_timeout(context, INFINITE);
|
||||
}
|
||||
|
||||
InterceptionDevice interception_wait_with_timeout(InterceptionContext context, unsigned long milliseconds)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
HANDLE wait_handles[INTERCEPTION_MAX_DEVICE];
|
||||
DWORD i, j, k;
|
||||
|
||||
if(!context) return 0;
|
||||
|
||||
for(i = 0, j = 0; i < INTERCEPTION_MAX_DEVICE; ++i)
|
||||
{
|
||||
if (device_array[i].unempty)
|
||||
wait_handles[j++] = device_array[i].unempty;
|
||||
}
|
||||
|
||||
k = WaitForMultipleObjects(j, wait_handles, FALSE, milliseconds);
|
||||
|
||||
if(k == WAIT_FAILED || k == WAIT_TIMEOUT) return 0;
|
||||
|
||||
for(i = 0, j = 0; i < INTERCEPTION_MAX_DEVICE; ++i)
|
||||
{
|
||||
if (device_array[i].unempty)
|
||||
if (k == j++)
|
||||
break;
|
||||
}
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
int interception_send(InterceptionContext context, InterceptionDevice device, const InterceptionStroke *stroke, unsigned int nstroke)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
DWORD strokeswritten = 0;
|
||||
|
||||
if(context == 0 || nstroke == 0 || interception_is_invalid(device) || !device_array[device - 1].handle) return 0;
|
||||
|
||||
if(interception_is_keyboard(device))
|
||||
{
|
||||
PKEYBOARD_INPUT_DATA rawstrokes = (PKEYBOARD_INPUT_DATA)HeapAlloc(GetProcessHeap(), 0, nstroke * sizeof(KEYBOARD_INPUT_DATA));
|
||||
unsigned int i;
|
||||
|
||||
if(!rawstrokes) return 0;
|
||||
|
||||
for(i = 0; i < nstroke; ++i)
|
||||
{
|
||||
InterceptionKeyStroke *key_stroke = (InterceptionKeyStroke *) stroke;
|
||||
|
||||
rawstrokes[i].UnitId = 0;
|
||||
rawstrokes[i].MakeCode = key_stroke[i].code;
|
||||
rawstrokes[i].Flags = key_stroke[i].state;
|
||||
rawstrokes[i].Reserved = 0;
|
||||
rawstrokes[i].ExtraInformation = key_stroke[i].information;
|
||||
}
|
||||
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_WRITE, rawstrokes,(DWORD)nstroke * sizeof(KEYBOARD_INPUT_DATA), NULL, 0, &strokeswritten, NULL);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, rawstrokes);
|
||||
|
||||
strokeswritten /= sizeof(KEYBOARD_INPUT_DATA);
|
||||
}
|
||||
else
|
||||
{
|
||||
PMOUSE_INPUT_DATA rawstrokes = (PMOUSE_INPUT_DATA)HeapAlloc(GetProcessHeap(), 0, nstroke * sizeof(MOUSE_INPUT_DATA));
|
||||
unsigned int i;
|
||||
|
||||
if(!rawstrokes) return 0;
|
||||
|
||||
for(i = 0; i < nstroke; ++i)
|
||||
{
|
||||
InterceptionMouseStroke *mouse_stroke = (InterceptionMouseStroke *) stroke;
|
||||
|
||||
rawstrokes[i].UnitId = 0;
|
||||
rawstrokes[i].Flags = mouse_stroke[i].flags;
|
||||
rawstrokes[i].ButtonFlags = mouse_stroke[i].state;
|
||||
rawstrokes[i].ButtonData = mouse_stroke[i].rolling;
|
||||
rawstrokes[i].RawButtons = 0;
|
||||
rawstrokes[i].LastX = mouse_stroke[i].x;
|
||||
rawstrokes[i].LastY = mouse_stroke[i].y;
|
||||
rawstrokes[i].ExtraInformation = mouse_stroke[i].information;
|
||||
}
|
||||
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_WRITE, rawstrokes, (DWORD)nstroke * sizeof(MOUSE_INPUT_DATA), NULL, 0, &strokeswritten, NULL);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, rawstrokes);
|
||||
|
||||
strokeswritten /= sizeof(MOUSE_INPUT_DATA);
|
||||
}
|
||||
|
||||
return strokeswritten;
|
||||
}
|
||||
|
||||
int interception_receive(InterceptionContext context, InterceptionDevice device, InterceptionStroke *stroke, unsigned int nstroke)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
DWORD strokesread = 0;
|
||||
|
||||
if(context == 0 || nstroke == 0 || interception_is_invalid(device) || !device_array[device - 1].handle) return 0;
|
||||
|
||||
if(interception_is_keyboard(device))
|
||||
{
|
||||
PKEYBOARD_INPUT_DATA rawstrokes = (PKEYBOARD_INPUT_DATA)HeapAlloc(GetProcessHeap(), 0, nstroke * sizeof(KEYBOARD_INPUT_DATA));
|
||||
unsigned int i;
|
||||
|
||||
if(!rawstrokes) return 0;
|
||||
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_READ, NULL, 0, rawstrokes, (DWORD)nstroke * sizeof(KEYBOARD_INPUT_DATA), &strokesread, NULL);
|
||||
|
||||
strokesread /= sizeof(KEYBOARD_INPUT_DATA);
|
||||
|
||||
for(i = 0; i < (unsigned int)strokesread; ++i)
|
||||
{
|
||||
InterceptionKeyStroke *key_stroke = (InterceptionKeyStroke *) stroke;
|
||||
|
||||
key_stroke[i].code = rawstrokes[i].MakeCode;
|
||||
key_stroke[i].state = rawstrokes[i].Flags;
|
||||
key_stroke[i].information = rawstrokes[i].ExtraInformation;
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, rawstrokes);
|
||||
}
|
||||
else
|
||||
{
|
||||
PMOUSE_INPUT_DATA rawstrokes = (PMOUSE_INPUT_DATA)HeapAlloc(GetProcessHeap(), 0, nstroke * sizeof(MOUSE_INPUT_DATA));
|
||||
unsigned int i;
|
||||
|
||||
if(!rawstrokes) return 0;
|
||||
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_READ, NULL, 0, rawstrokes, (DWORD)nstroke * sizeof(MOUSE_INPUT_DATA), &strokesread, NULL);
|
||||
|
||||
strokesread /= sizeof(MOUSE_INPUT_DATA);
|
||||
|
||||
for(i = 0; i < (unsigned int)strokesread; ++i)
|
||||
{
|
||||
InterceptionMouseStroke *mouse_stroke = (InterceptionMouseStroke *) stroke;
|
||||
|
||||
mouse_stroke[i].flags = rawstrokes[i].Flags;
|
||||
mouse_stroke[i].state = rawstrokes[i].ButtonFlags;
|
||||
mouse_stroke[i].rolling = rawstrokes[i].ButtonData;
|
||||
mouse_stroke[i].x = rawstrokes[i].LastX;
|
||||
mouse_stroke[i].y = rawstrokes[i].LastY;
|
||||
mouse_stroke[i].information = rawstrokes[i].ExtraInformation;
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, rawstrokes);
|
||||
}
|
||||
|
||||
return strokesread;
|
||||
}
|
||||
|
||||
unsigned int interception_get_hardware_id(InterceptionContext context, InterceptionDevice device, void *hardware_id_buffer, unsigned int buffer_size)
|
||||
{
|
||||
InterceptionDeviceArray device_array = (InterceptionDeviceArray)context;
|
||||
DWORD output_size = 0;
|
||||
|
||||
if(context == 0 || interception_is_invalid(device) || !device_array[device - 1].handle) return 0;
|
||||
|
||||
DeviceIoControl(device_array[device - 1].handle, IOCTL_GET_HARDWARE_ID, NULL, 0, hardware_id_buffer, buffer_size, &output_size, NULL);
|
||||
|
||||
return output_size;
|
||||
}
|
||||
|
||||
int interception_is_invalid(InterceptionDevice device)
|
||||
{
|
||||
return !interception_is_keyboard(device) && !interception_is_mouse(device);
|
||||
}
|
||||
|
||||
int interception_is_keyboard(InterceptionDevice device)
|
||||
{
|
||||
return device >= INTERCEPTION_KEYBOARD(0) && device <= INTERCEPTION_KEYBOARD(INTERCEPTION_MAX_KEYBOARD - 1);
|
||||
}
|
||||
|
||||
int interception_is_mouse(InterceptionDevice device)
|
||||
{
|
||||
return device >= INTERCEPTION_MOUSE(0) && device <= INTERCEPTION_MOUSE(INTERCEPTION_MAX_MOUSE - 1);
|
||||
}
|
373
src-interception/src/lib.rs
Normal file
373
src-interception/src/lib.rs
Normal file
@ -0,0 +1,373 @@
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
mod bindings;
|
||||
use bindings as raw;
|
||||
pub mod scancode;
|
||||
|
||||
pub use scancode::ScanCode;
|
||||
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::default::Default;
|
||||
use std::time::Duration;
|
||||
use std::vec::Vec;
|
||||
|
||||
pub type Device = i32;
|
||||
pub type Precedence = i32;
|
||||
|
||||
pub enum Filter {
|
||||
MouseFilter(MouseFilter),
|
||||
KeyFilter(KeyFilter),
|
||||
}
|
||||
|
||||
pub type Predicate = extern "C" fn(device: Device) -> bool;
|
||||
|
||||
bitflags! {
|
||||
pub struct MouseState: u16 {
|
||||
const LEFT_BUTTON_DOWN = 1;
|
||||
const LEFT_BUTTON_UP = 2;
|
||||
|
||||
const RIGHT_BUTTON_DOWN = 4;
|
||||
const RIGHT_BUTTON_UP = 8;
|
||||
|
||||
const MIDDLE_BUTTON_DOWN = 16;
|
||||
const MIDDLE_BUTTON_UP = 32;
|
||||
|
||||
const BUTTON_4_DOWN = 64;
|
||||
const BUTTON_4_UP = 128;
|
||||
|
||||
const BUTTON_5_DOWN = 256;
|
||||
const BUTTON_5_UP = 512;
|
||||
|
||||
const WHEEL = 1024;
|
||||
const HWHEEL = 2048;
|
||||
|
||||
// MouseFilter only
|
||||
const MOVE = 4096;
|
||||
}
|
||||
}
|
||||
|
||||
pub type MouseFilter = MouseState;
|
||||
|
||||
bitflags! {
|
||||
pub struct MouseFlags: u16 {
|
||||
const MOVE_RELATIVE = 0;
|
||||
const MOVE_ABSOLUTE = 1;
|
||||
|
||||
const VIRTUAL_DESKTOP = 2;
|
||||
const ATTRIBUTES_CHANGED = 4;
|
||||
|
||||
const MOVE_NO_COALESCE = 8;
|
||||
|
||||
const TERMSRV_SRC_SHADOW = 256;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct KeyState: u16 {
|
||||
const DOWN = 0;
|
||||
const UP = 1;
|
||||
|
||||
const E0 = 2;
|
||||
const E1 = 3;
|
||||
|
||||
const TERMSRV_SET_LED = 8;
|
||||
const TERMSRV_SHADOW = 16;
|
||||
const TERMSRV_VKPACKET = 32;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct KeyFilter: u16 {
|
||||
const DOWN = 1;
|
||||
const UP = 2;
|
||||
|
||||
const E0 = 4;
|
||||
const E1 = 8;
|
||||
|
||||
const TERMSRV_SET_LED = 16;
|
||||
const TERMSRV_SHADOW = 32;
|
||||
const TERMSRV_VKPACKET = 64;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Stroke {
|
||||
Mouse {
|
||||
state: MouseState,
|
||||
flags: MouseFlags,
|
||||
rolling: i16,
|
||||
x: i32,
|
||||
y: i32,
|
||||
information: u32,
|
||||
},
|
||||
|
||||
Keyboard {
|
||||
code: ScanCode,
|
||||
state: KeyState,
|
||||
information: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<raw::InterceptionMouseStroke> for Stroke {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(raw_stroke: raw::InterceptionMouseStroke) -> Result<Self, Self::Error> {
|
||||
let state = match MouseState::from_bits(raw_stroke.state) {
|
||||
Some(state) => state,
|
||||
None => return Err("Extra bits in raw mouse state"),
|
||||
};
|
||||
|
||||
let flags = match MouseFlags::from_bits(raw_stroke.flags) {
|
||||
Some(flags) => flags,
|
||||
None => return Err("Extra bits in raw mouse flags"),
|
||||
};
|
||||
|
||||
Ok(Stroke::Mouse {
|
||||
state: state,
|
||||
flags: flags,
|
||||
rolling: raw_stroke.rolling,
|
||||
x: raw_stroke.x,
|
||||
y: raw_stroke.y,
|
||||
information: raw_stroke.information,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<raw::InterceptionKeyStroke> for Stroke {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(raw_stroke: raw::InterceptionKeyStroke) -> Result<Self, Self::Error> {
|
||||
let state = match KeyState::from_bits(raw_stroke.state) {
|
||||
Some(state) => state,
|
||||
None => return Err("Extra bits in raw keyboard state"),
|
||||
};
|
||||
|
||||
let code = match ScanCode::try_from(raw_stroke.code) {
|
||||
Ok(code) => code,
|
||||
Err(_) => ScanCode::Esc,
|
||||
};
|
||||
|
||||
Ok(Stroke::Keyboard {
|
||||
code: code,
|
||||
state: state,
|
||||
information: raw_stroke.information,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Stroke> for raw::InterceptionMouseStroke {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(stroke: Stroke) -> Result<Self, Self::Error> {
|
||||
if let Stroke::Mouse {
|
||||
state,
|
||||
flags,
|
||||
rolling,
|
||||
x,
|
||||
y,
|
||||
information,
|
||||
} = stroke
|
||||
{
|
||||
Ok(raw::InterceptionMouseStroke {
|
||||
state: state.bits(),
|
||||
flags: flags.bits(),
|
||||
rolling: rolling,
|
||||
x: x,
|
||||
y: y,
|
||||
information: information,
|
||||
})
|
||||
} else {
|
||||
Err("Stroke must be a mouse stroke")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Stroke> for raw::InterceptionKeyStroke {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(stroke: Stroke) -> Result<Self, Self::Error> {
|
||||
if let Stroke::Keyboard {
|
||||
code,
|
||||
state,
|
||||
information,
|
||||
} = stroke
|
||||
{
|
||||
Ok(raw::InterceptionKeyStroke {
|
||||
code: code as u16,
|
||||
state: state.bits(),
|
||||
information: information,
|
||||
})
|
||||
} else {
|
||||
Err("Stroke must be a keyboard stroke")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Interception {
|
||||
ctx: raw::InterceptionContext,
|
||||
}
|
||||
|
||||
impl Interception {
|
||||
pub fn new() -> Option<Self> {
|
||||
let ctx = unsafe { raw::interception_create_context() };
|
||||
|
||||
if ctx == std::ptr::null_mut() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Interception { ctx: ctx })
|
||||
}
|
||||
|
||||
pub fn get_precedence(&self, device: Device) -> Precedence {
|
||||
unsafe { raw::interception_get_precedence(self.ctx, device) }
|
||||
}
|
||||
|
||||
pub fn set_precedence(&self, device: Device, precedence: Precedence) {
|
||||
unsafe { raw::interception_set_precedence(self.ctx, device, precedence) }
|
||||
}
|
||||
|
||||
pub fn get_filter(&self, device: Device) -> Filter {
|
||||
if is_invalid(device) {
|
||||
return Filter::KeyFilter(KeyFilter::empty());
|
||||
}
|
||||
|
||||
let raw_filter = unsafe { raw::interception_get_filter(self.ctx, device) };
|
||||
if is_mouse(device) {
|
||||
let filter = match MouseFilter::from_bits(raw_filter) {
|
||||
Some(filter) => filter,
|
||||
None => MouseFilter::empty(),
|
||||
};
|
||||
|
||||
Filter::MouseFilter(filter)
|
||||
} else {
|
||||
let filter = match KeyFilter::from_bits(raw_filter) {
|
||||
Some(filter) => filter,
|
||||
None => KeyFilter::empty(),
|
||||
};
|
||||
|
||||
Filter::KeyFilter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_filter(&self, predicate: Predicate, filter: Filter) {
|
||||
let filter = match filter {
|
||||
Filter::MouseFilter(filter) => filter.bits(),
|
||||
Filter::KeyFilter(filter) => filter.bits(),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let predicate = std::mem::transmute(Some(predicate));
|
||||
raw::interception_set_filter(self.ctx, predicate, filter)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(&self) -> Device {
|
||||
unsafe { raw::interception_wait(self.ctx) }
|
||||
}
|
||||
|
||||
pub fn wait_with_timeout(&self, duration: Duration) -> Device {
|
||||
let millis = match u32::try_from(duration.as_millis()) {
|
||||
Ok(m) => m,
|
||||
Err(_) => u32::MAX,
|
||||
};
|
||||
|
||||
unsafe { raw::interception_wait_with_timeout(self.ctx, millis) }
|
||||
}
|
||||
|
||||
pub fn send(&self, device: Device, strokes: &[Stroke]) -> i32 {
|
||||
if is_mouse(device) {
|
||||
self.send_internal::<raw::InterceptionMouseStroke>(device, strokes)
|
||||
} else if is_keyboard(device) {
|
||||
self.send_internal::<raw::InterceptionKeyStroke>(device, strokes)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn send_internal<T: TryFrom<Stroke>>(&self, device: Device, strokes: &[Stroke]) -> i32 {
|
||||
let mut raw_strokes = Vec::new();
|
||||
|
||||
for stroke in strokes {
|
||||
if let Ok(raw_stroke) = T::try_from(*stroke) {
|
||||
raw_strokes.push(raw_stroke)
|
||||
}
|
||||
}
|
||||
|
||||
let ptr = raw_strokes.as_ptr();
|
||||
let len = match u32::try_from(raw_strokes.len()) {
|
||||
Ok(l) => l,
|
||||
Err(_) => u32::MAX,
|
||||
};
|
||||
|
||||
unsafe { raw::interception_send(self.ctx, device, std::mem::transmute(ptr), len) }
|
||||
}
|
||||
|
||||
pub fn receive(&self, device: Device, strokes: &mut [Stroke]) -> i32 {
|
||||
if is_mouse(device) {
|
||||
self.receive_internal::<raw::InterceptionMouseStroke>(device, strokes)
|
||||
} else if is_keyboard(device) {
|
||||
self.receive_internal::<raw::InterceptionKeyStroke>(device, strokes)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_internal<T: TryInto<Stroke> + Default + Copy>(
|
||||
&self,
|
||||
device: Device,
|
||||
strokes: &mut [Stroke],
|
||||
) -> i32 {
|
||||
let mut raw_strokes: Vec<T> = Vec::with_capacity(strokes.len());
|
||||
raw_strokes.resize_with(strokes.len(), Default::default);
|
||||
|
||||
let ptr = raw_strokes.as_ptr();
|
||||
let len = match u32::try_from(raw_strokes.len()) {
|
||||
Ok(l) => l,
|
||||
Err(_) => u32::MAX,
|
||||
};
|
||||
|
||||
let num_read =
|
||||
unsafe { raw::interception_receive(self.ctx, device, std::mem::transmute(ptr), len) };
|
||||
|
||||
let mut num_valid: i32 = 0;
|
||||
for i in 0..num_read {
|
||||
if let Ok(stroke) = raw_strokes[i as usize].try_into() {
|
||||
strokes[num_valid as usize] = stroke;
|
||||
num_valid += 1;
|
||||
}
|
||||
}
|
||||
|
||||
num_valid
|
||||
}
|
||||
|
||||
pub fn get_hardware_id(&self, device: Device, buffer: &mut [u8]) -> u32 {
|
||||
let ptr = buffer.as_mut_ptr();
|
||||
let len = match u32::try_from(buffer.len()) {
|
||||
Ok(l) => l,
|
||||
Err(_) => u32::MAX,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
raw::interception_get_hardware_id(self.ctx, device, std::mem::transmute(ptr), len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Interception {
|
||||
fn drop(&mut self) {
|
||||
unsafe { raw::interception_destroy_context(self.ctx) }
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "C" fn is_invalid(device: Device) -> bool {
|
||||
unsafe { raw::interception_is_invalid(device) != 0 }
|
||||
}
|
||||
|
||||
pub extern "C" fn is_keyboard(device: Device) -> bool {
|
||||
unsafe { raw::interception_is_keyboard(device) != 0 }
|
||||
}
|
||||
|
||||
pub extern "C" fn is_mouse(device: Device) -> bool {
|
||||
unsafe { raw::interception_is_mouse(device) != 0 }
|
||||
}
|
147
src-interception/src/scancode.rs
Normal file
147
src-interception/src/scancode.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use num_enum::TryFromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ref: https://handmade.network/wiki/2823-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names
|
||||
#[derive(Serialize, Deserialize, Hash, Debug, Eq, PartialEq, Copy, Clone, TryFromPrimitive)]
|
||||
#[repr(u16)]
|
||||
pub enum ScanCode {
|
||||
Esc = 0x01,
|
||||
|
||||
Num1 = 0x02,
|
||||
Num2 = 0x03,
|
||||
Num3 = 0x04,
|
||||
Num4 = 0x05,
|
||||
Num5 = 0x06,
|
||||
Num6 = 0x07,
|
||||
Num7 = 0x08,
|
||||
Num8 = 0x09,
|
||||
Num9 = 0x0A,
|
||||
Num0 = 0x0B,
|
||||
|
||||
Minus = 0x0C,
|
||||
Equals = 0x0D,
|
||||
Backspace = 0x0E,
|
||||
|
||||
Tab = 0x0F,
|
||||
|
||||
Q = 0x10,
|
||||
W = 0x11,
|
||||
E = 0x12,
|
||||
R = 0x13,
|
||||
T = 0x14,
|
||||
Y = 0x15,
|
||||
U = 0x16,
|
||||
I = 0x17,
|
||||
O = 0x18,
|
||||
P = 0x19,
|
||||
|
||||
LeftBracket = 0x1A,
|
||||
RightBracket = 0x1B,
|
||||
Enter = 0x1C,
|
||||
|
||||
LeftControl = 0x1D,
|
||||
|
||||
A = 0x1E,
|
||||
S = 0x1F,
|
||||
D = 0x20,
|
||||
F = 0x21,
|
||||
G = 0x22,
|
||||
H = 0x23,
|
||||
J = 0x24,
|
||||
K = 0x25,
|
||||
L = 0x26,
|
||||
|
||||
SemiColon = 0x27,
|
||||
Apostrophe = 0x28,
|
||||
Grave = 0x29,
|
||||
LeftShift = 0x2A,
|
||||
BackSlash = 0x2B,
|
||||
|
||||
Z = 0x2C,
|
||||
X = 0x2D,
|
||||
C = 0x2E,
|
||||
V = 0x2F,
|
||||
B = 0x30,
|
||||
N = 0x31,
|
||||
M = 0x32,
|
||||
|
||||
Comma = 0x33,
|
||||
Period = 0x34,
|
||||
Slash = 0x35,
|
||||
RightShift = 0x36,
|
||||
NumpadMultiply = 0x37,
|
||||
LeftAlt = 0x38,
|
||||
Space = 0x39,
|
||||
CapsLock = 0x3A,
|
||||
|
||||
F1 = 0x3B,
|
||||
F2 = 0x3C,
|
||||
F3 = 0x3D,
|
||||
F4 = 0x3E,
|
||||
F5 = 0x3F,
|
||||
F6 = 0x40,
|
||||
F7 = 0x41,
|
||||
F8 = 0x42,
|
||||
F9 = 0x43,
|
||||
F10 = 0x44,
|
||||
|
||||
NumLock = 0x45,
|
||||
ScrollLock = 0x46,
|
||||
|
||||
Numpad7 = 0x47,
|
||||
Numpad8 = 0x48,
|
||||
Numpad9 = 0x49,
|
||||
|
||||
NumpadMinus = 0x4A,
|
||||
|
||||
Numpad4 = 0x4B,
|
||||
Numpad5 = 0x4C,
|
||||
Numpad6 = 0x4D,
|
||||
|
||||
NumpadPlus = 0x4E,
|
||||
|
||||
Numpad1 = 0x4F,
|
||||
Numpad2 = 0x50,
|
||||
Numpad3 = 0x51,
|
||||
Numpad0 = 0x52,
|
||||
|
||||
NumpadPeriod = 0x53,
|
||||
AltPrintScreen = 0x54, /* Alt + print screen. */
|
||||
Int1 = 0x56, /* Key between the left shift and Z. */
|
||||
|
||||
F11 = 0x57,
|
||||
F12 = 0x58,
|
||||
|
||||
Oem1 = 0x5A, /* VK_OEM_WSCTRL */
|
||||
Oem2 = 0x5B, /* VK_OEM_FINISH */
|
||||
Oem3 = 0x5C, /* VK_OEM_JUMP */
|
||||
|
||||
EraseEOF = 0x5D,
|
||||
|
||||
Oem4 = 0x5E, /* VK_OEM_BACKTAB */
|
||||
Oem5 = 0x5F, /* VK_OEM_AUTO */
|
||||
|
||||
Zoom = 0x62,
|
||||
Help = 0x63,
|
||||
|
||||
F13 = 0x64,
|
||||
F14 = 0x65,
|
||||
F15 = 0x66,
|
||||
F16 = 0x67,
|
||||
F17 = 0x68,
|
||||
F18 = 0x69,
|
||||
F19 = 0x6A,
|
||||
F20 = 0x6B,
|
||||
F21 = 0x6C,
|
||||
F22 = 0x6D,
|
||||
F23 = 0x6E,
|
||||
|
||||
Oem6 = 0x6F, /* VK_OEM_PA3 */
|
||||
Katakana = 0x70,
|
||||
Oem7 = 0x71, /* VK_OEM_RESET */
|
||||
F24 = 0x76,
|
||||
|
||||
SBCSChar = 0x77,
|
||||
Convert = 0x79,
|
||||
NonConvert = 0x7B, /* VK_OEM_PA1 */
|
||||
}
|
46
src-slider_io/Cargo.lock
generated
46
src-slider_io/Cargo.lock
generated
@ -613,6 +613,20 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interception"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"interception-sys",
|
||||
"num_enum",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interception-sys"
|
||||
version = "0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.3.0"
|
||||
@ -864,6 +878,27 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
@ -1053,6 +1088,16 @@ version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
@ -1308,6 +1353,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"image",
|
||||
"interception",
|
||||
"ipconfig",
|
||||
"log",
|
||||
"palette",
|
||||
|
@ -31,6 +31,7 @@ serialport = "4.0.1"
|
||||
wwserial = {path = "../src-wwserial" }
|
||||
vigem-client = { version = "0.1.2", features = ["unstable"] }
|
||||
winapi = "0.3.9"
|
||||
interception = {path = "../src-interception-rs" }
|
||||
ipconfig = "0.3.0"
|
||||
|
||||
# webserver
|
||||
|
42
src-slider_io/src/bin/test_directinput.rs
Normal file
42
src-slider_io/src/bin/test_directinput.rs
Normal file
@ -0,0 +1,42 @@
|
||||
extern crate slider_io;
|
||||
|
||||
use std::io;
|
||||
|
||||
use slider_io::{config::Config, context::Context};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::Builder::new()
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.init();
|
||||
|
||||
let config = Config::from_str(
|
||||
r##"{
|
||||
"deviceMode": "brokenithm",
|
||||
"outputMode": "kb-32-tasoller",
|
||||
"ledMode": "none",
|
||||
"disableAirStrings": false,
|
||||
"divaSerialPort": "COM1",
|
||||
"divaBrightness": 63,
|
||||
"brokenithmPort": 1606,
|
||||
"keyboardSensitivity": 20,
|
||||
"keyboardDirectInput": true,
|
||||
"outputPolling": "100",
|
||||
"outputWebsocketUrl": "localhost:3000",
|
||||
"ledFaster": false,
|
||||
"ledColorActive": "#ff00ff",
|
||||
"ledColorInactive": "#ffff00",
|
||||
"ledSensitivity": 20,
|
||||
"ledWebsocketUrl": "localhost:3001",
|
||||
"ledSerialPort": "COM5"
|
||||
}"##,
|
||||
)
|
||||
.unwrap();
|
||||
println!("{:?}", config);
|
||||
|
||||
let ctx = Context::new(config);
|
||||
|
||||
println!("Press enter to quit");
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input).unwrap();
|
||||
}
|
@ -37,6 +37,7 @@ impl Config {
|
||||
"divaBrightness": 63,
|
||||
"brokenithmPort": 1606,
|
||||
"keyboardSensitivity": 20,
|
||||
"keyboardDirectInput": false,
|
||||
"outputPolling": "100",
|
||||
"outputWebsocketUrl": "localhost:3000",
|
||||
"ledFaster": false,
|
||||
|
@ -41,6 +41,7 @@ pub enum OutputMode {
|
||||
layout: KeyboardLayout,
|
||||
polling: PollingRate,
|
||||
sensitivity: u8,
|
||||
direct_input: bool,
|
||||
},
|
||||
Gamepad {
|
||||
layout: GamepadLayout,
|
||||
@ -89,46 +90,55 @@ impl OutputMode {
|
||||
layout: KeyboardLayout::Tasoller,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-32-yuancon" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Yuancon,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-32-umiguri" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Umiguri,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-16" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::TasollerHalf,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-8" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::EightK,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-6" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::SixK,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-4" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::FourK,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-voltex" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Voltex,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"kb-neardayo" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Neardayo,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
direct_input: v["keyboardDirectInput"].as_bool()?,
|
||||
},
|
||||
"gamepad-voltex" => OutputMode::Gamepad {
|
||||
layout: GamepadLayout::Voltex,
|
||||
|
@ -1,13 +1,14 @@
|
||||
use interception::{Interception, KeyState, ScanCode, Stroke};
|
||||
use log::{error, info};
|
||||
use std::mem;
|
||||
use winapi::{
|
||||
ctypes::c_int,
|
||||
um::winuser::{SendInput, INPUT, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_KEYUP},
|
||||
um::winuser::{
|
||||
MapVirtualKeyA, SendInput, INPUT, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_KEYUP, MAPVK_VK_TO_VSC,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
config::KeyboardLayout,
|
||||
output::OutputHandler
|
||||
};
|
||||
use super::{config::KeyboardLayout, output::OutputHandler};
|
||||
|
||||
#[rustfmt::skip]
|
||||
const TASOLLER_KB_MAP: [usize; 41] = [
|
||||
@ -126,18 +127,25 @@ const VOLTEX_KB_MAP_NEARDAYO: [usize; 41] = [
|
||||
];
|
||||
|
||||
pub struct KeyboardOutput {
|
||||
ground_to_idx: [usize; 41],
|
||||
idx_to_keycode: [u16; 41],
|
||||
// keycode_to_idx: [usize; 256],
|
||||
input_to_idx: [usize; 41],
|
||||
key_idx_to_keycode: [u16; 41],
|
||||
key_idx_to_scancode: [Option<ScanCode>; 41],
|
||||
next_keys: [bool; 41],
|
||||
last_keys: [bool; 41],
|
||||
|
||||
direct_input: bool,
|
||||
interception_handle: Option<Interception>,
|
||||
|
||||
kb_buf: [INPUT; 41],
|
||||
kb_direct_buf: [Stroke; 41],
|
||||
n_kb_buf: u32,
|
||||
}
|
||||
|
||||
// interception isn't send, but lazy to wrap
|
||||
unsafe impl Send for KeyboardOutput {}
|
||||
|
||||
impl KeyboardOutput {
|
||||
pub fn new(layout: KeyboardLayout) -> Self {
|
||||
pub fn new(layout: KeyboardLayout, direct_input: bool) -> Self {
|
||||
let kb_map = match layout {
|
||||
KeyboardLayout::Tasoller => &TASOLLER_KB_MAP,
|
||||
KeyboardLayout::Yuancon => &YUANCON_KB_MAP,
|
||||
@ -150,20 +158,43 @@ impl KeyboardOutput {
|
||||
KeyboardLayout::Neardayo => &VOLTEX_KB_MAP_NEARDAYO,
|
||||
};
|
||||
|
||||
let mut ground_to_idx = [0 as usize; 41];
|
||||
let mut idx_to_keycode = [0 as u16; 41];
|
||||
let mut input_to_key_idx = [0 as usize; 41];
|
||||
let mut key_idx_to_keycode = [0 as u16; 41];
|
||||
let mut key_idx_to_scancode = [None as Option<ScanCode>; 41];
|
||||
let mut keycode_to_idx = [0xffff as usize; 256];
|
||||
let mut keycode_count: usize = 0;
|
||||
|
||||
for (ground, keycode) in kb_map.iter().enumerate() {
|
||||
if keycode_to_idx[*keycode] == 0xffff {
|
||||
keycode_to_idx[*keycode] = keycode_count;
|
||||
idx_to_keycode[keycode_count] = *keycode as u16;
|
||||
key_idx_to_keycode[keycode_count] = *keycode as u16;
|
||||
key_idx_to_scancode[keycode_count] =
|
||||
ScanCode::try_from(unsafe { MapVirtualKeyA((*keycode) as u32, MAPVK_VK_TO_VSC) as u16 })
|
||||
.ok();
|
||||
// info!(
|
||||
// "mapped {:?} to {:?}",
|
||||
// key_idx_to_keycode[keycode_count], key_idx_to_scancode[keycode_count]
|
||||
// );
|
||||
keycode_count += 1;
|
||||
}
|
||||
ground_to_idx[ground] = keycode_to_idx[*keycode]
|
||||
input_to_key_idx[ground] = keycode_to_idx[*keycode]
|
||||
}
|
||||
|
||||
let interception_handle = match direct_input {
|
||||
true => {
|
||||
let inner_handle = Interception::new();
|
||||
|
||||
if inner_handle.is_some() {
|
||||
info!("Keyboard emulation with interception loaded");
|
||||
} else {
|
||||
error!("Keyboard emulation cannot load interception, falling back to SendKeys()");
|
||||
}
|
||||
inner_handle
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
let direct_input = interception_handle.is_some();
|
||||
|
||||
let mut kb_buf = [INPUT {
|
||||
type_: INPUT_KEYBOARD,
|
||||
u: unsafe { mem::zeroed() },
|
||||
@ -178,13 +209,24 @@ impl KeyboardOutput {
|
||||
inner.dwExtraInfo = 0;
|
||||
}
|
||||
|
||||
let kb_direct_buf = [Stroke::Keyboard {
|
||||
code: ScanCode::Esc,
|
||||
state: KeyState::UP,
|
||||
information: 0,
|
||||
}; 41];
|
||||
|
||||
Self {
|
||||
ground_to_idx,
|
||||
idx_to_keycode,
|
||||
// keycode_to_idx,
|
||||
input_to_idx: input_to_key_idx,
|
||||
key_idx_to_keycode,
|
||||
key_idx_to_scancode,
|
||||
next_keys: [false; 41],
|
||||
last_keys: [false; 41],
|
||||
|
||||
direct_input,
|
||||
interception_handle,
|
||||
|
||||
kb_buf,
|
||||
kb_direct_buf,
|
||||
n_kb_buf: 0,
|
||||
}
|
||||
}
|
||||
@ -198,24 +240,52 @@ impl KeyboardOutput {
|
||||
.zip(self.last_keys.iter_mut())
|
||||
.enumerate()
|
||||
{
|
||||
let keycode = self.idx_to_keycode[i];
|
||||
if keycode == 0 {
|
||||
let keycode = self.key_idx_to_keycode[i];
|
||||
let scancode = self.key_idx_to_scancode[i];
|
||||
|
||||
if (!self.direct_input && keycode == 0) || (self.direct_input && scancode.is_none()) {
|
||||
continue;
|
||||
}
|
||||
match (*n, *l) {
|
||||
(true, false) => {
|
||||
match (self.direct_input, *n, *l) {
|
||||
(false, true, false) => {
|
||||
let inner: &mut KEYBDINPUT = unsafe { self.kb_buf[self.n_kb_buf as usize].u.ki_mut() };
|
||||
inner.wVk = keycode;
|
||||
inner.dwFlags = 0;
|
||||
self.n_kb_buf += 1;
|
||||
// println!("{} down", keycode);
|
||||
}
|
||||
(false, true) => {
|
||||
(false, false, true) => {
|
||||
let inner: &mut KEYBDINPUT = unsafe { self.kb_buf[self.n_kb_buf as usize].u.ki_mut() };
|
||||
inner.wVk = keycode;
|
||||
inner.dwFlags = KEYEVENTF_KEYUP;
|
||||
self.n_kb_buf += 1;
|
||||
// println!("{} up", keycode);
|
||||
}
|
||||
(true, true, false) => {
|
||||
// info!("keydown {:?}", scancode);
|
||||
let inner: &mut Stroke = &mut self.kb_direct_buf[self.n_kb_buf as usize];
|
||||
if let Stroke::Keyboard {
|
||||
code,
|
||||
state,
|
||||
information: _,
|
||||
} = inner
|
||||
{
|
||||
*code = scancode.unwrap();
|
||||
*state = KeyState::DOWN;
|
||||
self.n_kb_buf += 1;
|
||||
}
|
||||
}
|
||||
(true, false, true) => {
|
||||
// info!("keyup {:?}", scancode);
|
||||
let inner: &mut Stroke = &mut self.kb_direct_buf[self.n_kb_buf as usize];
|
||||
if let Stroke::Keyboard {
|
||||
code,
|
||||
state,
|
||||
information: _,
|
||||
} = inner
|
||||
{
|
||||
*code = scancode.unwrap();
|
||||
*state = KeyState::UP;
|
||||
self.n_kb_buf += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -223,12 +293,19 @@ impl KeyboardOutput {
|
||||
}
|
||||
|
||||
if self.n_kb_buf != 0 {
|
||||
unsafe {
|
||||
SendInput(
|
||||
self.n_kb_buf,
|
||||
self.kb_buf.as_mut_ptr(),
|
||||
mem::size_of::<INPUT>() as c_int,
|
||||
);
|
||||
match self.direct_input {
|
||||
false => unsafe {
|
||||
SendInput(
|
||||
self.n_kb_buf,
|
||||
self.kb_buf.as_mut_ptr(),
|
||||
mem::size_of::<INPUT>() as c_int,
|
||||
);
|
||||
},
|
||||
true => {
|
||||
if let Some(handle) = self.interception_handle.as_mut() {
|
||||
handle.send(1, &self.kb_direct_buf[0..self.n_kb_buf as usize]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -239,7 +316,7 @@ impl OutputHandler for KeyboardOutput {
|
||||
self.next_keys.fill(false);
|
||||
for (idx, x) in flat_input.iter().enumerate() {
|
||||
if *x {
|
||||
self.next_keys[self.ground_to_idx[idx]] = true;
|
||||
self.next_keys[self.input_to_idx[idx]] = true;
|
||||
}
|
||||
}
|
||||
self.send();
|
||||
|
@ -42,9 +42,10 @@ impl AsyncJob for OutputJob {
|
||||
layout,
|
||||
polling,
|
||||
sensitivity,
|
||||
direct_input,
|
||||
} => {
|
||||
self.sensitivity = sensitivity;
|
||||
self.handler = Some(Box::new(KeyboardOutput::new(layout.clone())));
|
||||
self.handler = Some(Box::new(KeyboardOutput::new(layout.clone(), direct_input)));
|
||||
self.timer = interval(Duration::from_micros(polling.to_t_u64()));
|
||||
|
||||
true
|
||||
|
15
src-tauri/Cargo.lock
generated
15
src-tauri/Cargo.lock
generated
@ -1497,6 +1497,20 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interception"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"interception-sys",
|
||||
"num_enum",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interception-sys"
|
||||
version = "0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.3.0"
|
||||
@ -2954,6 +2968,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"image 0.23.14",
|
||||
"interception",
|
||||
"ipconfig",
|
||||
"log",
|
||||
"palette",
|
||||
|
@ -38,6 +38,7 @@ fn main() {
|
||||
env_logger::Builder::new()
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.init();
|
||||
info!("Starting slidershim");
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
@ -45,7 +46,9 @@ fn main() {
|
||||
simple_logging::log_to_file(log_file_path.as_path(), log::LevelFilter::Debug).unwrap();
|
||||
}
|
||||
|
||||
info!("Loading config");
|
||||
let config = Arc::new(Mutex::new(Some(slider_io::Config::load())));
|
||||
info!("Loading manager");
|
||||
let manager = Arc::new(Mutex::new(slider_io::Manager::new()));
|
||||
{
|
||||
let config_handle = config.lock();
|
||||
@ -55,6 +58,7 @@ fn main() {
|
||||
manager_handle.update_config(config_handle_ref.clone());
|
||||
}
|
||||
|
||||
info!("Running tauri");
|
||||
tauri::Builder::default()
|
||||
.system_tray(
|
||||
// System tray content
|
||||
|
@ -15,6 +15,7 @@
|
||||
let divaBrightness = 63;
|
||||
let brokenithmPort = 1606;
|
||||
let keyboardSensitivity = 20;
|
||||
let keyboardDirectInput = false;
|
||||
let outputPolling = "100";
|
||||
let outputWebsocketUrl = "http://localhost:3000";
|
||||
let ledFaster = false;
|
||||
@ -69,6 +70,7 @@
|
||||
divaBrightness = payload.divaBrightness || 63;
|
||||
brokenithmPort = payload.brokenithmPort || 1606;
|
||||
keyboardSensitivity = payload.keyboardSensitivity || 20;
|
||||
keyboardDirectInput = payload.keyboardDirectInput || false;
|
||||
outputPolling = payload.outputPolling || "100";
|
||||
outputWebsocketUrl =
|
||||
payload.outputWebsocketUrl || "http://localhost:3000/";
|
||||
@ -125,6 +127,7 @@
|
||||
divaBrightness,
|
||||
brokenithmPort,
|
||||
keyboardSensitivity,
|
||||
keyboardDirectInput,
|
||||
outputPolling,
|
||||
outputWebsocketUrl,
|
||||
ledFaster,
|
||||
@ -386,6 +389,28 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if outputMode.slice(0, 2) === "kb"}
|
||||
<div class="row">
|
||||
<div class="label" title="Larger means harder to trigger">
|
||||
Use DirectInput emulation
|
||||
</div>
|
||||
<div class="input">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={keyboardDirectInput}
|
||||
on:change={markDirty}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label" />
|
||||
<div class="input comment">
|
||||
DirectInput emulation requires <Link
|
||||
href="https://github.com/oblitum/Interception/releases/tag/v1.0.1">Interception</Link
|
||||
> to be installed
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if outputMode === "websocket"}
|
||||
<div class="row">
|
||||
<div class="label">Output URL</div>
|
||||
|
Loading…
Reference in New Issue
Block a user