def get_double_click_distance(): try: return GetSystemMetrics(win32con.SM_CXDOUBLECLK), GetSystemMetrics( win32con.SM_CYDOUBLECLK) except Exception as e: log.warn("failed to get double click distance: %s", e) return -1, -1
def pointer_grab(window, *args): hwnd = get_window_handle(window) grablog("pointer_grab%s window=%s, hwnd=%s", args, window, hwnd) if not hwnd: window._client.pointer_grabbed = None return wrect = RECT() GetWindowRect(hwnd, byref(wrect)) grablog("GetWindowRect(%i)=%s", hwnd, wrect) if DwmGetWindowAttribute: # Vista & 7 stuff rect = RECT() DWMWA_EXTENDED_FRAME_BOUNDS = 9 DwmGetWindowAttribute(HWND(hwnd), DWORD(DWMWA_EXTENDED_FRAME_BOUNDS), byref(rect), sizeof(rect)) #wx1,wy1,wx2,wy2 = rect.left, rect.top, rect.right, rect.bottom grablog("DwmGetWindowAttribute: DWMWA_EXTENDED_FRAME_BOUNDS(%i)=%s", hwnd, (rect.left, rect.top, rect.right, rect.bottom)) bx = GetSystemMetrics(win32con.SM_CXSIZEFRAME) by = GetSystemMetrics(win32con.SM_CYSIZEFRAME) top = by style = GetWindowLongW(hwnd, win32con.GWL_STYLE) if style & win32con.WS_CAPTION: top += GetSystemMetrics(win32con.SM_CYCAPTION) grablog(" window style=%s, SIZEFRAME=%s, top=%i", style_str(style), (bx, by), top) coords = wrect.left + bx, wrect.top + top, wrect.right - bx, wrect.bottom - by clip = RECT(*coords) r = ClipCursor(clip) grablog("ClipCursor%s=%s", coords, r) window._client.pointer_grabbed = window._id
def set_icon_from_data(self, pixels, has_alpha, w, h, rowstride, options=None): #this is convoluted but it works.. log("set_icon_from_data%s", ("%s pixels" % len(pixels), has_alpha, w, h, rowstride, options)) from PIL import Image #@UnresolvedImport if has_alpha: img_format = "RGBA" else: img_format = "RGBX" rgb_format = typedict(options or {}).strget("rgb_format", "RGBA") img = Image.frombuffer(img_format, (w, h), pixels, "raw", rgb_format, rowstride, 1) assert img, "failed to load image from buffer (%i bytes for %ix%i %s)" % ( len(pixels), w, h, rgb_format) #apparently, we have to use SM_CXSMICON (small icon) and not SM_CXICON (regular size): icon_w = GetSystemMetrics(win32con.SM_CXSMICON) icon_h = GetSystemMetrics(win32con.SM_CYSMICON) if w != icon_w or h != icon_h: log("resizing tray icon to %ix%i", icon_w, icon_h) img = img.resize((icon_w, icon_h), Image.ANTIALIAS) rowstride = w * 4 hicon = image_to_ICONINFO(img) self.do_set_icon(hicon, DestroyIcon) UpdateWindow(self.hwnd) self.reset_function = (self.set_icon_from_data, pixels, has_alpha, w, h, rowstride)
def get_fixed_cursor_size(): try: w = GetSystemMetrics(win32con.SM_CXCURSOR) h = GetSystemMetrics(win32con.SM_CYCURSOR) return w, h except Exception as e: log.warn("failed to get window frame size information: %s", e) #best to try to use a limit anyway: return 32, 32
def set_icon_from_data(self, pixels, has_alpha, w, h, rowstride, options={}): #this is convoluted but it works.. log("set_icon_from_data%s", ("%s pixels" % len(pixels), has_alpha, w, h, rowstride, options)) from PIL import Image #@UnresolvedImport if has_alpha: img_format = "RGBA" else: img_format = "RGBX" rgb_format = options.get("rgb_format", "RGBA") img = Image.frombuffer(img_format, (w, h), pixels, "raw", rgb_format, rowstride, 1) assert img, "failed to load image from buffer (%i bytes for %ix%i %s)" % ( len(pixels), w, h, rgb_format) #apparently, we have to use SM_CXSMICON (small icon) and not SM_CXICON (regular size): icon_w = GetSystemMetrics(win32con.SM_CXSMICON) icon_h = GetSystemMetrics(win32con.SM_CYSMICON) if w != icon_w or h != icon_h: log("resizing tray icon to %ix%i", icon_w, icon_h) img = img.resize((w, h), Image.ANTIALIAS) bitmap = 0 mask = 0 try: from xpra.codecs.argb.argb import rgba_to_bgra #@UnresolvedImport bgra = memoryview_to_bytes(rgba_to_bgra(img.tobytes())) bitmap = rgba_to_bitmap(bgra, icon_w, icon_h) mask = CreateBitmap(icon_w, icon_h, 1, 1, None) iconinfo = ICONINFO() iconinfo.fIcon = True iconinfo.hbmMask = mask iconinfo.hbmColor = bitmap hicon = CreateIconIndirect(byref(iconinfo)) log("CreateIconIndirect()=%s", hicon) if not hicon: raise ctypes.WinError(ctypes.get_last_error()) except Exception: log.error("Error: failed to set tray icon", exc_info=True) hicon = FALLBACK_ICON finally: if mask: DeleteObject(mask) if bitmap: DeleteObject(bitmap) self.do_set_icon(hicon) UpdateWindow(self.hwnd) self.reset_function = (self.set_icon_from_data, pixels, has_alpha, w, h, rowstride)
def get_window_frame_sizes(): try: #normal resizable windows: rx = GetSystemMetrics(win32con.SM_CXSIZEFRAME) ry = GetSystemMetrics(win32con.SM_CYSIZEFRAME) #non-resizable windows: fx = GetSystemMetrics(win32con.SM_CXFIXEDFRAME) fy = GetSystemMetrics(win32con.SM_CYFIXEDFRAME) #min size: mx = GetSystemMetrics(win32con.SM_CXMIN) my = GetSystemMetrics(win32con.SM_CYMIN) #size of menu bar: m = GetSystemMetrics(win32con.SM_CYMENU) #border: b = GetSystemMetrics(win32con.SM_CYBORDER) #caption: c = GetSystemMetrics(win32con.SM_CYCAPTION) return { "normal": (rx, ry), "fixed": (fx, fy), "minimum": (mx, my), "menu-bar": m, "border": b, "caption": c, "offset": (rx, ry + c), #left, right, top, bottom: "frame": (rx, rx, ry + c, ry), } except Exception as e: log.warn("failed to get window frame size information: %s", e) return None
def move_cb(self, *args): pos = wintypes.POINT() GetCursorPos(byref(pos)) x, y = pos.x, pos.y w = GetSystemMetrics(win32con.SM_CXSMICON) h = GetSystemMetrics(win32con.SM_CYSMICON) x += self.offset_x y += self.offset_y log("move_cb%s x=%s, y=%s, size=%i, %i", args, x, y, w, h) self.recalculate_geometry(x, y, w, h) if self.mouseover_cb: self.mouseover_cb(x, y)
def notify(hwnd, app_id, title, message, timeout=5000, icon=None): log("notify%s", (hwnd, app_id, title, message, timeout, icon)) if timeout <= 0: timeout = 5000 szInfo = chop_string(message, 255, False) #prevent overflow szInfoTitle = chop_string(title, 63) hicon = 0 if icon: try: from PIL import Image from xpra.codecs.pillow.decoder import open_only w, h, data = icon[1:4] img = open_only(data) from xpra.platform.win32.win32_NotifyIcon import image_to_ICONINFO iw = GetSystemMetrics(SM_CXSMICON) ih = GetSystemMetrics(SM_CYSMICON) if w != iw or h != ih: img = img.resize((iw, ih), Image.ANTIALIAS) log("notification icon resized to %s", img.size) hicon = image_to_ICONINFO(img) log("notify: image_to_ICONINFO(%s)=%#x", img, hicon) except Exception as e: log("notify%s", (hwnd, app_id, title, message, timeout, icon), exc_info=True) log.error("Error: failed to set notification icon:") log.error(" %s", e) from xpra.platform.win32.win32_NotifyIcon import Shell_NotifyIconA, XPRA_GUID, getNOTIFYICONDATAClass nc = getNOTIFYICONDATAClass(tip_size=128) nid = nc() nid.cbSize = sizeof(nc) nid.hWnd = hwnd nid.uID = app_id nid.uFlags = NIF_INFO nid.guidItem = XPRA_GUID try: nid.szInfo = szInfo except: nid.szInfo = szInfo.decode() v = chop_string(title, 63) try: nid.szInfoTitle = szInfoTitle except: nid.szInfoTitle = szInfoTitle.decode() nid.uVersion = timeout nid.dwInfoFlags = NIIF_INFO if hicon: nid.hIcon = nid.hBalloonIcon = hicon nid.dwInfoFlags = NIIF_USER Shell_NotifyIconA(NIM_MODIFY, byref(nid)) log("notify using %s", Shell_NotifyIconA)
def __init__(self): GTKShadowServerBase.__init__(self) self.keycodes = {} self.cursor_handle = None self.cursor_data = None if GetSystemMetrics(win32con.SM_SAMEDISPLAYFORMAT) == 0: raise InitException( "all the monitors must use the same display format") el = get_win32_event_listener() from xpra.net.bytestreams import set_continue_wait #on win32, we want to wait just a little while, #to prevent servers spinning wildly on non-blocking sockets: set_continue_wait(5) #TODO: deal with those messages? el.add_event_callback(win32con.WM_POWERBROADCAST, self.power_broadcast_event) #el.add_event_callback(WM_WTSSESSION_CHANGE, self.session_change_event) #these are bound to callbacks in the client, #but on the server we just ignore them: el.ignore_events.update({ win32con.WM_ACTIVATEAPP: "WM_ACTIVATEAPP", win32con.WM_MOVE: "WM_MOVE", win32con.WM_INPUTLANGCHANGE: "WM_INPUTLANGCHANGE", win32con.WM_WININICHANGE: "WM_WININICHANGE", }) #non-blocking server sockets (TCP and named pipes): from xpra.net.bytestreams import CONTINUE_ERRNO import errno CONTINUE_ERRNO[ errno.WSAEWOULDBLOCK] = "WSAEWOULDBLOCK" #@UndefinedVariable
def get_mouse_config(): #not all are present in win32con? SM_CMOUSEBUTTONS = 43 SM_CXDRAG = 68 SM_CYDRAG = 69 SM_MOUSEPRESENT = 19 SM_MOUSEHORIZONTALWHEELPRESENT = 91 SM_SWAPBUTTON = 23 SM_MOUSEWHEELPRESENT = 75 wheel_info = { "vertical": GetSystemMetrics(SM_MOUSEWHEELPRESENT), "horizontal": GetSystemMetrics(SM_MOUSEHORIZONTALWHEELPRESENT), } SPI_GETWHEELSCROLLLINES = 104 SPI_GETWHEELSCROLLCHARS = 0x006C SPI_GETMOUSEVANISH = 4128 #rate for each direction: _add_SPI(wheel_info, SPI_GETWHEELSCROLLLINES, "lines", int, 3) _add_SPI(wheel_info, SPI_GETWHEELSCROLLCHARS, "chars", int, 3) info = { "present": bool(GetSystemMetrics(SM_MOUSEPRESENT)), "wheel": wheel_info, "buttons": GetSystemMetrics(SM_CMOUSEBUTTONS), "swap": bool(GetSystemMetrics(SM_SWAPBUTTON)), "drag": { "x": GetSystemMetrics(SM_CXDRAG), "y": GetSystemMetrics(SM_CYDRAG), } } _add_SPI(info, SPI_GETMOUSEVANISH, "vanish", bool, False) return info
def notify(hwnd, app_id, title, message, timeout=5000, icon=None): nid = PyNOTIFYICONDATA() nid.hWnd = hwnd nid.uID = app_id nid.uFlags = NIF_INFO nid.guidItem = XPRA_GUID_BYTES nid.szInfo = chop_string(message, 255, False) #prevent overflow nid.szInfoTitle = chop_string(title, 63) if timeout <= 0: timeout = 5000 nid.uTimeoutOrVersion = timeout #if no icon is supplied, we can use: # NIIF_INFO, NIIF_WARNING or NIIF_ERROR nid.dwInfoFlags = NIIF_INFO if icon: try: from PIL import Image from xpra.codecs.pillow.decoder import open_only w, h, data = icon[1:4] img = open_only(data) from xpra.platform.win32.win32_NotifyIcon import image_to_ICONINFO iw = GetSystemMetrics(SM_CXSMICON) ih = GetSystemMetrics(SM_CYSMICON) if w != iw or h != ih: img = img.resize((iw, ih), Image.ANTIALIAS) log("notification icon resized to %s", img.size) hicon = image_to_ICONINFO(img) log("notify: image_to_ICONINFO(%s)=%#x", img, hicon) nid.hIcon = hicon nid.hBalloonIcon = hicon except Exception as e: log("notify%s", (hwnd, app_id, title, message, timeout, icon), exc_info=True) log.error("Error: failed to set notification icon:") log.error(" %s", e) else: nid.dwInfoFlags = NIIF_USER Shell_NotifyIcon = windll.shell32.Shell_NotifyIcon Shell_NotifyIcon(NIM_MODIFY, nid.pack()) log("notify using %s", Shell_NotifyIcon)
def init_dpi(): log("init_dpi() DPI_AWARE=%s, DPI_AWARENESS=%s", DPI_AWARE, DPI_AWARENESS) #tell win32 we handle dpi if not DPI_AWARE: log.warn("SetProcessDPIAware not set due to environment override") return w, h = GetSystemMetrics(0), GetSystemMetrics(1) try: SetProcessDPIAware = user32.SetProcessDPIAware dpiaware = SetProcessDPIAware() log("SetProcessDPIAware: %s()=%s", SetProcessDPIAware, dpiaware) assert dpiaware != 0 except Exception as e: log.warn("SetProcessDPIAware() failed: %s", e) if DPI_AWARENESS <= 0: log.warn("SetProcessDPIAwareness not set due to environment override") return try: Process_System_DPI_Aware = 1 Process_DPI_Unaware = 0 Process_Per_Monitor_DPI_Aware = 2 assert DPI_AWARENESS in (Process_System_DPI_Aware, Process_DPI_Unaware, Process_Per_Monitor_DPI_Aware) SetProcessDpiAwarenessInternal = user32.SetProcessDpiAwarenessInternal dpiawareness = SetProcessDpiAwarenessInternal(DPI_AWARENESS) log("SetProcessDPIAwareness: %s(%s)=%s", SetProcessDpiAwarenessInternal, DPI_AWARENESS, dpiawareness) assert dpiawareness == 0 except Exception as e: log("SetProcessDpiAwarenessInternal(%s) failed: %s", DPI_AWARENESS, e) log(" (not available on MS Windows before version 8.1)") actual_w, actual_h = GetSystemMetrics(0), GetSystemMetrics(1) if actual_w != w or actual_h != h: #MS Windows is going to lie to us.. global DPI_SCALING DPI_SCALING = round(100 * ((actual_w / w) + (actual_h / h))) / 200 log("DPI_SCALING=%s", DPI_SCALING)
def __init__(self): super().__init__() self.keycodes = {} self.cursor_handle = None self.cursor_data = None self.cursor_errors = [0, 0] if GetSystemMetrics(win32con.SM_SAMEDISPLAYFORMAT)==0: raise InitException("all the monitors must use the same display format") el = get_win32_event_listener() #TODO: deal with those messages? el.add_event_callback(win32con.WM_POWERBROADCAST, self.power_broadcast_event) #el.add_event_callback(WM_WTSSESSION_CHANGE, self.session_change_event) #these are bound to callbacks in the client, #but on the server we just ignore them: el.ignore_events.update({ win32con.WM_ACTIVATEAPP : "WM_ACTIVATEAPP", win32con.WM_MOVE : "WM_MOVE", win32con.WM_INPUTLANGCHANGE : "WM_INPUTLANGCHANGE", win32con.WM_WININICHANGE : "WM_WININICHANGE", })
def get_size(self): w = GetSystemMetrics(win32con.SM_CXSMICON) h = GetSystemMetrics(win32con.SM_CYSMICON) return w, h
def get_root_window_size(): w = GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) h = GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) return w, h
def get_window_min_size(): return GetSystemMetrics(win32con.SM_CXMIN), GetSystemMetrics( win32con.SM_CYMIN)
def get_virtualscreenmetrics(): dx = GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) dy = GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) dw = GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) dh = GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) return dx, dy, dw, dh