Skip to content

Instantly share code, notes, and snippets.

@therealchjones
Last active February 16, 2022 22:39
Show Gist options
  • Save therealchjones/148ec3965bc989fe84431341c8f30b97 to your computer and use it in GitHub Desktop.
Save therealchjones/148ec3965bc989fe84431341c8f30b97 to your computer and use it in GitHub Desktop.
Keycodes for Parallels Desktop
#!/usr/bin/env python3
# Parallels Virtualization SDK 17 for Mac:
# https://www.parallels.com/download/pvsdk17/
import prlsdkapi
import string
# Using Parallels SDK to send keycodes/keystrokes to Parallels Desktop virtual
# machine example ("Creating a Simple OS Installation Program"):
# https://download.parallels.com/desktop/v17/docs/en_US/Parallels%20Virtualization%20SDK%20Programmer's%20Guide/27991.htm
# Tables & dictionaries below are generated from a Parallels Desktop 17 virtual
# machine with a clean installation of macOS 12.2 Monterey and the default
# (U.S.) keyboard layout. Different operating systems, versions, and keyboard
# layouts may have different results.
# Tab-delimited:
# Keycode name Keycode output LEFT_SHIFT+Keycode output
# A a A
# B b B
# C c C
# D d D
# E e E
# F f F
# G g G
# H h H
# I i I
# J j J
# K k K
# L l L
# M m M
# N n N
# O o O
# P p P
# Q q Q
# R r R
# S s S
# T t T
# U u U
# V v V
# W w W
# X x X
# Y y Y
# Z z Z
# 0 0 )
# 1 1 !
# 2 2 @
# 3 3 #
# 4 4 $
# 5 5 %
# 6 6 ^
# 7 7 &
# 8 8 *
# 9 9 (
# BACKSLASH \ |
# CBRACE_LEFT [ {
# CBRACE_RIGHT ] }
# COLON ; :
# GREATER . >
# LESS , <
# LESS_GREATER § ±
# MINUS - _
# PAD_EQUAL = =
# PAD_PLUS + +
# PAD_MINUS - -
# PAD_SLASH / /
# PAD_STAR * *
# PLUS = +
# QUOTE ' "
# SLASH / ?
# TILDA ` ~
VM_NAME = "macOS"
def sendKey(vm, io, key):
if io is None:
raise RuntimeError("IO not connected to virtual machine")
keycodes = prlsdkapi.prlsdk.consts.ScanCodesList
chars = {
'a': 'A',
'b': 'B',
'c': 'C',
'd': 'D',
'e': 'E',
'f': 'F',
'g': 'G',
'h': 'H',
'i': 'I',
'j': 'J',
'k': 'K',
'l': 'L',
'm': 'M',
'n': 'N',
'o': 'O',
'p': 'P',
'q': 'Q',
'r': 'R',
's': 'S',
't': 'T',
'u': 'U',
'v': 'V',
'w': 'W',
'x': 'X',
'y': 'Y',
'z': 'Z',
'0': '0',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7',
'8': '8',
'9': '9',
'\\': 'BACKSLASH',
'[': 'CBRACE_LEFT',
']': 'CBRACE_RIGHT',
';': 'COLON',
'.': 'GREATER',
',': 'LESS',
'§': 'LESS_GREATER',
'-': 'MINUS',
'=': 'PAD_EQUAL',
'+': 'PAD_PLUS',
'/': 'PAD_SLASH',
'*': 'PAD_STAR',
"'": 'QUOTE',
'`': 'TILDA',
' ': 'SPACE',
' ': 'TAB'
}
shiftChars = {
'A': 'A',
'B': 'B',
'C': 'C',
'D': 'D',
'E': 'E',
'F': 'F',
'G': 'G',
'H': 'H',
'I': 'I',
'J': 'J',
'K': 'K',
'L': 'L',
'M': 'M',
'N': 'N',
'O': 'O',
'P': 'P',
'Q': 'Q',
'R': 'R',
'S': 'S',
'T': 'T',
'U': 'U',
'V': 'V',
'W': 'W',
'X': 'X',
'Y': 'Y',
'Z': 'Z',
')': '0',
'!': '1',
'@': '2',
'#': '3',
'$': '4',
'%': '5',
'^': '6',
'&': '7',
'*': '8',
'(': '9',
'|': 'BACKSLASH',
'{': 'CBRACE_LEFT',
'}': 'CBRACE_RIGHT',
':': 'COLON',
'>': 'GREATER',
'<': 'LESS',
'±': 'LESS_GREATER',
'_': 'MINUS',
'"': 'QUOTE',
'?': 'SLASH',
'~': 'TILDA'
}
if key in chars:
io.send_key_event(vm, keycodes[chars[key]], prlsdkapi.consts.PKE_CLICK)
elif key in shiftChars:
io.send_key_event(
vm, keycodes['SHIFT_LEFT'], prlsdkapi.consts.PKE_PRESS
)
io.send_key_event(
vm, keycodes[shiftChars[key]], prlsdkapi.consts.PKE_CLICK
)
io.send_key_event(
vm, keycodes['SHIFT_LEFT'], prlsdkapi.consts.PKE_RELEASE
)
elif key in keycodes:
io.send_key_event(
vm, keycodes[key], prlsdkapi.consts.PKE_CLICK
)
elif key.upper() in keycodes:
io.send_key_event(
vm, keycodes[key.upper()], prlsdkapi.consts.PKE_CLICK
)
else:
raise RuntimeError("Unable to find keycode for '{}'".format(key))
def sendEnter(vm, io):
sendKey(vm, io, 'enter')
def makeKeycodeTable(vm, io):
lookup = prlsdkapi.consts.ScanCodesList
characters = list(string.ascii_uppercase + string.digits)
fkeys = []
for i in range(1, 24):
fkeys.append('F'+str(i))
mediakeys = [
'APP_CALCULATOR', 'APP_MAIL', 'APP_MYCOMPUTER', 'EJECT',
'MEDIA_NEXT_TRACK', 'MEDIA_PLAY_PAUSE', 'MEDIA_PREV_TRACK',
'MEDIA_PLAY', 'MEDIA_STOP', 'MEDIA_SELECT', 'MENU', 'MUTE',
'SYSTEM_POWER', 'SYSTEM_SLEEP', 'SYSTEM_WAKE', 'VOLUME_DOWN',
'VOLUME_UP', 'WWW_BACK', 'WWW_FAVORITES', 'WWW_FORWARD', 'WWW_HOME',
'WWW_REFRESH', 'WWW_SEARCH', 'WWW_STOP'
]
modifierkeys = [
'ALT_LEFT', 'ALT_RIGHT', 'CMD_LEFT', 'CMD_RIGHT', 'CTRL_LEFT',
'CTRL_RIGHT', 'SHIFT_LEFT', 'SHIFT_RIGHT'
]
symbols = [
'BACKSLASH', 'CBRACE_LEFT', 'CBRACE_RIGHT', 'COLON', 'DOLLAR',
'GREATER', 'LESS', 'LESS_GREATER', 'MINUS', 'PAD_EQUAL',
'PAD_PLUS', 'PAD_MINUS', 'PAD_SLASH', 'PAD_STAR', 'PLUS', 'QUOTE',
'SLASH', 'SPACE', 'TAB', 'TILDA'
]
actions = [
'BACKSPACE', 'BREAK', 'CAPS_LOCK', 'DELETE', 'DOWN', 'END', 'ENTER',
'ESC', 'HOME', 'INSERT', 'LEFT', 'NUM_LOCK', 'PAD_DEL', 'PAD_ENTER',
'PAGE_DOWN', 'PAGE_UP', 'PAUSE_BREAK', 'PRINT_SCREEN', 'PRINT_SCREEN2',
'RIGHT', 'SCROLL_LOCK', 'SYSRQ', 'UP'
]
others = [
'BRAZILIAN_KEYPAD', 'EURO', 'EUROPE_1', 'HANGUEL', 'HANJA', 'HENKAN',
'HIRAGANA', 'HIRAGANA_KATAKANA', 'KATAKANA', 'MUHENKAN', 'PAD_0',
'PAD_1', 'PAD_2', 'PAD_3', 'PAD_4', 'PAD_5', 'PAD_6', 'PAD_7', 'PAD_8',
'PAD_9', 'PC9800_KEYPAD', 'RO', 'YEN', 'ZENKAKU_HANKAKU'
]
basics = characters
basics.extend(symbols)
for key in basics:
print(key + ',')
io.send_key_event(
vm, lookup[key], prlsdkapi.consts.PKE_CLICK)
io.send_key_event(
vm, lookup['SHIFT_LEFT'], prlsdkapi.consts.PKE_PRESS)
io.send_key_event(
vm, lookup[key], prlsdkapi.consts.PKE_CLICK
)
io.send_key_event(
vm, lookup['SHIFT_LEFT'], prlsdkapi.consts.PKE_RELEASE
)
sendEnter(vm, io)
def sendCommand(text, vm, io):
for i in range(len(text)):
sendKey(vm, io, text[i])
sendEnter(vm, io)
prlsdkapi.init_desktop_sdk()
service = prlsdkapi.Server()
service.login_local().wait()
vmList = service.get_vm_list().wait()
vm = None
vmName = VM_NAME
for i in range(vmList.get_params_count()):
if vmList.get_param_by_index(i).get_name() == vmName:
vm = vmList.get_param_by_index(i)
break
if vm is None:
raise RuntimeError(
"Unable to access virtual machine '{}'.".format(vmName))
io = prlsdkapi.VmIO()
runstate = vm.get_state().wait().get_param().get_state()
if runstate != prlsdkapi.consts.VMS_RUNNING:
raise RuntimeError(
"Virtual machine '{}' is not running".format(vm.get_name()))
io.connect_to_vm(vm).wait()
makeKeycodeTable(vm, io)
sendCommand('echo "The quick brown fox jumps over the lazy dog."', vm, io)
io.disconnect_from_vm(vm)
prlsdkapi.deinit_sdk()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment