Создание скрипта для разделения устройств

Tạo vào: 15 tháng 10, 2024

Trả lời bằng O1 Preview bởi Chat01

Câu hỏi

я пишу скрипт по функционалу подобный программе астер для создания двух рабочих станций из одного системного блока. моя задача разделить фокус на телек с геймпадем и монитор с клавой и мышью, чтобы они не пересекались между собой и не мешали друг другу. я использую винду 11 и 1080 тиай . я составил скелет скрипта, помоги мне реализовать методы в нём. отмечу что помимо мышки клавы и геймпада мне нужно также разделить вывод звука на телек для игр и на пк колонки для монитора.
вот мой код:
"
import win32api
import win32gui
import win32con
import pyHook
import pythoncom

Глобальные переменные для хранения состояния

devices = {}
windows = {}

def detect_devices():
# Функция для обнаружения подключенных устройств ввода
pass

def assign_device_to_window(device_id, window_handle):
# Функция для привязки устройства ввода к определенному окну
pass

def on_keyboard_event(event):
# Обработчик событий клавиатуры
# Проверяем, какое устройство сгенерировало событие и перенаправляем его
pass

def on_mouse_event(event):
# Обработчик событий мыши
pass

def main():
# Инициализация устройств и окон
detect_devices()

text
# Настройка хуков для перехвата событий ввода hm = pyHook.HookManager() hm.KeyDown = on_keyboard_event hm.MouseAll = on_mouse_event hm.HookKeyboard() hm.HookMouse() # Запуск цикла обработки сообщений pythoncom.PumpMessages()

if name == "main":
main()
"
реализуй сколько сможешь методов и скажи на верном ли я пути.

Suy nghĩ

Создание разделенных узлов

Пользователь хочет реализовать два рабочих узла из одного блока системы, отделяя фокус/ввод между TV с геймпадом и монитором с клавиатурой и мышью, и разделяя аудиовыход для TV и PC.

Реализуем методы

Импортируются модули, задаются глобальные переменные для хранения состояния устройств и окон, создаются функции для обнаружения устройств, назначения их окнам, обработки событий клавиатуры и мыши, запускается цикл сообщений.

Реализация методов

Я работаю над методами для подключения устройств ввода и вывода, включая клавиатуру, мышь и звук. Для Python 3.x лучше использовать библиотеку 'pywin32' вместо 'pyHook', которая поддерживает только Python 2.x.

Ищу подходящие инструменты

Я использую функции 'win32api' и 'pyHook' для детекции устройств и назначения их окнам, а также 'winmm' или 'mmdeviceapi' через 'pycaw' для управления аудио.

Определяю различия

Windows Raw Input API функционирует, различая устройства ввода. Определяю устройства через 'win32api.GetRawInputDeviceList', маппирую их к окошкам и перехватываю события для наводки вывода аудио на нужные устройства.

Trả lời

Вы на правильном пути, но есть некоторые нюансы, которые стоит учесть при реализации вашего скрипта. Основная задача — разделить устройства ввода и аудиовыходы между двумя рабочими станциями на одном ПК. Давайте рассмотрим, как можно реализовать ваши методы.

Обнаружение устройств ввода

Функция detect_devices() должна обнаруживать подключенные устройства ввода и сохранять информацию о них в глобальную переменную devices. В Windows для этого можно использовать API Raw Input. Ниже приведен пример реализации:

python
import win32api import win32gui import win32con import ctypes from ctypes import wintypes def detect_devices(): global devices devices = {} # Получаем список устройств ввода num_devices = ctypes.c_uint() ctypes.windll.user32.GetRawInputDeviceList(None, ctypes.byref(num_devices), ctypes.sizeof(RAWINPUTDEVICELIST)) device_list = (RAWINPUTDEVICELIST * num_devices.value)() ctypes.windll.user32.GetRawInputDeviceList(device_list, ctypes.byref(num_devices), ctypes.sizeof(RAWINPUTDEVICELIST)) for device in device_list: device_handle = device.hDevice device_name_size = ctypes.c_uint() ctypes.windll.user32.GetRawInputDeviceInfoA(device_handle, RIDI_DEVICENAME, None, ctypes.byref(device_name_size)) device_name = ctypes.create_string_buffer(device_name_size.value) ctypes.windll.user32.GetRawInputDeviceInfoA(device_handle, RIDI_DEVICENAME, device_name, ctypes.byref(device_name_size)) # Получаем информацию об устройстве rid_device_info = RID_DEVICE_INFO() rid_device_info.cbSize = ctypes.sizeof(RID_DEVICE_INFO) cbSize = ctypes.c_uint(ctypes.sizeof(RID_DEVICE_INFO)) ctypes.windll.user32.GetRawInputDeviceInfoA(device_handle, RIDI_DEVICEINFO, ctypes.byref(rid_device_info), ctypes.byref(cbSize)) # Сохраняем устройство в словарь devices[device_handle] = { 'name': device_name.value.decode(), 'type': rid_device_info.dwType }

Для этого нам понадобятся некоторые структуры и константы:

python
# Константы для Raw Input RIDI_DEVICENAME = 0x20000007 RIDI_DEVICEINFO = 0x2000000b RIM_TYPEMOUSE = 0 RIM_TYPEKEYBOARD = 1 RIM_TYPEHID = 2 class RAWINPUTDEVICELIST(ctypes.Structure): _fields_ = [("hDevice", wintypes.HANDLE), ("dwType", wintypes.DWORD)] class RID_DEVICE_INFO_MOUSE(ctypes.Structure): _fields_ = [("dwId", wintypes.DWORD), ("dwNumberOfButtons", wintypes.DWORD), ("dwSampleRate", wintypes.DWORD), ("fHasHorizontalWheel", wintypes.BOOL)] class RID_DEVICE_INFO_KEYBOARD(ctypes.Structure): _fields_ = [("dwType", wintypes.DWORD), ("dwSubType", wintypes.DWORD), ("dwKeyboardMode", wintypes.DWORD), ("dwNumberOfFunctionKeys", wintypes.DWORD), ("dwNumberOfIndicators", wintypes.DWORD), ("dwNumberOfKeysTotal", wintypes.DWORD)] class RID_DEVICE_INFO_HID(ctypes.Structure): _fields_ = [("dwVendorId", wintypes.DWORD), ("dwProductId", wintypes.DWORD), ("dwVersionNumber", wintypes.DWORD), ("usUsagePage", wintypes.USHORT), ("usUsage", wintypes.USHORT)] class RID_DEVICE_INFO_UNION(ctypes.Union): _fields_ = [("mouse", RID_DEVICE_INFO_MOUSE), ("keyboard", RID_DEVICE_INFO_KEYBOARD), ("hid", RID_DEVICE_INFO_HID)] class RID_DEVICE_INFO(ctypes.Structure): _fields_ = [("cbSize", wintypes.DWORD), ("dwType", wintypes.DWORD), ("info", RID_DEVICE_INFO_UNION)]

Привязка устройства к окну

Функция assign_device_to_window(device_id, window_handle) должна сопоставлять идентификатор устройства с определенным окном. Для этого можно создать словарь, который хранит эту информацию:

python
def assign_device_to_window(device_id, window_handle): global windows windows[device_id] = window_handle

Обработка событий клавиатуры и мыши

К сожалению, библиотека pyHook не предоставляет информацию об устройстве, с которого было получено событие. Для получения данных о конкретном устройстве ввода необходимо использовать более низкоуровневые API, такие как Raw Input или DirectInput.

Ниже представлен пример использования API Raw Input для перехвата событий от конкретных устройств:

python
def register_raw_input_devices(): rid = (RAWINPUTDEVICE * 2)() # Регистрация клавиатуры rid[0].usUsagePage = 0x01 rid[0].usUsage = 0x06 # Клавиатура rid[0].dwFlags = RIDEV_INPUTSINK rid[0].hwndTarget = hwnd # Дескриптор окна # Регистрация мыши rid[1].usUsagePage = 0x01 rid[1].usUsage = 0x02 # Мышь rid[1].dwFlags = RIDEV_INPUTSINK rid[1].hwndTarget = hwnd # Дескриптор окна if not ctypes.windll.user32.RegisterRawInputDevices(rid, 2, ctypes.sizeof(RAWINPUTDEVICE)): print("Ошибка регистрации устройств Raw Input")

Структуры для Raw Input устройств:

python
class RAWINPUTDEVICE(ctypes.Structure): _fields_ = [("usUsagePage", wintypes.USHORT), ("usUsage", wintypes.USHORT), ("dwFlags", wintypes.DWORD), ("hwndTarget", wintypes.HWND)] RIDEV_INPUTSINK = 0x00000100

Обработка сообщений Windows для получения событий Raw Input:

python
def wnd_proc(hwnd, msg, wparam, lparam): if msg == win32con.WM_INPUT: handle_raw_input(lparam) return ctypes.windll.user32.DefWindowProcW(hwnd, msg, wparam, lparam)

Функция для обработки Raw Input событий:

python
def handle_raw_input(lparam): raw_input_data = RAWINPUT() data_size = ctypes.c_uint(ctypes.sizeof(RAWINPUT)) ctypes.windll.user32.GetRawInputData(lparam, RID_INPUT, ctypes.byref(raw_input_data), ctypes.byref(data_size), ctypes.sizeof(RAWINPUTHEADER)) device_handle = raw_input_data.header.hDevice if device_handle in windows: target_window = windows[device_handle] # Перенаправляем событие в соответствующее окно # Здесь необходимо реализовать логику передачи события

Структуры для Raw Input данных:

python
class RAWINPUTHEADER(ctypes.Structure): _fields_ = [("dwType", wintypes.DWORD), ("dwSize", wintypes.DWORD), ("hDevice", wintypes.HANDLE), ("wParam", wintypes.WPARAM)] class RAWMOUSE(ctypes.Structure): _fields_ = [("usFlags", wintypes.USHORT), ("ulButtons", wintypes.ULONG), ("usButtonFlags", wintypes.USHORT), ("usButtonData", wintypes.USHORT), ("ulRawButtons", wintypes.ULONG), ("lLastX", wintypes.LONG), ("lLastY", wintypes.LONG), ("ulExtraInformation", wintypes.ULONG)] class RAWKEYBOARD(ctypes.Structure): _fields_ = [("MakeCode", wintypes.USHORT), ("Flags", wintypes.USHORT), ("Reserved", wintypes.USHORT), ("VKey", wintypes.USHORT), ("Message", wintypes.UINT), ("ExtraInformation", wintypes.ULONG)] class RAWHID(ctypes.Structure): _fields_ = [("dwSizeHid", wintypes.DWORD), ("dwCount", wintypes.DWORD), ("bRawData", ctypes.POINTER(ctypes.c_byte))] class RAWINPUT_UNION(ctypes.Union): _fields_ = [("mouse", RAWMOUSE), ("keyboard", RAWKEYBOARD), ("hid", RAWHID)] class RAWINPUT(ctypes.Structure): _fields_ = [("header", RAWINPUTHEADER), ("data", RAWINPUT_UNION)] RID_INPUT = 0x10000003

Инициализация окна для приема сообщений

Для получения сообщений Windows необходимо создать невидимое окно и запустить цикл обработки сообщений:

python
def main(): # Инициализация устройств и окон detect_devices() # Создаем невидимое окно wc = win32gui.WNDCLASS() wc.lpszClassName = 'RawInputClass' wc.lpfnWndProc = wnd_proc class_atom = win32gui.RegisterClass(wc) hwnd = win32gui.CreateWindow(wc.lpszClassName, 'Raw Input Window', 0, 0, 0, 0, 0, 0, 0, 0, None) # Регистрируем устройства Raw Input register_raw_input_devices() # Запуск цикла обработки сообщений win32gui.PumpMessages()

Разделение аудиовыходов

Для управления аудиовыходами отдельных приложений можно использовать библиотеку PyCaw, которая предоставляет доступ к Windows Core Audio API.

Пример использования PyCaw для установки устройства вывода для конкретного приложения:

python
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume def set_application_output(application_name, device_id): sessions = AudioUtilities.GetAllSessions() for session in sessions: if session.Process and session.Process.name() == application_name: volume = session._ctl.QueryInterface(IAudioEndpointVolume) # Здесь необходимо установить устройство вывода для сессии # К сожалению, PyCaw не предоставляет прямого метода для этого

К сожалению, управление устройством вывода на уровне отдельных приложений в Windows не тривиально и может потребовать использования дополнительных библиотек или даже создания виртуальных аудиоустройств.

Заключение

Вы на правильном пути, но для реализации вашей задачи потребуется использовать более низкоуровневые API и, возможно, написать дополнительные обертки над ними. Вот основные моменты:

  • Обнаружение устройств: Используйте Windows Raw Input API для получения списка подключенных устройств ввода.
  • Привязка устройств к окнам: Создайте словарь для сопоставления идентификаторов устройств с оконными дескрипторами.
  • Обработка событий ввода: Используйте Raw Input API для получения событий от конкретных устройств и перенаправляйте их в нужные окна.
  • Разделение аудиовыходов: Исследуйте возможности Windows для установки устройства вывода для отдельных приложений. Возможно, потребуется использовать дополнительные инструменты или библиотеки.

Обратите внимание, что работа с Raw Input API и управлением аудиоустройствами может быть сложной и потребует тщательного тестирования. Рекомендуется ознакомиться с официальной документацией Microsoft и примерами использования этих API.

Chia sẻ Q&A này