From 58e3e8aa03e34b9b50ff92d20c74c1b110770536 Mon Sep 17 00:00:00 2001 From: Roel Date: Wed, 9 Mar 2011 16:28:05 +0100 Subject: [PATCH 1/6] Added MouseButtons class containing button names, it also supports all scroll buttons and thumb buttons. Adapted the windows, mac and unix modules to use that class. In reply to: https://github.com/pepijndevos/PyMouse/commit/43b69c8e83e8b7000ad05d034826ea8051f9aa42#commitcomment-295776 --- pymouse/__init__.py | 1 + pymouse/base.py | 16 +++++++++ pymouse/mac.py | 60 ++++++++++++++++++++++++-------- pymouse/unix.py | 44 ++++++++++++++++++++---- pymouse/windows.py | 83 ++++++++++++++++++++++++++++++++------------- 5 files changed, 161 insertions(+), 43 deletions(-) diff --git a/pymouse/__init__.py b/pymouse/__init__.py index 0d8d6dd..7049bcc 100644 --- a/pymouse/__init__.py +++ b/pymouse/__init__.py @@ -34,3 +34,4 @@ else: from unix import PyMouse, PyMouseEvent +from base import MouseButtons diff --git a/pymouse/base.py b/pymouse/base.py index 492e57f..a542fe0 100644 --- a/pymouse/base.py +++ b/pymouse/base.py @@ -22,6 +22,22 @@ from threading import Thread + +class MouseButtons: + + BUTTON_LEFT = 1 + BUTTON_MIDDLE = 2 + BUTTON_RIGHT = 3 + BUTTON_SCROLL_UP = 4 + BUTTON_SCROLL_DOWN = 5 + BUTTON_SCROLL_LEFT = 6 + BUTTON_SCROLL_RIGHT = 7 + BUTTON_THUMB_BACK = 8 + BUTTON_THUMB_FORWARD = 9 + + + + class PyMouseMeta(object): def press(self, x, y, button = 1): diff --git a/pymouse/mac.py b/pymouse/mac.py index e4b5ba4..0852cd7 100644 --- a/pymouse/mac.py +++ b/pymouse/mac.py @@ -21,13 +21,44 @@ pressID = [None, kCGEventLeftMouseDown, kCGEventRightMouseDown, kCGEventOtherMouseDown] releaseID = [None, kCGEventLeftMouseUp, kCGEventRightMouseUp, kCGEventOtherMouseUp] + + +def get_button_code(event_message): + """ Platform specific ! """ + + if type in pressID: + code = pressID.index(type) + state = True + elif type in releaseID: + code = releaseID.index(type) + state = False + else: + code = None + state = False + + return (code, state) + + +def get_event_code(button_code, state): + """ Platform specific ! """ + + if state: + code = pressID[button_code] + + else: + code = releaseID[button_code] + + return code + + + class PyMouse(PyMouseMeta): - def press(self, x, y, button = 1): - event = CGEventCreateMouseEvent(None, pressID[button], (x, y), button - 1) + def press(self, x, y, button=1): + event = CGEventCreateMouseEvent(None, get_event_code(button, True), (x, y), button - 1) CGEventPost(kCGHIDEventTap, event) - def release(self, x, y, button = 1): - event = CGEventCreateMouseEvent(None, releaseID[button], (x, y), button - 1) + def release(self, x, y, button=1): + event = CGEventCreateMouseEvent(None, get_event_code(button, False), (x, y), button - 1) CGEventPost(kCGHIDEventTap, event) def move(self, x, y): @@ -48,12 +79,12 @@ def run(self): kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, - CGEventMaskBit(kCGEventMouseMoved) | - CGEventMaskBit(kCGEventLeftMouseDown) | - CGEventMaskBit(kCGEventLeftMouseUp) | - CGEventMaskBit(kCGEventRightMouseDown) | - CGEventMaskBit(kCGEventRightMouseUp) | - CGEventMaskBit(kCGEventOtherMouseDown) | + CGEventMaskBit(kCGEventMouseMoved) | + CGEventMaskBit(kCGEventLeftMouseDown) | + CGEventMaskBit(kCGEventLeftMouseUp) | + CGEventMaskBit(kCGEventRightMouseDown) | + CGEventMaskBit(kCGEventRightMouseUp) | + CGEventMaskBit(kCGEventOtherMouseDown) | CGEventMaskBit(kCGEventOtherMouseUp), self.handler, None) @@ -68,10 +99,11 @@ def run(self): def handler(self, proxy, type, event, refcon): (x, y) = CGEventGetLocation(event) - if type in pressID: - self.click(x, y, pressID.index(type), True) - elif type in releaseID: - self.click(x, y, releaseID.index(type), False) + + (code, state) = get_button_code(type) + + if code is not None: + self.click(x, y, code, state) else: self.move(x, y) diff --git a/pymouse/unix.py b/pymouse/unix.py index d2eadc9..afc7f03 100644 --- a/pymouse/unix.py +++ b/pymouse/unix.py @@ -20,8 +20,36 @@ from Xlib.ext import record from Xlib.protocol import rq -from base import PyMouseMeta, PyMouseEventMeta - +from base import PyMouseMeta, PyMouseEventMeta, MouseButtons + + +def get_button_code(event_message): + """ Platform specific ! """ + + try: + code = (None, + MouseButtons.BUTTON_LEFT, + MouseButtons.BUTTON_MIDDLE, + MouseButtons.BUTTON_RIGHT, + MouseButtons.BUTTON_SCROLL_UP, + MouseButtons.BUTTON_SCROLL_DOWN, + MouseButtons.BUTTON_SCROLL_LEFT, + MouseButtons.BUTTON_SCROLL_RIGHT, + MouseButtons.BUTTON_THUMB_BACK, + MouseButtons.BUTTON_THUMB_FORWARD)[event_message] + + except IndexError: + code = None + + return code + + +def get_event_code(button_code): + """ Platform specific ! """ + + return button_code + + class PyMouse(PyMouseMeta): def __init__(self): @@ -31,12 +59,12 @@ def __init__(self): def press(self, x, y, button = 1): self.move(x, y) - fake_input(self.display, X.ButtonPress, [None, 1, 3, 2, 4, 5][button]) + fake_input(self.display, X.ButtonPress, get_event_code(button)) self.display.sync() def release(self, x, y, button = 1): self.move(x, y) - fake_input(self.display, X.ButtonRelease, [None, 1, 3, 2, 4, 5][button]) + fake_input(self.display, X.ButtonRelease, get_event_code(button)) self.display.sync() def move(self, x, y): @@ -92,10 +120,14 @@ def handler(self, reply): while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) + + if event.type == X.ButtonPress: - self.click(event.root_x, event.root_y, (None, 1, 3, 2, 3, 3, 3)[event.detail], True) + print event.detail + self.click(event.root_x, event.root_y, get_button_code(event.detail), True) elif event.type == X.ButtonRelease: - self.click(event.root_x, event.root_y, (None, 1, 3, 2, 3, 3, 3)[event.detail], False) + print event.detail + self.click(event.root_x, event.root_y, get_button_code(event.detail), False) else: self.move(event.root_x, event.root_y) diff --git a/pymouse/windows.py b/pymouse/windows.py index 88809b5..a0ec07a 100644 --- a/pymouse/windows.py +++ b/pymouse/windows.py @@ -15,11 +15,55 @@ # limitations under the License. from ctypes import * -import win32api,win32con -from base import PyMouseMeta, PyMouseEventMeta +import win32api, win32con +from base import PyMouseMeta, PyMouseEventMeta, MouseButtons import pythoncom, pyHook from time import sleep + +def get_button_code(event_message): + """ Platform specific ! """ + + try: + if event_message == pyHook.HookConstants.WM_LBUTTONDOWN: + code = (MouseButtons.BUTTON_LEFT, True) + elif event_message == pyHook.HookConstants.WM_LBUTTONUP: + code = (MouseButtons.BUTTON_LEFT, False) + elif event_message == pyHook.HookConstants.WM_RBUTTONDOWN: + code = (MouseButtons.BUTTON_RIGHT, True) + elif event_message == pyHook.HookConstants.WM_RBUTTONUP: + code = (MouseButtons.BUTTON_RIGHT, False) + elif event_message == pyHook.HookConstants.WM_MBUTTONDOWN: + code = (MouseButtons.BUTTON_MIDDLE, True) + elif event_message == pyHook.HookConstants.WM_MBUTTONUP: + code = (MouseButtons.BUTTON_MIDDLE, False) + + except IndexError: + code = (None, False) + + return code + + +def get_event_code(button_code): + """ Platform specific ! """ + + # Windows only supports Left, Middle and Right buttons + if button_code not in (1, 2, 3): + raise ValueError("Event code not recognized!") + + # swap button 2 and 3, because Windows uses 3 for middle button and 2 for right button + if button_code == 2: + button_code == 3 + elif button_code == 3: + button_code = 2 + + code = 2 ** ((2 * button_code) - 1) + + return code + + + + class POINT(Structure): _fields_ = [("x", c_ulong), ("y", c_ulong)] @@ -27,14 +71,14 @@ class POINT(Structure): class PyMouse(PyMouseMeta): """MOUSEEVENTF_(button and action) constants are defined at win32con, buttonAction is that value""" - def press(self, x, y, button = 1): - buttonAction = 2**((2*button)-1) - self.move(x,y) + def press(self, x, y, button=1): + buttonAction = get_event_code(button) + self.move(x, y) win32api.mouse_event(buttonAction, x, y) - def release(self, x, y, button = 1): - buttonAction = 2**((2*button)) - self.move(x,y) + def release(self, x, y, button=1): + buttonAction = get_event_code(button) + self.move(x, y) win32api.mouse_event(buttonAction, x, y) def move(self, x, y): @@ -68,23 +112,16 @@ def stop(self): self.state = False def _click(self, event): - x,y = event.Position - - if event.Message == pyHook.HookConstants.WM_LBUTTONDOWN: - self.click(x, y, 1, True) - elif event.Message == pyHook.HookConstants.WM_LBUTTONUP: - self.click(x, y, 1, False) - elif event.Message == pyHook.HookConstants.WM_RBUTTONDOWN: - self.click(x, y, 2, True) - elif event.Message == pyHook.HookConstants.WM_RBUTTONUP: - self.click(x, y, 2, False) - elif event.Message == pyHook.HookConstants.WM_MBUTTONDOWN: - self.click(x, y, 3, True) - elif event.Message == pyHook.HookConstants.WM_MBUTTONUP: - self.click(x, y, 3, False) + x, y = event.Position + + (button, state) = get_button_code(event.Message) + + if button is not None: + self.click(x, y, button, state) + return not self.capture def _move(self, event): - x,y = event.Position + x, y = event.Position self.move(x, y) return not self.captureMove From 0372b2158d13199d15b68a851c56ae04289e92ff Mon Sep 17 00:00:00 2001 From: Roel Date: Wed, 9 Mar 2011 16:53:57 +0100 Subject: [PATCH 2/6] Button are now 'translated' on mac too Not tested on Mac and Windows --- pymouse/mac.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/pymouse/mac.py b/pymouse/mac.py index 0852cd7..55cf173 100644 --- a/pymouse/mac.py +++ b/pymouse/mac.py @@ -16,7 +16,7 @@ from Quartz import * from AppKit import NSEvent -from base import PyMouseMeta, PyMouseEventMeta +from base import PyMouseMeta, PyMouseEventMeta, MouseButtons pressID = [None, kCGEventLeftMouseDown, kCGEventRightMouseDown, kCGEventOtherMouseDown] releaseID = [None, kCGEventLeftMouseUp, kCGEventRightMouseUp, kCGEventOtherMouseUp] @@ -26,11 +26,24 @@ def get_button_code(event_message): """ Platform specific ! """ - if type in pressID: - code = pressID.index(type) + + if event_message == kCGEventLeftMouseDown: + code = MouseButtons.BUTTON_LEFT + state = True + elif event_message == kCGEventLeftMouseUp: + code = MouseButtons.BUTTON_LEFT + state = False + elif event_message == kCGEventRightMouseDown: + code = MouseButtons.BUTTON_RIGHT + state = True + elif event_message == kCGEventRightMouseUp: + code = MouseButtons.BUTTON_RIGHT + state = False + elif event_message == kCGEventOtherMouseDown: + code = MouseButtons.BUTTON_MIDDLE state = True - elif type in releaseID: - code = releaseID.index(type) + elif event_message == kCGEventOtherMouseUp: + code = MouseButtons.BUTTON_MIDDLE state = False else: code = None @@ -42,9 +55,13 @@ def get_button_code(event_message): def get_event_code(button_code, state): """ Platform specific ! """ + # Mac only supports Left, Middle and Right buttons + if button_code not in (1, 2, 3): + raise ValueError("Event code not recognized!") + + if state: code = pressID[button_code] - else: code = releaseID[button_code] From a5b7015b538b7cf7459d0a4f794cc4e2cbf28534 Mon Sep 17 00:00:00 2001 From: Roel Date: Wed, 9 Mar 2011 17:12:45 +0100 Subject: [PATCH 3/6] Button release on windows should work now --- pymouse/windows.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pymouse/windows.py b/pymouse/windows.py index a0ec07a..653c9de 100644 --- a/pymouse/windows.py +++ b/pymouse/windows.py @@ -44,7 +44,7 @@ def get_button_code(event_message): return code -def get_event_code(button_code): +def get_event_code(button_code, state): """ Platform specific ! """ # Windows only supports Left, Middle and Right buttons @@ -56,8 +56,12 @@ def get_event_code(button_code): button_code == 3 elif button_code == 3: button_code = 2 + + if state: + code = 2 ** ((2 * button_code) - 1) + else: + code = 2 ** ((2 * button_code)) - code = 2 ** ((2 * button_code) - 1) return code @@ -72,12 +76,12 @@ class PyMouse(PyMouseMeta): """MOUSEEVENTF_(button and action) constants are defined at win32con, buttonAction is that value""" def press(self, x, y, button=1): - buttonAction = get_event_code(button) + buttonAction = get_event_code(button, True) self.move(x, y) win32api.mouse_event(buttonAction, x, y) def release(self, x, y, button=1): - buttonAction = get_event_code(button) + buttonAction = get_event_code(button, False) self.move(x, y) win32api.mouse_event(buttonAction, x, y) From 42087dbe0c3a54bd2504bde48cfe9f5ca29547bc Mon Sep 17 00:00:00 2001 From: Roel Date: Wed, 9 Mar 2011 17:17:39 +0100 Subject: [PATCH 4/6] button swap on windows was not programmed correctly --- pymouse/windows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymouse/windows.py b/pymouse/windows.py index 653c9de..8dae4a4 100644 --- a/pymouse/windows.py +++ b/pymouse/windows.py @@ -53,7 +53,7 @@ def get_event_code(button_code, state): # swap button 2 and 3, because Windows uses 3 for middle button and 2 for right button if button_code == 2: - button_code == 3 + button_code = 3 elif button_code == 3: button_code = 2 From 99e1ab9cae9bec22c2a1ec958d333ffc719fafa6 Mon Sep 17 00:00:00 2001 From: Roel Date: Wed, 9 Mar 2011 17:28:43 +0100 Subject: [PATCH 5/6] Made errors more meaningfull --- pymouse/mac.py | 2 +- pymouse/unix.py | 2 +- pymouse/windows.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pymouse/mac.py b/pymouse/mac.py index 55cf173..a85a950 100644 --- a/pymouse/mac.py +++ b/pymouse/mac.py @@ -57,7 +57,7 @@ def get_event_code(button_code, state): # Mac only supports Left, Middle and Right buttons if button_code not in (1, 2, 3): - raise ValueError("Event code not recognized!") + raise ValueError("Button not supported") if state: diff --git a/pymouse/unix.py b/pymouse/unix.py index afc7f03..d5f4c00 100644 --- a/pymouse/unix.py +++ b/pymouse/unix.py @@ -39,7 +39,7 @@ def get_button_code(event_message): MouseButtons.BUTTON_THUMB_FORWARD)[event_message] except IndexError: - code = None + raise ValueError("Button not supported") return code diff --git a/pymouse/windows.py b/pymouse/windows.py index 8dae4a4..5d07303 100644 --- a/pymouse/windows.py +++ b/pymouse/windows.py @@ -49,7 +49,7 @@ def get_event_code(button_code, state): # Windows only supports Left, Middle and Right buttons if button_code not in (1, 2, 3): - raise ValueError("Event code not recognized!") + raise ValueError("Button not supported") # swap button 2 and 3, because Windows uses 3 for middle button and 2 for right button if button_code == 2: From e9d5d68d1ae97bbc1623cec4b50e9e8e97bc0cea Mon Sep 17 00:00:00 2001 From: Roel Date: Wed, 9 Mar 2011 17:58:38 +0100 Subject: [PATCH 6/6] removed printing of debug information --- pymouse/unix.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pymouse/unix.py b/pymouse/unix.py index d5f4c00..98f148b 100644 --- a/pymouse/unix.py +++ b/pymouse/unix.py @@ -123,10 +123,8 @@ def handler(self, reply): if event.type == X.ButtonPress: - print event.detail self.click(event.root_x, event.root_y, get_button_code(event.detail), True) elif event.type == X.ButtonRelease: - print event.detail self.click(event.root_x, event.root_y, get_button_code(event.detail), False) else: self.move(event.root_x, event.root_y)