├── 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 | [](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 |
--------------------------------------------------------------------------------