Skip to content

Commit

Permalink
darwin: add touch support
Browse files Browse the repository at this point in the history
  • Loading branch information
doronz88 committed Mar 17, 2022
1 parent 1d7a675 commit 0edc54f
Show file tree
Hide file tree
Showing 2 changed files with 262 additions and 2 deletions.
162 changes: 161 additions & 1 deletion src/rpcclient/rpcclient/darwin/consts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from enum import Enum
from enum import Enum, auto

kCFAllocatorDefault = 0
MACH_PORT_NULL = 0
Expand Down Expand Up @@ -628,3 +628,163 @@ class NSStringEncoding(Enum):
kSecCodeMagicEmbeddedSignature = 0xfade0cc0 # single-architecture embedded signature
kSecCodeMagicDetachedSignature = 0xfade0cc1 # detached multi-architecture signature
kSecCodeMagicEntitlement = 0xfade7171 # entitlement blob


class IOHIDDigitizerTransducerType(Enum):
kIOHIDDigitizerTransducerTypeStylus = 0
kIOHIDDigitizerTransducerTypePuck = 1
kIOHIDDigitizerTransducerTypeFinger = 2
kIOHIDDigitizerTransducerTypeHand = 3


class IOHIDSwipeMask(Enum):
kIOHIDSwipeNone = 0x00000000
kIOHIDSwipeUp = 0x00000001
kIOHIDSwipeDown = 0x00000002
kIOHIDSwipeLeft = 0x00000004
kIOHIDSwipeRight = 0x00000008
kIOHIDScaleExpand = 0x00000010
kIOHIDScaleContract = 0x00000020
kIOHIDRotateCW = 0x00000040
kIOHIDRotateCCW = 0x00000080


def IOHIDEventTypeMask(type_):
return 1 << type_


def IOHIDEventFieldBase(type_):
return type_ << 16


def IOHIDEventFieldOffsetOf(field):
return field & 0xffff


class IOHIDEventType(Enum):
kIOHIDEventTypeNULL = 0
kIOHIDEventTypeVendorDefined = 1
kIOHIDEventTypeButton = 2
kIOHIDEventTypeKeyboard = 3
kIOHIDEventTypeTranslation = 4
kIOHIDEventTypeRotation = 5
kIOHIDEventTypeScroll = 6
kIOHIDEventTypeScale = 7
kIOHIDEventTypeZoom = 8
kIOHIDEventTypeVelocity = 9
kIOHIDEventTypeOrientation = 10
kIOHIDEventTypeDigitizer = 11
kIOHIDEventTypeAmbientLightSensor = 12
kIOHIDEventTypeAccelerometer = 13
kIOHIDEventTypeProximity = 14
kIOHIDEventTypeTemperature = 15
kIOHIDEventTypeNavigationSwipe = 16
kIOHIDEventTypePointer = 17
kIOHIDEventTypeProgress = 18
kIOHIDEventTypeMultiAxisPointer = 19
kIOHIDEventTypeGyro = 20
kIOHIDEventTypeCompass = 21
kIOHIDEventTypeZoomToggle = 22
kIOHIDEventTypeDockSwipe = 23 # just like kIOHIDEventTypeNavigationSwipe, but intended for consumption by Dock
kIOHIDEventTypeSymbolicHotKey = 24
kIOHIDEventTypePower = 25
kIOHIDEventTypeLED = 26
kIOHIDEventTypeFluidTouchGesture = 27 # This will eventually superseed Navagation and Dock swipes
kIOHIDEventTypeBoundaryScroll = 28
kIOHIDEventTypeBiometric = 29
kIOHIDEventTypeUnicode = 30
kIOHIDEventTypeAtmosphericPressure = 31
kIOHIDEventTypeForce = 32
kIOHIDEventTypeMotionActivity = 33
kIOHIDEventTypeMotionGesture = 34
kIOHIDEventTypeGameController = 35
kIOHIDEventTypeHumidity = 36
kIOHIDEventTypeCollection = 37
kIOHIDEventTypeBrightness = 38
kIOHIDEventTypeCount = 39 # This should always be last

# DEPRECATED:
kIOHIDEventTypeSwipe = 16
kIOHIDEventTypeMouse = 17


class IOHIDDigitizerEventMask(Enum):
kIOHIDDigitizerEventRange = 1 << 0
kIOHIDDigitizerEventTouch = 1 << 1
kIOHIDDigitizerEventPosition = 1 << 2
kIOHIDDigitizerEventStop = 1 << 3
kIOHIDDigitizerEventPeak = 1 << 4
kIOHIDDigitizerEventIdentity = 1 << 5
kIOHIDDigitizerEventAttribute = 1 << 6
kIOHIDDigitizerEventCancel = 1 << 7
kIOHIDDigitizerEventStart = 1 << 8
kIOHIDDigitizerEventResting = 1 << 9
kIOHIDDigitizerEventFromEdgeFlat = 1 << 10
kIOHIDDigitizerEventFromEdgeTip = 1 << 11
kIOHIDDigitizerEventFromCorner = 1 << 12
kIOHIDDigitizerEventSwipePending = 1 << 13
kIOHIDDigitizerEventFromEdgeForcePending = 1 << 14
kIOHIDDigitizerEventFromEdgeForceActive = 1 << 15
kIOHIDDigitizerEventForcePopped = 1 << 16
kIOHIDDigitizerEventSwipeUp = 1 << 24
kIOHIDDigitizerEventSwipeDown = 1 << 25
kIOHIDDigitizerEventSwipeLeft = 1 << 26
kIOHIDDigitizerEventSwipeRight = 1 << 27
kIOHIDDigitizerEventEstimatedAltitude = 1 << 28
kIOHIDDigitizerEventEstimatedAzimuth = 1 << 29
kIOHIDDigitizerEventEstimatedPressure = 1 << 30
kIOHIDDigitizerEventSwipeMask = 0xF << 24


class IOHIDEventField(Enum):
kIOHIDEventFieldIsRelative = IOHIDEventFieldBase(IOHIDEventType.kIOHIDEventTypeNULL.value)
kIOHIDEventFieldIsCollection = auto()
kIOHIDEventFieldIsPixelUnits = auto()
kIOHIDEventFieldIsCenterOrigin = auto()
kIOHIDEventFieldIsBuiltIn = auto()


class IOHIDEventOptionBits(Enum):
kIOHIDEventOptionNone = 0
kIOHIDEventOptionIsAbsolute = 1 << 0
kIOHIDEventOptionIsCollection = 1 << 1
kIOHIDEventOptionIsPixelUnits = 1 << 2
kIOHIDEventOptionIsCenterOrigin = 1 << 3
kIOHIDEventOptionIsBuiltIn = 1 << 4

# misspellings
kIOHIDEventOptionPixelUnits = kIOHIDEventOptionIsPixelUnits


class IOHIDEventFieldDigitizer(Enum):
kIOHIDEventFieldDigitizerX = IOHIDEventFieldBase(IOHIDEventType.kIOHIDEventTypeDigitizer.value)
kIOHIDEventFieldDigitizerY = auto()
kIOHIDEventFieldDigitizerZ = auto()
kIOHIDEventFieldDigitizerButtonMask = auto()
kIOHIDEventFieldDigitizerType = auto()
kIOHIDEventFieldDigitizerIndex = auto()
kIOHIDEventFieldDigitizerIdentity = auto()
kIOHIDEventFieldDigitizerEventMask = auto()
kIOHIDEventFieldDigitizerRange = auto()
kIOHIDEventFieldDigitizerTouch = auto()
kIOHIDEventFieldDigitizerPressure = auto()
kIOHIDEventFieldDigitizerAuxiliaryPressure = auto() # BarrelPressure
kIOHIDEventFieldDigitizerTwist = auto()
kIOHIDEventFieldDigitizerTiltX = auto()
kIOHIDEventFieldDigitizerTiltY = auto()
kIOHIDEventFieldDigitizerAltitude = auto()
kIOHIDEventFieldDigitizerAzimuth = auto()
kIOHIDEventFieldDigitizerQuality = auto()
kIOHIDEventFieldDigitizerDensity = auto()
kIOHIDEventFieldDigitizerIrregularity = auto()
kIOHIDEventFieldDigitizerMajorRadius = auto()
kIOHIDEventFieldDigitizerMinorRadius = auto()
kIOHIDEventFieldDigitizerCollection = auto()
kIOHIDEventFieldDigitizerCollectionChord = auto()
kIOHIDEventFieldDigitizerChildEventMask = auto()
kIOHIDEventFieldDigitizerIsDisplayIntegrated = auto()
kIOHIDEventFieldDigitizerQualityRadiiAccuracy = auto()
kIOHIDEventFieldDigitizerGenerationCount = auto()
kIOHIDEventFieldDigitizerWillUpdateMask = auto()
kIOHIDEventFieldDigitizerDidUpdateMask = auto()
kIOHIDEventFieldDigitizerEstimatedMask = auto()
102 changes: 101 additions & 1 deletion src/rpcclient/rpcclient/darwin/hid.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,46 @@
import contextlib
import time
from enum import Enum
from typing import Union

from rpcclient.darwin.consts import kCFAllocatorDefault, kHIDUsage_Csmr_Menu, kHIDUsage_Csmr_Power, \
kHIDUsage_Csmr_VolumeDecrement, kHIDUsage_Csmr_VolumeIncrement, kHIDPage_Consumer, kHIDUsage_Csmr_Mute, \
kHIDUsage_Csmr_ACSearch, kHIDUsage_Csmr_PlayOrPause, kHIDUsage_Csmr_Play, kHIDUsage_Csmr_Pause, \
kHIDUsage_Csmr_Rewind, kHIDUsage_Csmr_RandomPlay, kHIDUsage_Csmr_Repeat, kHIDUsage_Csmr_FastForward
kHIDUsage_Csmr_Rewind, kHIDUsage_Csmr_RandomPlay, kHIDUsage_Csmr_Repeat, kHIDUsage_Csmr_FastForward, \
IOHIDEventField, IOHIDDigitizerEventMask, IOHIDDigitizerTransducerType, IOHIDEventFieldDigitizer
from rpcclient.exceptions import BadReturnValueError


class TouchEventType(Enum):
TOUCH_DOWN = 0
TOUCH_UP = 1
TOUCH_MOVE = 2


EVENT_TYPE_PARAMS = {
TouchEventType.TOUCH_DOWN: {
'event_flags': (IOHIDDigitizerEventMask.kIOHIDDigitizerEventAttribute.value |
IOHIDDigitizerEventMask.kIOHIDDigitizerEventTouch.value |
IOHIDDigitizerEventMask.kIOHIDDigitizerEventIdentity.value),
'button_mask': 1,
'touch': True,
},
TouchEventType.TOUCH_MOVE: {
'event_flags': (IOHIDDigitizerEventMask.kIOHIDDigitizerEventPosition.value |
IOHIDDigitizerEventMask.kIOHIDDigitizerEventAttribute.value),
'button_mask': 1,
'touch': True,
},
TouchEventType.TOUCH_UP: {
'event_flags': (IOHIDDigitizerEventMask.kIOHIDDigitizerEventAttribute.value |
IOHIDDigitizerEventMask.kIOHIDDigitizerEventTouch.value |
IOHIDDigitizerEventMask.kIOHIDDigitizerEventIdentity.value),
'button_mask': 0,
'touch': False,
},
}


class Hid:
""" Control HID devices and simulate events """

Expand Down Expand Up @@ -69,6 +102,73 @@ def send_keyboard_event(self, page: int, key_code: int, down: bool):
page, key_code, down, 0)
self.dispatch(event)

def send_swipe_right(self):
self.send_swipe(0.5, 0.5, 1.0, 0.5)

def send_swipe_left(self):
self.send_swipe(0.5, 0.5, 0.0, 0.5)

def send_swipe_up(self):
self.send_swipe(0.5, 1.5, 0.5, 0.5)

def send_swipe_down(self):
self.send_swipe(0.5, 0.5, 0.5, 1.5)

def send_swipe(self, from_x: float, from_y: float, to_x: float, to_y: float):
self.send_touch_event(TouchEventType.TOUCH_DOWN, from_x, from_y)
self.send_touch_event(TouchEventType.TOUCH_MOVE, to_x, to_y)
self.send_touch_event(TouchEventType.TOUCH_UP, to_x, to_y)

def send_touch_event(self, event_type: TouchEventType, x: float, y: float, pressure: float = 0.4):
params = EVENT_TYPE_PARAMS[event_type]

timestamp = self._client.symbols.mach_absolute_time()

event_flags = params['event_flags']
touch = params['touch']
button_mask = params['button_mask']

parent = self.create_digitizer_event(IOHIDDigitizerTransducerType.kIOHIDDigitizerTransducerTypeHand,
0, 0, event_flags, 0, x, y, 0.0, 0.0, 0.0, touch, touch, 0,
timestamp=timestamp)

child = self.create_digitizer_finger_event(2, 2, event_flags, button_mask, x, y, 0.0, pressure, 0.0,
touch, touch, 0, timestamp=timestamp)

self._client.symbols.IOHIDEventAppendEvent(parent, child)
self.dispatch(parent)

def create_digitizer_event(self, type_: Union[IOHIDDigitizerTransducerType, int], index: int, identity: int,
event_mask: int, button_mask: int, x: float, y: float, z: float, tip_pressure: float,
barrel_pressure: float, range_: bool, touch: bool, options: int, timestamp=None):
if timestamp is None:
timestamp = self._client.symbols.mach_absolute_time()

event = self._client.symbols.IOHIDEventCreateDigitizerEvent(
kCFAllocatorDefault, timestamp, type_, index, identity, event_mask,
button_mask, x, y, z, tip_pressure, barrel_pressure, range_, touch, options)

self._client.symbols.IOHIDEventSetIntegerValue(event, IOHIDEventField.kIOHIDEventFieldIsBuiltIn, 0)
self._client.symbols.IOHIDEventSetIntegerValue(
event, IOHIDEventFieldDigitizer.kIOHIDEventFieldDigitizerIsDisplayIntegrated, 1)
self._client.symbols.IOHIDEventSetSenderID(event, 0x8000000817319375)

return event

def create_digitizer_finger_event(self, index: int, identity: int, event_mask: int,
button_mask: int, x: float, y: float, z: float, tip_pressure: float,
twist: float, range_: bool, touch: bool, options: int, timestamp=None):
if timestamp is None:
timestamp = self._client.symbols.mach_absolute_time()

event = self._client.symbols.IOHIDEventCreateDigitizerFingerEvent(
kCFAllocatorDefault, timestamp, index, identity, event_mask,
button_mask, x, y, z, tip_pressure, twist, range_, touch, options)

self._client.symbols.IOHIDEventSetFloatValue(
event, IOHIDEventFieldDigitizer.kIOHIDEventFieldDigitizerMajorRadius, 0.5)
return event

@contextlib.contextmanager
def create_hid_client(self):
client = self._client.symbols.IOHIDEventSystemClientCreate(0)
Expand Down

0 comments on commit 0edc54f

Please sign in to comment.