_pyautogui_win.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. # Windows implementation of PyAutoGUI functions.
  2. # BSD license
  3. # Al Sweigart al@inventwithpython.com
  4. import ctypes
  5. import ctypes.wintypes
  6. import pyautogui
  7. from pyautogui import LEFT, MIDDLE, RIGHT
  8. import sys
  9. if sys.platform != 'win32':
  10. raise Exception('The pyautogui_win module should only be loaded on a Windows system.')
  11. # Fixes the scaling issues where PyAutoGUI was reporting the wrong resolution:
  12. try:
  13. ctypes.windll.user32.SetProcessDPIAware()
  14. except AttributeError:
  15. pass # Windows XP doesn't support this, so just do nothing.
  16. """
  17. A lot of this code is probably repeated from win32 extensions module, but I didn't want to have that dependency.
  18. Note: According to http://msdn.microsoft.com/en-us/library/windows/desktop/ms646260(v=vs.85).aspx
  19. the ctypes.windll.user32.mouse_event() function has been superseded by SendInput.
  20. SendInput() is documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
  21. UPDATE: SendInput() doesn't seem to be working for me. I've switched back to mouse_event()."""
  22. # Event codes to be passed to the mouse_event() win32 function.
  23. # Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273(v=vs.85).aspx
  24. MOUSEEVENTF_MOVE = 0x0001
  25. MOUSEEVENTF_LEFTDOWN = 0x0002
  26. MOUSEEVENTF_LEFTUP = 0x0004
  27. MOUSEEVENTF_LEFTCLICK = MOUSEEVENTF_LEFTDOWN + MOUSEEVENTF_LEFTUP
  28. MOUSEEVENTF_RIGHTDOWN = 0x0008
  29. MOUSEEVENTF_RIGHTUP = 0x0010
  30. MOUSEEVENTF_RIGHTCLICK = MOUSEEVENTF_RIGHTDOWN + MOUSEEVENTF_RIGHTUP
  31. MOUSEEVENTF_MIDDLEDOWN = 0x0020
  32. MOUSEEVENTF_MIDDLEUP = 0x0040
  33. MOUSEEVENTF_MIDDLECLICK = MOUSEEVENTF_MIDDLEDOWN + MOUSEEVENTF_MIDDLEUP
  34. MOUSEEVENTF_ABSOLUTE = 0x8000
  35. MOUSEEVENTF_WHEEL = 0x0800
  36. MOUSEEVENTF_HWHEEL = 0x01000
  37. # Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646304(v=vs.85).aspx
  38. KEYEVENTF_KEYDOWN = 0x0000 # Technically this constant doesn't exist in the MS documentation. It's the lack of KEYEVENTF_KEYUP that means pressing the key down.
  39. KEYEVENTF_KEYUP = 0x0002
  40. # Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
  41. INPUT_MOUSE = 0
  42. INPUT_KEYBOARD = 1
  43. # These ctypes structures are for Win32 INPUT, MOUSEINPUT, KEYBDINPUT, and HARDWAREINPUT structures,
  44. # used by SendInput and documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
  45. # Thanks to BSH for this StackOverflow answer: https://stackoverflow.com/questions/18566289/how-would-you-recreate-this-windows-api-structure-with-ctypes
  46. class MOUSEINPUT(ctypes.Structure):
  47. _fields_ = [
  48. ('dx', ctypes.wintypes.LONG),
  49. ('dy', ctypes.wintypes.LONG),
  50. ('mouseData', ctypes.wintypes.DWORD),
  51. ('dwFlags', ctypes.wintypes.DWORD),
  52. ('time', ctypes.wintypes.DWORD),
  53. ('dwExtraInfo', ctypes.POINTER(ctypes.wintypes.ULONG)),
  54. ]
  55. class KEYBDINPUT(ctypes.Structure):
  56. _fields_ = [
  57. ('wVk', ctypes.wintypes.WORD),
  58. ('wScan', ctypes.wintypes.WORD),
  59. ('dwFlags', ctypes.wintypes.DWORD),
  60. ('time', ctypes.wintypes.DWORD),
  61. ('dwExtraInfo', ctypes.POINTER(ctypes.wintypes.ULONG)),
  62. ]
  63. class HARDWAREINPUT(ctypes.Structure):
  64. _fields_ = [
  65. ('uMsg', ctypes.wintypes.DWORD),
  66. ('wParamL', ctypes.wintypes.WORD),
  67. ('wParamH', ctypes.wintypes.DWORD)
  68. ]
  69. class INPUT(ctypes.Structure):
  70. class _I(ctypes.Union):
  71. _fields_ = [
  72. ('mi', MOUSEINPUT),
  73. ('ki', KEYBDINPUT),
  74. ('hi', HARDWAREINPUT),
  75. ]
  76. _anonymous_ = ('i', )
  77. _fields_ = [
  78. ('type', ctypes.wintypes.DWORD),
  79. ('i', _I),
  80. ]
  81. # End of the SendInput win32 data structures.
  82. """ Keyboard key mapping for pyautogui:
  83. Documented at http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
  84. The *KB dictionaries in pyautogui map a string that can be passed to keyDown(),
  85. keyUp(), or press() into the code used for the OS-specific keyboard function.
  86. They should always be lowercase, and the same keys should be used across all OSes."""
  87. keyboardMapping = dict([(key, None) for key in pyautogui.KEY_NAMES])
  88. keyboardMapping.update({
  89. 'backspace': 0x08, # VK_BACK
  90. '\b': 0x08, # VK_BACK
  91. 'super': 0x5B, #VK_LWIN
  92. 'tab': 0x09, # VK_TAB
  93. '\t': 0x09, # VK_TAB
  94. 'clear': 0x0c, # VK_CLEAR
  95. 'enter': 0x0d, # VK_RETURN
  96. '\n': 0x0d, # VK_RETURN
  97. 'return': 0x0d, # VK_RETURN
  98. 'shift': 0x10, # VK_SHIFT
  99. 'ctrl': 0x11, # VK_CONTROL
  100. 'alt': 0x12, # VK_MENU
  101. 'pause': 0x13, # VK_PAUSE
  102. 'capslock': 0x14, # VK_CAPITAL
  103. 'kana': 0x15, # VK_KANA
  104. 'hanguel': 0x15, # VK_HANGUEL
  105. 'hangul': 0x15, # VK_HANGUL
  106. 'junja': 0x17, # VK_JUNJA
  107. 'final': 0x18, # VK_FINAL
  108. 'hanja': 0x19, # VK_HANJA
  109. 'kanji': 0x19, # VK_KANJI
  110. 'esc': 0x1b, # VK_ESCAPE
  111. 'escape': 0x1b, # VK_ESCAPE
  112. 'convert': 0x1c, # VK_CONVERT
  113. 'nonconvert': 0x1d, # VK_NONCONVERT
  114. 'accept': 0x1e, # VK_ACCEPT
  115. 'modechange': 0x1f, # VK_MODECHANGE
  116. ' ': 0x20, # VK_SPACE
  117. 'space': 0x20, # VK_SPACE
  118. 'pgup': 0x21, # VK_PRIOR
  119. 'pgdn': 0x22, # VK_NEXT
  120. 'pageup': 0x21, # VK_PRIOR
  121. 'pagedown': 0x22, # VK_NEXT
  122. 'end': 0x23, # VK_END
  123. 'home': 0x24, # VK_HOME
  124. 'left': 0x25, # VK_LEFT
  125. 'up': 0x26, # VK_UP
  126. 'right': 0x27, # VK_RIGHT
  127. 'down': 0x28, # VK_DOWN
  128. 'select': 0x29, # VK_SELECT
  129. 'print': 0x2a, # VK_PRINT
  130. 'execute': 0x2b, # VK_EXECUTE
  131. 'prtsc': 0x2c, # VK_SNAPSHOT
  132. 'prtscr': 0x2c, # VK_SNAPSHOT
  133. 'prntscrn': 0x2c, # VK_SNAPSHOT
  134. 'printscreen': 0x2c, # VK_SNAPSHOT
  135. 'insert': 0x2d, # VK_INSERT
  136. 'del': 0x2e, # VK_DELETE
  137. 'delete': 0x2e, # VK_DELETE
  138. 'help': 0x2f, # VK_HELP
  139. 'win': 0x5b, # VK_LWIN
  140. 'winleft': 0x5b, # VK_LWIN
  141. 'winright': 0x5c, # VK_RWIN
  142. 'apps': 0x5d, # VK_APPS
  143. 'sleep': 0x5f, # VK_SLEEP
  144. 'num0': 0x60, # VK_NUMPAD0
  145. 'num1': 0x61, # VK_NUMPAD1
  146. 'num2': 0x62, # VK_NUMPAD2
  147. 'num3': 0x63, # VK_NUMPAD3
  148. 'num4': 0x64, # VK_NUMPAD4
  149. 'num5': 0x65, # VK_NUMPAD5
  150. 'num6': 0x66, # VK_NUMPAD6
  151. 'num7': 0x67, # VK_NUMPAD7
  152. 'num8': 0x68, # VK_NUMPAD8
  153. 'num9': 0x69, # VK_NUMPAD9
  154. 'multiply': 0x6a, # VK_MULTIPLY ??? Is this the numpad *?
  155. 'add': 0x6b, # VK_ADD ??? Is this the numpad +?
  156. 'separator': 0x6c, # VK_SEPARATOR ??? Is this the numpad enter?
  157. 'subtract': 0x6d, # VK_SUBTRACT ??? Is this the numpad -?
  158. 'decimal': 0x6e, # VK_DECIMAL
  159. 'divide': 0x6f, # VK_DIVIDE
  160. 'f1': 0x70, # VK_F1
  161. 'f2': 0x71, # VK_F2
  162. 'f3': 0x72, # VK_F3
  163. 'f4': 0x73, # VK_F4
  164. 'f5': 0x74, # VK_F5
  165. 'f6': 0x75, # VK_F6
  166. 'f7': 0x76, # VK_F7
  167. 'f8': 0x77, # VK_F8
  168. 'f9': 0x78, # VK_F9
  169. 'f10': 0x79, # VK_F10
  170. 'f11': 0x7a, # VK_F11
  171. 'f12': 0x7b, # VK_F12
  172. 'f13': 0x7c, # VK_F13
  173. 'f14': 0x7d, # VK_F14
  174. 'f15': 0x7e, # VK_F15
  175. 'f16': 0x7f, # VK_F16
  176. 'f17': 0x80, # VK_F17
  177. 'f18': 0x81, # VK_F18
  178. 'f19': 0x82, # VK_F19
  179. 'f20': 0x83, # VK_F20
  180. 'f21': 0x84, # VK_F21
  181. 'f22': 0x85, # VK_F22
  182. 'f23': 0x86, # VK_F23
  183. 'f24': 0x87, # VK_F24
  184. 'numlock': 0x90, # VK_NUMLOCK
  185. 'scrolllock': 0x91, # VK_SCROLL
  186. 'shiftleft': 0xa0, # VK_LSHIFT
  187. 'shiftright': 0xa1, # VK_RSHIFT
  188. 'ctrlleft': 0xa2, # VK_LCONTROL
  189. 'ctrlright': 0xa3, # VK_RCONTROL
  190. 'altleft': 0xa4, # VK_LMENU
  191. 'altright': 0xa5, # VK_RMENU
  192. 'browserback': 0xa6, # VK_BROWSER_BACK
  193. 'browserforward': 0xa7, # VK_BROWSER_FORWARD
  194. 'browserrefresh': 0xa8, # VK_BROWSER_REFRESH
  195. 'browserstop': 0xa9, # VK_BROWSER_STOP
  196. 'browsersearch': 0xaa, # VK_BROWSER_SEARCH
  197. 'browserfavorites': 0xab, # VK_BROWSER_FAVORITES
  198. 'browserhome': 0xac, # VK_BROWSER_HOME
  199. 'volumemute': 0xad, # VK_VOLUME_MUTE
  200. 'volumedown': 0xae, # VK_VOLUME_DOWN
  201. 'volumeup': 0xaf, # VK_VOLUME_UP
  202. 'nexttrack': 0xb0, # VK_MEDIA_NEXT_TRACK
  203. 'prevtrack': 0xb1, # VK_MEDIA_PREV_TRACK
  204. 'stop': 0xb2, # VK_MEDIA_STOP
  205. 'playpause': 0xb3, # VK_MEDIA_PLAY_PAUSE
  206. 'launchmail': 0xb4, # VK_LAUNCH_MAIL
  207. 'launchmediaselect': 0xb5, # VK_LAUNCH_MEDIA_SELECT
  208. 'launchapp1': 0xb6, # VK_LAUNCH_APP1
  209. 'launchapp2': 0xb7, # VK_LAUNCH_APP2
  210. })
  211. # There are other virtual key constants that are not used here because the printable ascii keys are
  212. # handled in the following `for` loop.
  213. # The virtual key constants that aren't used are:
  214. # VK_OEM_1, VK_OEM_PLUS, VK_OEM_COMMA, VK_OEM_MINUS, VK_OEM_PERIOD, VK_OEM_2, VK_OEM_3, VK_OEM_4,
  215. # VK_OEM_5, VK_OEM_6, VK_OEM_7, VK_OEM_8, VK_PACKET, VK_ATTN, VK_CRSEL, VK_EXSEL, VK_EREOF,
  216. # VK_PLAY, VK_ZOOM, VK_NONAME, VK_PA1, VK_OEM_CLEAR
  217. # Populate the basic printable ascii characters.
  218. # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscana
  219. for c in range(32, 128):
  220. keyboardMapping[chr(c)] = ctypes.windll.user32.VkKeyScanA(ctypes.wintypes.WCHAR(chr(c)))
  221. def _keyDown(key):
  222. """Performs a keyboard key press without the release. This will put that
  223. key in a held down state.
  224. NOTE: For some reason, this does not seem to cause key repeats like would
  225. happen if a keyboard key was held down on a text field.
  226. Args:
  227. key (str): The key to be pressed down. The valid names are listed in
  228. pyautogui.KEY_NAMES.
  229. Returns:
  230. None
  231. """
  232. if key not in keyboardMapping or keyboardMapping[key] is None:
  233. return
  234. needsShift = pyautogui.isShiftCharacter(key)
  235. """
  236. # OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start.
  237. if key in keyboardMapping.keys():
  238. vkCode = keyboardMapping[key]
  239. elif len(key) == 1:
  240. # note: I could use this case to update keyboardMapping to cache the VkKeyScan results, but I've decided not to just to make any possible bugs easier to reproduce.
  241. vkCode = ctypes.windll.user32.VkKeyScanW(ctypes.wintypes.WCHAR(key))
  242. if vkCode == -1:
  243. raise ValueError('There is no VK code for key "%s"' % (key))
  244. if vkCode > 0x100: # the vk code will be > 0x100 if it needs shift
  245. vkCode -= 0x100
  246. needsShift = True
  247. """
  248. mods, vkCode = divmod(keyboardMapping[key], 0x100)
  249. for apply_mod, vk_mod in [(mods & 4, 0x12), (mods & 2, 0x11),
  250. (mods & 1 or needsShift, 0x10)]: #HANKAKU not supported! mods & 8
  251. if apply_mod:
  252. ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYDOWN, 0) #
  253. ctypes.windll.user32.keybd_event(vkCode, 0, KEYEVENTF_KEYDOWN, 0)
  254. for apply_mod, vk_mod in [(mods & 1 or needsShift, 0x10), (mods & 2, 0x11),
  255. (mods & 4, 0x12)]: #HANKAKU not supported! mods & 8
  256. if apply_mod:
  257. ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYUP, 0) #
  258. def _keyUp(key):
  259. """Performs a keyboard key release (without the press down beforehand).
  260. Args:
  261. key (str): The key to be released up. The valid names are listed in
  262. pyautogui.KEY_NAMES.
  263. Returns:
  264. None
  265. """
  266. if key not in keyboardMapping or keyboardMapping[key] is None:
  267. return
  268. needsShift = pyautogui.isShiftCharacter(key)
  269. """
  270. # OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start.
  271. if key in keyboardMapping.keys():
  272. vkCode = keyboardMapping[key]
  273. elif len(key) == 1:
  274. # note: I could use this case to update keyboardMapping to cache the VkKeyScan results, but I've decided not to just to make any possible bugs easier to reproduce.
  275. vkCode = ctypes.windll.user32.VkKeyScanW(ctypes.wintypes.WCHAR(key))
  276. if vkCode == -1:
  277. raise ValueError('There is no VK code for key "%s"' % (key))
  278. if vkCode > 0x100: # the vk code will be > 0x100 if it needs shift
  279. vkCode -= 0x100
  280. needsShift = True
  281. """
  282. mods, vkCode = divmod(keyboardMapping[key], 0x100)
  283. for apply_mod, vk_mod in [(mods & 4, 0x12), (mods & 2, 0x11),
  284. (mods & 1 or needsShift, 0x10)]: #HANKAKU not supported! mods & 8
  285. if apply_mod:
  286. ctypes.windll.user32.keybd_event(vk_mod, 0, 0, 0) #
  287. ctypes.windll.user32.keybd_event(vkCode, 0, KEYEVENTF_KEYUP, 0)
  288. for apply_mod, vk_mod in [(mods & 1 or needsShift, 0x10), (mods & 2, 0x11),
  289. (mods & 4, 0x12)]: #HANKAKU not supported! mods & 8
  290. if apply_mod:
  291. ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYUP, 0) #
  292. def _position():
  293. """Returns the current xy coordinates of the mouse cursor as a two-integer
  294. tuple by calling the GetCursorPos() win32 function.
  295. Returns:
  296. (x, y) tuple of the current xy coordinates of the mouse cursor.
  297. """
  298. cursor = ctypes.wintypes.POINT()
  299. ctypes.windll.user32.GetCursorPos(ctypes.byref(cursor))
  300. return (cursor.x, cursor.y)
  301. def _size():
  302. """Returns the width and height of the screen as a two-integer tuple.
  303. Returns:
  304. (width, height) tuple of the screen size, in pixels.
  305. """
  306. return (ctypes.windll.user32.GetSystemMetrics(0), ctypes.windll.user32.GetSystemMetrics(1))
  307. def _moveTo(x, y):
  308. """Send the mouse move event to Windows by calling SetCursorPos() win32
  309. function.
  310. Args:
  311. button (str): The mouse button, either 'left', 'middle', or 'right'
  312. x (int): The x position of the mouse event.
  313. y (int): The y position of the mouse event.
  314. Returns:
  315. None
  316. """
  317. ctypes.windll.user32.SetCursorPos(x, y)
  318. # This was a possible solution to issue #314 https://github.com/asweigart/pyautogui/issues/314
  319. # but I'd like to hang on to SetCursorPos because mouse_event() has been superseded.
  320. #_sendMouseEvent(MOUSEEVENTF_MOVE + MOUSEEVENTF_ABSOLUTE, x, y)
  321. def _mouseDown(x, y, button):
  322. """Send the mouse down event to Windows by calling the mouse_event() win32
  323. function.
  324. Args:
  325. x (int): The x position of the mouse event.
  326. y (int): The y position of the mouse event.
  327. button (str): The mouse button, either 'left', 'middle', or 'right'
  328. Returns:
  329. None
  330. """
  331. if button not in (LEFT, MIDDLE, RIGHT):
  332. raise ValueError('button arg to _click() must be one of "left", "middle", or "right", not %s' % button)
  333. if button == LEFT:
  334. EV = MOUSEEVENTF_LEFTDOWN
  335. elif button == MIDDLE:
  336. EV = MOUSEEVENTF_MIDDLEDOWN
  337. elif button == RIGHT:
  338. EV = MOUSEEVENTF_RIGHTDOWN
  339. try:
  340. _sendMouseEvent(EV, x, y)
  341. except (PermissionError, OSError):
  342. # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
  343. pass
  344. def _mouseUp(x, y, button):
  345. """Send the mouse up event to Windows by calling the mouse_event() win32
  346. function.
  347. Args:
  348. x (int): The x position of the mouse event.
  349. y (int): The y position of the mouse event.
  350. button (str): The mouse button, either 'left', 'middle', or 'right'
  351. Returns:
  352. None
  353. """
  354. if button not in (LEFT, MIDDLE, RIGHT):
  355. raise ValueError('button arg to _click() must be one of "left", "middle", or "right", not %s' % button)
  356. if button == LEFT:
  357. EV = MOUSEEVENTF_LEFTUP
  358. elif button == MIDDLE:
  359. EV = MOUSEEVENTF_MIDDLEUP
  360. elif button == RIGHT:
  361. EV = MOUSEEVENTF_RIGHTUP
  362. try:
  363. _sendMouseEvent(EV, x, y)
  364. except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
  365. pass
  366. def _click(x, y, button):
  367. """Send the mouse click event to Windows by calling the mouse_event() win32
  368. function.
  369. Args:
  370. button (str): The mouse button, either 'left', 'middle', or 'right'
  371. x (int): The x position of the mouse event.
  372. y (int): The y position of the mouse event.
  373. Returns:
  374. None
  375. """
  376. if button not in (LEFT, MIDDLE, RIGHT):
  377. raise ValueError('button arg to _click() must be one of "left", "middle", or "right", not %s' % button)
  378. if button == LEFT:
  379. EV = MOUSEEVENTF_LEFTCLICK
  380. elif button == MIDDLE:
  381. EV = MOUSEEVENTF_MIDDLECLICK
  382. elif button ==RIGHT:
  383. EV = MOUSEEVENTF_RIGHTCLICK
  384. try:
  385. _sendMouseEvent(EV, x, y)
  386. except (PermissionError, OSError):
  387. # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
  388. pass
  389. def _mouse_is_swapped():
  390. # TODO - measure the performance of checking this setting for each click.
  391. # 23 is SM_SWAPBUTTON: "Nonzero if the meanings of the left and right mouse buttons are swapped; otherwise, 0."
  392. return ctypes.windll.user32.GetSystemMetrics(23) != 0
  393. def _sendMouseEvent(ev, x, y, dwData=0):
  394. """The helper function that actually makes the call to the mouse_event()
  395. win32 function.
  396. Args:
  397. ev (int): The win32 code for the mouse event. Use one of the MOUSEEVENTF_*
  398. constants for this argument.
  399. x (int): The x position of the mouse event.
  400. y (int): The y position of the mouse event.
  401. dwData (int): The argument for mouse_event()'s dwData parameter. So far
  402. this is only used by mouse scrolling.
  403. Returns:
  404. None
  405. """
  406. assert x != None and y != None, 'x and y cannot be set to None'
  407. # TODO: ARG! For some reason, SendInput isn't working for mouse events. I'm switching to using the older mouse_event win32 function.
  408. #mouseStruct = MOUSEINPUT()
  409. #mouseStruct.dx = x
  410. #mouseStruct.dy = y
  411. #mouseStruct.mouseData = ev
  412. #mouseStruct.time = 0
  413. #mouseStruct.dwExtraInfo = ctypes.pointer(ctypes.c_ulong(0)) # according to https://stackoverflow.com/questions/13564851/generate-keyboard-events I can just set this. I don't really care about this value.
  414. #inputStruct = INPUT()
  415. #inputStruct.mi = mouseStruct
  416. #inputStruct.type = INPUT_MOUSE
  417. #ctypes.windll.user32.SendInput(1, ctypes.pointer(inputStruct), ctypes.sizeof(inputStruct))
  418. # TODO Note: We need to handle additional buttons, which I believe is documented here:
  419. # https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-mouse_event
  420. width, height = _size()
  421. convertedX = 65536 * x // width + 1
  422. convertedY = 65536 * y // height + 1
  423. ctypes.windll.user32.mouse_event(ev, ctypes.c_long(convertedX), ctypes.c_long(convertedY), dwData, 0)
  424. # TODO: Too many false positives with this code: See: https://github.com/asweigart/pyautogui/issues/108
  425. #if ctypes.windll.kernel32.GetLastError() != 0:
  426. # raise ctypes.WinError()
  427. def _scroll(clicks, x=None, y=None):
  428. """Send the mouse vertical scroll event to Windows by calling the
  429. mouse_event() win32 function.
  430. Args:
  431. clicks (int): The amount of scrolling to do. A positive value is the mouse
  432. wheel moving forward (scrolling up), a negative value is backwards (down).
  433. x (int): The x position of the mouse event.
  434. y (int): The y position of the mouse event.
  435. Returns:
  436. None
  437. """
  438. startx, starty = _position()
  439. width, height = _size()
  440. if x is None:
  441. x = startx
  442. else:
  443. if x < 0:
  444. x = 0
  445. elif x >= width:
  446. x = width - 1
  447. if y is None:
  448. y = starty
  449. else:
  450. if y < 0:
  451. y = 0
  452. elif y >= height:
  453. y = height - 1
  454. try:
  455. _sendMouseEvent(MOUSEEVENTF_WHEEL, x, y, dwData=clicks)
  456. except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
  457. pass
  458. def _hscroll(clicks, x, y):
  459. """Send the mouse horizontal scroll event to Windows by calling the
  460. mouse_event() win32 function.
  461. Args:
  462. clicks (int): The amount of scrolling to do. A positive value is the mouse
  463. wheel moving right, a negative value is moving left.
  464. x (int): The x position of the mouse event.
  465. y (int): The y position of the mouse event.
  466. Returns:
  467. None
  468. """
  469. return _scroll(clicks, x, y)
  470. def _vscroll(clicks, x, y):
  471. """A wrapper for _scroll(), which does vertical scrolling.
  472. Args:
  473. clicks (int): The amount of scrolling to do. A positive value is the mouse
  474. wheel moving forward (scrolling up), a negative value is backwards (down).
  475. x (int): The x position of the mouse event.
  476. y (int): The y position of the mouse event.
  477. Returns:
  478. None
  479. """
  480. return _scroll(clicks, x, y)