Last active
May 20, 2018 18:44
-
-
Save Versatilus/10b5e37ec411e2679b6bf3851318b489 to your computer and use it in GitHub Desktop.
Adaptation of Dragonfly's Text action to use Unicode keyboard emulation for use with Caster.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
# | |
# This file uses code from and is intended to replace the Text Action class | |
# distributed as part of Dragonfly. | |
# (c) Copyright 2018 by Eric Lewis Paulson | |
# with portions | |
# (c) Copyright 2007, 2008 by Christo Butcher | |
# Licensed under the LGPL. | |
# | |
# Dragonfly 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. | |
# | |
# Dragonfly 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 Dragonfly. If not, see | |
# <http://www.gnu.org/licenses/>. | |
# | |
""" | |
Text action | |
============================================================================ | |
This section describes the :class:`Text` action object. This type of | |
action is used for typing text into the foreground application. | |
It differs from the :class:`Key` action in that :class:`Text` is used for | |
typing literal text, while :class:`dragonfly.actions.action_key.Key` | |
emulates pressing keys on the keyboard. An example of this is that the | |
arrow-keys are not part of a text and so cannot be typed using the | |
:class:`Text` action, but can be sent by the | |
:class:`dragonfly.actions.action_key.Key` action. | |
""" | |
from ctypes import * | |
import dragonfly | |
from caster.lib import settings, utilities | |
from dragonfly import ActionBase, Keyboard, typeables | |
#--------------------------------------------------------------------------- | |
if "require_scancodes" not in settings.SETTINGS["miscellaneous"]: | |
settings.SETTINGS["miscellaneous"]["require_scancodes"] = [ | |
"tvnviewer.exe", "vncviewer.exe", "mstsc.exe", "virtualbox.exe" | |
] | |
settings.save_config() | |
require_scancodes = settings.SETTINGS["miscellaneous"]["require_scancodes"] | |
def needs_scancodes(): | |
""" Does the current foreground window require keyboard scancodes? """ | |
found = False | |
foreground_executable = dragonfly.Window.get_foreground().executable.lower() | |
for program in require_scancodes: | |
if program.lower() in foreground_executable: | |
found = True | |
break | |
return found | |
# The following code is based on https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx | |
class KEYBDINPUT(Structure): | |
_fields_ = [("wVk", c_ushort), ("wScan", c_ushort), ("dwFlags", c_ulong), | |
("time", c_ulong), ("dwExtraInfo", POINTER(c_ulong))] | |
class MOUSEINPUT(Structure): | |
_fields_ = [("dx", c_long), ("dy", c_long), ("mouseData", c_ulong), | |
("dwFlags", c_ulong), ("time", c_ulong), ("dwExtraInfo", | |
POINTER(c_ulong))] | |
class HARDWAREINPUT(Structure): | |
_fields_ = [("uMsg", c_ulong), ("wParamL", c_ushort), ("wParamH", c_ushort)] | |
class _INPUTUNION(Union): | |
_fields_ = [("mi", MOUSEINPUT), ("ki", KEYBDINPUT), ("hi", HARDWAREINPUT)] | |
class INPUT(Structure): | |
_anonymous_ = ("u", ) | |
_fields_ = [("type", c_ulong), ("u", _INPUTUNION)] | |
INPUT_MOUSE = 0 | |
INPUT_KEYBOARD = 1 | |
INPUT_HARDWARE = 2 | |
SendInput = windll.LoadLibrary("User32").SendInput | |
SendInput.argtypes = [c_uint, POINTER(INPUT), c_int] | |
def key_press(character, key_up=False): | |
""" Creates an INPUT structure appropriate to send a Unicode keystroke. | |
Arguments: | |
- *character* (16-bit unsigned integer) -- a UTF-16LE encoded | |
character, possibly part of a surrogate pair | |
- *key_up* (boolean) -- state of the KEYEVENTF_KEYUP flag | |
""" | |
ip = INPUT(type=INPUT_KEYBOARD) | |
ip.ki.wVk = 0 | |
ip.ki.wScan = c_ushort(character) | |
ip.ki.dwFlags = 4 # KEYEVENTF_UNICODE | |
if key_up: | |
ip.ki.dwFlags |= 2 # KEYEVENTF_KEYUP | |
ip.ki.time = 0 | |
ip.ki.dwExtraInfo = pointer(c_ulong(0)) | |
return ip | |
class Text(ActionBase): | |
""" | |
Action that sends keyboard events to type text. | |
Arguments: | |
- *spec* (*str*) -- the text to type | |
- *static* (boolean) -- | |
no op used to maintain the legacy call signature | |
- *pause* (*float*) -- | |
the time to pause between each keystroke, given | |
in seconds | |
- *autofmt* (boolean) -- | |
no op used to maintain the legacy call signature | |
""" | |
_pause_default = 0.03 | |
_keyboard = Keyboard() | |
_specials = { | |
"\n": typeables["enter"], | |
"\t": typeables["tab"], | |
} | |
def __init__(self, spec=None, static=False, pause=_pause_default, autofmt=False): | |
self._pause = pause | |
self._spec = spec | |
ActionBase.__init__(self) | |
def _execute(self, data=None): | |
""" Convert and execute the given *spec* as keyboard events. """ | |
from struct import unpack | |
from time import sleep | |
if not needs_scancodes(): | |
for character in unicode(self._spec): | |
if character in self._specials: | |
typeable = self._specials[character] | |
self._keyboard.send_keyboard_events(typeable.events(self._pause)) | |
else: | |
byte_stream = character.encode("utf-16-le") | |
for short in unpack("<" + str(len(byte_stream) // 2) + "H", | |
byte_stream): | |
SendInput(1, byref(key_press(short)), sizeof(INPUT)) | |
SendInput(1, byref(key_press(short, key_up=True)), sizeof(INPUT)) | |
sleep(self._pause) | |
else: | |
events = [] | |
for character in self._spec: | |
try: | |
if character in self._specials: | |
typeable = self._specials[character] | |
else: | |
typeable = Keyboard.get_typeable(character) | |
events.extend(typeable.events(self._pause)) | |
except ValueError, e: | |
utilities.simple_log() | |
# Send keyboard events. | |
self._keyboard.send_keyboard_events(events) | |
return True | |
dragonfly.actions.Text = Text | |
dragonfly.actions.action_text.Text = Text | |
dragonfly.Text = Text |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment