def got_token(self, targets, target_data=None, claim=True, _synchronous_client=False): # the remote end now owns the clipboard self.cancel_emit_token() if not self._enabled: return self._got_token_events += 1 log( "got token, selection=%s, targets=%s, target data=%s, claim=%s, can-receive=%s", self._selection, targets, target_data, claim, self._can_receive) if self._can_receive: self.targets = _filter_targets(targets or ()) self.target_data = target_data or {} if targets: self.got_contents("TARGETS", "ATOM", 32, targets) if target_data: for target, td_def in target_data.items(): dtype, dformat, data = td_def dtype = bytestostr(dtype) self.got_contents(target, dtype, dformat, data) #since we claim to be greedy #the peer should have sent us the target and target_data, #if not then request it: if not targets: self.send_clipboard_request_handler(self, self._selection, "TARGETS") if not claim: log("token packet without claim, not setting the token flag") return self._have_token = True if self._can_receive: self.claim()
def get_contents(self, target, got_contents): log("get_contents%s", (target, got_contents)) if target == "TARGETS": #we only support text at the moment: got_contents("ATOM", 32, [ "TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING" ]) return if target not in ("TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING"): #we don't know how to handle this target, #return an empty response: got_contents(target, 8, b"") return def got_text(text): log("got_text(%s)", ellipsizer(text)) got_contents(target, 8, text) def errback(error_text=""): log.error("Error: failed to get clipboard data") if error_text: log.error(" %s", error_text) got_contents(target, 8, b"") self.get_clipboard_text(got_text, errback)
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 got_image(img_data, trusted=False): log("got_image(%i bytes)", len(img_data)) img_data = self.filter_data(dtype=target, dformat=8, data=img_data, trusted=trusted) got_contents(target, 8, img_data)
def with_clipboard_lock(self, success_callback, failure_callback, retries=RETRY, delay=DELAY): log("with_clipboard_lock%s", (success_callback, failure_callback, retries, delay)) r = OpenClipboard(self.window) if r: log("OpenClipboard(%#x)=%s", self.window, r) try: r = success_callback() log("%s()=%s", success_callback, r) if r: return finally: r = CloseClipboard() log("CloseClipboard()=%s", r) e = WinError(GetLastError()) owner = GetClipboardOwner() log("OpenClipboard(%#x)=%s, current owner: %s", self.window, e, get_owner_info(owner, self.window)) if retries <= 0: failure_callback( "OpenClipboard: too many failed attempts, giving up") return #try again later: GLib.timeout_add(delay, self.with_clipboard_lock, success_callback, failure_callback, retries - 1, delay + 5)
def set_clipboard_text(self, text, retry=5): log("set_clipboard_text(%s, %i)", text, retry) r = self.do_set_clipboard_text(text) if not r: if retry > 0: glib.timeout_add(5, self.set_clipboard_text, text, retry - 1) else: self.set_err("failed to set clipboard buffer")
def got_contents(self, target, dtype=None, dformat=None, data=None): #if this is the special target 'TARGETS', cache the result: if target == "TARGETS" and dtype == "ATOM" and dformat == 32: self.targets = _filter_targets(data) #TODO: tell system what targets we have log("got_contents: tell OS we have %s", csv(self.targets)) if dformat == 8 and dtype in TEXT_TARGETS: log("we got a byte string: %s", data) self.set_clipboard_text(data)
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 wnd_proc(self, hwnd, msg, wparam, lparam): r = DefWindowProcW(hwnd, msg, wparam, lparam) if msg in CLIPBOARD_EVENTS: log("clipboard event: %s", CLIPBOARD_EVENTS.get(msg)) if msg == WM_CLIPBOARDUPDATE: for proxy in self._clipboard_proxies.values(): if not proxy._block_owner_change: proxy.schedule_emit_token() return r
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 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_clipboard_formats(): formats = [] fmt = 0 while True: fmt = EnumClipboardFormats(fmt) if fmt: formats.append(fmt) else: break log("get_clipboard formats()=%s", csv(format_name(x) for x in formats)) return formats
def got_clipboard_lock(): EmptyClipboard() c = 0 for fmt, handle in image_formats.items(): log("do_set_clipboard_image: %s", format_name(fmt)) r = SetClipboardData(fmt, handle) if not r: e = WinError(GetLastError()) log("SetClipboardData(%s, %#x)=%s (%s)", format_name(fmt), handle, r, e) else: c += 1 return bool(c)
def got_clipboard_lock(): formats = get_clipboard_formats() fnames = format_names(formats) targets = [] if win32con.CF_UNICODETEXT in formats: targets += ["text/plain;charset=utf-8", "UTF8_STRING", "CF_UNICODETEXT"] if win32con.CF_TEXT in formats or win32con.CF_OEMTEXT in formats: targets += ["TEXT", "STRING", "text/plain"] #if any(x in fnames for x in ("CF_DIB", "CF_BITMAP", "CF_DIBV5")): if "CF_DIBV5" in fnames: targets += ["image/png", "image/jpeg"] log("targets(%s)=%s", csv(fnames), csv(targets)) got_contents("ATOM", 32, targets) return True
def get_contents(self, target, got_contents): log("get_contents%s", (target, got_contents)) if target=="TARGETS": def got_clipboard_lock(): formats = get_clipboard_formats() fnames = format_names(formats) targets = [] if win32con.CF_UNICODETEXT in formats: targets += ["text/plain;charset=utf-8", "UTF8_STRING", "CF_UNICODETEXT"] if win32con.CF_TEXT in formats or win32con.CF_OEMTEXT in formats: targets += ["TEXT", "STRING", "text/plain"] #if any(x in fnames for x in ("CF_DIB", "CF_BITMAP", "CF_DIBV5")): if "CF_DIBV5" in fnames: targets += ["image/png", "image/jpeg"] log("targets(%s)=%s", csv(fnames), csv(targets)) got_contents("ATOM", 32, targets) return True def lockerror(_message): #assume text: got_contents("ATOM", 32, ["TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING"]) self.with_clipboard_lock(got_clipboard_lock, lockerror) return def nodata(*args): log("nodata%s", args) got_contents(target, 8, b"") if target in ("image/png", "image/jpeg"): def got_image(img_data, trusted=False): log("got_image(%i bytes)", len(img_data)) img_data = self.filter_data(dtype=target, dformat=8, data=img_data, trusted=trusted) got_contents(target, 8, img_data) img_format = target.split("/")[-1].upper() #ie: "PNG" or "JPEG" self.get_clipboard_image(img_format, got_image, nodata) return if target not in ("TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING"): #we don't know how to handle this target, #return an empty response: nodata() return def got_text(text): log("got_text(%s)", ellipsizer(text)) got_contents(target, 8, text) def errback(error_text=""): log("errback(%s)", error_text) if error_text: log.warn("Warning: failed to get clipboard data as text") log.warn(" %s", error_text) got_contents(target, 8, b"") utf8 = target.lower().find("utf")>=0 self.get_clipboard_text(utf8, got_text, errback)
def wnd_proc(self, hwnd, msg, wparam, lparam): r = DefWindowProcW(hwnd, msg, wparam, lparam) if msg in CLIPBOARD_EVENTS: owner = GetClipboardOwner() log("clipboard event: %s, current owner: %s", CLIPBOARD_EVENTS.get(msg), get_owner_info(owner, self.window)) if msg == WM_CLIPBOARDUPDATE and owner != self.window: owner = GetClipboardOwner() owner_info = get_owner_info(owner, self.window) if is_blacklisted(owner_info): #ie: don't try to sync from VirtualBox log("CLIPBOARDUPDATE coming from '%s' ignored", owner_info) return r for proxy in self._clipboard_proxies.values(): if not proxy._block_owner_change: proxy.schedule_emit_token() return r
def w_to_utf8(data): wstr = cast(data, LPCWSTR) ulen = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, None, 0, None, None) if ulen>MAX_CLIPBOARD_PACKET_SIZE: raise Exception("unicode data is too large: %i bytes" % ulen) buf = create_string_buffer(ulen) l = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, byref(buf), ulen, None, None) if l==0: raise Exception("failed to convert to UTF8: %s" % FormatError(get_last_error())) 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: return s.decode("utf8").replace("\r\n", "\n").encode("utf8") return strtobytes(s)
def with_clipboard_lock(self, success_callback, failure_callback, retries=5, delay=5): r = OpenClipboard(self.window) log("OpenClipboard(%#x)=%s", self.window, r) if r: try: success_callback() return finally: CloseClipboard() if GetLastError()!=ERROR_ACCESS_DENIED: failure_callback("OpenClipboard: access denied") return log("clipboard lock: access denied") if retries<=0: failure_callback("OpenClipboard: too many failed attemps, giving up") return #try again later: glib.timeout_add(delay, self.with_clipboard_lock, success_callback, failure_callback, retries-1, delay)
def init_window(self): log("Win32Clipboard.init_window() creating clipboard window class and instance" ) self.wndclass = WNDCLASSEX() self.wndclass.cbSize = sizeof(WNDCLASSEX) self.wndclass.lpfnWndProc = WNDPROC(self.wnd_proc) self.wndclass.style = win32con.CS_GLOBALCLASS self.wndclass.hInstance = GetModuleHandleA(0) self.wndclass.lpszClassName = clipboard_window_class_name self.wndclass_handle = RegisterClassExW(byref(self.wndclass)) log("RegisterClassExA(%s)=%#x", self.wndclass.lpszClassName, self.wndclass_handle) if self.wndclass_handle == 0: raise WinError() style = win32con.WS_CAPTION #win32con.WS_OVERLAPPED self.window = CreateWindowExW(0, self.wndclass_handle, u"Clipboard", style, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, win32con.HWND_MESSAGE, 0, self.wndclass.hInstance, None) log("clipboard window=%s", self.window) if not self.window: raise WinError() if not AddClipboardFormatListener(self.window): log.warn("Warning: failed to setup clipboard format listener") log.warn(" %s", get_last_error())
def with_clipboard_lock(self, success_callback, failure_callback, retries=5, delay=5): r = OpenClipboard(self.window) if r: log("OpenClipboard(%#x)=%s", self.window, r) try: success_callback() return finally: CloseClipboard() log("OpenClipboard(%#x)=%s, owner=%#x", self.window, WinError(GetLastError()), GetClipboardOwner()) if retries <= 0: failure_callback( "OpenClipboard: too many failed attemps, giving up") return #try again later: glib.timeout_add(delay, self.with_clipboard_lock, success_callback, failure_callback, retries - 1, delay + 5)
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_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 got_contents(self, target, dtype=None, dformat=None, data=None): #if this is the special target 'TARGETS', cache the result: if target=="TARGETS" and dtype=="ATOM" and dformat==32: self.targets = _filter_targets(data) #TODO: tell system what targets we have log("got_contents: tell OS we have %s", csv(self.targets)) image_formats = tuple(x for x in ("image/png", "image/jpeg") if x in self.targets) if image_formats: #request it: self.send_clipboard_request_handler(self, self._selection, image_formats[0]) elif dformat==8 and dtype in TEXT_TARGETS: log("we got a byte string: %s", data) self.set_clipboard_text(data) elif dformat==8 and dtype.startswith("image/"): img_format = dtype.split("/")[-1] #ie: 'png' self.set_clipboard_image(img_format, data) else: log("no handling: target=%s, dtype=%s, dformat=%s, data=%s", target, dtype, dformat, ellipsizer(data))
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 got_text(text): log("got_text(%s)", ellipsizer(text)) got_contents(target, 8, text)
CLIPBOARD_FORMATS = { win32con.CF_BITMAP: "CF_BITMAP", win32con.CF_DIB: "CF_DIB", win32con.CF_DIBV5: "CF_DIBV5", win32con.CF_ENHMETAFILE: "CF_ENHMETAFILE", win32con.CF_METAFILEPICT: "CF_METAFILEPICT", win32con.CF_OEMTEXT: "CF_OEMTEXT", win32con.CF_TEXT: "CF_TEXT", win32con.CF_UNICODETEXT: "CF_UNICODETEXT", } RETRY = envint("XPRA_CLIPBOARD_RETRY", 5) DELAY = envint("XPRA_CLIPBOARD_INITIAL_DELAY", 10) CONVERT_LINE_ENDINGS = envbool("XPRA_CONVERT_LINE_ENDINGS", True) log("win32 clipboard: RETRY=%i, DELAY=%i, CONVERT_LINE_ENDINGS=%s", RETRY, DELAY, CONVERT_LINE_ENDINGS) #can be used to blacklist problematic clipboard peers: #ie: VBoxTray.exe BLACKLISTED_CLIPBOARD_CLIENTS = [ x for x in os.environ.get("XPRA_BLACKLISTED_CLIPBOARD_CLIENTS", "").split( ",") if x ] log("BLACKLISTED_CLIPBOARD_CLIENTS=%s", BLACKLISTED_CLIPBOARD_CLIENTS) clipboard_window_class_name = "XpraWin32Clipboard" def is_blacklisted(owner_info): return any(owner_info.find(x) >= 0 for x in BLACKLISTED_CLIPBOARD_CLIENTS)
def empty_clipboard(self): r = EmptyClipboard() log("EmptyClipboard()=%s", r) 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()
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 errback(error_text=""): log("errback(%s)", error_text) if error_text: log.warn("Warning: failed to get clipboard data") log.warn(" %s", error_text) got_contents(target, 8, b"")