xpra icon
Bug tracker and wiki

Ticket #812: clipboard-win32.patch

File clipboard-win32.patch, 8.7 KB (added by totaamwin32, 4 months ago)

win32 native clipboard work in progress

  • tests/xpra/clipboard/test_clipboard_helper.py

     
     1#!/usr/bin/env python
     2
     3from xpra.platform import program_context
     4from xpra.platform.win32.clipboard import Win32Clipboard, log
     5
     6def main():
     7        with program_context("Clipboard-Test", "Clipboard Test Tool"):
     8                def send_packet_cb(*args):
     9                        print("send_packet_cb%s" % (args,))
     10                def progress_cb(*args):
     11                        print("progress_cb%s" % (args,))
     12                try:
     13                        log("creating %s", Win32Clipboard)
     14                        c = Win32Clipboard(send_packet_cb, progress_cb)
     15                        log("sending all tokens")
     16                        c.enable_selections(["CLIPBOARD"])
     17                        c.set_direction(True, True)
     18                        c.send_all_tokens()
     19                        log("faking clipboard request")
     20                        c._process_clipboard_request(["clipboard-request", 1, "CLIPBOARD", "TARGETS"])
     21                        def set_contents():
     22                               
     23                                pass
     24                        #c._process_clipboard_token(["clipboard-token", "CLIPBOARD", ])
     25                        #_process_clipboard_contents(self, packet):
     26                        from xpra.gtk_common.gobject_compat import import_glib
     27                        glib = import_glib()
     28                        main_loop = glib.MainLoop()
     29                        glib.timeout_add(1000, set_contents)
     30                        log("main loop=%s", main_loop)
     31                        main_loop.run()
     32                except:
     33                        log.error("", exc_info=True)
     34
     35if __name__ == "__main__":
     36        main()
  • xpra/platform/win32/clipboard.py

    Property changes on: tests/xpra/clipboard/test_clipboard_helper.py
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1# This file is part of Xpra.
     2# Copyright (C) 2019 Antoine Martin <antoine@xpra.org>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
     5
     6from ctypes import (
     7    sizeof, byref, cast,
     8    get_last_error, create_string_buffer, WinError,
     9    )
     10from xpra.platform.win32.common import (
     11    WNDCLASSEX, GetLastError, ERROR_ACCESS_DENIED, WNDPROC, LPCWSTR,
     12    DefWindowProcW,
     13    GetModuleHandleA, RegisterClassExW,
     14    CreateWindowExW, DestroyWindow,
     15    OpenClipboard, EmptyClipboard, CloseClipboard, GetClipboardData,
     16    GlobalLock, GlobalUnlock, GlobalAlloc, GlobalFree,
     17    WideCharToMultiByte, MultiByteToWideChar,
     18    SetClipboardData)
     19from xpra.platform.win32 import win32con
     20from xpra.clipboard.clipboard_core import (
     21    ClipboardProtocolHelperCore, ClipboardProxyCore, log,
     22    )
     23from xpra.gtk_common.gobject_compat import import_glib
     24
     25glib = import_glib()
     26
     27
     28CP_UTF8 = 65001
     29MB_ERR_INVALID_CHARS = 0x00000008
     30GMEM_MOVEABLE = 0x0002
     31
     32#initialize the window we will use
     33#for communicating with the OS clipboard API:
     34
     35class Win32Clipboard(ClipboardProtocolHelperCore):
     36    """
     37        Use Native win32 API to access the clipboard
     38    """
     39    def __init__(self, send_packet_cb, progress_cb=None, **kwargs):
     40        log("creating clipboard window")
     41        class_name = "XpraWin32Clipboard"
     42        wndclass = WNDCLASSEX()
     43        wndclass.cbSize = sizeof(WNDCLASSEX)
     44        wndclass.lpfnWndProc = WNDPROC(self.wnd_proc)
     45        wndclass.style =  win32con.CS_GLOBALCLASS
     46        wndclass.hInstance = GetModuleHandleA(0)
     47        wndclass.lpszClassName = class_name
     48        wndclass_handle = RegisterClassExW(byref(wndclass))
     49        log("RegisterClassExA(%s)=%#x", wndclass.lpszClassName, wndclass_handle)
     50        if wndclass_handle==0:
     51            raise WinError(get_last_error())
     52        style = win32con.WS_CAPTION   #win32con.WS_OVERLAPPED
     53        self.window = CreateWindowExW(0, wndclass_handle, u"Clipboard", style,
     54                                      0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
     55                                      win32con.HWND_MESSAGE, 0, wndclass.hInstance, None)
     56        log("clipboard window=%s", self.window)
     57        if not self.window:
     58            raise WinError(get_last_error())
     59        ClipboardProtocolHelperCore.__init__(self, send_packet_cb, progress_cb)
     60
     61    def wnd_proc(self, hwnd, msg, wparam, lparam):
     62        log("wnd_proc%s", (hwnd, msg, wparam, lparam))
     63        r = DefWindowProcW(hwnd, msg, wparam, lparam)
     64        log("DefWindowProc%s=%s", (hwnd, msg, wparam, lparam), r)
     65        return r
     66
     67    def cleanup(self):
     68        ClipboardProtocolHelperCore.cleanup(self)
     69        w = self.window
     70        if w:
     71            self.window = None
     72            DestroyWindow(w)
     73
     74    def make_proxy(self, selection):
     75        return Win32ClipboardProxy(self.window, selection)
     76
     77
     78class Win32ClipboardProxy(ClipboardProxyCore):
     79    def __init__(self, window, selection):
     80        self.window = window
     81        ClipboardProxyCore.__init__(self, selection)
     82
     83    def with_clipboard_lock(self, success_callback, failure_callback, retries=5, delay=5):
     84        r = OpenClipboard(self.window)
     85        if r:
     86            try:
     87                success_callback()
     88                return
     89            finally:
     90                CloseClipboard()
     91        if GetLastError()!=ERROR_ACCESS_DENIED:
     92            failure_callback()
     93            return
     94        if retries<=0:
     95            failure_callback()
     96            return
     97        #try again later:
     98        glib.timeout_add(delay, self.with_clipboard_lock,
     99                         success_callback, failure_callback, retries-1, delay)
     100
     101    def clear(self):
     102        def clear_error():
     103            log.error("Error: failed to clear the clipboard")
     104        self.with_clipboard_lock(EmptyClipboard, clear_error)
     105
     106    def get_contents(self, target, got_contents):
     107        def got_text(text):
     108            log.info("got_text(%s)", text)
     109            got_contents("bytes", 8, text.decode("utf-8"))
     110        def errback():
     111            log.error("Error: failed to get clipboard data")
     112            import traceback
     113            traceback.print_stack()
     114        self.get_clipboard_text(got_text, errback)
     115
     116    def get_clipboard_text(self, callback, errback):
     117        def do_get_data():
     118            data_handle = GetClipboardData(win32con.CF_UNICODETEXT)
     119            if not data_handle:
     120                errback()
     121                return
     122            data = GlobalLock(data_handle)
     123            if not data:
     124                errback()
     125                return
     126            try:
     127                buf = create_string_buffer(1024)
     128                wstr = cast(data, LPCWSTR)
     129                l = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, byref(buf), 1024, None, None)
     130                if l>0:
     131                    s = buf.raw[:l]
     132                    if s and s[-1:]=="\0":
     133                        s = s[:-1]
     134                    log.info("got data: %s", s)
     135                    callback(s)
     136                else:
     137                    errback()
     138            finally:
     139                GlobalUnlock(data)
     140               
     141        self.with_clipboard_lock(do_get_data, errback)
     142
     143    def set_clipboard_text(self, text):
     144        #convert to wide char
     145        #get the length in wide chars:
     146        wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, len(text), None, 0)
     147        if not wlen:
     148            return
     149        #allocate some memory for it:
     150        buf = GlobalAlloc(GMEM_MOVEABLE, wlen*2)
     151        if not buf:
     152            return
     153        locked = GlobalLock(buf)
     154        if not locked:
     155            GlobalFree(buf)
     156            return
     157        try:
     158            r = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, len(text), locked, wlen)
     159            if not r:
     160                return
     161        finally:
     162            GlobalUnlock(locked)
     163
     164        def do_set_data():
     165            try:
     166                EmptyClipboard()
     167                if not SetClipboardData(win32con.CF_UNICODETEXT, buf):
     168                    return
     169                #done!
     170            finally:
     171                GlobalFree(buf)
     172        def set_error():
     173            GlobalFree(buf)
     174            log.error("Error: failed to set clipboard data")
     175        self.with_clipboard_lock(do_set_data, set_error)
     176
     177    def __repr__(self):
     178        return "Win32ClipboardProxy"
  • xpra/platform/win32/features.py

     
    1010
    1111CLIPBOARDS=["CLIPBOARD"]
    1212CLIPBOARD_GREEDY = True
    13 CLIPBOARD_NATIVE_CLASS = "xpra.clipboard.translated_clipboard.TranslatedClipboardProtocolHelper"
     13#CLIPBOARD_NATIVE_CLASS = "xpra.clipboard.translated_clipboard.TranslatedClipboardProtocolHelper"
     14CLIPBOARD_NATIVE_CLASS = "xpra.platform.win32.clipboard.Win32Clipboard"
    1415
    1516EXECUTABLE_EXTENSION = "exe"
    1617