win32_clipboard.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # -*- coding: utf-8 -*-
  2. # *****************************************************************************
  3. # Copyright (C) 2003-2006 Jack Trainor.
  4. # Copyright (C) 2006-2020 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
  5. # Copyright (C) 2020 Bassem Girgis. <brgirgis@gmail.com>
  6. #
  7. # Distributed under the terms of the BSD License. The full license is in
  8. # the file COPYING, distributed as part of this software.
  9. # *****************************************************************************
  10. ###################################
  11. #
  12. # Based on recipe posted to ctypes-users
  13. # see archive
  14. # http://aspn.activestate.com/ASPN/Mail/Message/ctypes-users/1771866
  15. #
  16. #
  17. ##########################################################################
  18. #
  19. # The Python win32clipboard lib functions work well enough ... except that they
  20. # can only cut and paste items from within one application, not across
  21. # applications or processes.
  22. #
  23. # I've written a number of Python text filters I like to run on the contents of
  24. # the clipboard so I need to call the Windows clipboard API with global memory
  25. # for my filters to work properly.
  26. #
  27. # Here's some sample code solving this problem using ctypes.
  28. #
  29. # This is my first work with ctypes. It's powerful stuff, but passing
  30. # arguments in and out of functions is tricky. More sample code would have
  31. # been helpful, hence this contribution.
  32. #
  33. ##########################################################################
  34. import ctypes
  35. import ctypes.wintypes as wintypes
  36. from ctypes import (
  37. addressof,
  38. c_buffer,
  39. c_char_p,
  40. c_int,
  41. c_size_t,
  42. c_void_p,
  43. c_wchar_p,
  44. cast,
  45. create_unicode_buffer,
  46. sizeof,
  47. windll,
  48. wstring_at,
  49. )
  50. from typing import Union
  51. from pyreadline3.keysyms.winconstants import CF_UNICODETEXT, GHND
  52. from pyreadline3.unicode_helper import ensure_unicode
  53. OpenClipboard = windll.user32.OpenClipboard
  54. OpenClipboard.argtypes = [wintypes.HWND]
  55. OpenClipboard.restype = wintypes.BOOL
  56. EmptyClipboard = windll.user32.EmptyClipboard
  57. GetClipboardData = windll.user32.GetClipboardData
  58. GetClipboardData.argtypes = [wintypes.UINT]
  59. GetClipboardData.restype = wintypes.HANDLE
  60. GetClipboardFormatName = windll.user32.GetClipboardFormatNameA
  61. GetClipboardFormatName.argtypes = [wintypes.UINT, c_char_p, c_int]
  62. SetClipboardData = windll.user32.SetClipboardData
  63. SetClipboardData.argtypes = [wintypes.UINT, wintypes.HANDLE]
  64. SetClipboardData.restype = wintypes.HANDLE
  65. EnumClipboardFormats = windll.user32.EnumClipboardFormats
  66. EnumClipboardFormats.argtypes = [c_int]
  67. CloseClipboard = windll.user32.CloseClipboard
  68. CloseClipboard.argtypes = []
  69. GlobalAlloc = windll.kernel32.GlobalAlloc
  70. GlobalAlloc.argtypes = [wintypes.UINT, c_size_t]
  71. GlobalAlloc.restype = wintypes.HGLOBAL
  72. GlobalLock = windll.kernel32.GlobalLock
  73. GlobalLock.argtypes = [wintypes.HGLOBAL]
  74. GlobalLock.restype = c_void_p
  75. GlobalUnlock = windll.kernel32.GlobalUnlock
  76. GlobalUnlock.argtypes = [c_int]
  77. _strncpy = ctypes.windll.kernel32.lstrcpynW
  78. _strncpy.restype = c_wchar_p
  79. _strncpy.argtypes = [c_wchar_p, c_wchar_p, c_size_t]
  80. def _enum() -> None:
  81. OpenClipboard(0)
  82. q = EnumClipboardFormats(0)
  83. while q:
  84. q = EnumClipboardFormats(q)
  85. CloseClipboard()
  86. def _get_format_name(format_str: str) -> bytes:
  87. buffer = c_buffer(100)
  88. bufferSize = sizeof(buffer)
  89. OpenClipboard(0)
  90. GetClipboardFormatName(format_str, buffer, bufferSize)
  91. CloseClipboard()
  92. return buffer.value
  93. def get_clipboard_text() -> str:
  94. text = ""
  95. if OpenClipboard(0):
  96. h_clip_mem = GetClipboardData(CF_UNICODETEXT)
  97. if h_clip_mem:
  98. text = wstring_at(GlobalLock(h_clip_mem))
  99. GlobalUnlock(h_clip_mem)
  100. CloseClipboard()
  101. return text
  102. def set_clipboard_text(text: Union[str, bytes]) -> None:
  103. buffer = create_unicode_buffer(ensure_unicode(text))
  104. buffer_size = sizeof(buffer)
  105. h_global_mem = GlobalAlloc(GHND, c_size_t(buffer_size))
  106. GlobalLock.restype = c_void_p
  107. lp_global_mem = GlobalLock(h_global_mem)
  108. _strncpy(
  109. cast(lp_global_mem, c_wchar_p),
  110. cast(addressof(buffer), c_wchar_p),
  111. c_size_t(buffer_size),
  112. )
  113. GlobalUnlock(c_int(h_global_mem))
  114. if OpenClipboard(0):
  115. EmptyClipboard()
  116. SetClipboardData(CF_UNICODETEXT, h_global_mem)
  117. CloseClipboard()
  118. if __name__ == "__main__":
  119. txt = get_clipboard_text() # display last text clipped
  120. print(txt)