first commit
This commit is contained in:
367
myenv/lib/python3.10/site-packages/pynput/keyboard/_darwin.py
Normal file
367
myenv/lib/python3.10/site-packages/pynput/keyboard/_darwin.py
Normal file
@@ -0,0 +1,367 @@
|
||||
# coding=utf-8
|
||||
# pynput
|
||||
# Copyright (C) 2015-2024 Moses Palmér
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Lesser General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
The keyboard implementation for *macOS*.
|
||||
"""
|
||||
|
||||
# pylint: disable=C0111
|
||||
# The documentation is extracted from the base classes
|
||||
|
||||
# pylint: disable=R0903
|
||||
# We implement stubs
|
||||
|
||||
import enum
|
||||
|
||||
from Quartz import (
|
||||
CGEventCreateKeyboardEvent,
|
||||
CGEventGetFlags,
|
||||
CGEventGetIntegerValueField,
|
||||
CGEventGetType,
|
||||
CGEventKeyboardGetUnicodeString,
|
||||
CGEventKeyboardSetUnicodeString,
|
||||
CGEventMaskBit,
|
||||
CGEventPost,
|
||||
CGEventSetFlags,
|
||||
kCGEventFlagMaskAlternate,
|
||||
kCGEventFlagMaskCommand,
|
||||
kCGEventFlagMaskControl,
|
||||
kCGEventFlagMaskShift,
|
||||
kCGEventFlagsChanged,
|
||||
kCGEventKeyDown,
|
||||
kCGEventKeyUp,
|
||||
kCGHIDEventTap,
|
||||
kCGKeyboardEventKeycode,
|
||||
NSEvent,
|
||||
NSSystemDefined)
|
||||
|
||||
from pynput._util.darwin import (
|
||||
get_unicode_to_keycode_map,
|
||||
keycode_context,
|
||||
ListenerMixin)
|
||||
from pynput._util.darwin_vks import SYMBOLS
|
||||
from . import _base
|
||||
|
||||
|
||||
# From hidsystem/ev_keymap.h
|
||||
NX_KEYTYPE_PLAY = 16
|
||||
NX_KEYTYPE_MUTE = 7
|
||||
NX_KEYTYPE_SOUND_DOWN = 1
|
||||
NX_KEYTYPE_SOUND_UP = 0
|
||||
NX_KEYTYPE_NEXT = 17
|
||||
NX_KEYTYPE_PREVIOUS = 18
|
||||
NX_KEYTYPE_EJECT = 14
|
||||
|
||||
# pylint: disable=C0103; We want to use the names from the C API
|
||||
# This is undocumented, but still widely known
|
||||
kSystemDefinedEventMediaKeysSubtype = 8
|
||||
|
||||
# We extract this here since the name is very long
|
||||
otherEventWithType = getattr(
|
||||
NSEvent,
|
||||
'otherEventWithType_'
|
||||
'location_'
|
||||
'modifierFlags_'
|
||||
'timestamp_'
|
||||
'windowNumber_'
|
||||
'context_'
|
||||
'subtype_'
|
||||
'data1_'
|
||||
'data2_')
|
||||
# pylint: enable=C0103
|
||||
|
||||
|
||||
class KeyCode(_base.KeyCode):
|
||||
_PLATFORM_EXTENSIONS = (
|
||||
# Whether this is a media key
|
||||
'_is_media',
|
||||
)
|
||||
|
||||
# Be explicit about fields
|
||||
_is_media = None
|
||||
|
||||
@classmethod
|
||||
def _from_media(cls, vk, **kwargs):
|
||||
"""Creates a media key from a key code.
|
||||
|
||||
:param int vk: The key code.
|
||||
|
||||
:return: a key code
|
||||
"""
|
||||
return cls.from_vk(vk, _is_media=True, **kwargs)
|
||||
|
||||
def _event(self, modifiers, mapping, is_pressed):
|
||||
"""This key as a *Quartz* event.
|
||||
|
||||
:param set modifiers: The currently active modifiers.
|
||||
|
||||
:param mapping: The current keyboard mapping.
|
||||
|
||||
:param bool is_press: Whether to generate a press event.
|
||||
|
||||
:return: a *Quartz* event
|
||||
"""
|
||||
vk = self.vk or mapping.get(self.char)
|
||||
if self._is_media:
|
||||
result = otherEventWithType(
|
||||
NSSystemDefined,
|
||||
(0, 0),
|
||||
0xa00 if is_pressed else 0xb00,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
8,
|
||||
(self.vk << 16) | ((0xa if is_pressed else 0xb) << 8),
|
||||
-1).CGEvent()
|
||||
else:
|
||||
result = CGEventCreateKeyboardEvent(
|
||||
None, 0 if vk is None else vk, is_pressed)
|
||||
|
||||
CGEventSetFlags(
|
||||
result,
|
||||
0
|
||||
| (kCGEventFlagMaskAlternate
|
||||
if Key.alt in modifiers else 0)
|
||||
|
||||
| (kCGEventFlagMaskCommand
|
||||
if Key.cmd in modifiers else 0)
|
||||
|
||||
| (kCGEventFlagMaskControl
|
||||
if Key.ctrl in modifiers else 0)
|
||||
|
||||
| (kCGEventFlagMaskShift
|
||||
if Key.shift in modifiers else 0))
|
||||
|
||||
if vk is None and self.char is not None:
|
||||
CGEventKeyboardSetUnicodeString(
|
||||
result, len(self.char), self.char)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# pylint: disable=W0212
|
||||
class Key(enum.Enum):
|
||||
# Default keys
|
||||
alt = KeyCode.from_vk(0x3A)
|
||||
alt_l = KeyCode.from_vk(0x3A)
|
||||
alt_r = KeyCode.from_vk(0x3D)
|
||||
alt_gr = KeyCode.from_vk(0x3D)
|
||||
backspace = KeyCode.from_vk(0x33)
|
||||
caps_lock = KeyCode.from_vk(0x39)
|
||||
cmd = KeyCode.from_vk(0x37)
|
||||
cmd_l = KeyCode.from_vk(0x37)
|
||||
cmd_r = KeyCode.from_vk(0x36)
|
||||
ctrl = KeyCode.from_vk(0x3B)
|
||||
ctrl_l = KeyCode.from_vk(0x3B)
|
||||
ctrl_r = KeyCode.from_vk(0x3E)
|
||||
delete = KeyCode.from_vk(0x75)
|
||||
down = KeyCode.from_vk(0x7D)
|
||||
end = KeyCode.from_vk(0x77)
|
||||
enter = KeyCode.from_vk(0x24)
|
||||
esc = KeyCode.from_vk(0x35)
|
||||
f1 = KeyCode.from_vk(0x7A)
|
||||
f2 = KeyCode.from_vk(0x78)
|
||||
f3 = KeyCode.from_vk(0x63)
|
||||
f4 = KeyCode.from_vk(0x76)
|
||||
f5 = KeyCode.from_vk(0x60)
|
||||
f6 = KeyCode.from_vk(0x61)
|
||||
f7 = KeyCode.from_vk(0x62)
|
||||
f8 = KeyCode.from_vk(0x64)
|
||||
f9 = KeyCode.from_vk(0x65)
|
||||
f10 = KeyCode.from_vk(0x6D)
|
||||
f11 = KeyCode.from_vk(0x67)
|
||||
f12 = KeyCode.from_vk(0x6F)
|
||||
f13 = KeyCode.from_vk(0x69)
|
||||
f14 = KeyCode.from_vk(0x6B)
|
||||
f15 = KeyCode.from_vk(0x71)
|
||||
f16 = KeyCode.from_vk(0x6A)
|
||||
f17 = KeyCode.from_vk(0x40)
|
||||
f18 = KeyCode.from_vk(0x4F)
|
||||
f19 = KeyCode.from_vk(0x50)
|
||||
f20 = KeyCode.from_vk(0x5A)
|
||||
home = KeyCode.from_vk(0x73)
|
||||
left = KeyCode.from_vk(0x7B)
|
||||
page_down = KeyCode.from_vk(0x79)
|
||||
page_up = KeyCode.from_vk(0x74)
|
||||
right = KeyCode.from_vk(0x7C)
|
||||
shift = KeyCode.from_vk(0x38)
|
||||
shift_l = KeyCode.from_vk(0x38)
|
||||
shift_r = KeyCode.from_vk(0x3C)
|
||||
space = KeyCode.from_vk(0x31, char=' ')
|
||||
tab = KeyCode.from_vk(0x30)
|
||||
up = KeyCode.from_vk(0x7E)
|
||||
|
||||
media_play_pause = KeyCode._from_media(NX_KEYTYPE_PLAY)
|
||||
media_volume_mute = KeyCode._from_media(NX_KEYTYPE_MUTE)
|
||||
media_volume_down = KeyCode._from_media(NX_KEYTYPE_SOUND_DOWN)
|
||||
media_volume_up = KeyCode._from_media(NX_KEYTYPE_SOUND_UP)
|
||||
media_previous = KeyCode._from_media(NX_KEYTYPE_PREVIOUS)
|
||||
media_next = KeyCode._from_media(NX_KEYTYPE_NEXT)
|
||||
media_eject = KeyCode._from_media(NX_KEYTYPE_EJECT)
|
||||
# pylint: enable=W0212
|
||||
|
||||
|
||||
class Controller(_base.Controller):
|
||||
_KeyCode = KeyCode
|
||||
_Key = Key
|
||||
|
||||
def __init__(self):
|
||||
super(Controller, self).__init__()
|
||||
self._mapping = get_unicode_to_keycode_map()
|
||||
|
||||
def _handle(self, key, is_press):
|
||||
with self.modifiers as modifiers:
|
||||
CGEventPost(
|
||||
kCGHIDEventTap,
|
||||
(key if key not in (k for k in Key) else key.value)._event(
|
||||
modifiers, self._mapping, is_press))
|
||||
|
||||
|
||||
class Listener(ListenerMixin, _base.Listener):
|
||||
#: The events that we listen to
|
||||
_EVENTS = (
|
||||
CGEventMaskBit(kCGEventKeyDown) |
|
||||
CGEventMaskBit(kCGEventKeyUp) |
|
||||
CGEventMaskBit(kCGEventFlagsChanged) |
|
||||
CGEventMaskBit(NSSystemDefined)
|
||||
)
|
||||
|
||||
# pylint: disable=W0212
|
||||
#: A mapping from keysym to special key
|
||||
_SPECIAL_KEYS = {
|
||||
(key.value.vk, key.value._is_media): key
|
||||
for key in Key}
|
||||
# pylint: enable=W0212
|
||||
|
||||
#: The event flags set for the various modifier keys
|
||||
_MODIFIER_FLAGS = {
|
||||
Key.alt: kCGEventFlagMaskAlternate,
|
||||
Key.alt_l: kCGEventFlagMaskAlternate,
|
||||
Key.alt_r: kCGEventFlagMaskAlternate,
|
||||
Key.cmd: kCGEventFlagMaskCommand,
|
||||
Key.cmd_l: kCGEventFlagMaskCommand,
|
||||
Key.cmd_r: kCGEventFlagMaskCommand,
|
||||
Key.ctrl: kCGEventFlagMaskControl,
|
||||
Key.ctrl_l: kCGEventFlagMaskControl,
|
||||
Key.ctrl_r: kCGEventFlagMaskControl,
|
||||
Key.shift: kCGEventFlagMaskShift,
|
||||
Key.shift_l: kCGEventFlagMaskShift,
|
||||
Key.shift_r: kCGEventFlagMaskShift}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Listener, self).__init__(*args, **kwargs)
|
||||
self._flags = 0
|
||||
self._context = None
|
||||
self._intercept = self._options.get(
|
||||
'intercept',
|
||||
None)
|
||||
|
||||
def _run(self):
|
||||
with keycode_context() as context:
|
||||
self._context = context
|
||||
try:
|
||||
super(Listener, self)._run()
|
||||
finally:
|
||||
self._context = None
|
||||
|
||||
def _handle_message(self, _proxy, event_type, event, _refcon, injected):
|
||||
# Convert the event to a KeyCode; this may fail, and in that case we
|
||||
# pass None
|
||||
try:
|
||||
key = self._event_to_key(event)
|
||||
except IndexError:
|
||||
key = None
|
||||
|
||||
try:
|
||||
if event_type == kCGEventKeyDown:
|
||||
# This is a normal key press
|
||||
self.on_press(key, injected)
|
||||
|
||||
elif event_type == kCGEventKeyUp:
|
||||
# This is a normal key release
|
||||
self.on_release(key, injected)
|
||||
|
||||
elif key == Key.caps_lock:
|
||||
# We only get an event when caps lock is toggled, so we fake
|
||||
# press and release
|
||||
self.on_press(key, injected)
|
||||
self.on_release(key, injected)
|
||||
|
||||
elif event_type == NSSystemDefined:
|
||||
sys_event = NSEvent.eventWithCGEvent_(event)
|
||||
if sys_event.subtype() == kSystemDefinedEventMediaKeysSubtype:
|
||||
# The key in the special key dict; True since it is a media
|
||||
# key
|
||||
key = ((sys_event.data1() & 0xffff0000) >> 16, True)
|
||||
if key in self._SPECIAL_KEYS:
|
||||
flags = sys_event.data1() & 0x0000ffff
|
||||
is_press = ((flags & 0xff00) >> 8) == 0x0a
|
||||
if is_press:
|
||||
self.on_press(self._SPECIAL_KEYS[key])
|
||||
else:
|
||||
self.on_release(self._SPECIAL_KEYS[key])
|
||||
|
||||
else:
|
||||
# This is a modifier event---excluding caps lock---for which we
|
||||
# must check the current modifier state to determine whether
|
||||
# the key was pressed or released
|
||||
flags = CGEventGetFlags(event)
|
||||
is_press = flags & self._MODIFIER_FLAGS.get(key, 0)
|
||||
if is_press:
|
||||
self.on_press(key, injected)
|
||||
else:
|
||||
self.on_release(key, injected)
|
||||
|
||||
finally:
|
||||
# Store the current flag mask to be able to detect modifier state
|
||||
# changes
|
||||
self._flags = CGEventGetFlags(event)
|
||||
|
||||
def _event_to_key(self, event):
|
||||
"""Converts a *Quartz* event to a :class:`KeyCode`.
|
||||
|
||||
:param event: The event to convert.
|
||||
|
||||
:return: a :class:`pynput.keyboard.KeyCode`
|
||||
|
||||
:raises IndexError: if the key code is invalid
|
||||
"""
|
||||
vk = CGEventGetIntegerValueField(
|
||||
event, kCGKeyboardEventKeycode)
|
||||
event_type = CGEventGetType(event)
|
||||
is_media = True if event_type == NSSystemDefined else None
|
||||
|
||||
# First try special keys...
|
||||
key = (vk, is_media)
|
||||
if key in self._SPECIAL_KEYS:
|
||||
return self._SPECIAL_KEYS[key]
|
||||
|
||||
# ...then try characters...
|
||||
length, chars = CGEventKeyboardGetUnicodeString(
|
||||
event, 100, None, None)
|
||||
try:
|
||||
printable = chars.isprintable()
|
||||
except AttributeError:
|
||||
printable = chars.isalnum()
|
||||
if not printable and vk in SYMBOLS \
|
||||
and CGEventGetFlags(event) \
|
||||
& kCGEventFlagMaskControl:
|
||||
return KeyCode.from_char(SYMBOLS[vk], vk=vk)
|
||||
elif length > 0:
|
||||
return KeyCode.from_char(chars, vk=vk)
|
||||
|
||||
# ...and fall back on a virtual key code
|
||||
return KeyCode.from_vk(vk)
|
||||
Reference in New Issue
Block a user