Module refinery.lib.winclip
Windows-specific module to obtain data in different complex formats from the clipboard. Primarily, this can retrieve image data from the clipboard.
Expand source code Browse git
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Windows-specific module to obtain data in different complex formats from the clipboard.
Primarily, this can retrieve image data from the clipboard.
"""
import os
import enum
if os.name == 'nt':
import ctypes
import ctypes.wintypes as w
from ctypes import sizeof
u32 = ctypes.WinDLL('user32')
k32 = ctypes.WinDLL('kernel32')
IsClipboardFormatAvailable = u32.IsClipboardFormatAvailable
IsClipboardFormatAvailable.argtypes = w.UINT,
IsClipboardFormatAvailable.restype = w.BOOL
GlobalAlloc = k32.GlobalAlloc
GlobalAlloc.argtypes = w.UINT, w.UINT
GlobalAlloc.restype = w.HGLOBAL
GlobalFree = k32.GlobalFree
GlobalFree.argtypes = w.HGLOBAL,
GlobalFree.restype = w.HGLOBAL
GlobalSize = k32.GlobalSize
GlobalSize.argtypes = w.HGLOBAL,
GlobalSize.restype = w.UINT
SetClipboardData = u32.SetClipboardData
SetClipboardData.argtypes = w.UINT, w.HANDLE
SetClipboardData.restype = w.HANDLE
EmptyClipboard = u32.EmptyClipboard
OpenClipboard = u32.OpenClipboard
OpenClipboard.argtypes = w.HWND,
OpenClipboard.restype = w.BOOL
GetClipboardData = u32.GetClipboardData
GetClipboardData.argtypes = w.UINT,
GetClipboardData.restype = w.HANDLE
GlobalLock = k32.GlobalLock
GlobalLock.argtypes = w.HGLOBAL,
GlobalLock.restype = w.LPVOID
GlobalUnlock = k32.GlobalUnlock
GlobalUnlock.argtypes = w.HGLOBAL,
GlobalUnlock.restype = w.BOOL
CloseClipboard = u32.CloseClipboard
CloseClipboard.argtypes = None
CloseClipboard.restype = w.BOOL
class FieldsFromTypeHints(type(ctypes.Structure)):
def __new__(cls, name, bases, namespace):
from typing import get_type_hints
class AnnotationDummy:
__annotations__ = namespace.get('__annotations__', {})
annotations = get_type_hints(AnnotationDummy)
namespace['_fields_'] = list(annotations.items())
namespace['_pack_'] = 1
return type(ctypes.Structure).__new__(cls, name, bases, namespace)
class BITMAPINFOHEADER(ctypes.Structure, metaclass=FieldsFromTypeHints):
biSize : w.DWORD
biWidth : w.LONG
biHeight : w.LONG
biPlanes : w.WORD
biBitCount : w.WORD
biCompression : w.DWORD
biSizeImage : w.DWORD
biXPelsPerMeter : w.LONG
biYPelsPerMeter : w.LONG
biClrUsed : w.DWORD
biClrImportant : w.DWORD
class BITMAPFILEHEADER(ctypes.Structure, metaclass=FieldsFromTypeHints):
bfType : w.WORD
bfSize : w.DWORD
bfReserved1 : w.WORD
bfReserved2 : w.WORD
bfOffBits : w.DWORD
GMEM_DDESHARE = 0x2000
GMEM_ZEROINIT = 0x0040
class CF(enum.IntEnum):
TEXT = 1
DIB = 8
OEMTEXT = 7
UNICODETEXT = 13
class GlobalMemory:
def __init__(self, data, size=None):
self.data = data
self.size = size or (len(data) + 2)
self.buffer = GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT, self.size)
def __enter__(self):
locked = GlobalLock(ctypes.c_void_p(self.buffer))
ctypes.windll.msvcrt.memset(
ctypes.c_char_p(locked), 0, self.size)
ctypes.windll.msvcrt.memcpy(
ctypes.c_char_p(locked), self.data, len(self.data))
GlobalUnlock(locked)
return self
def __exit__(self, *args):
return None
class ClipBoard:
def __init__(self, mode: CF):
self.mode = mode
def __enter__(self):
OpenClipboard(None)
return self
def __exit__(self, *args):
CloseClipboard()
def paste(self):
hg = GetClipboardData(self.mode.value)
if self.mode is CF.TEXT or self.mode is CF.OEMTEXT:
return ctypes.c_char_p(hg).value
if self.mode is CF.UNICODETEXT:
return ctypes.c_wchar_p(hg).value.encode('utf8')
if self.mode is CF.DIB:
def get_pixel_data_offset_for_packed_dib(header: BITMAPINFOHEADER) -> int:
extra = 0
if header.biSize == sizeof(BITMAPINFOHEADER):
if header.biBitCount > 8:
if header.biCompression == 3: # BI_BITFIELDS
extra += 12
if header.biCompression == 6:
extra += 16
if header.biClrUsed > 0:
extra += header.biClrUsed * 4
else:
if header.biBitCount <= 8:
extra += 4 << header.biBitCount
return header.biSize + extra
bm_header = BITMAPINFOHEADER.from_address(hg)
bm_stride = ((((bm_header.biWidth * bm_header.biBitCount) + 31) & ~31) >> 3)
bm_size = abs(bm_header.biHeight) * bm_stride
bm_offset = get_pixel_data_offset_for_packed_dib(bm_header)
size = max(bm_offset + bm_size, GlobalSize(hg))
data = ctypes.cast(hg, ctypes.POINTER(ctypes.c_ubyte * size))
size += sizeof(BITMAPFILEHEADER)
bfh = BITMAPFILEHEADER(0x4D42, size, 0, 0, sizeof(BITMAPFILEHEADER) + bm_offset)
bitmap = bytes(data.contents)
return bytes(bfh) + bitmap
def copy(self, data):
EmptyClipboard()
size = len(data)
if self.mode in (CF.TEXT, CF.OEMTEXT):
size += 1
if self.mode is CF.UNICODETEXT:
size += 2
glob = GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT, size)
lock = GlobalLock(ctypes.c_void_p(glob))
ctypes.windll.msvcrt.memset(ctypes.c_char_p(lock), 0, size)
ctypes.windll.msvcrt.memcpy(ctypes.c_char_p(lock), data, len(data))
GlobalUnlock(lock)
SetClipboardData(self.mode.value, glob)
def get_any_data():
for mode in [
CF.UNICODETEXT,
CF.TEXT,
CF.DIB,
CF.OEMTEXT,
]:
if not IsClipboardFormatAvailable(mode.value):
continue
with ClipBoard(mode) as cp:
data = cp.paste()
if not data:
continue
return mode, data
return None, B''
Functions
def get_any_data()
-
Expand source code Browse git
def get_any_data(): for mode in [ CF.UNICODETEXT, CF.TEXT, CF.DIB, CF.OEMTEXT, ]: if not IsClipboardFormatAvailable(mode.value): continue with ClipBoard(mode) as cp: data = cp.paste() if not data: continue return mode, data return None, B''
Classes
class CF (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code Browse git
class CF(enum.IntEnum): TEXT = 1 DIB = 8 OEMTEXT = 7 UNICODETEXT = 13
Ancestors
- enum.IntEnum
- builtins.int
- enum.Enum
Class variables
var TEXT
var DIB
var OEMTEXT
var UNICODETEXT
class GlobalMemory (data, size=None)
-
Expand source code Browse git
class GlobalMemory: def __init__(self, data, size=None): self.data = data self.size = size or (len(data) + 2) self.buffer = GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT, self.size) def __enter__(self): locked = GlobalLock(ctypes.c_void_p(self.buffer)) ctypes.windll.msvcrt.memset( ctypes.c_char_p(locked), 0, self.size) ctypes.windll.msvcrt.memcpy( ctypes.c_char_p(locked), self.data, len(self.data)) GlobalUnlock(locked) return self def __exit__(self, *args): return None
class ClipBoard (mode)
-
Expand source code Browse git
class ClipBoard: def __init__(self, mode: CF): self.mode = mode def __enter__(self): OpenClipboard(None) return self def __exit__(self, *args): CloseClipboard() def paste(self): hg = GetClipboardData(self.mode.value) if self.mode is CF.TEXT or self.mode is CF.OEMTEXT: return ctypes.c_char_p(hg).value if self.mode is CF.UNICODETEXT: return ctypes.c_wchar_p(hg).value.encode('utf8') if self.mode is CF.DIB: def get_pixel_data_offset_for_packed_dib(header: BITMAPINFOHEADER) -> int: extra = 0 if header.biSize == sizeof(BITMAPINFOHEADER): if header.biBitCount > 8: if header.biCompression == 3: # BI_BITFIELDS extra += 12 if header.biCompression == 6: extra += 16 if header.biClrUsed > 0: extra += header.biClrUsed * 4 else: if header.biBitCount <= 8: extra += 4 << header.biBitCount return header.biSize + extra bm_header = BITMAPINFOHEADER.from_address(hg) bm_stride = ((((bm_header.biWidth * bm_header.biBitCount) + 31) & ~31) >> 3) bm_size = abs(bm_header.biHeight) * bm_stride bm_offset = get_pixel_data_offset_for_packed_dib(bm_header) size = max(bm_offset + bm_size, GlobalSize(hg)) data = ctypes.cast(hg, ctypes.POINTER(ctypes.c_ubyte * size)) size += sizeof(BITMAPFILEHEADER) bfh = BITMAPFILEHEADER(0x4D42, size, 0, 0, sizeof(BITMAPFILEHEADER) + bm_offset) bitmap = bytes(data.contents) return bytes(bfh) + bitmap def copy(self, data): EmptyClipboard() size = len(data) if self.mode in (CF.TEXT, CF.OEMTEXT): size += 1 if self.mode is CF.UNICODETEXT: size += 2 glob = GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT, size) lock = GlobalLock(ctypes.c_void_p(glob)) ctypes.windll.msvcrt.memset(ctypes.c_char_p(lock), 0, size) ctypes.windll.msvcrt.memcpy(ctypes.c_char_p(lock), data, len(data)) GlobalUnlock(lock) SetClipboardData(self.mode.value, glob)
Methods
def paste(self)
-
Expand source code Browse git
def paste(self): hg = GetClipboardData(self.mode.value) if self.mode is CF.TEXT or self.mode is CF.OEMTEXT: return ctypes.c_char_p(hg).value if self.mode is CF.UNICODETEXT: return ctypes.c_wchar_p(hg).value.encode('utf8') if self.mode is CF.DIB: def get_pixel_data_offset_for_packed_dib(header: BITMAPINFOHEADER) -> int: extra = 0 if header.biSize == sizeof(BITMAPINFOHEADER): if header.biBitCount > 8: if header.biCompression == 3: # BI_BITFIELDS extra += 12 if header.biCompression == 6: extra += 16 if header.biClrUsed > 0: extra += header.biClrUsed * 4 else: if header.biBitCount <= 8: extra += 4 << header.biBitCount return header.biSize + extra bm_header = BITMAPINFOHEADER.from_address(hg) bm_stride = ((((bm_header.biWidth * bm_header.biBitCount) + 31) & ~31) >> 3) bm_size = abs(bm_header.biHeight) * bm_stride bm_offset = get_pixel_data_offset_for_packed_dib(bm_header) size = max(bm_offset + bm_size, GlobalSize(hg)) data = ctypes.cast(hg, ctypes.POINTER(ctypes.c_ubyte * size)) size += sizeof(BITMAPFILEHEADER) bfh = BITMAPFILEHEADER(0x4D42, size, 0, 0, sizeof(BITMAPFILEHEADER) + bm_offset) bitmap = bytes(data.contents) return bytes(bfh) + bitmap
def copy(self, data)
-
Expand source code Browse git
def copy(self, data): EmptyClipboard() size = len(data) if self.mode in (CF.TEXT, CF.OEMTEXT): size += 1 if self.mode is CF.UNICODETEXT: size += 2 glob = GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT, size) lock = GlobalLock(ctypes.c_void_p(glob)) ctypes.windll.msvcrt.memset(ctypes.c_char_p(lock), 0, size) ctypes.windll.msvcrt.memcpy(ctypes.c_char_p(lock), data, len(data)) GlobalUnlock(lock) SetClipboardData(self.mode.value, glob)