Home | History | Annotate | Download | only in win32
      1 #!/usr/bin/env python
      2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """SiteCompare module for simulating mouse input.
      7 
      8 This module contains functions that can be used to simulate a user
      9 navigating using a pointing device. This includes mouse movement,
     10 clicking with any button, and dragging.
     11 """
     12 
     13 import time                 # for sleep
     14 
     15 import win32api             # for mouse_event
     16 import win32con             # Windows constants
     17 import win32gui             # for window functions
     18 
     19 
     20 def ScreenToMouse(pt):
     21   """Convert a value in screen coordinates to mouse coordinates.
     22 
     23   Mouse coordinates are specified as a percentage of screen dimensions,
     24   normalized to 16 bits. 0 represents the far left/top of the screen,
     25   65535 represents the far right/bottom. This function assumes that
     26   the size of the screen is fixed at module load time and does not change
     27 
     28   Args:
     29     pt: the point of the coords to convert
     30 
     31   Returns:
     32     the converted point
     33   """
     34 
     35   # Initialize the screen dimensions on first execution. Note that this
     36   # function assumes that the screen dimensions do not change during run.
     37   if not ScreenToMouse._SCREEN_DIMENSIONS:
     38     desktop = win32gui.GetClientRect(win32gui.GetDesktopWindow())
     39     ScreenToMouse._SCREEN_DIMENSIONS = (desktop[2], desktop[3])
     40 
     41   return ((65535 * pt[0]) / ScreenToMouse._SCREEN_DIMENSIONS[0],
     42           (65535 * pt[1]) / ScreenToMouse._SCREEN_DIMENSIONS[1])
     43 
     44 ScreenToMouse._SCREEN_DIMENSIONS = None
     45 
     46 
     47 def PressButton(down, button='left'):
     48   """Simulate a mouse button press or release at the current mouse location.
     49 
     50   Args:
     51     down: whether the button is pressed or released
     52     button: which button is pressed
     53 
     54   Returns:
     55     None
     56   """
     57 
     58   # Put the mouse_event flags in a convenient dictionary by button
     59   flags = {
     60     'left':   (win32con.MOUSEEVENTF_LEFTUP,   win32con.MOUSEEVENTF_LEFTDOWN),
     61     'middle': (win32con.MOUSEEVENTF_MIDDLEUP, win32con.MOUSEEVENTF_MIDDLEDOWN),
     62     'right':  (win32con.MOUSEEVENTF_RIGHTUP,  win32con.MOUSEEVENTF_RIGHTDOWN)
     63     }
     64 
     65   # hit the button
     66   win32api.mouse_event(flags[button][down], 0, 0)
     67 
     68 
     69 def ClickButton(button='left', click_time=0):
     70   """Press and release a mouse button at the current mouse location.
     71 
     72   Args:
     73     button: which button to click
     74     click_time: duration between press and release
     75 
     76   Returns:
     77     None
     78   """
     79   PressButton(True, button)
     80   time.sleep(click_time)
     81   PressButton(False, button)
     82 
     83 
     84 def DoubleClickButton(button='left', click_time=0, time_between_clicks=0):
     85   """Double-click a mouse button at the current mouse location.
     86 
     87   Args:
     88     button: which button to click
     89     click_time: duration between press and release
     90     time_between_clicks: time to pause between clicks
     91 
     92   Returns:
     93     None
     94   """
     95   ClickButton(button, click_time)
     96   time.sleep(time_between_clicks)
     97   ClickButton(button, click_time)
     98 
     99 
    100 def MoveToLocation(pos, duration=0, tick=0.01):
    101   """Move the mouse cursor to a specified location, taking the specified time.
    102 
    103   Args:
    104     pos: position (in screen coordinates) to move to
    105     duration: amount of time the move should take
    106     tick: amount of time between successive moves of the mouse
    107 
    108   Returns:
    109     None
    110   """
    111   # calculate the number of moves to reach the destination
    112   num_steps = (duration/tick)+1
    113 
    114   # get the current and final mouse position in mouse coords
    115   current_location = ScreenToMouse(win32gui.GetCursorPos())
    116   end_location = ScreenToMouse(pos)
    117 
    118   # Calculate the step size
    119   step_size = ((end_location[0]-current_location[0])/num_steps,
    120                (end_location[1]-current_location[1])/num_steps)
    121   step = 0
    122 
    123   while step < num_steps:
    124     # Move the mouse one step
    125     current_location = (current_location[0]+step_size[0],
    126                         current_location[1]+step_size[1])
    127 
    128     # Coerce the coords to int to avoid a warning from pywin32
    129     win32api.mouse_event(
    130       win32con.MOUSEEVENTF_MOVE|win32con.MOUSEEVENTF_ABSOLUTE,
    131       int(current_location[0]), int(current_location[1]))
    132 
    133     step += 1
    134     time.sleep(tick)
    135 
    136 
    137 def ClickAtLocation(pos, button='left', click_time=0):
    138   """Simulate a mouse click in a particular location, in screen coordinates.
    139 
    140   Args:
    141     pos: position in screen coordinates (x,y)
    142     button: which button to click
    143     click_time: duration of the click
    144 
    145   Returns:
    146     None
    147   """
    148   MoveToLocation(pos)
    149   ClickButton(button, click_time)
    150 
    151 
    152 def ClickInWindow(hwnd, offset=None, button='left', click_time=0):
    153   """Simulate a user mouse click in the center of a window.
    154 
    155   Args:
    156     hwnd: handle of the window to click in
    157     offset: where to click, defaults to dead center
    158     button: which button to click
    159     click_time: duration of the click
    160 
    161   Returns:
    162     Nothing
    163   """
    164 
    165   rect = win32gui.GetClientRect(hwnd)
    166   if offset is None: offset = (rect[2]/2, rect[3]/2)
    167 
    168   # get the screen coordinates of the window's center
    169   pos = win32gui.ClientToScreen(hwnd, offset)
    170 
    171   ClickAtLocation(pos, button, click_time)
    172 
    173 
    174 def DoubleClickInWindow(
    175   hwnd, offset=None, button='left', click_time=0, time_between_clicks=0.1):
    176   """Simulate a user mouse double click in the center of a window.
    177 
    178   Args:
    179     hwnd: handle of the window to click in
    180     offset: where to click, defaults to dead center
    181     button: which button to click
    182     click_time: duration of the clicks
    183     time_between_clicks: length of time to pause between clicks
    184 
    185   Returns:
    186     Nothing
    187   """
    188   ClickInWindow(hwnd, offset, button, click_time)
    189   time.sleep(time_between_clicks)
    190   ClickInWindow(hwnd, offset, button, click_time)
    191 
    192 
    193 def main():
    194   # We're being invoked rather than imported. Let's do some tests
    195 
    196   screen_size = win32gui.GetClientRect(win32gui.GetDesktopWindow())
    197   screen_size = (screen_size[2], screen_size[3])
    198 
    199   # move the mouse (instantly) to the upper right corner
    200   MoveToLocation((screen_size[0], 0))
    201 
    202   # move the mouse (over five seconds) to the lower left corner
    203   MoveToLocation((0, screen_size[1]), 5)
    204 
    205   # click the left mouse button. This will open up the Start menu
    206   # if the taskbar is at the bottom
    207 
    208   ClickButton()
    209 
    210   # wait a bit, then click the right button to open the context menu
    211   time.sleep(3)
    212   ClickButton('right')
    213 
    214   # move the mouse away and then click the left button to dismiss the
    215   # context menu
    216   MoveToLocation((screen_size[0]/2, screen_size[1]/2), 3)
    217   MoveToLocation((0, 0), 3)
    218   ClickButton()
    219 
    220 
    221 if __name__ == "__main__":
    222   sys.exit(main())
    223