def get_text(): formats = get_clipboard_formats() matching = [] for u in (utf8, not utf8): if u: fmts = [win32con.CF_UNICODETEXT] else: fmts = [win32con.CF_TEXT, win32con.CF_OEMTEXT] matching += [fmt for fmt in formats if fmt in fmts] log("supported formats: %s (prefer utf8: %s)", csv(format_names(matching)), utf8) if not matching: log("no supported formats, only: %s", csv(format_names(formats))) errback() return True data_handle = None fmt = None for fmt in matching: data_handle = GetClipboardData(fmt) log("GetClipboardData(%s)=%#x", format_name(fmt), data_handle or 0) if data_handle: break if not data_handle: log("no valid data handle using %s (may try again)", csv(format_names(matching))) return False data = GlobalLock(data_handle) if not data: log("failed to lock data handle %#x (may try again)", data_handle) return False log("got data handle lock %#x for format '%s'", data, format_name(fmt)) try: if fmt == win32con.CF_UNICODETEXT: try: v = w_to_utf8(data) except Exception as e: log("w_to_utf8(..)", exc_info=True) errback(str(e)) return True callback(v) return True #CF_TEXT or CF_OEMTEXT: astr = cast(data, LPCSTR) s = astr.value.decode("latin1") if CONVERT_LINE_ENDINGS: s = s.replace("\r\n", "\n") b = s.encode("latin1") ulen = len(b) if ulen > MAX_CLIPBOARD_PACKET_SIZE: errback("text data is too large: %i characters" % ulen) return True log("got %i bytes of TEXT data: %s", len(b), ellipsizer(b)) callback(b) return True finally: GlobalUnlock(data)
def get_text(): data_handle = GetClipboardData(win32con.CF_UNICODETEXT) if not data_handle: errback("no data handle") return data = GlobalLock(data_handle) if not data: errback("failed to lock handle") return try: wstr = cast(data, LPCWSTR) ulen = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, None, 0, None, None) if ulen>MAX_CLIPBOARD_PACKET_SIZE: errback("too much data") return buf = create_string_buffer(ulen) l = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, byref(buf), ulen, None, None) if l>0: if buf.raw[l-1:l]==b"\0": s = buf.raw[:l-1] else: s = buf.raw[:l] log("got %i bytes of data: %s", len(s), repr_ellipsized(str(s))) callback(strtobytes(s)) else: errback("failed to convert to UTF8: %s" % FormatError(get_last_error())) finally: GlobalUnlock(data)
def set_clipboard_image(self, img_format, img_data): image_formats = {} if COMPRESSED_IMAGES: #first save it as binary compressed data: fmt_name = LPCSTR(img_format.upper().encode("latin1")+b"\0") #ie: "PNG" fmt = RegisterClipboardFormatA(fmt_name) if fmt: buf = create_string_buffer(img_data) pbuf = cast(byref(buf), c_void_p) l = len(img_data) data_handle = GlobalAlloc(GMEM_MOVEABLE, l) if not data_handle: log.error("Error: failed to allocate %i bytes of global memory", l) return True data = GlobalLock(data_handle) if not data: log("failed to lock data handle %#x (may try again)", data_handle) return False log("got data handle lock %#x for %i bytes of '%s' data", data, l, img_format) try: memmove(data, pbuf, l) finally: GlobalUnlock(data) image_formats[fmt] = data_handle #also convert it to a bitmap: from PIL import Image buf = BytesIO(img_data) img = Image.open(buf) if img.mode!="RGBA": img = img.convert("RGBA") rgb_data = img.tobytes("raw", "BGRA") w, h = img.size log("set_clipboard_image(%s, %s) image size=%s, BGR buffer=%i bytes", img_format, ellipsizer(data), img.size, len(rgb_data)) header = BITMAPINFOHEADER() memset(byref(header), 0, sizeof(BITMAPINFOHEADER )) header.biSize = sizeof(BITMAPINFOHEADER) header.biWidth = w header.biHeight = -h header.biPlanes = 1 header.biBitCount = 32 header.biCompression = BI_RGB header.biSizeImage = 0 header.biXPelsPerMeter = 10 header.biYPelsPerMeter = 10 bitmapinfo = BITMAPINFO() bitmapinfo.bmiColors = 0 memmove(byref(bitmapinfo.bmiHeader), byref(header), sizeof(BITMAPINFOHEADER)) rgb_buf = create_string_buffer(rgb_data) pbuf = cast(byref(rgb_buf), c_void_p) hdc = GetDC(None) CBM_INIT = 4 bitmap = CreateDIBitmap(hdc, byref(header), CBM_INIT, pbuf, byref(bitmapinfo), win32con.DIB_RGB_COLORS) ReleaseDC(None, hdc) image_formats[win32con.CF_BITMAP] = bitmap self.do_set_clipboard_image(image_formats)
def set_clipboard_text(self, text): #convert to wide char #get the length in wide chars: if CONVERT_LINE_ENDINGS: text = text.decode("utf8").replace("\n", "\r\n").encode("utf8") wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, len(text), None, 0) if not wlen: self.set_err("failed to prepare to convert to wide char") return True log("MultiByteToWideChar wlen=%i", wlen) #allocate some memory for it: l = (wlen+1)*2 buf = GlobalAlloc(GMEM_MOVEABLE, l) if not buf: self.set_err("failed to allocate %i bytes of global memory" % l) return True log("GlobalAlloc buf=%#x", buf) locked = GlobalLock(buf) if not locked: self.set_err("failed to lock buffer %#x" % buf) GlobalFree(buf) return True try: locked_buf = cast(locked, LPWSTR) r = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, len(text), locked_buf, wlen) if not r: self.set_err("failed to convert to wide char") GlobalFree(buf) return True finally: GlobalUnlock(locked) #we're going to alter the clipboard ourselves, #ignore messages until we're done: self._block_owner_change = True def cleanup(): GLib.idle_add(self.remove_block) def set_clipboard_data(): r = EmptyClipboard() log("EmptyClipboard()=%s", r) if not r: self.set_err("failed to empty the clipboard") return False r = SetClipboardData(win32con.CF_UNICODETEXT, buf) if not r: e = WinError(GetLastError()) log("SetClipboardData(CF_UNICODETEXT, %i chars)=%s (%s)", wlen, r, e) return False log("SetClipboardData(CF_UNICODETEXT, %i chars)=%s", wlen, r) cleanup() return True def set_clipboard_error(error_text=""): log("set_clipboard_error(%s)", error_text) if error_text: log.warn("Warning: failed to set clipboard data") log.warn(" %s", error_text) cleanup() self.with_clipboard_lock(set_clipboard_data, set_clipboard_error)
def do_set_clipboard_text(self, text): #convert to wide char #get the length in wide chars: wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, len(text), None, 0) if not wlen: self.set_err("failed to prepare to convert to wide char") return False log("MultiByteToWideChar wlen=%i", wlen) #allocate some memory for it: l = (wlen+1)*2 buf = GlobalAlloc(GMEM_MOVEABLE, l) if not buf: self.set_err("failed to allocate %i bytes of global memory" % l) return False log("GlobalAlloc buf=%#x", buf) locked = GlobalLock(buf) if not locked: self.set_err("failed to lock buffer %#x" % buf) GlobalFree(buf) return False try: locked_buf = cast(locked, LPWSTR) r = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, len(text), locked_buf, wlen) if not r: self.set_err("failed to convert to wide char") return False finally: GlobalUnlock(locked) #we're going to alter the clipboard ourselves, #ignore messages until we're done: self._block_owner_change = True #def empty_error(): # self.set_err("failed to empty the clipboard") #self.with_clipboard_lock(EmptyClipboard, empty_error) def cleanup(): GlobalFree(buf) glib.idle_add(self.remove_block) ret = [False] def do_set_data(): if not EmptyClipboard(): self.set_err("failed to empty the clipboard") if not SetClipboardData(win32con.CF_UNICODETEXT, buf): #no need to warn here #set_clipboard_text() will try again return log("SetClipboardData(..) done") cleanup() ret[0] = True def set_error(error_text=""): log.error("Error: failed to set clipboard data") if error_text: log.error(" %s", error_text) cleanup() self.with_clipboard_lock(do_set_data, set_error) return ret[0]
def get_text(): formats = [] fmt = 0 while True: fmt = EnumClipboardFormats(fmt) if fmt: formats.append(fmt) else: break log("clipboard formats: %s", csv(format_name(x) for x in formats)) matching = [] for u in (utf8, not utf8): if u: fmts = [win32con.CF_UNICODETEXT] else: fmts = [win32con.CF_TEXT, win32con.CF_OEMTEXT] matching += [fmt for fmt in formats if fmt in fmts] log("supported formats: %s (prefer utf8: %s)", csv(format_name(x) for x in matching), utf8) if not matching: log("no supported formats, only: %s", csv(format_name(x) for x in formats)) errback() return True data_handle = None for fmt in matching: data_handle = GetClipboardData(fmt) log("GetClipboardData(%s)=%#x", format_name(fmt), data_handle or 0) if data_handle: break if not data_handle: log("no valid data handle using %s (may try again)", csv(format_name(x) for x in matching)) return False data = GlobalLock(data_handle) if not data: log("failed to lock data handle %#x (may try again)", data_handle) return False log("got data handle lock %#x for format '%s'", data, format_name(fmt)) try: if fmt == win32con.CF_UNICODETEXT: wstr = cast(data, LPCWSTR) ulen = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, None, 0, None, None) if ulen > MAX_CLIPBOARD_PACKET_SIZE: errback("unicode data is too large: %i bytes" % ulen) return True buf = create_string_buffer(ulen) l = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, byref(buf), ulen, None, None) if l == 0: errback("failed to convert to UTF8: %s" % FormatError(get_last_error())) return True if buf.raw[l - 1:l] == b"\0": s = buf.raw[:l - 1] else: s = buf.raw[:l] log("got %i bytes of UNICODE data: %s", len(s), ellipsizer(s)) if CONVERT_LINE_ENDINGS: v = s.decode("utf8").replace("\r\n", "\n").encode("utf8") else: v = strtobytes(s) callback(v) return True #CF_TEXT or CF_OEMTEXT: astr = cast(data, LPCSTR) s = astr.value.decode("latin1") if CONVERT_LINE_ENDINGS: s = s.replace("\r\n", "\n") b = s.encode("latin1") ulen = len(b) if ulen > MAX_CLIPBOARD_PACKET_SIZE: errback("text data is too large: %i characters" % ulen) return True log("got %i bytes of TEXT data: %s", len(b), ellipsizer(b)) callback(b) return True finally: GlobalUnlock(data)
def got_clipboard_lock(): if COMPRESSED_IMAGES: fmt_name = LPCSTR(img_format.upper().encode("latin1") + b"\0") #ie: "PNG" fmt = RegisterClipboardFormatA(fmt_name) if fmt: data_handle = GetClipboardData(fmt) if data_handle: size = GlobalSize(data_handle) data = GlobalLock(data_handle) log("GetClipboardData(%s)=%#x size=%s, data=%#x", img_format.upper(), data_handle, size, data) if data and size: try: cdata = (c_char * size).from_address(data) finally: GlobalUnlock(data) got_image(bytes(cdata), False) return True data_handle = GetClipboardData(win32con.CF_DIBV5) log("CF_BITMAP=%s", data_handle) data = GlobalLock(data_handle) if not data: log("failed to lock data handle %#x (may try again)", data_handle) return False try: header = cast(data, PBITMAPV5HEADER).contents offset = header.bV5Size + header.bV5ClrUsed * 4 w, h = header.bV5Width, abs(header.bV5Height) bits = header.bV5BitCount log( "offset=%s, width=%i, height=%i, compression=%s", offset, w, h, BI_FORMATS.get(header.bV5Compression, header.bV5Compression)) log("planes=%i, bitcount=%i", header.bV5Planes, bits) log("colorspace=%s", COLOR_PROFILES.get(header.bV5CSType, header.bV5CSType)) #if header.bV5Compression in (BI_JPEG, BI_PNG): # pass if header.bV5Compression != BI_RGB: errback( "cannot handle %s compression yet" % BI_FORMATS.get( header.bV5Compression, header.bV5Compression)) return True if bits == 24: save_format = "RGB" rgb_format = "BGR" stride = roundup(w * 3, 4) elif bits == 32: save_format = "RGBA" rgb_format = "BGRA" stride = w * 4 else: errback( "cannot handle image data with %i bits per pixel yet" % bits) return True img_size = stride * h rgb_data = (c_char * img_size).from_address(data + offset) from PIL import Image, ImageOps img = Image.frombytes(save_format, (w, h), rgb_data, "raw", rgb_format, stride, 1) if header.bV5Height > 0: img = ImageOps.flip(img) buf = BytesIO() img.save(buf, format=save_format) data = buf.getvalue() buf.close() got_image(data, True) return True finally: GlobalUnlock(data)