├── lib ├── __init__.py ├── interception.dll ├── interception.lib ├── InterceptionWrapper.py └── AutoHotPy.py ├── character_classes ├── __init__.py ├── destroyer.py └── spoiler.py ├── .gitignore ├── img ├── target_bar.png └── template_target.png ├── destroyer.py ├── spoiler.py ├── README.md ├── LICENSE ├── launcher.py ├── functions.py └── bot.py /lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /character_classes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /.idea/ 3 | /venv/ 4 | -------------------------------------------------------------------------------- /img/target_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maaaxim/bot/HEAD/img/target_bar.png -------------------------------------------------------------------------------- /lib/interception.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maaaxim/bot/HEAD/lib/interception.dll -------------------------------------------------------------------------------- /lib/interception.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maaaxim/bot/HEAD/lib/interception.lib -------------------------------------------------------------------------------- /img/template_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maaaxim/bot/HEAD/img/template_target.png -------------------------------------------------------------------------------- /destroyer.py: -------------------------------------------------------------------------------- 1 | from launcher import Launcher 2 | 3 | if __name__ == "__main__": 4 | launcher = Launcher("Destroyer") 5 | 6 | 7 | -------------------------------------------------------------------------------- /spoiler.py: -------------------------------------------------------------------------------- 1 | from launcher import Launcher 2 | 3 | if __name__ == "__main__": 4 | launcher = Launcher("Spoiler") 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lineage 2 Interlude bot PROTOTYPE 2 | 3 | About:
4 | https://habrahabr.ru/post/346258/ 5 | 6 | Video:
7 | [![Youtube video](https://img.youtube.com/vi/vlV1oMwDLpQ/0.jpg)](https://www.youtube.com/watch?v=vlV1oMwDLpQ) 8 | 9 | @TODO 10 | 1. Recognize NPC's and other players 11 | 2. Get self HP 12 | 3. Use self buffs 13 | 4. Support 2nd window 14 | 5. Support buffers 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Maxim Loboda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /character_classes/destroyer.py: -------------------------------------------------------------------------------- 1 | from functions import * 2 | from bot import Bot 3 | 4 | 5 | class Destroyer (Bot): 6 | 7 | def loop(self, stop_event): 8 | """ 9 | main bot logic 10 | """ 11 | 12 | while not stop_event.is_set(): 13 | 14 | time.sleep(0.2) 15 | 16 | # Continue attacking if victim is alive 17 | targeted_hp = self.get_targeted_hp() 18 | if targeted_hp > 0: 19 | self.useless_steps = 0 20 | 21 | print("attack the target") 22 | self.autohot_py.N1.press() 23 | continue 24 | elif targeted_hp == 0: 25 | 26 | print("target is dead") 27 | continue 28 | else: 29 | print("no target yet") 30 | # Find and click on the victim 31 | if self.set_target(): 32 | self.useless_steps = 0 33 | print("set_target - attack") 34 | self.autohot_py.N1.press() 35 | continue 36 | 37 | # Find and click on the victim 38 | if self.set_target(): 39 | self.useless_steps = 0 40 | print("set_target - attack") 41 | self.autohot_py.N1.press() 42 | continue 43 | 44 | if self.useless_steps > 5: 45 | # We're stuck, go somewhere 46 | self.useless_steps = 0 47 | print("go_somewhere - we're stuck") 48 | self.go_somewhere() 49 | else: 50 | # Turn on 90 degrees 51 | self.turn() 52 | print("turn") 53 | 54 | print("next iteration") 55 | pass 56 | 57 | print("loop finished!") -------------------------------------------------------------------------------- /character_classes/spoiler.py: -------------------------------------------------------------------------------- 1 | from functions import * 2 | from bot import Bot 3 | 4 | 5 | class Spoiler (Bot): 6 | 7 | def loop(self, stop_event): 8 | """ 9 | main bot logic 10 | """ 11 | 12 | spoiled = False 13 | 14 | while not stop_event.is_set(): 15 | 16 | time.sleep(0.2) 17 | 18 | # Continue attacking if victim is alive 19 | targeted_hp = self.get_targeted_hp() 20 | if targeted_hp > 0: 21 | self.useless_steps = 0 22 | 23 | if targeted_hp < 40 and not spoiled: 24 | print("spoil") 25 | spoiled = True 26 | self.autohot_py.N2.press() 27 | time.sleep(0.5) 28 | 29 | print("attack the target") 30 | self.autohot_py.N1.press() 31 | continue 32 | elif targeted_hp == 0: 33 | 34 | if spoiled is True: 35 | spoiled = False 36 | print("sweep") 37 | time.sleep(0.3) 38 | self.autohot_py.N3.press() 39 | time.sleep(0.5) 40 | self.autohot_py.N3.press() 41 | 42 | print("target is dead") 43 | continue 44 | else: 45 | print("no target yet") 46 | # Find and click on the victim 47 | if self.set_target(): 48 | spoiled = False 49 | self.useless_steps = 0 50 | print("set_target - attack") 51 | self.autohot_py.N1.press() 52 | continue 53 | 54 | if self.useless_steps > 2: 55 | # We're stuck, go somewhere 56 | self.useless_steps = 0 57 | print("go_somewhere - we're stuck") 58 | self.go_somewhere() 59 | else: 60 | # Turn on 90 degrees 61 | self.useless_steps += 1 62 | self.turn() 63 | print("turn") 64 | 65 | print("next iteration") 66 | pass 67 | 68 | print("loop finished!") -------------------------------------------------------------------------------- /launcher.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from lib.AutoHotPy import AutoHotPy 3 | from character_classes.destroyer import Destroyer 4 | from character_classes.spoiler import Spoiler 5 | 6 | 7 | class Singleton(type): 8 | 9 | _instances = {} 10 | 11 | def __call__(cls, *args, **kwargs): 12 | """ 13 | kind of constructor = get instance 14 | """ 15 | if cls not in cls._instances: 16 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 17 | return cls._instances[cls] 18 | 19 | 20 | class Launcher: 21 | 22 | __metaclass__ = Singleton 23 | 24 | def __init__(self, character_class): 25 | 26 | # create AutoHotPy instance and set stop event handler 27 | auto_py = AutoHotPy() 28 | auto_py.registerExit(auto_py.ESC, self.stop_bot_event_handler) 29 | 30 | # init bot stop event 31 | self.bot_thread_stop_event = threading.Event() 32 | 33 | # init threads 34 | self.auto_py_thread = threading.Thread(target=self.start_auto_py, args=(auto_py,)) 35 | self.bot_thread = threading.Thread(target=self.start_bot, args=(auto_py, self.bot_thread_stop_event, character_class)) 36 | 37 | # start threads 38 | self.auto_py_thread.start() 39 | self.bot_thread.start() 40 | 41 | def stop_bot(self): 42 | """ 43 | send stop signal to bot thread 44 | """ 45 | self.bot_thread_stop_event.set() 46 | 47 | @staticmethod 48 | def start_bot(auto, stop_event, character_class): 49 | """ 50 | start bot loop 51 | """ 52 | 53 | classmap = { 54 | 'Destroyer': Destroyer, 55 | 'Spoiler' : Spoiler 56 | } 57 | 58 | bot = classmap[character_class](auto) 59 | bot.loop(stop_event) 60 | 61 | @staticmethod 62 | def start_auto_py(auto): 63 | """ 64 | start AutoHotPy 65 | """ 66 | auto.start() 67 | 68 | @staticmethod 69 | def stop_bot_event_handler(auto, event): 70 | """ 71 | exit the program when you press ESC 72 | """ 73 | auto.stop() 74 | launcher = Launcher() 75 | launcher.stop_bot() 76 | -------------------------------------------------------------------------------- /functions.py: -------------------------------------------------------------------------------- 1 | import time 2 | import win32gui 3 | 4 | from PIL import ImageOps, Image, ImageGrab 5 | from numpy import * 6 | import time 7 | import cv2 8 | import win32gui 9 | 10 | 11 | WINDOW_SUBSTRING = "Lineage" 12 | 13 | 14 | # Brazenhem algo 15 | def draw_line(x1=0, y1=0, x2=0, y2=0): 16 | 17 | coordinates = [] 18 | 19 | dx = x2 - x1 20 | dy = y2 - y1 21 | 22 | sign_x = 1 if dx > 0 else -1 if dx < 0 else 0 23 | sign_y = 1 if dy > 0 else -1 if dy < 0 else 0 24 | 25 | if dx < 0: 26 | dx = -dx 27 | if dy < 0: 28 | dy = -dy 29 | 30 | if dx > dy: 31 | pdx, pdy = sign_x, 0 32 | es, el = dy, dx 33 | else: 34 | pdx, pdy = 0, sign_y 35 | es, el = dx, dy 36 | 37 | x, y = x1, y1 38 | 39 | error, t = el / 2, 0 40 | 41 | coordinates.append([x, y]) 42 | 43 | while t < el: 44 | error -= es 45 | if error < 0: 46 | error += el 47 | x += sign_x 48 | y += sign_y 49 | else: 50 | x += pdx 51 | y += pdy 52 | t += 1 53 | coordinates.append([x, y]) 54 | 55 | return coordinates 56 | 57 | 58 | # Smooth move mouse from current pos to xy 59 | def smooth_move(autohotpy, x, y): 60 | flags, hcursor, (startX, startY) = win32gui.GetCursorInfo() 61 | coordinates = draw_line(startX, startY, x, y) 62 | x = 0 63 | for dot in coordinates: 64 | x += 1 65 | if x % 2 == 0 and x % 3 == 0: 66 | time.sleep(0.01) 67 | autohotpy.moveMouseToPosition(dot[0], dot[1]) 68 | 69 | 70 | def get_window_info(): 71 | # set window info 72 | window_info = {} 73 | win32gui.EnumWindows(set_window_coordinates, window_info) 74 | return window_info 75 | 76 | 77 | # EnumWindows handler 78 | # sets L2 window coordinates 79 | def set_window_coordinates(hwnd, window_info): 80 | if win32gui.IsWindowVisible(hwnd): 81 | if WINDOW_SUBSTRING in win32gui.GetWindowText(hwnd): 82 | rect = win32gui.GetWindowRect(hwnd) 83 | x = rect[0] 84 | y = rect[1] 85 | w = rect[2] - x 86 | h = rect[3] - y 87 | window_info['x'] = x 88 | window_info['y'] = y 89 | window_info['width'] = w 90 | window_info['height'] = h 91 | window_info['name'] = win32gui.GetWindowText(hwnd) 92 | win32gui.SetForegroundWindow(hwnd) 93 | 94 | 95 | def get_screen(x1, y1, x2, y2): 96 | box = (x1 + 8, y1 + 30, x2 - 8, y2) 97 | screen = ImageGrab.grab(box) 98 | img = array(screen.getdata(), dtype=uint8).reshape((screen.size[1], screen.size[0], 3)) 99 | return img 100 | 101 | 102 | def get_target_centers(img): 103 | 104 | # Hide buff line 105 | # img[0:70, 0:500] = (0, 0, 0) 106 | 107 | # Hide your name in first camera position (default) 108 | img[210:230, 350:440] = (0, 0, 0) 109 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 110 | # cv2.imwrite('1_gray_img.png', gray) 111 | 112 | # Find only white text 113 | ret, threshold1 = cv2.threshold(gray, 252, 255, cv2.THRESH_BINARY) 114 | # cv2.imwrite('2_threshold1_img.png', threshold1) 115 | 116 | # Morphological transformation 117 | kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 5)) 118 | closed = cv2.morphologyEx(threshold1, cv2.MORPH_CLOSE, kernel) 119 | # cv2.imwrite('3_morphologyEx_img.png', closed) 120 | closed = cv2.erode(closed, kernel, iterations=1) 121 | # cv2.imwrite('4_erode_img.png', closed) 122 | closed = cv2.dilate(closed, kernel, iterations=1) 123 | # cv2.imwrite('5_dilate_img.png', closed) 124 | 125 | (_, centers, hierarchy) = cv2.findContours(closed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 126 | return centers 127 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | from functions import * 2 | from lib.InterceptionWrapper import InterceptionMouseState, InterceptionMouseStroke 3 | import cv2 4 | import numpy as np 5 | 6 | 7 | class Bot: 8 | 9 | def __init__(self, autohot_py): 10 | self.autohot_py = autohot_py 11 | self.step = 0 12 | self.window_info = get_window_info() 13 | self.useless_steps = 0 14 | 15 | def set_default_camera(self): 16 | self.autohot_py.PAGE_DOWN.press() 17 | time.sleep(0.2) 18 | self.autohot_py.PAGE_DOWN.press() 19 | time.sleep(0.2) 20 | self.autohot_py.PAGE_DOWN.press() 21 | 22 | def go_somewhere(self): 23 | """ 24 | click to go 25 | """ 26 | self.set_default_camera() 27 | smooth_move(self.autohot_py, 900, 650) # @TODO dynamic 28 | stroke = InterceptionMouseStroke() 29 | stroke.state = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN 30 | self.autohot_py.sendToDefaultMouse(stroke) 31 | stroke.state = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_UP 32 | self.autohot_py.sendToDefaultMouse(stroke) 33 | self.set_default_camera() 34 | 35 | def turn(self): 36 | """ 37 | turn right 38 | """ 39 | time.sleep(0.02) 40 | smooth_move(self.autohot_py, 300, 500) # @TODO dynamic 41 | stroke = InterceptionMouseStroke() 42 | stroke.state = InterceptionMouseState.INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN 43 | self.autohot_py.sendToDefaultMouse(stroke) 44 | smooth_move(self.autohot_py, 305, 500) # @TODO dynamic 45 | stroke.state = InterceptionMouseState.INTERCEPTION_MOUSE_RIGHT_BUTTON_UP 46 | self.autohot_py.sendToDefaultMouse(stroke) 47 | 48 | def get_targeted_hp(self): 49 | """ 50 | return victim's hp 51 | or -1 if there is no target 52 | """ 53 | 54 | hp_color = [214, 24, 65] 55 | target_widget_coordinates = {} 56 | filled_red_pixels = 1 57 | 58 | img = get_screen( 59 | self.window_info["x"], 60 | self.window_info["y"], 61 | self.window_info["x"] + self.window_info["width"], 62 | self.window_info["y"] + self.window_info["height"] - 190 63 | ) 64 | 65 | img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 66 | 67 | template = cv2.imread('img/target_bar.png', 0) 68 | # w, h = template.shape[::-1] 69 | 70 | res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) 71 | threshold = 0.8 72 | loc = np.where(res >= threshold) 73 | if count_nonzero(loc) == 2: 74 | for pt in zip(*loc[::-1]): 75 | target_widget_coordinates = {"x": pt[0], "y": pt[1]} 76 | # cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (255, 255, 255), 2) 77 | 78 | if not target_widget_coordinates: 79 | return -1 80 | 81 | pil_image_hp = get_screen( 82 | self.window_info["x"] + target_widget_coordinates['x'] + 15, 83 | self.window_info["y"] + target_widget_coordinates['y'] + 31, 84 | self.window_info["x"] + target_widget_coordinates['x'] + 164, 85 | self.window_info["y"] + target_widget_coordinates['y'] + 62 86 | ) 87 | 88 | pixels = pil_image_hp[0].tolist() 89 | for pixel in pixels: 90 | if pixel == hp_color: 91 | filled_red_pixels += 1 92 | 93 | percent = 100 * filled_red_pixels / 150 94 | return percent 95 | 96 | def set_target(self): 97 | """ 98 | find target and click 99 | """ 100 | img = get_screen( 101 | self.window_info["x"], 102 | self.window_info["y"], 103 | self.window_info["x"] + self.window_info["width"], 104 | self.window_info["y"] + self.window_info["height"] - 300 105 | ) 106 | 107 | cnts = get_target_centers(img) 108 | approxes = [] 109 | hulls = [] 110 | for cnt in cnts: 111 | approxes.append(cv2.approxPolyDP(cnt, 0.01 * cv2.arcLength(cnt, True), True)) 112 | hulls.append(cv2.convexHull(cnt)) 113 | left = list(cnt[cnt[:, :, 0].argmin()][0]) 114 | right = list(cnt[cnt[:, :, 0].argmax()][0]) 115 | if right[0] - left[0] < 20: 116 | continue 117 | center = round((right[0] + left[0]) / 2) 118 | center = int(center) 119 | 120 | # smooth_move(self.autohot_py, center + self.window_info["x"], left[1] + 110 + self.window_info["y"]) 121 | # time.sleep(0.1) 122 | # if self.find_from_targeted(left, right): 123 | # self.click_target() 124 | # return True 125 | 126 | # Slide mouse down to find target 127 | iterator = 50 128 | while iterator < 150: 129 | time.sleep(0.3) 130 | smooth_move( 131 | self.autohot_py, 132 | center + self.window_info["x"], 133 | left[1] + iterator + self.window_info["y"] 134 | ) 135 | if self.find_from_targeted(left, right): 136 | self.click_target() 137 | return True 138 | iterator += 30 139 | 140 | return False 141 | 142 | def find_from_targeted(self, left, right): 143 | 144 | # @TODO ignore red target - it is attacked and dead 145 | template = cv2.imread('img/template_target.png', 0) 146 | 147 | # print template.shape 148 | roi = get_screen( 149 | self.window_info["x"], 150 | self.window_info["y"], 151 | self.window_info["x"] + self.window_info["width"], 152 | self.window_info["y"] + self.window_info["height"] - 300 153 | ) 154 | 155 | roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) 156 | ret, th1 = cv2.threshold(roi, 224, 255, cv2.THRESH_TOZERO_INV) 157 | ret, th2 = cv2.threshold(th1, 135, 255, cv2.THRESH_BINARY) 158 | ret, tp1 = cv2.threshold(template, 224, 255, cv2.THRESH_TOZERO_INV) 159 | ret, tp2 = cv2.threshold(tp1, 135, 255, cv2.THRESH_BINARY) 160 | if not hasattr(th2, 'shape'): 161 | return False 162 | wth, hth = th2.shape 163 | wtp, htp = tp2.shape 164 | if wth > wtp and hth > htp: 165 | res = cv2.matchTemplate(th2, tp2, cv2.TM_CCORR_NORMED) 166 | if res.any(): 167 | min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) 168 | if max_val > 0.7: 169 | return True 170 | else: 171 | return False 172 | return False 173 | 174 | def click_target(self): 175 | # time.sleep(0.02) 176 | stroke = InterceptionMouseStroke() 177 | stroke.state = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN 178 | self.autohot_py.sendToDefaultMouse(stroke) 179 | stroke.state = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_UP 180 | self.autohot_py.sendToDefaultMouse(stroke) 181 | -------------------------------------------------------------------------------- /lib/InterceptionWrapper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Emilio Moretti 4 | Copyright 2013 Emilio Moretti 5 | This program is distributed under the terms of the GNU Lesser General Public License. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with this program. If not, see . 19 | """ 20 | # ctypes to work with interception .dll 21 | import ctypes 22 | 23 | class InterceptionKeyState(object): 24 | INTERCEPTION_KEY_DOWN = 0x00 25 | INTERCEPTION_KEY_UP = 0x01 26 | INTERCEPTION_KEY_E0 = 0x02 27 | INTERCEPTION_KEY_E1 = 0x04 28 | INTERCEPTION_KEY_TERMSRV_SET_LED = 0x08 29 | INTERCEPTION_KEY_TERMSRV_SHADOW = 0x10 30 | INTERCEPTION_KEY_TERMSRV_VKPACKET = 0x20 31 | 32 | class InterceptionFilterKeyState(object): 33 | INTERCEPTION_FILTER_KEY_NONE = 0x0000 34 | INTERCEPTION_FILTER_KEY_ALL = 0xFFFF 35 | INTERCEPTION_FILTER_KEY_DOWN = InterceptionKeyState.INTERCEPTION_KEY_UP 36 | INTERCEPTION_FILTER_KEY_UP = InterceptionKeyState.INTERCEPTION_KEY_UP << 1 37 | INTERCEPTION_FILTER_KEY_E0 = InterceptionKeyState.INTERCEPTION_KEY_E0 << 1 38 | INTERCEPTION_FILTER_KEY_E1 = InterceptionKeyState.INTERCEPTION_KEY_E1 << 1 39 | INTERCEPTION_FILTER_KEY_TERMSRV_SET_LED = InterceptionKeyState.INTERCEPTION_KEY_TERMSRV_SET_LED << 1 40 | INTERCEPTION_FILTER_KEY_TERMSRV_SHADOW = InterceptionKeyState.INTERCEPTION_KEY_TERMSRV_SHADOW << 1 41 | INTERCEPTION_FILTER_KEY_TERMSRV_VKPACKET = InterceptionKeyState.INTERCEPTION_KEY_TERMSRV_VKPACKET << 1 42 | 43 | class InterceptionMouseState(object): 44 | INTERCEPTION_MOUSE_MOVE = 0x000 45 | INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN = 0x001 46 | INTERCEPTION_MOUSE_LEFT_BUTTON_UP = 0x002 47 | INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN = 0x004 48 | INTERCEPTION_MOUSE_RIGHT_BUTTON_UP = 0x008 49 | INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN = 0x010 50 | INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP = 0x020 51 | INTERCEPTION_MOUSE_BUTTON_1_DOWN = INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN 52 | INTERCEPTION_MOUSE_BUTTON_1_UP = INTERCEPTION_MOUSE_LEFT_BUTTON_UP 53 | INTERCEPTION_MOUSE_BUTTON_2_DOWN = INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN 54 | INTERCEPTION_MOUSE_BUTTON_2_UP = INTERCEPTION_MOUSE_RIGHT_BUTTON_UP 55 | INTERCEPTION_MOUSE_BUTTON_3_DOWN = INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN 56 | INTERCEPTION_MOUSE_BUTTON_3_UP = INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP 57 | INTERCEPTION_MOUSE_BUTTON_4_DOWN = 0x040 58 | INTERCEPTION_MOUSE_BUTTON_4_UP = 0x080 59 | INTERCEPTION_MOUSE_BUTTON_5_DOWN = 0x100 60 | INTERCEPTION_MOUSE_BUTTON_5_UP = 0x200 61 | INTERCEPTION_MOUSE_WHEEL = 0x400 62 | INTERCEPTION_MOUSE_HWHEEL = 0x800 63 | 64 | class InterceptionFilterMouseState(object): 65 | INTERCEPTION_FILTER_MOUSE_NONE = 0x0000 66 | INTERCEPTION_FILTER_MOUSE_ALL = 0xFFFF 67 | INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN 68 | INTERCEPTION_FILTER_MOUSE_LEFT_BUTTON_UP = InterceptionMouseState.INTERCEPTION_MOUSE_LEFT_BUTTON_UP 69 | INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN 70 | INTERCEPTION_FILTER_MOUSE_RIGHT_BUTTON_UP = InterceptionMouseState.INTERCEPTION_MOUSE_RIGHT_BUTTON_UP 71 | INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_MIDDLE_BUTTON_DOWN 72 | INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_UP = InterceptionMouseState.INTERCEPTION_MOUSE_MIDDLE_BUTTON_UP 73 | INTERCEPTION_FILTER_MOUSE_BUTTON_1_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_DOWN 74 | INTERCEPTION_FILTER_MOUSE_BUTTON_1_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_UP 75 | INTERCEPTION_FILTER_MOUSE_BUTTON_2_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_DOWN 76 | INTERCEPTION_FILTER_MOUSE_BUTTON_2_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_UP 77 | INTERCEPTION_FILTER_MOUSE_BUTTON_3_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_DOWN 78 | INTERCEPTION_FILTER_MOUSE_BUTTON_3_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_UP 79 | INTERCEPTION_FILTER_MOUSE_BUTTON_4_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_DOWN 80 | INTERCEPTION_FILTER_MOUSE_BUTTON_4_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_UP 81 | INTERCEPTION_FILTER_MOUSE_BUTTON_5_DOWN = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_DOWN 82 | INTERCEPTION_FILTER_MOUSE_BUTTON_5_UP = InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_UP 83 | INTERCEPTION_FILTER_MOUSE_WHEEL = InterceptionMouseState.INTERCEPTION_MOUSE_WHEEL 84 | INTERCEPTION_FILTER_MOUSE_HWHEEL = InterceptionMouseState.INTERCEPTION_MOUSE_HWHEEL 85 | INTERCEPTION_FILTER_MOUSE_MOVE = 0x1000 86 | 87 | class InterceptionMouseFlag(object): 88 | """ 89 | If INTERCEPTION_MOUSE_MOVE_ABSOLUTE value is specified, dx and dy contain 90 | normalized absolute coordinates between 0 and 65,535. 91 | The event procedure maps these coordinates onto the display surface. 92 | Coordinate (0,0) maps onto the upper-left corner of the display surface; 93 | coordinate (65535,65535) maps onto the lower-right corner. 94 | In a multimonitor system, the coordinates map to the primary monitor. 95 | http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273%28v=vs.85%29.aspx 96 | """ 97 | INTERCEPTION_MOUSE_MOVE_RELATIVE = 0x000 98 | INTERCEPTION_MOUSE_MOVE_ABSOLUTE = 0x001 99 | INTERCEPTION_MOUSE_VIRTUAL_DESKTOP = 0x002 100 | INTERCEPTION_MOUSE_ATTRIBUTES_CHANGED = 0x004 101 | INTERCEPTION_MOUSE_MOVE_NOCOALESCE = 0x008 102 | INTERCEPTION_MOUSE_TERMSRV_SRC_SHADOW = 0x100 103 | 104 | class InterceptionMouseStroke(ctypes.Structure): 105 | _fields_ = [("state", ctypes.c_ushort), 106 | ("flags", ctypes.c_ushort), 107 | ("rolling", ctypes.c_short), 108 | ("x", ctypes.c_int), 109 | ("y", ctypes.c_int), 110 | ("information", ctypes.c_uint)] 111 | 112 | 113 | class InterceptionKeyStroke(ctypes.Structure): 114 | _fields_ = [("code", ctypes.c_ushort), 115 | ("state", ctypes.c_ushort), 116 | ("information", ctypes.c_uint)] 117 | 118 | class Point(ctypes.Structure): 119 | _fields_ = [("x", ctypes.c_long), 120 | ("y", ctypes.c_long)] 121 | 122 | #typedef char InterceptionStroke[sizeof(InterceptionMouseStroke)]; 123 | InterceptionStroke = InterceptionMouseStroke * 1 124 | 125 | #typedef void *InterceptionContext; 126 | InterceptionContext_p = ctypes.c_void_p 127 | 128 | #typedef int InterceptionDevice; 129 | InterceptionDevice = ctypes.c_int 130 | 131 | #typedef int InterceptionPrecedence; 132 | InterceptionPrecedence = ctypes.c_int 133 | 134 | #typedef unsigned short InterceptionFilter; 135 | InterceptionFilter = ctypes.c_uint 136 | 137 | 138 | 139 | 140 | class InterceptionWrapper(object): 141 | INTERCEPTION_MAX_KEYBOARD = 10 142 | INTERCEPTION_MAX_MOUSE = 10 143 | INTERCEPTION_MAX_DEVICE = ((INTERCEPTION_MAX_KEYBOARD) + (INTERCEPTION_MAX_MOUSE)) 144 | 145 | def INTERCEPTION_KEYBOARD(self, index): 146 | return ((index) + 1) 147 | 148 | def INTERCEPTION_MOUSE(self, index): 149 | return ((self.INTERCEPTION_MAX_KEYBOARD) + (index) + 1) 150 | 151 | def __interception_is_invalid(self, device): 152 | """ 153 | int ITERCEPTION_API interception_is_invalid(InterceptionDevice device); 154 | """ 155 | return self.interceptionDll.interception_is_invalid(device) 156 | 157 | def __interception_is_keyboard(self, device): 158 | """ 159 | int ITERCEPTION_API interception_is_keyboard(InterceptionDevice device); 160 | """ 161 | return self.interceptionDll.interception_is_keyboard(device) 162 | 163 | def __interception_is_mouse(self, device): 164 | """ 165 | int ITERCEPTION_API interception_is_mouse(InterceptionDevice device); 166 | """ 167 | return self.interceptionDll.interception_is_mouse(device) 168 | 169 | def __init__(self): 170 | # Load DLL into memory. 171 | self.interceptionDll = ctypes.WinDLL ("./lib/interception.dll") 172 | 173 | # Setup return types 174 | self.interceptionDll.interception_create_context.restype = InterceptionContext_p 175 | self.interceptionDll.interception_get_filter.restype = InterceptionFilter 176 | self.interceptionDll.interception_get_precedence.restype = InterceptionPrecedence 177 | self.interceptionDll.interception_wait.restype = InterceptionDevice 178 | self.interceptionDll.interception_wait_with_timeout.restype = InterceptionDevice 179 | self.interceptionDll.interception_is_invalid.restype = ctypes.c_int 180 | self.interceptionDll.interception_is_keyboard.restype = ctypes.c_int 181 | self.interceptionDll.interception_is_mouse.restype = ctypes.c_int 182 | self.interceptionDll.interception_send.restype = ctypes.c_int 183 | self.interceptionDll.interception_receive.restype = ctypes.c_int 184 | self.interceptionDll.interception_get_hardware_id.restype = ctypes.c_uint 185 | 186 | 187 | #Setup callback functions (that actually call the dll again) 188 | funct_type = ctypes.WINFUNCTYPE(ctypes.c_int,InterceptionDevice) 189 | self.interception_is_invalid = funct_type(self.__interception_is_invalid) 190 | self.interception_is_keyboard = funct_type(self.__interception_is_keyboard) 191 | self.interception_is_mouse = funct_type(self.__interception_is_mouse) 192 | 193 | 194 | def interception_create_context(self): 195 | """ 196 | InterceptionContext ITERCEPTION_API interception_create_context(void); 197 | """ 198 | return self.interceptionDll.interception_create_context() 199 | 200 | def interception_destroy_context(self, context): 201 | """ 202 | void ITERCEPTION_API interception_destroy_context(InterceptionContext context); 203 | """ 204 | return self.interceptionDll.interception_destroy_context(context) 205 | 206 | def interception_set_filter(self, context, predicate, filter1): 207 | """ 208 | void ITERCEPTION_API interception_set_filter(InterceptionContext context, InterceptionPredicate predicate, InterceptionFilter filter); 209 | """ 210 | return self.interceptionDll.interception_set_filter(context, predicate, filter1) 211 | 212 | def interception_get_filter(self, context, device): 213 | """ 214 | InterceptionFilter ITERCEPTION_API interception_get_filter(InterceptionContext context, InterceptionDevice device); 215 | """ 216 | return self.interceptionDll.interception_get_filter(context, device) 217 | 218 | def interception_get_precedence(self, context, device): 219 | """ 220 | InterceptionPrecedence ITERCEPTION_API interception_get_precedence(InterceptionContext context, InterceptionDevice device); 221 | """ 222 | return self.interceptionDll.interception_get_precedence(context, device) 223 | 224 | def interception_set_precedence(self, context, device, precedence): 225 | """ 226 | void ITERCEPTION_API interception_set_precedence(InterceptionContext context, InterceptionDevice device, InterceptionPrecedence precedence); 227 | """ 228 | return self.interceptionDll.interception_set_precedence(context, device, precedence) 229 | 230 | def interception_wait(self, context): 231 | """ 232 | InterceptionDevice ITERCEPTION_API interception_wait(InterceptionContext context); 233 | """ 234 | return self.interceptionDll.interception_wait(context) 235 | 236 | def interception_wait_with_timeout(self, context): 237 | """ 238 | InterceptionDevice ITERCEPTION_API interception_wait_with_timeout(InterceptionContext context, unsigned long milliseconds); 239 | """ 240 | return self.interceptionDll.interception_wait_with_timeout(context) 241 | 242 | def interception_send(self, context, device, stroke_p, nstroke): 243 | """ 244 | #int ITERCEPTION_API interception_send(InterceptionContext context, InterceptionDevice device, const InterceptionStroke *stroke, unsigned int nstroke); 245 | """ 246 | return self.interceptionDll.interception_send(context, device, stroke_p, nstroke) 247 | 248 | def interception_receive(self, context, device, stroke_p, nstroke): 249 | """ 250 | int ITERCEPTION_API interception_receive(InterceptionContext context, InterceptionDevice device, InterceptionStroke *stroke, unsigned int nstroke); 251 | """ 252 | return self.interceptionDll.interception_receive(context, device, stroke_p, nstroke) 253 | 254 | def interception_get_hardware_id(self, context, device, hardware_id_buffer_p, buffer_size): 255 | """ 256 | unsigned int ITERCEPTION_API interception_get_hardware_id(InterceptionContext context, InterceptionDevice device, void *hardware_id_buffer, unsigned int buffer_size); 257 | """ 258 | return self.interceptionDll.interception_get_hardware_id(context, device, hardware_id_buffer_p, buffer_size) 259 | 260 | 261 | #TODO: 262 | #the following typedef is not needed. it basically defines the function to send to set_filter (we do that manually) 263 | #typedef int (*InterceptionPredicate)(InterceptionDevice device); 264 | -------------------------------------------------------------------------------- /lib/AutoHotPy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Emilio Moretti 4 | Copyright 2013 Emilio Moretti 5 | This program is distributed under the terms of the GNU Lesser General Public License. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with this program. If not, see . 19 | """ 20 | from lib.InterceptionWrapper import * 21 | import collections,time,threading,Queue,copy,ctypes 22 | 23 | 24 | class FunctionRunner(threading.Thread): 25 | def __init__(self,queue): 26 | threading.Thread.__init__(self) 27 | self.queue = queue 28 | def run(self): 29 | while True: 30 | #grabs a function from queue 31 | task = self.queue.get() 32 | 33 | #run it 34 | task.run() 35 | 36 | #signals to queue job is done 37 | self.queue.task_done() 38 | 39 | class Task(object): 40 | def __init__(self,autohotpy,f,param): 41 | self.function = f 42 | self.arg1 = autohotpy 43 | self.arg2 = param 44 | def run(self): 45 | self.function(self.arg1,self.arg2) 46 | 47 | class Key(object): 48 | def __init__(self,auto,code,str_repr,*args): 49 | self.auto = auto 50 | self.code = code 51 | if (len(args) != 0): 52 | self.state = args[0] 53 | else: 54 | self.state = 0 55 | self.key_id = auto.get_key_id(self.code, self.state) 56 | self.string_representation = str_repr 57 | 58 | def get_id(self): 59 | return self.key_id 60 | 61 | def up(self): 62 | up = InterceptionKeyStroke() 63 | up.code = self.code 64 | up.state = InterceptionKeyState.INTERCEPTION_KEY_UP | self.state 65 | self.auto.sendToDefaultKeyboard(up) 66 | 67 | def press(self): 68 | event = InterceptionKeyStroke() 69 | event.code = self.code 70 | event.state = InterceptionKeyState.INTERCEPTION_KEY_DOWN | self.state 71 | self.auto.sendToDefaultKeyboard(event) 72 | self.auto.sleep() 73 | event.state = InterceptionKeyState.INTERCEPTION_KEY_UP | self.state 74 | self.auto.sendToDefaultKeyboard(event) 75 | 76 | def down(self): 77 | down = InterceptionKeyStroke() 78 | down.code = self.code 79 | down.state = InterceptionKeyState.INTERCEPTION_KEY_DOWN | self.state 80 | self.auto.sendToDefaultKeyboard(down) 81 | 82 | def isPressed(self): 83 | return bool(not(self.auto.getKeyboardState(self.code,self.state) & InterceptionKeyState.INTERCEPTION_KEY_UP)) 84 | 85 | def __int__(self): 86 | return int(self.code) 87 | 88 | def __str__(self): 89 | return self.string_representation 90 | 91 | class AutoHotPy(object): 92 | 93 | def __init__(self): 94 | self.exit_configured = False 95 | self.user32 = ctypes.windll.user32 96 | 97 | #configure user32 98 | self.user32.GetCursorPos.restype = ctypes.POINTER(Point) 99 | #default interval between keypress 100 | self.default_interval = 0.01 101 | #Threads queue 102 | self.kb_queue = Queue.Queue() 103 | self.mouse_queue = Queue.Queue() 104 | self.macro_queue = Queue.Queue() 105 | 106 | # Handlers 107 | self.keyboard_handler_down = collections.defaultdict(self.__default_element) 108 | self.keyboard_handler_hold = collections.defaultdict(self.__default_element) 109 | self.keyboard_handler_up = collections.defaultdict(self.__default_element) 110 | self.mouse_handler_hold = collections.defaultdict(self.__default_element) 111 | self.mouse_handler = collections.defaultdict(self.__default_element) 112 | self.mouse_move_handler = None 113 | self.keyboard_state = collections.defaultdict(self.__default_kb_element) 114 | self.mouse_state = collections.defaultdict(self.__default_element) 115 | self.loopingCall = None 116 | 117 | #are we recording a macro? Not yet... 118 | self.recording_macro = False 119 | self.enable_mouse_macro = True 120 | self.enable_kb_macro = True 121 | self.last_macro = [] 122 | self.keys = collections.defaultdict(self.__default_kb_element) 123 | 124 | # Default key scancodes (you can send your own anyway) 125 | # WARNING! Most of these depend on the keyboard implementation!!!! 126 | self.ESC=Key(self,0x1,"ESC") 127 | self.N1=Key(self,0x2,"N1") 128 | self.N2=Key(self,0x3,"N2") 129 | self.N3=Key(self,0x4,"N3") 130 | self.N4=Key(self,0x5,"N4") 131 | self.N5=Key(self,0x6,"N5") 132 | self.N6=Key(self,0x7,"N6") 133 | self.N7=Key(self,0x8,"N7") 134 | self.N8=Key(self,0x9,"N8") 135 | self.N9=Key(self,0x0A,"N9") 136 | self.N0=Key(self,0x0B,"N0") 137 | self.DASH=Key(self,0x0C,"DASH") 138 | #self.Err:520=Key(self,0x0D) 139 | self.BACKSPACE=Key(self,0x0E,"BACKSPACE") 140 | self.TAB=Key(self,0x0F,"TAB") 141 | self.Q=Key(self,0x10,"Q") 142 | self.W=Key(self,0x11,"W") 143 | self.E=Key(self,0x12,"E") 144 | self.R=Key(self,0x13,"R") 145 | self.T=Key(self,0x14,"T") 146 | self.Y=Key(self,0x15,"Y") 147 | self.U=Key(self,0x16,"U") 148 | self.I=Key(self,0x17,"I") 149 | self.O=Key(self,0x18,"O") 150 | self.P=Key(self,0x19,"P") 151 | self.BRACKET_LEFT=Key(self,0x1A,"BRACKET_LEFT") 152 | self.BRACKET_RIGHT=Key(self,0x1B,"BRACKET_RIGHT") 153 | self.ENTER=Key(self,0x1C,"ENTER") 154 | self.LEFT_CTRL=Key(self,0x1D,"LEFT_CTRL") 155 | self.RIGHT_CTRL=Key(self,0x1D,"RIGHT_CTRL", InterceptionKeyState.INTERCEPTION_KEY_E0) 156 | self.A=Key(self,0x1E,"A") 157 | self.S=Key(self,0x1F,"S") 158 | self.D=Key(self,0x20,"D") 159 | self.F=Key(self,0x21,"F") 160 | self.G=Key(self,0x22,"G") 161 | self.H=Key(self,0x23,"H") 162 | self.J=Key(self,0x24,"J") 163 | self.K=Key(self,0x25,"K") 164 | self.L=Key(self,0x26,"L") 165 | self.SEMICOLON=Key(self,0x27,"SEMICOLON") 166 | self.APOSTROPHE=Key(self,0x28,"APOSTROPHE") 167 | self.GRAVE_ACCENT=Key(self,0x29,"GRAVE_ACCENT") 168 | self.LEFT_SHIFT=Key(self,0x2A,"LEFT_SHIFT") 169 | self.BACKSLASH=Key(self,0x2B,"BACKSLASH") 170 | self.Z=Key(self,0x2C,"Z") 171 | self.X=Key(self,0x2D,"X") 172 | self.C=Key(self,0x2E,"C") 173 | self.V=Key(self,0x2F,"V") 174 | self.B=Key(self,0x30,"B") 175 | self.N=Key(self,0x31,"N") 176 | self.M=Key(self,0x32,"M") 177 | self.COMMA=Key(self,0x33,"COMMA") 178 | self.DOT=Key(self,0x34,"DOT") 179 | self.SLASH=Key(self,0x35,"SLASH") 180 | self.RIGHT_SHIFT=Key(self,0x36,"RIGHT_SHIFT") 181 | self.PRINT_SCREEN=Key(self,0x37,"PRINT_SCREEN", InterceptionKeyState.INTERCEPTION_KEY_E0) 182 | self.LEFT_ALT=Key(self,0x38,"LEFT_ALT") 183 | self.RIGHT_ALT=Key(self,0x38,"RIGHT_ALT", InterceptionKeyState.INTERCEPTION_KEY_E0) 184 | self.SPACE=Key(self,0x39,"SPACE") 185 | self.CAPSLOCK=Key(self,0x3A,"CAPSLOCK") 186 | self.F1=Key(self,0x3B,"F1") 187 | self.F2=Key(self,0x3C,"F2") 188 | self.F3=Key(self,0x3D,"F3") 189 | self.F4=Key(self,0x3E,"F4") 190 | self.F5=Key(self,0x3F,"F5") 191 | self.F6=Key(self,0x40,"F6") 192 | self.F7=Key(self,0x41,"F7") 193 | self.F8=Key(self,0x42,"F8") 194 | self.F9=Key(self,0x43,"F9") 195 | self.F10=Key(self,0x44,"F10") 196 | self.NUMLOCK=Key(self,0x45,"NUMLOCK") 197 | self.SCROLLLOCK=Key(self,0x46,"SCROLLLOCK") 198 | self.HOME=Key(self,0x47,"HOME") 199 | self.UP_ARROW=Key(self,0x48,"UP_ARROW", InterceptionKeyState.INTERCEPTION_KEY_E0) 200 | self.PAGE_UP=Key(self,0x49,"PAGE_UP", InterceptionKeyState.INTERCEPTION_KEY_E0) 201 | self.DASH_NUM=Key(self,0x4A,"DASH_NUM") 202 | self.LEFT_ARROW=Key(self,0x4B,"LEFT_ARROW", InterceptionKeyState.INTERCEPTION_KEY_E0) 203 | self.NUMERIC_5 =Key(self,0x4C,"NUMERIC_5") 204 | self.RIGHT_ARROW=Key(self,0x4D,"RIGHT_ARROW", InterceptionKeyState.INTERCEPTION_KEY_E0) 205 | self.PLUS=Key(self,0x4E,"PLUS") 206 | self.END=Key(self,0x4F,"END", InterceptionKeyState.INTERCEPTION_KEY_E0) 207 | self.DOWN_ARROW=Key(self,0x50,"DOWN_ARROW", InterceptionKeyState.INTERCEPTION_KEY_E0) 208 | self.PAGE_DOWN=Key(self,0x51,"PAGE_DOWN", InterceptionKeyState.INTERCEPTION_KEY_E0) 209 | self.INSERT=Key(self,0x52,"INSERT", InterceptionKeyState.INTERCEPTION_KEY_E0) 210 | self.DELETE=Key(self,0x53,"DELETE", InterceptionKeyState.INTERCEPTION_KEY_E0) 211 | self.SHIFT_F1=Key(self,0x54,"SHIFT_F1") 212 | self.SHIFT_F2=Key(self,0x55,"SHIFT_F2") 213 | self.SHIFT_F3=Key(self,0x56,"SHIFT_F3") 214 | #self.SHIFT_F4=Key(self,0x57) 215 | #self.SHIFT_F5=Key(self,0x58) 216 | self.F11=Key(self,0x57,"F11") #these are not common. and might vary from one keyboard to another 217 | self.F12=Key(self,0x58,"F12") 218 | self.SHIFT_F6=Key(self,0x59,"SHIFT_F6") 219 | self.SHIFT_F7=Key(self,0x5A,"SHIFT_F7") 220 | self.SHIFT_F8=Key(self,0x5B,"SHIFT_F8") 221 | self.SYSTEM=Key(self,0x5B,"SYSTEM", InterceptionKeyState.INTERCEPTION_KEY_E0) #commonly known as windows key 222 | self.SHIFT_F9=Key(self,0x5C,"SHIFT_F9") 223 | self.SHIFT_F10=Key(self,0x5D,"SHIFT_F10") 224 | self.CTRL_F1=Key(self,0x5E,"CTRL_F1") 225 | self.CTRL_F2=Key(self,0x5F,"CTRL_F2") 226 | self.CTRL_F3=Key(self,0x60,"CTRL_F3") 227 | self.CTRL_F4=Key(self,0x61,"CTRL_F4") 228 | self.CTRL_F5=Key(self,0x62,"CTRL_F5") 229 | self.CTRL_F6=Key(self,0x63,"CTRL_F6") 230 | self.CTRL_F7=Key(self,0x64,"CTRL_F7") 231 | self.CTRL_F8=Key(self,0x65,"CTRL_F8") 232 | self.CTRL_F9=Key(self,0x66,"CTRL_F9") 233 | self.CTRL_F10=Key(self,0x67,"CTRL_F10") 234 | self.ALT_F1=Key(self,0x68,"ALT_F1") 235 | self.ALT_F2=Key(self,0x69,"ALT_F2") 236 | self.ALT_F3=Key(self,0x6A,"ALT_F3") 237 | self.ALT_F4=Key(self,0x6B,"ALT_F4") 238 | self.ALT_F5=Key(self,0x6C,"ALT_F5") 239 | self.ALT_F6=Key(self,0x6D,"ALT_F6") 240 | self.ALT_F7=Key(self,0x6E,"ALT_F7") 241 | self.ALT_F8=Key(self,0x6F,"ALT_F8") 242 | self.ALT_F9=Key(self,0x70,"ALT_F9") 243 | self.ALT_F10=Key(self,0x71,"ALT_F10") 244 | self.CTRL_PRINT_SCREEN=Key(self,0x72,"CTRL_PRINT_SCREEN") 245 | self.CTRL_LEFT_ARROW=Key(self,0x73,"CTRL_LEFT_ARROW") 246 | self.CTRL_RIGHT_ARROW=Key(self,0x74,"CTRL_RIGHT_ARROW") 247 | self.CTRL_END=Key(self,0x75,"CTRL_END") 248 | self.CTRL_PAGE_DOWN=Key(self,0x76,"CTRL_PAGE_DOWN") 249 | self.CTRL_HOME=Key(self,0x77,"CTRL_HOME") 250 | self.ALT_1=Key(self,0x78,"ALT_1") 251 | self.ALT_2=Key(self,0x79,"ALT_2") 252 | self.ALT_3=Key(self,0x7A,"ALT_3") 253 | self.ALT_4=Key(self,0x7B,"ALT_4") 254 | self.ALT_5=Key(self,0x7C,"ALT_5") 255 | self.ALT_6=Key(self,0x7D,"ALT_6") 256 | self.ALT_7=Key(self,0x7E,"ALT_7") 257 | self.ALT_8=Key(self,0x7F,"ALT_8") 258 | self.ALT_9=Key(self,0x80,"ALT_9") 259 | self.ALT_0=Key(self,0x81,"ALT_0") 260 | self.ALT_DASH=Key(self,0x82,"ALT_DASH") 261 | self.ALT_EQUALS=Key(self,0x82,"ALT_EQUALS") 262 | self.CTRL_PAGE_UP=Key(self,0x84,"CTRL_PAGE_UP") 263 | #self.F11=Key(self,0x85) 264 | #self.F12=Key(self,0x86) 265 | self.SHIFT_F11=Key(self,0x87,"SHIFT_F11") 266 | self.SHIFT_F12=Key(self,0x88,"SHIFT_F12") 267 | self.CTRL_F11=Key(self,0x89,"CTRL_F11") 268 | self.CTRL_F12=Key(self,0x8A,"CTRL_F12") 269 | self.ALT_F11=Key(self,0x8B,"ALT_F11") 270 | self.ALT_F12=Key(self,0x8C,"ALT_F12") 271 | self.CTRL_UP_ARROW=Key(self,0x8C,"CTRL_UP_ARROW") 272 | self.CTRL_DASH_NUM=Key(self,0x8E,"CTRL_DASH_NUM") 273 | self.CTRL_5_NUM=Key(self,0x8F,"CTRL_5_NUM") 274 | self.CTRL_PLUS_NUM=Key(self,0x90,"CTRL_PLUS_NUM") 275 | self.CTRL_DOWN_ARROW=Key(self,0x91,"CTRL_DOWN_ARROW") 276 | self.CTRL_INSERT=Key(self,0x92,"CTRL_INSERT") 277 | self.CTRL_DELETE=Key(self,0x93,"CTRL_DELETE") 278 | self.CTRL_TAB=Key(self,0x94,"CTRL_TAB") 279 | self.CTRL_SLASH_NUM=Key(self,0x95,"CTRL_SLASH_NUM") 280 | self.CTRL_ASTERISK_NUM=Key(self,0x96,"CTRL_ASTERISK_NUM") 281 | self.ALT_HOME=Key(self,0x97,"ALT_HOME") 282 | self.ALT_UP_ARROW=Key(self,0x98,"ALT_UP_ARROW") 283 | self.ALT_PAGE_UP=Key(self,0x99,"ALT_PAGE_UP") 284 | #self. =Key(self,0x9A) 285 | self.ALT_LEFT_ARROW=Key(self,0x9B,"ALT_LEFT_ARROW") 286 | #self. =Key(self,0x9C) 287 | self.ALT_RIGHT_ARROW=Key(self,0x9D,"ALT_RIGHT_ARROW") 288 | #self. =Key(self,0x9E) 289 | self.ALT_END=Key(self,0x9F,"ALT_END") 290 | self.ALT_DOWN_ARROW=Key(self,0xA0,"ALT_DOWN_ARROW") 291 | self.ALT_PAGE_DOWN=Key(self,0xA1,"ALT_PAGE_DOWN") 292 | self.ALT_INSERT=Key(self,0xA2,"ALT_INSERT") 293 | self.ALT_DELETE=Key(self,0xA3,"ALT_DELETE") 294 | self.ALT_SLASH_NUM=Key(self,0xA4,"ALT_SLASH_NUM") 295 | self.ALT_TAB=Key(self,0xA5,"ALT_TAB") 296 | self.ALT_ENTER_NUM=Key(self,0xA6,"ALT_ENTER_NUM") 297 | 298 | #lets create a dictionary with the keys 299 | #this is used when we save a macro to a file 300 | self.keys[self.ESC.get_id()]=self.ESC 301 | self.keys[self.N1.get_id()]=self.N1 302 | self.keys[self.N2.get_id()]=self.N2 303 | self.keys[self.N3.get_id()]=self.N3 304 | self.keys[self.N4.get_id()]=self.N4 305 | self.keys[self.N5.get_id()]=self.N5 306 | self.keys[self.N6.get_id()]=self.N6 307 | self.keys[self.N7.get_id()]=self.N7 308 | self.keys[self.N8.get_id()]=self.N8 309 | self.keys[self.N9.get_id()]=self.N9 310 | self.keys[self.N0.get_id()]=self.N0 311 | self.keys[self.DASH.get_id()]=self.DASH 312 | #self.keys[self.Err:520.get_id()]=self. 313 | self.keys[self.BACKSPACE.get_id()]=self.BACKSPACE 314 | self.keys[self.TAB.get_id()]=self.TAB 315 | self.keys[self.Q.get_id()]=self.Q 316 | self.keys[self.W.get_id()]=self.W 317 | self.keys[self.E.get_id()]=self.E 318 | self.keys[self.R.get_id()]=self.R 319 | self.keys[self.T.get_id()]=self.T 320 | self.keys[self.Y.get_id()]=self.Y 321 | self.keys[self.U.get_id()]=self.U 322 | self.keys[self.I.get_id()]=self.I 323 | self.keys[self.O.get_id()]=self.O 324 | self.keys[self.P.get_id()]=self.P 325 | self.keys[self.BRACKET_LEFT.get_id()]=self.BRACKET_LEFT 326 | self.keys[self.BRACKET_RIGHT.get_id()]=self.BRACKET_RIGHT 327 | self.keys[self.ENTER.get_id()]=self.ENTER 328 | self.keys[self.LEFT_CTRL.get_id()]=self.LEFT_CTRL 329 | self.keys[self.RIGHT_CTRL.get_id()]=self.RIGHT_CTRL 330 | self.keys[self.A.get_id()]=self.A 331 | self.keys[self.S.get_id()]=self.S 332 | self.keys[self.D.get_id()]=self.D 333 | self.keys[self.F.get_id()]=self.F 334 | self.keys[self.G.get_id()]=self.G 335 | self.keys[self.H.get_id()]=self.H 336 | self.keys[self.J.get_id()]=self.J 337 | self.keys[self.K.get_id()]=self.K 338 | self.keys[self.L.get_id()]=self.L 339 | self.keys[self.SEMICOLON.get_id()]=self.SEMICOLON 340 | self.keys[self.APOSTROPHE.get_id()]=self.APOSTROPHE 341 | self.keys[self.GRAVE_ACCENT.get_id()]=self.GRAVE_ACCENT 342 | self.keys[self.LEFT_SHIFT.get_id()]=self.LEFT_SHIFT 343 | self.keys[self.BACKSLASH.get_id()]=self.BACKSLASH 344 | self.keys[self.Z.get_id()]=self.Z 345 | self.keys[self.X.get_id()]=self.X 346 | self.keys[self.C.get_id()]=self.C 347 | self.keys[self.V.get_id()]=self.V 348 | self.keys[self.B.get_id()]=self.B 349 | self.keys[self.N.get_id()]=self.N 350 | self.keys[self.M.get_id()]=self.M 351 | self.keys[self.COMMA.get_id()]=self.COMMA 352 | self.keys[self.DOT.get_id()]=self.DOT 353 | self.keys[self.SLASH.get_id()]=self.SLASH 354 | self.keys[self.RIGHT_SHIFT.get_id()]=self.RIGHT_SHIFT 355 | self.keys[self.PRINT_SCREEN.get_id()]=self.PRINT_SCREEN 356 | self.keys[self.LEFT_ALT.get_id()]=self.LEFT_ALT 357 | self.keys[self.RIGHT_ALT.get_id()]=self.RIGHT_ALT 358 | self.keys[self.SPACE.get_id()]=self.SPACE 359 | self.keys[self.CAPSLOCK.get_id()]=self.CAPSLOCK 360 | self.keys[self.F1.get_id()]=self.F1 361 | self.keys[self.F2.get_id()]=self.F2 362 | self.keys[self.F3.get_id()]=self.F3 363 | self.keys[self.F4.get_id()]=self.F4 364 | self.keys[self.F5.get_id()]=self.F5 365 | self.keys[self.F6.get_id()]=self.F6 366 | self.keys[self.F7.get_id()]=self.F7 367 | self.keys[self.F8.get_id()]=self.F8 368 | self.keys[self.F9.get_id()]=self.F9 369 | self.keys[self.F10.get_id()]=self.F10 370 | self.keys[self.NUMLOCK.get_id()]=self.NUMLOCK 371 | self.keys[self.SCROLLLOCK.get_id()]=self.SCROLLLOCK 372 | self.keys[self.HOME.get_id()]=self.HOME 373 | self.keys[self.UP_ARROW.get_id()]=self.UP_ARROW 374 | self.keys[self.PAGE_UP.get_id()]=self.PAGE_UP 375 | self.keys[self.DASH_NUM.get_id()]=self.DASH_NUM 376 | self.keys[self.LEFT_ARROW.get_id()]=self.LEFT_ARROW 377 | self.keys[self.NUMERIC_5 .get_id()]=self.NUMERIC_5 378 | self.keys[self.RIGHT_ARROW.get_id()]=self.RIGHT_ARROW 379 | self.keys[self.PLUS.get_id()]=self.PLUS 380 | self.keys[self.END.get_id()]=self.END 381 | self.keys[self.DOWN_ARROW.get_id()]=self.DOWN_ARROW 382 | self.keys[self.PAGE_DOWN.get_id()]=self.PAGE_DOWN 383 | self.keys[self.INSERT.get_id()]=self.INSERT 384 | self.keys[self.DELETE.get_id()]=self.DELETE 385 | self.keys[self.SHIFT_F1.get_id()]=self.SHIFT_F1 386 | self.keys[self.SHIFT_F2.get_id()]=self.SHIFT_F2 387 | self.keys[self.SHIFT_F3.get_id()]=self.SHIFT_F3 388 | #self.keys[self.SHIFT_F4.get_id()]=self.SHIFT_F4 389 | #self.keys[self.SHIFT_F5.get_id()]=self.SHIFT_F5 390 | self.keys[self.F11.get_id()]=self.F11 #these are not common. and might vary from one keyboard to another 391 | self.keys[self.F12.get_id()]=self.F12 392 | self.keys[self.SHIFT_F6.get_id()]=self.SHIFT_F6 393 | self.keys[self.SHIFT_F7.get_id()]=self.SHIFT_F7 394 | self.keys[self.SHIFT_F8.get_id()]=self.SHIFT_F8 395 | self.keys[self.SYSTEM.get_id()]=self.SYSTEM #commonly known as windows key 396 | self.keys[self.SHIFT_F9.get_id()]=self.SHIFT_F9 397 | self.keys[self.SHIFT_F10.get_id()]=self.SHIFT_F10 398 | self.keys[self.CTRL_F1.get_id()]=self.CTRL_F1 399 | self.keys[self.CTRL_F2.get_id()]=self.CTRL_F2 400 | self.keys[self.CTRL_F3.get_id()]=self.CTRL_F3 401 | self.keys[self.CTRL_F4.get_id()]=self.CTRL_F4 402 | self.keys[self.CTRL_F5.get_id()]=self.CTRL_F5 403 | self.keys[self.CTRL_F6.get_id()]=self.CTRL_F6 404 | self.keys[self.CTRL_F7.get_id()]=self.CTRL_F7 405 | self.keys[self.CTRL_F8.get_id()]=self.CTRL_F8 406 | self.keys[self.CTRL_F9.get_id()]=self.CTRL_F9 407 | self.keys[self.CTRL_F10.get_id()]=self.CTRL_F10 408 | self.keys[self.ALT_F1.get_id()]=self.ALT_F1 409 | self.keys[self.ALT_F2.get_id()]=self.ALT_F2 410 | self.keys[self.ALT_F3.get_id()]=self.ALT_F3 411 | self.keys[self.ALT_F4.get_id()]=self.ALT_F4 412 | self.keys[self.ALT_F5.get_id()]=self.ALT_F5 413 | self.keys[self.ALT_F6.get_id()]=self.ALT_F6 414 | self.keys[self.ALT_F7.get_id()]=self.ALT_F7 415 | self.keys[self.ALT_F8.get_id()]=self.ALT_F8 416 | self.keys[self.ALT_F9.get_id()]=self.ALT_F9 417 | self.keys[self.ALT_F10.get_id()]=self.ALT_F10 418 | self.keys[self.CTRL_PRINT_SCREEN.get_id()]=self.CTRL_PRINT_SCREEN 419 | self.keys[self.CTRL_LEFT_ARROW.get_id()]=self.CTRL_LEFT_ARROW 420 | self.keys[self.CTRL_RIGHT_ARROW.get_id()]=self.CTRL_RIGHT_ARROW 421 | self.keys[self.CTRL_END.get_id()]=self.CTRL_END 422 | self.keys[self.CTRL_PAGE_DOWN.get_id()]=self.CTRL_PAGE_DOWN 423 | self.keys[self.CTRL_HOME.get_id()]=self.CTRL_HOME 424 | self.keys[self.ALT_1.get_id()]=self.ALT_1 425 | self.keys[self.ALT_2.get_id()]=self.ALT_2 426 | self.keys[self.ALT_3.get_id()]=self.ALT_3 427 | self.keys[self.ALT_4.get_id()]=self.ALT_4 428 | self.keys[self.ALT_5.get_id()]=self.ALT_5 429 | self.keys[self.ALT_6.get_id()]=self.ALT_6 430 | self.keys[self.ALT_7.get_id()]=self.ALT_7 431 | self.keys[self.ALT_8.get_id()]=self.ALT_8 432 | self.keys[self.ALT_9.get_id()]=self.ALT_9 433 | self.keys[self.ALT_0.get_id()]=self.ALT_0 434 | self.keys[self.ALT_DASH.get_id()]=self.ALT_DASH 435 | self.keys[self.ALT_EQUALS.get_id()]=self.ALT_EQUALS 436 | self.keys[self.CTRL_PAGE_UP.get_id()]=self.CTRL_PAGE_UP 437 | #self.keys[self.F11.get_id()]=self. 438 | #self.keys[self.F12.get_id()]=self. 439 | self.keys[self.SHIFT_F11.get_id()]=self.SHIFT_F11 440 | self.keys[self.SHIFT_F12.get_id()]=self.SHIFT_F12 441 | self.keys[self.CTRL_F11.get_id()]=self.CTRL_F11 442 | self.keys[self.CTRL_F12.get_id()]=self.CTRL_F12 443 | self.keys[self.ALT_F11.get_id()]=self.ALT_F11 444 | self.keys[self.ALT_F12.get_id()]=self.ALT_F12 445 | self.keys[self.CTRL_UP_ARROW.get_id()]=self.CTRL_UP_ARROW 446 | self.keys[self.CTRL_DASH_NUM.get_id()]=self.CTRL_DASH_NUM 447 | self.keys[self.CTRL_5_NUM.get_id()]=self.CTRL_5_NUM 448 | self.keys[self.CTRL_PLUS_NUM.get_id()]=self.CTRL_PLUS_NUM 449 | self.keys[self.CTRL_DOWN_ARROW.get_id()]=self.CTRL_DOWN_ARROW 450 | self.keys[self.CTRL_INSERT.get_id()]=self.CTRL_INSERT 451 | self.keys[self.CTRL_DELETE.get_id()]=self.CTRL_DELETE 452 | self.keys[self.CTRL_TAB.get_id()]=self.CTRL_TAB 453 | self.keys[self.CTRL_SLASH_NUM.get_id()]=self.CTRL_SLASH_NUM 454 | self.keys[self.CTRL_ASTERISK_NUM.get_id()]=self.CTRL_ASTERISK_NUM 455 | self.keys[self.ALT_HOME.get_id()]=self.ALT_HOME 456 | self.keys[self.ALT_UP_ARROW.get_id()]=self.ALT_UP_ARROW 457 | self.keys[self.ALT_PAGE_UP.get_id()]=self.ALT_PAGE_UP 458 | #self.keys[self. .get_id()]=self. 459 | self.keys[self.ALT_LEFT_ARROW.get_id()]=self.ALT_LEFT_ARROW 460 | #self.keys[self. .get_id()]=self. 461 | self.keys[self.ALT_RIGHT_ARROW.get_id()]=self.ALT_RIGHT_ARROW 462 | #self.keys[self. .get_id()]=self. 463 | self.keys[self.ALT_END.get_id()]=self.ALT_END 464 | self.keys[self.ALT_DOWN_ARROW.get_id()]=self.ALT_DOWN_ARROW 465 | self.keys[self.ALT_PAGE_DOWN.get_id()]=self.ALT_PAGE_DOWN 466 | self.keys[self.ALT_INSERT.get_id()]=self.ALT_INSERT 467 | self.keys[self.ALT_DELETE.get_id()]=self.ALT_DELETE 468 | self.keys[self.ALT_SLASH_NUM.get_id()]=self.ALT_SLASH_NUM 469 | self.keys[self.ALT_TAB.get_id()]=self.ALT_TAB 470 | self.keys[self.ALT_ENTER_NUM.get_id()]=self.ALT_ENTER_NUM 471 | 472 | def get_key_id(self, code, state): 473 | """ 474 | a key id is a combination of the code and the state ignoring 475 | up and down bits. This is done to consider E0 and E1 states 476 | to differentiate left and right control keys, arrows from numbers, etc 477 | """ 478 | return int("0x%s%s"% (hex(code).replace('0x', ''),hex(state & 0xFE).replace('0x', '')),16) 479 | 480 | def __default_kb_element(self): 481 | """ 482 | if there is not state, it has never been pressed, so it's up 483 | """ 484 | return InterceptionKeyState.INTERCEPTION_KEY_UP 485 | 486 | def __default_element(self): 487 | """ 488 | Used to return None instead of a key exception for maps 489 | """ 490 | return None 491 | 492 | def __null_handler(self,interception,event): 493 | """ 494 | Used as a null handler to disable events like "hold" 495 | """ 496 | return None 497 | 498 | def runMacro(self, autohotpy, macro_list): 499 | """ 500 | go trough the events list and run the events in the specified time 501 | run this in another thread or you will block the execution 502 | autohotpy is in the parameters because I wanted to execute this as a task 503 | """ 504 | 505 | def getTimeDifference(old,new): 506 | if (old == 0): 507 | return 0 508 | return new-old 509 | last_time=0 510 | #removing invalid events that the macro accidentally stores 511 | #startkey UP is pressed as first char 512 | #startkey DOWN is pressed as last char 513 | macro_valid_elements = macro_list[1:len(macro_list)-1] 514 | for event in macro_valid_elements: 515 | self.sleep(getTimeDifference(last_time,event[0])) #wait before firing the event 516 | last_time = event[0] 517 | if (isinstance(event[1],InterceptionMouseStroke)): 518 | # print("Stroke state:" + str(hex(event[1].state))) 519 | # print("Stroke flags:" + str(hex(event[1].flags))) 520 | # print("Stroke information:" + str(hex(event[1].information))) 521 | # print("Stroke rolling:" + str(hex(event[1].rolling))) 522 | # print("Stroke x:" + str(hex(event[1].x))) 523 | # print("Stroke y:" + str(hex(event[1].y))) 524 | self.sendToDefaultMouse(event[1]) 525 | elif(isinstance(event[1],InterceptionKeyStroke)): 526 | # print("Stroke scancode:" + str(hex(event[1].code))) 527 | # print("Stroke state:" + str(hex(event[1].state))) 528 | self.sendToDefaultKeyboard(event[1]) 529 | 530 | def sleep(self, *args): 531 | """ 532 | Sleep. If no parameters are sent, default_interval is assumed. 533 | useful for waiting between keypress. 534 | """ 535 | if (len(args) == 0): 536 | interval = self.default_interval 537 | else: 538 | interval=args[0] 539 | 540 | time.sleep(interval) 541 | 542 | def start(self): 543 | if (not self.exit_configured): 544 | raise Exception("Configure a way to close the process before starting") 545 | #Load the dll and setup the required functions 546 | self.interception = InterceptionWrapper() 547 | # Setup context 548 | self.context = self.interception.interception_create_context() 549 | if (self.context == None): 550 | raise Exception("Interception driver not installed!\nInstall required drivers to continue.") 551 | self.running = True 552 | 553 | # Setup filters. 554 | self.interception.interception_set_filter(self.context, self.interception.interception_is_keyboard, InterceptionFilterKeyState.INTERCEPTION_FILTER_KEY_ALL); 555 | self.interception.interception_set_filter(self.context, self.interception.interception_is_mouse, InterceptionFilterMouseState.INTERCEPTION_FILTER_MOUSE_ALL); 556 | 557 | # Store a default keyboard and a default mouse 558 | hardware_id = ctypes.c_byte * 512 559 | for i in range(10): 560 | current_dev = self.interception.INTERCEPTION_KEYBOARD(i) 561 | if (self.interception.interception_is_keyboard(current_dev)): 562 | size = self.interception.interception_get_hardware_id(self.context, current_dev, ctypes.byref(hardware_id()), 512); 563 | if (size != 0): 564 | self.default_keyboard_device = current_dev 565 | break 566 | for i in range(10): 567 | current_dev = self.interception.INTERCEPTION_MOUSE(i) 568 | if (self.interception.interception_is_mouse(current_dev)): 569 | size = self.interception.interception_get_hardware_id(self.context, current_dev, ctypes.byref(hardware_id()), 512); 570 | if (size != 0): 571 | self.default_mouse_device = current_dev 572 | break 573 | 574 | 575 | # Start threads. These will run the functions the user writes 576 | self.kb_thread = FunctionRunner(self.kb_queue) 577 | self.kb_thread.setDaemon(True) 578 | self.kb_thread.start() 579 | self.mouse_thread = FunctionRunner(self.mouse_queue) 580 | self.mouse_thread.setDaemon(True) 581 | self.mouse_thread.start() 582 | self.macro_thread = FunctionRunner(self.macro_queue) 583 | self.macro_thread.setDaemon(True) 584 | self.macro_thread.start() 585 | 586 | 587 | #reserve space for the stroke 588 | stroke = InterceptionStroke() 589 | 590 | while (self.running): 591 | device = self.interception.interception_wait(self.context) 592 | # print("#####DEVICE ID:"+str(device)) 593 | if (self.interception.interception_receive(self.context, device, ctypes.byref(stroke), 1) > 0): 594 | if (self.interception.interception_is_keyboard(device)): 595 | kb_event=ctypes.cast(stroke, ctypes.POINTER(InterceptionKeyStroke)).contents 596 | if (self.recording_macro & self.enable_kb_macro): 597 | self.last_macro.append((time.time(), copy.deepcopy(kb_event))) 598 | current_key = self.get_key_id(kb_event.code,kb_event.state) 599 | current_state = self.keyboard_state[current_key] #current state for the key 600 | self.keyboard_state[current_key] = kb_event.state 601 | if (kb_event.state & InterceptionKeyState.INTERCEPTION_KEY_UP): #up 602 | user_function = self.keyboard_handler_up[current_key] 603 | else:# down 604 | if (current_state == kb_event.state): 605 | user_function = self.keyboard_handler_hold[current_key] 606 | else: 607 | user_function = self.keyboard_handler_down[current_key] 608 | 609 | 610 | if (user_function): 611 | self.kb_queue.put(Task(self,user_function,copy.deepcopy(kb_event))) 612 | else: 613 | self.interception.interception_send(self.context, device, ctypes.byref(stroke), 1) 614 | 615 | elif (self.interception.interception_is_mouse(device)): 616 | mouse_event=ctypes.cast(stroke, ctypes.POINTER(InterceptionMouseStroke)).contents 617 | if (self.recording_macro & self.enable_mouse_macro): 618 | self.last_macro.append((time.time(), copy.deepcopy(mouse_event))) 619 | if (mouse_event.state != InterceptionMouseState.INTERCEPTION_MOUSE_MOVE): 620 | current_state_changed = self.__toggleMouseState(mouse_event) 621 | if (current_state_changed): 622 | user_function = self.mouse_handler[mouse_event.state] 623 | else: 624 | #TODO: implement something to make a fake on hold. Mouse clicks don't automatically resend events like keyboard keys do 625 | user_function = self.mouse_handler_hold[mouse_event.state] 626 | else: 627 | user_function = self.mouse_move_handler 628 | #print("Stroke state:" + str(hex(mouse_event.state))) 629 | #print("Stroke flags:" + str(hex(mouse_event.flags))) 630 | #print("Stroke information:" + str(hex(mouse_event.information))) 631 | #print("Stroke rolling:" + str(hex(mouse_event.rolling))) 632 | #print("Stroke x:" + str(hex(mouse_event.x))) 633 | #print("Stroke y:" + str(hex(mouse_event.y))) 634 | #print("position 1:" +str(win32gui.GetCursorPos())) 635 | if (user_function): 636 | self.mouse_queue.put(Task(self,user_function,copy.deepcopy(mouse_event))) 637 | else: 638 | self.interception.interception_send(self.context, device, ctypes.byref(stroke), 1) 639 | if self.loopingCall != None: 640 | self.loopingCall(self) 641 | self.macro_queue.join() 642 | self.kb_queue.join() 643 | self.mouse_queue.join() 644 | self.interception.interception_destroy_context(self.context) 645 | 646 | def __toggleMouseState(self, mouse_event): 647 | """ 648 | applies the mouse state change 649 | returns False if no changes were made 650 | """ 651 | BUTTON1=1 652 | BUTTON2=2 653 | BUTTON3=3 654 | BUTTON4=4 655 | BUTTON5=5 656 | WHEEL=6 657 | HWHEEL=7 658 | newState = mouse_event.state 659 | if ((newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_DOWN) | (newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_UP)): 660 | return self.__updateButtonState(BUTTON1,newState) 661 | elif ((newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_DOWN) | (newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_UP)): 662 | return self.__updateButtonState(BUTTON2,newState) 663 | elif ((newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_DOWN) | (newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_UP)): 664 | return self.__updateButtonState(BUTTON3,newState) 665 | elif ((newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_DOWN) | (newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_UP)): 666 | return self.__updateButtonState(BUTTON4,newState) 667 | elif ((newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_DOWN) | (newState == InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_UP)): 668 | return self.__updateButtonState(BUTTON5,newState) 669 | elif (newState == InterceptionMouseState.INTERCEPTION_MOUSE_WHEEL): 670 | return self.__updateButtonState(WHEEL,mouse_event.rolling) 671 | elif (newState == InterceptionMouseState.INTERCEPTION_MOUSE_HWHEEL): 672 | return self.__updateButtonState(HWHEEL,mouse_event.rolling) 673 | 674 | 675 | def __updateButtonState(self, button, newState): 676 | """ 677 | returns true if the button state has changed 678 | """ 679 | #Update button 1 if needed 680 | current_state = self.mouse_state[button] 681 | if (current_state==newState): 682 | return False 683 | else: 684 | self.mouse_state[button] = newState 685 | return True 686 | 687 | def isRunning(self): 688 | return self.running 689 | 690 | def stop(self): 691 | self.running = False 692 | 693 | def getKeyboardState(self, code, state): 694 | """ 695 | Return the key state for a given scancode + state mask 696 | """ 697 | return self.keyboard_state[self.get_key_id(code,state)] 698 | 699 | def getMouseState(self, code): 700 | return self.mouse_state[code] 701 | 702 | def registerExit(self, key, handler): 703 | self.exit_configured = True 704 | self.keyboard_handler_down[key.get_id()] = handler 705 | 706 | def registerForKeyDown(self, key, handler): 707 | self.keyboard_handler_down[key.get_id()] = handler 708 | 709 | def registerForKeyDownAndDisableHoldEvent(self, key, handler): 710 | self.keyboard_handler_down[key.get_id()] = handler 711 | self.keyboard_handler_hold[key.get_id()] = self.__null_handler 712 | 713 | def registerForKeyUp(self, key, handler): 714 | self.keyboard_handler_up[key.get_id()] = handler 715 | 716 | def registerForKeyHold(self, key, handler): 717 | self.keyboard_handler_hold[key.get_id()] = handler 718 | 719 | def registerForMouseButton(self, key, handler): 720 | self.mouse_handler[int(key)] = handler 721 | 722 | def registerForMouseButtonAndDisableHoldEvent(self, key, handler): 723 | self.mouse_handler[int(key)] = handler 724 | self.mouse_handler_hold[int(key)] = self.__null_handler 725 | 726 | def registerForMouseButtonHold(self, key, handler): 727 | self.keyboard_handler_hold[int(key)] = handler 728 | 729 | def registerForMouseMovement(self, handler): 730 | self.mouse_move_handler = handler 731 | 732 | def sendToDefaultMouse(self, stroke): 733 | self.interception.interception_send(self.context, self.default_mouse_device, ctypes.byref(stroke), 1) 734 | 735 | def sendToDefaultKeyboard(self, stroke): 736 | self.interception.interception_send(self.context, self.default_keyboard_device, ctypes.byref(stroke), 1) 737 | 738 | def sendToDevice(self, device, stroke): 739 | self.interception.interception_send(self.context, device, ctypes.byref(stroke), 1) 740 | 741 | def mouseMacroStartStop(self): 742 | """ 743 | start/stop recording a macro that only takes mouse events into account 744 | """ 745 | if (self.recording_macro): 746 | self.recording_macro = False 747 | else: 748 | self.enable_mouse_macro = True 749 | self.enable_kb_macro = False 750 | self.recording_macro = True 751 | def keyboardMacroStartStop(self): 752 | """ 753 | start/stop recording a macro that only saves keyboard events 754 | """ 755 | 756 | if (self.recording_macro): 757 | self.recording_macro = False 758 | else: 759 | self.enable_mouse_macro = False 760 | self.enable_kb_macro = True 761 | self.recording_macro = True 762 | 763 | def macroStartStop(self): 764 | """ 765 | start/stop recording a macro 766 | """ 767 | if (self.recording_macro): 768 | self.recording_macro = False 769 | else: 770 | self.enable_mouse_macro = True 771 | self.enable_kb_macro = True 772 | self.clearLastRecordedMacro() #clear old macro (if any) 773 | self.recording_macro = True 774 | 775 | def fireLastRecordedMacro(self): 776 | self.recording_macro = False 777 | self.macro_queue.put(Task(self,self.runMacro,self.last_macro)) 778 | 779 | def clearLastRecordedMacro(self): 780 | self.last_macro = [] 781 | 782 | def __getMouseFlagsString(self, event): 783 | flags = event.flags 784 | 785 | if (flags & InterceptionMouseFlag.INTERCEPTION_MOUSE_MOVE_ABSOLUTE): 786 | flags_string = "InterceptionMouseFlag.INTERCEPTION_MOUSE_MOVE_ABSOLUTE" 787 | else: 788 | flags_string = "InterceptionMouseFlag.INTERCEPTION_MOUSE_MOVE_RELATIVE" 789 | if (flags & InterceptionMouseFlag.INTERCEPTION_MOUSE_VIRTUAL_DESKTOP): 790 | flags_string += "& InterceptionMouseFlag.INTERCEPTION_MOUSE_VIRTUAL_DESKTOP" 791 | if (flags & InterceptionMouseFlag.INTERCEPTION_MOUSE_ATTRIBUTES_CHANGED): 792 | flags_string += "& InterceptionMouseFlag.INTERCEPTION_MOUSE_ATTRIBUTES_CHANGED" 793 | if (flags & InterceptionMouseFlag.INTERCEPTION_MOUSE_MOVE_NOCOALESCE): 794 | flags_string += "& InterceptionMouseFlag.INTERCEPTION_MOUSE_MOVE_NOCOALESCE" 795 | if (flags & InterceptionMouseFlag.INTERCEPTION_MOUSE_TERMSRV_SRC_SHADOW): 796 | flags_string += "& InterceptionMouseFlag.INTERCEPTION_MOUSE_TERMSRV_SRC_SHADOW" 797 | 798 | return flags_string 799 | 800 | def __getMouseStateString(self, event): 801 | return { 802 | 0x000 : "InterceptionMouseState.INTERCEPTION_MOUSE_MOVE", 803 | 0x001 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_DOWN", 804 | 0x002 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_1_UP", 805 | 0x004 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_DOWN", 806 | 0x008 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_2_UP", 807 | 0x010 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_DOWN", 808 | 0x020 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_3_UP", 809 | 0x040 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_DOWN", 810 | 0x080 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_4_UP", 811 | 0x100 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_DOWN", 812 | 0x200 : "InterceptionMouseState.INTERCEPTION_MOUSE_BUTTON_5_UP", 813 | 0x400 : "InterceptionMouseState.INTERCEPTION_MOUSE_WHEEL", 814 | 0x800 : "InterceptionMouseState.INTERCEPTION_MOUSE_HWHEEL", 815 | }[event.state] 816 | 817 | def saveLastRecordedMacro(self, filename,*args): 818 | openfile = open(filename, 'w') 819 | output_script_text = "from AutoHotPy import AutoHotPy\nfrom InterceptionWrapper import *\ndef exitAutoHotKey(autohotpy,event):\n autohotpy.stop()\ndef recorded_macro(autohotpy, event):\n" 820 | openfile.write(output_script_text) 821 | 822 | if (len(args) == 1): 823 | openfile.write(" autohotpy.moveMouseToPosition("+str(args[0][0])+","+str(args[0][1])+")\n") 824 | 825 | def getTimeDifference(old,new): 826 | if (old == 0): 827 | return 0 828 | return new-old 829 | 830 | def getEventKeyId(event): 831 | return self.get_key_id(event.code, event.state) 832 | 833 | last_time=0 834 | #removing invalid events that the macro accidentally stores 835 | #startkey UP is pressed as first char 836 | #startkey DOWN is pressed as last char 837 | macro_valid_elements = self.last_macro[1:len(self.last_macro)-1] 838 | for event in macro_valid_elements: 839 | sleep_time = getTimeDifference(last_time,event[0]) 840 | last_time = event[0] 841 | openfile.write(" autohotpy.sleep("+str(sleep_time)+")\n") 842 | if (isinstance(event[1],InterceptionMouseStroke)): 843 | openfile.write(" stroke = InterceptionMouseStroke()\n") 844 | openfile.write(" stroke.state = "+self.__getMouseStateString(event[1])+"\n") 845 | openfile.write(" stroke.flags = "+self.__getMouseFlagsString(event[1])+"\n") 846 | openfile.write(" stroke.rolling = "+str(event[1].rolling)+"\n") 847 | openfile.write(" stroke.x = "+str(event[1].x)+"\n") 848 | openfile.write(" stroke.y = "+str(event[1].y)+"\n") 849 | openfile.write(" stroke.information = "+str(event[1].information)+"\n") 850 | openfile.write(" autohotpy.sendToDefaultMouse(stroke)\n") 851 | elif(isinstance(event[1],InterceptionKeyStroke)): 852 | keyid =getEventKeyId(event[1]) 853 | if (keyid != None): 854 | key = self.keys[keyid] 855 | keyname = str(key) 856 | 857 | if (event[1].state & InterceptionKeyState.INTERCEPTION_KEY_UP): 858 | openfile.write(" autohotpy."+keyname+".up()\n") 859 | else: 860 | openfile.write(" autohotpy."+keyname+".down()\n") 861 | else: 862 | code = event[1].code 863 | state = event[1].state 864 | openfile.write(" stroke = InterceptionMouseStroke()\n") 865 | openfile.write(" stroke.code = "+str(code)+"\n") 866 | openfile.write(" stroke.state = "+str(state)+"\n") 867 | openfile.write(" stroke.information = "+str(event[1].information)+"\n") 868 | openfile.write(" autohotpy.sendToDefaultKeyboard(stroke)\n") 869 | openfile.write("if __name__==\"__main__\":\n auto = AutoHotPy()\n auto.registerExit(auto.ESC,exitAutoHotKey)\n auto.registerForKeyDown(auto.F1,recorded_macro)\n auto.start()\n") 870 | openfile.close() 871 | 872 | 873 | def isRecording(self): 874 | return self.recording_macro 875 | 876 | def getMousePosition(self): 877 | #x, y = win32api.GetCursorPos() 878 | res = Point() 879 | self.user32.GetCursorPos(ctypes.byref(res)) 880 | return (res.x,res.y) 881 | 882 | def moveMouseToPosition(self, x, y): 883 | width_constant = 65535.0/float(self.user32.GetSystemMetrics(0)) 884 | height_constant = 65535.0/float(self.user32.GetSystemMetrics (1)) 885 | # move mouse to the specified position 886 | stroke = InterceptionMouseStroke() 887 | stroke.state = InterceptionMouseState.INTERCEPTION_MOUSE_MOVE 888 | stroke.flags = InterceptionMouseFlag.INTERCEPTION_MOUSE_MOVE_ABSOLUTE 889 | stroke.x = int(float(x)*width_constant) 890 | stroke.y = int(float(y)*height_constant) 891 | self.sendToDefaultMouse(stroke) 892 | 893 | def run(self, macro, trigger_event): 894 | """ 895 | manually send a macro to be run 896 | """ 897 | if (isinstance(trigger_event,InterceptionMouseStroke)): 898 | self.mouse_queue.put(Task(self, macro, trigger_event)) 899 | elif(isinstance(trigger_event,InterceptionKeyStroke)): 900 | self.kb_queue.put(Task(self, macro, trigger_event)) 901 | 902 | --------------------------------------------------------------------------------