xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #1354: window_hooks.2.py

File window_hooks.2.py, 5.0 KB (added by Antoine Martin, 5 years ago)

just continue to run the other minmax hooks after our one

Line 
1# This file is part of Xpra.
2# Copyright (C) 2011 Serviware (Arthur Huillet, <ahuillet@serviware.com>)
3# Copyright (C) 2010-2016 Antoine Martin <antoine@devloop.org.uk>
4# Copyright (C) 2008, 2010 Nathaniel Smith <njs@pobox.com>
5# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
6# later version. See the file COPYING for details.
7
8from xpra.util import envbool
9from xpra.log import Logger
10log = Logger("win32", "window", "util")
11vlog = Logger("verbose")
12
13import win32con         #@UnresolvedImport
14import win32api         #@UnresolvedImport
15import ctypes
16from ctypes import c_int, c_long
17from ctypes.wintypes import POINT
18from xpra.platform.win32.wndproc_events import WNDPROC_EVENT_NAMES
19
20#use ctypes to ensure we call the "W" version:
21SetWindowLong = ctypes.windll.user32.SetWindowLongW
22CallWindowProc = ctypes.windll.user32.CallWindowProcW
23WndProcType = ctypes.WINFUNCTYPE(c_int, c_long, c_int, c_int, c_int)
24
25
26class MINMAXINFO(ctypes.Structure):
27    _fields_ = [
28                ("ptReserved",      POINT),
29                ("ptMaxSize",       POINT),
30                ("ptMaxPosition",   POINT),
31                ("ptMinTrackSize",  POINT),
32                ("ptMaxTrackSize",  POINT),
33               ]
34
35
36#loosely based on this recipe:
37#http://code.activestate.com/recipes/334779-pygtk-win32-extension-empower-gtk-with-win32-windo/
38#and this WM_GETMINMAXINFO ctypes code:
39#https://github.com/Mozillion/SublimeSpeech/blob/master/lib/dragonfly/windows/dialog_base.py
40#only hardcoded for handling WM_GETMINMAXINFO,
41#but should be pretty easy to tweak if needed.
42
43HOOK_MINMAXINFO = envbool("XPRA_WIN32_MINMAXINFO", True)
44
45
46class Win32Hooks(object):
47
48    def __init__(self, hwnd):
49        self._hwnd = hwnd
50        self._message_map = {}
51        self.max_size = None
52        if HOOK_MINMAXINFO:
53            self.add_window_event_handler(win32con.WM_GETMINMAXINFO, self.on_getminmaxinfo)
54        try:
55            #we only use this code for resizable windows, so use SM_C?SIZEFRAME:
56            self.frame_width = win32api.GetSystemMetrics(win32con.SM_CXSIZEFRAME)
57            self.frame_height = win32api.GetSystemMetrics(win32con.SM_CYSIZEFRAME)
58            self.caption_height = win32api.GetSystemMetrics(win32con.SM_CYCAPTION);
59        except:
60            self.frame_width = 4
61            self.frame_height = 4
62            self.caption_height = 26
63        log("Win32Hooks: window frame size is %sx%s", self.frame_width, self.frame_height)
64        log("Win32Hooks: message_map=%s", self._message_map)
65        self._oldwndproc = None
66
67    def add_window_event_handler(self, event, handler):
68        self._message_map[event] = handler
69
70    def setup(self):
71        assert self._oldwndproc is None
72        self._newwndproc = WndProcType(self._wndproc)
73        self._oldwndproc = SetWindowLong(self._hwnd, win32con.GWL_WNDPROC, self._newwndproc)
74
75    def on_getminmaxinfo(self, hwnd, msg, wparam, lparam):
76        if self.max_size and lparam:
77            info = ctypes.cast(lparam, ctypes.POINTER(MINMAXINFO)).contents
78            width, height = self.max_size
79            style = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE)
80            if style & win32con.WS_BORDER:
81                fw, fh = self.frame_width, self.frame_height
82            else:
83                fw, fh = 0, 0
84            w = width + fw*2
85            h = height + self.caption_height + fh*2
86            point  = POINT(w, h)
87            info.ptMaxSize       = point
88            info.ptMaxTrackSize  = point
89            log("on_getminmaxinfo window=%#x max_size=%s, frame=%sx%s, minmaxinfo size=%sx%s", hwnd, self.max_size, fw, fh, w, h)
90            return None
91        log("on_getminmaxinfo window=%#x max_size=%s", hwnd, self.max_size)
92
93    def cleanup(self, *args):
94        log("cleanup%s", args)
95        self._message_map = {}
96        #since we assume the window is closed, restoring the wnd proc may be redundant here:
97        if not self._oldwndproc or not self._hwnd:
98            return
99        try:
100            SetWindowLong(self._hwnd, win32con.GWL_WNDPROC, self._oldwndproc)
101            self._oldwndproc = None
102            self._hwnd = None
103        except:
104            log.error("cleanup", exc_info=True)
105
106    def _wndproc(self, hwnd, msg, wparam, lparam):
107        event_name = WNDPROC_EVENT_NAMES.get(msg, msg)
108        callback = self._message_map.get(msg)
109        vlog("_wndproc%s event name=%s, callback=%s", (hwnd, msg, wparam, lparam), event_name, callback)
110        v = None
111        if callback:
112            #run our callback
113            try:
114                v = callback(hwnd, msg, wparam, lparam)
115                vlog("%s%s=%s", callback, (hwnd, msg, wparam, lparam), v)
116            except Exception as e:
117                log.error("Error: callback %s failed:", callback)
118                log.error(" %s", e)
119        #if our callback doesn't define the return value, use the default handler:
120        if v is None:
121            v = CallWindowProc(self._oldwndproc, hwnd, msg, wparam, lparam)
122            vlog("_wndproc%s return value=%s", (hwnd, msg, wparam, lparam), v)
123        return v