python 对 windows 的输入进行了优化? (和 xbox 指南按钮可以检测到吗?)
python inputs on windows optimized ? ( and xbox guide button possible to detect ? )
简而言之:
关于如何收听 Xbox 指南按钮的任何建议?
要么
关于如何使低端 PC 的“输入”更优化和更快的任何建议?
哎。我一直在寻找一种方法来简化 windows 上的屏幕截图。目前唯一的内置功能是 win+prtsc ,在使用控制器进行游戏时很难按下。我也恰好擅长编程,但我不熟悉 python...
上的输入库
所以我使用“pyautogui”库来模拟按下 win+prtSc ,并使用“pynput”库来监听键盘上的按钮“/”来做截图(好吧,作弊截图。windows 正在截图,我只是模拟按下“win+prtSc”,所以我将我的脚本命名为“screenshit”)。
到目前为止一切顺利。
每个屏幕截图都按“/”按钮并不难。但为了扩展它,我想使用我的控制器来截取屏幕截图。但问题是 pynput 不支持游戏手柄控制器!所以我使用了另一个名为“输入”的库。这个确实支持游戏手柄控制器。然而,它至少使用了 CPU 的 30% 用于工作……这很糟糕!
这是使用“输入”库检测 START(通常的地图按钮)的代码:
while 1:
events = get_gamepad()
if events:
print(events[0].code)
if events[0].code == 'START' :
ScreenShot()
我猜 'get_gamepad()' 优化不够...但如果我错了或使用不当,请指导我!
所以,我想使用的另一件事是 center/guide/xbox 按钮。这个按钮:
xbox center button
但是“inputs”和“pynput”库似乎都没有听这个按钮...
关于如何听这个按钮有什么建议吗?
或
关于如何使低端 PC 的“输入”更优化和更快的建议?
您是否尝试过在 while 循环中休眠(一毫秒左右)?也许 returns 功能立即生效,这就是您 CPU 使用率高的原因?
例如在我当前使用的库(Java、LWJGL + GLFW)中,没有控制器输入事件。相反,您可以随时请求它们。这会导致同样的问题。
关于 XBox 按钮:我的库也没有它的事件。该按钮可在 Windows 10 上打开 XBox 菜单。可能它 100% 专用于该菜单,否则无法使用。 (因此检测不到)
Python 的 inputs
库使用 XInput API 作为 Windows 上的游戏手柄输入,这有一些内置限制。在 XInput 中,应用程序使用 XInputGetState
方法访问当前游戏手柄状态:
https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetstate
按钮和轴状态在 XINPUT_GAMEPAD
结构中返回,它甚至无法表示 Xbox 按钮。某些应用程序通过调用替代(未记录)方法来解决此问题,该方法具有与 XInputGetState
相同的签名和与 XINPUT_GAMEPAD
相同格式的 returns 数据,但包含 Xbox 按钮的额外位状态。
https://www.google.com/search?q=%22XInputGetStateEx%22
即使您使用未记录的 API 方法,您 仍然 不会获得 Xbox 按钮输入,除非您禁用启动 Xbox 的 Windows 设置当您按下 Xbox 按钮时,Xbox Game Bar。启用此设置后,将禁止所有其他应用程序使用 Xbox 按钮。
另请注意,XInput 没有任何类型的输入事件。每次调用 XInputGetState
时,它只是根据最近的输入返回游戏手柄的当前状态。调用它快于 250Hz 是不必要的,因为这是 Xbox 控制器可以发送新输入的最快速度。对于您的屏幕截图应用程序,60Hz 应该足够了。这意味着在 get_gamepad
.
调用之间增加约 16 毫秒的睡眠时间
解决方案:
我在谷歌上搜索了几个小时,终于找到了一个解决方案,即通过虚拟同时按下 win+PrtSc 来使用 Xbox360 的引导按钮截取屏幕截图。然后也播放一点 capture.wav 声音来播放它。
所以代码是:
// compile using :
// g++ prtScX.cpp -lwinmm
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
static const int guide_button_value = 0x0400;
/* the secret function outputs a different struct than the official GetState. */
typedef struct
{
unsigned long eventCount;
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD_SECRET;
// returns 0 on success, 1167 on not connected. Might be others.
int(__stdcall *secret_get_gamepad)(int, XINPUT_GAMEPAD_SECRET *); // function pointer to secret (guide compatible) getstate */
void Screenshot();
int main(int argc, char **argv)
{
TCHAR xinput_dll_path[MAX_PATH];
GetSystemDirectory(xinput_dll_path, sizeof(xinput_dll_path));
strcat(xinput_dll_path, "\xinput1_3.dll");
HINSTANCE xinput_dll = LoadLibrary(xinput_dll_path);
secret_get_gamepad = (int(__stdcall *)(int, XINPUT_GAMEPAD_SECRET *))GetProcAddress(xinput_dll, (LPCSTR)100); // load ordinal 100
XINPUT_GAMEPAD_SECRET pad1;
printf("listening to keys now...\n");
if (secret_get_gamepad(0, &pad1) != 0)
{
printf("Error, make sure your player 1 pad is connected.\n");
return -1;
}
for (;;) // forever
{
secret_get_gamepad(0, &pad1);
if (pad1.wButtons & guide_button_value)
{
printf("Guide button is down.\n");
Screenshot();
Sleep(500);
}
Sleep(33);
}
// you should probably clean up by unloading the DLL.
return 0;
}
void Screenshot()
{
// WIN KEY
// ...
INPUT win_in;
// ...
// Set up a generic keyboard event.
win_in.type = INPUT_KEYBOARD;
win_in.ki.wScan = 0; // hardware scan code for key
win_in.ki.time = 0;
win_in.ki.dwExtraInfo = 0;
// Press the "A" key
win_in.ki.wVk = VK_LWIN; // virtual-key code for the "win" key
// PRTSC KEY
INPUT prtcs_in;
// Set up a generic keyboard event.
prtcs_in.type = INPUT_KEYBOARD;
prtcs_in.ki.wScan = 0; // hardware scan code for key
prtcs_in.ki.time = 0;
prtcs_in.ki.dwExtraInfo = 0;
// Press the "A" key
prtcs_in.ki.wVk = VK_SNAPSHOT; // virtual-key code for the "PrtSc" key
// actually hitting
win_in.ki.dwFlags = WM_KEYDOWN; // key press down
SendInput(1, &win_in, sizeof(INPUT));
prtcs_in.ki.dwFlags = 0; // key press down
SendInput(1, &prtcs_in, sizeof(INPUT));
win_in.ki.dwFlags = WM_KEYUP; // key press up
SendInput(1, &win_in, sizeof(INPUT));
PlaySound(TEXT("capture.wav"), NULL, SND_FILENAME | SND_ASYNC);
}
要工作,它需要在与此脚本相同的目录中发出“capture.wav”声音。
简而言之: 关于如何收听 Xbox 指南按钮的任何建议? 要么 关于如何使低端 PC 的“输入”更优化和更快的任何建议?
哎。我一直在寻找一种方法来简化 windows 上的屏幕截图。目前唯一的内置功能是 win+prtsc ,在使用控制器进行游戏时很难按下。我也恰好擅长编程,但我不熟悉 python...
上的输入库所以我使用“pyautogui”库来模拟按下 win+prtSc ,并使用“pynput”库来监听键盘上的按钮“/”来做截图(好吧,作弊截图。windows 正在截图,我只是模拟按下“win+prtSc”,所以我将我的脚本命名为“screenshit”)。
到目前为止一切顺利。
每个屏幕截图都按“/”按钮并不难。但为了扩展它,我想使用我的控制器来截取屏幕截图。但问题是 pynput 不支持游戏手柄控制器!所以我使用了另一个名为“输入”的库。这个确实支持游戏手柄控制器。然而,它至少使用了 CPU 的 30% 用于工作……这很糟糕! 这是使用“输入”库检测 START(通常的地图按钮)的代码:
while 1:
events = get_gamepad()
if events:
print(events[0].code)
if events[0].code == 'START' :
ScreenShot()
我猜 'get_gamepad()' 优化不够...但如果我错了或使用不当,请指导我!
所以,我想使用的另一件事是 center/guide/xbox 按钮。这个按钮: xbox center button
但是“inputs”和“pynput”库似乎都没有听这个按钮...
关于如何听这个按钮有什么建议吗?
或
关于如何使低端 PC 的“输入”更优化和更快的建议?
您是否尝试过在 while 循环中休眠(一毫秒左右)?也许 returns 功能立即生效,这就是您 CPU 使用率高的原因?
例如在我当前使用的库(Java、LWJGL + GLFW)中,没有控制器输入事件。相反,您可以随时请求它们。这会导致同样的问题。
关于 XBox 按钮:我的库也没有它的事件。该按钮可在 Windows 10 上打开 XBox 菜单。可能它 100% 专用于该菜单,否则无法使用。 (因此检测不到)
Python 的 inputs
库使用 XInput API 作为 Windows 上的游戏手柄输入,这有一些内置限制。在 XInput 中,应用程序使用 XInputGetState
方法访问当前游戏手柄状态:
https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetstate
按钮和轴状态在 XINPUT_GAMEPAD
结构中返回,它甚至无法表示 Xbox 按钮。某些应用程序通过调用替代(未记录)方法来解决此问题,该方法具有与 XInputGetState
相同的签名和与 XINPUT_GAMEPAD
相同格式的 returns 数据,但包含 Xbox 按钮的额外位状态。
https://www.google.com/search?q=%22XInputGetStateEx%22
即使您使用未记录的 API 方法,您 仍然 不会获得 Xbox 按钮输入,除非您禁用启动 Xbox 的 Windows 设置当您按下 Xbox 按钮时,Xbox Game Bar。启用此设置后,将禁止所有其他应用程序使用 Xbox 按钮。
另请注意,XInput 没有任何类型的输入事件。每次调用 XInputGetState
时,它只是根据最近的输入返回游戏手柄的当前状态。调用它快于 250Hz 是不必要的,因为这是 Xbox 控制器可以发送新输入的最快速度。对于您的屏幕截图应用程序,60Hz 应该足够了。这意味着在 get_gamepad
.
解决方案:
我在谷歌上搜索了几个小时,终于找到了一个解决方案,即通过虚拟同时按下 win+PrtSc 来使用 Xbox360 的引导按钮截取屏幕截图。然后也播放一点 capture.wav 声音来播放它。
所以代码是:
// compile using :
// g++ prtScX.cpp -lwinmm
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
static const int guide_button_value = 0x0400;
/* the secret function outputs a different struct than the official GetState. */
typedef struct
{
unsigned long eventCount;
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD_SECRET;
// returns 0 on success, 1167 on not connected. Might be others.
int(__stdcall *secret_get_gamepad)(int, XINPUT_GAMEPAD_SECRET *); // function pointer to secret (guide compatible) getstate */
void Screenshot();
int main(int argc, char **argv)
{
TCHAR xinput_dll_path[MAX_PATH];
GetSystemDirectory(xinput_dll_path, sizeof(xinput_dll_path));
strcat(xinput_dll_path, "\xinput1_3.dll");
HINSTANCE xinput_dll = LoadLibrary(xinput_dll_path);
secret_get_gamepad = (int(__stdcall *)(int, XINPUT_GAMEPAD_SECRET *))GetProcAddress(xinput_dll, (LPCSTR)100); // load ordinal 100
XINPUT_GAMEPAD_SECRET pad1;
printf("listening to keys now...\n");
if (secret_get_gamepad(0, &pad1) != 0)
{
printf("Error, make sure your player 1 pad is connected.\n");
return -1;
}
for (;;) // forever
{
secret_get_gamepad(0, &pad1);
if (pad1.wButtons & guide_button_value)
{
printf("Guide button is down.\n");
Screenshot();
Sleep(500);
}
Sleep(33);
}
// you should probably clean up by unloading the DLL.
return 0;
}
void Screenshot()
{
// WIN KEY
// ...
INPUT win_in;
// ...
// Set up a generic keyboard event.
win_in.type = INPUT_KEYBOARD;
win_in.ki.wScan = 0; // hardware scan code for key
win_in.ki.time = 0;
win_in.ki.dwExtraInfo = 0;
// Press the "A" key
win_in.ki.wVk = VK_LWIN; // virtual-key code for the "win" key
// PRTSC KEY
INPUT prtcs_in;
// Set up a generic keyboard event.
prtcs_in.type = INPUT_KEYBOARD;
prtcs_in.ki.wScan = 0; // hardware scan code for key
prtcs_in.ki.time = 0;
prtcs_in.ki.dwExtraInfo = 0;
// Press the "A" key
prtcs_in.ki.wVk = VK_SNAPSHOT; // virtual-key code for the "PrtSc" key
// actually hitting
win_in.ki.dwFlags = WM_KEYDOWN; // key press down
SendInput(1, &win_in, sizeof(INPUT));
prtcs_in.ki.dwFlags = 0; // key press down
SendInput(1, &prtcs_in, sizeof(INPUT));
win_in.ki.dwFlags = WM_KEYUP; // key press up
SendInput(1, &win_in, sizeof(INPUT));
PlaySound(TEXT("capture.wav"), NULL, SND_FILENAME | SND_ASYNC);
}
要工作,它需要在与此脚本相同的目录中发出“capture.wav”声音。