add skeleton for variant type support
[pywinlite.git] / wndclass.py
blobae49bd59be965d8cc9ffc390b72eea5a6aea7dab
1 #Copyright (c) 2008 Vincent Povirk
3 #Permission is hereby granted, free of charge, to any person
4 #obtaining a copy of this software and associated documentation
5 #files (the "Software"), to deal in the Software without
6 #restriction, including without limitation the rights to use,
7 #copy, modify, merge, publish, distribute, sublicense, and/or sell
8 #copies of the Software, and to permit persons to whom the
9 #Software is furnished to do so, subject to the following
10 #conditions:
12 #The above copyright notice and this permission notice shall be
13 #included in all copies or substantial portions of the Software.
15 #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 #OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 #HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 #WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 #FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 #OTHER DEALINGS IN THE SOFTWARE.
24 # Window Class definitions
25 # see http://msdn.microsoft.com/en-us/library/ms632596(VS.85).aspx
27 try:
28 import _thread
29 except ImportError:
30 import thread as _thread # this module was renamed between 2.x and 3.x
31 import random
33 from ctypes import Structure, WINFUNCTYPE, POINTER, windll, byref, sizeof
34 from windef import UINT, INT, LONG, WORD, DWORD, HINSTANCE, HICON, HCURSOR, HBRUSH, HMENU, HWND, LPTSTR, LPCTSTR, LPVOID, LRESULT, WPARAM, LPARAM, create_tchar_buffer, BOOL
35 from winlitecfg import get_aw_symbols
36 from winliteutils import NONZERO
37 _user32 = windll.user32
39 CS_VREDRAW = 0x00000001
40 CS_HREDRAW = 0x00000002
41 CS_KEYCVTWINDOW = 0x00000004
42 CS_DBLCLKS = 0x00000008
43 CS_OWNDC = 0x00000020
44 CS_CLASSDC = 0x00000040
45 CS_PARENTDC = 0x00000080
46 CS_NOKEYCVT = 0x00000100
47 CS_NOCLOSE = 0x00000200
48 CS_SAVEBITS = 0x00000800
49 CS_BYTEALIGNCLIENT = 0x00001000
50 CS_BYTEALIGNWINDOW = 0x00002000
51 CS_GLOBALCLASS = 0x00004000
52 CS_IME = 0x00010000
53 CS_DROPSHADOW = 0x00020000
55 GCL_MENUNAME = -8
56 GCL_HBRBACKGROUND = -10
57 GCL_HCURSOR = -12
58 GCL_HICON = -14
59 GCL_HMODULE = -16
60 GCL_WNDPROC = -24
61 GCL_HICONSM = -34
62 GCL_CBWNDEXTRA = -18
63 GCL_CBCLSEXTRA = -20
64 GCL_STYLE = -26
66 GCLP_MENUNAME = -8
67 GCLP_HBRBACKGROUND = -10
68 GCLP_HCURSOR = -12
69 GCLP_HICON = -14
70 GCLP_HMODULE = -16
71 GCLP_WNDPROC = -24
72 GCLP_HICONSM = -34
74 GWL_EXSTYLE = -20
75 GWL_STYLE = -16
76 GWL_USERDATA = -21
77 GWL_ID = -12
78 GWL_HWNDPARENT = -8
79 GWL_HINSTANCE = -6
80 GWL_WNDPROC = -4
81 DWL_MSGRESULT = 0
82 DWL_DLGPROC = 4
83 DWL_USER = 8
85 # special COLOR_* values copied here to avoid importing brushes
86 COLOR_SCROLLBAR = 0
87 COLOR_BACKGROUND = 1
88 COLOR_ACTIVECAPTION = 2
89 COLOR_INACTIVECAPTION = 3
90 COLOR_MENU = 4
91 COLOR_WINDOW = 5
92 COLOR_WINDOWFRAME = 6
93 COLOR_MENUTEXT = 7
94 COLOR_WINDOWTEXT = 8
95 COLOR_CAPTIONTEXT = 9
96 COLOR_ACTIVEBORDER = 10
97 COLOR_INACTIVEBORDER = 11
98 COLOR_APPWORKSPACE = 12
99 COLOR_HIGHLIGHT = 13
100 COLOR_HIGHLIGHTTEXT = 14
101 COLOR_BTNFACE = 15
102 COLOR_BTNSHADOW = 16
103 COLOR_GRAYTEXT = 17
104 COLOR_BTNTEXT = 18
105 COLOR_INACTIVECAPTIONTEXT = 19
106 COLOR_BTNHIGHLIGHT = 20
108 CW_USEDEFAULT = 0x80000000
110 WS_OVERLAPPED = 0x00000000
111 WS_POPUP = 0x80000000
112 WS_CHILD = 0x40000000
113 WS_MINIMIZE = 0x20000000
114 WS_VISIBLE = 0x10000000
115 WS_DISABLED = 0x08000000
116 WS_CLIPSIBLINGS = 0x04000000
117 WS_CLIPCHILDREN = 0x02000000
118 WS_MAXIMIZE = 0x01000000
119 WS_CAPTION = 0x00C00000
120 WS_BORDER = 0x00800000
121 WS_DLGFRAME = 0x00400000
122 WS_VSCROLL = 0x00200000
123 WS_HSCROLL = 0x00100000
124 WS_SYSMENU = 0x00080000
125 WS_THICKFRAME = 0x00040000
126 WS_GROUP = 0x00020000
127 WS_TABSTOP = 0x00010000
128 WS_MINIMIZEBOX = 0x00020000
129 WS_MAXIMIZEBOX = 0x00010000
130 WS_TILED = WS_OVERLAPPED
131 WS_ICONIC = WS_MINIMIZE
132 WS_SIZEBOX = WS_THICKFRAME
133 WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME| WS_MINIMIZEBOX | WS_MAXIMIZEBOX
134 WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU
135 WS_CHILDWINDOW = WS_CHILD
136 WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW
138 WS_EX_DLGMODALFRAME = 0x00000001
139 WS_EX_DRAGDETECT = 0x00000002
140 WS_EX_NOPARENTNOTIFY = 0x00000004
141 WS_EX_TOPMOST = 0x00000008
142 WS_EX_ACCEPTFILES = 0x00000010
143 WS_EX_TRANSPARENT = 0x00000020
144 WS_EX_MDICHILD = 0x00000040
145 WS_EX_TOOLWINDOW = 0x00000080
146 WS_EX_WINDOWEDGE = 0x00000100
147 WS_EX_CLIENTEDGE = 0x00000200
148 WS_EX_CONTEXTHELP = 0x00000400
149 WS_EX_RIGHT = 0x00001000
150 WS_EX_LEFT = 0x00000000
151 WS_EX_RTLREADING = 0x00002000
152 WS_EX_LTRREADING = 0x00000000
153 WS_EX_LEFTSCROLLBAR = 0x00004000
154 WS_EX_RIGHTSCROLLBAR = 0x00000000
155 WS_EX_CONTROLPARENT = 0x00010000
156 WS_EX_STATICEDGE = 0x00020000
157 WS_EX_APPWINDOW = 0x00040000
158 WS_EX_LAYERED = 0x00080000
159 WS_EX_NOINHERITLAYOUT = 0x00100000
160 WS_EX_LAYOUTRTL = 0x00400000
161 WS_EX_COMPOSITED = 0x02000000
162 WS_EX_NOACTIVATE = 0x08000000
164 WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE|WS_EX_CLIENTEDGE)
165 WS_EX_PALETTEWINDOW = (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW|WS_EX_TOPMOST)
167 SW_HIDE = 0
168 SW_SHOWNORMAL = 1
169 SW_NORMAL = 1
170 SW_SHOWMINIMIZED = 2
171 SW_SHOWMAXIMIZED = 3
172 SW_MAXIMIZE = 3
173 SW_SHOWNOACTIVATE = 4
174 SW_SHOW = 5
175 SW_MINIMIZE = 6
176 SW_SHOWMINNOACTIVE = 7
177 SW_SHOWNA = 8
178 SW_RESTORE = 9
179 SW_SHOWDEFAULT = 10
180 SW_FORCEMINIMIZE = 11
181 SW_MAX = 11
183 WNDPROC = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)
185 class WNDCLASS(Structure):
186 _fields_ = [
187 ('style', UINT),
188 ('lpfnWndProc', WNDPROC),
189 ('cbClsExtra', INT),
190 ('cbWndExtra', INT),
191 ('hInstance', HINSTANCE),
192 ('hIcon', HICON),
193 ('hCursor', HCURSOR),
194 ('hbrBackground', HBRUSH),
195 ('lpszMenuName', LPCTSTR),
196 ('lpszClassName', LPCTSTR),
198 LPWNDCLASS = PWNDCLASS = POINTER(WNDCLASS)
200 class WNDCLASSEX(Structure):
201 _fields_ = [
202 ('cbSize', UINT),
203 ('style', UINT),
204 ('lpfnWndProc', WNDPROC),
205 ('cbClsExtra', INT),
206 ('cbWndExtra', INT),
207 ('hInstance', HINSTANCE),
208 ('hIcon', HICON),
209 ('hCursor', HCURSOR),
210 ('hbrBackground', HBRUSH),
211 ('lpszMenuName', LPCTSTR),
212 ('lpszClassName', LPCTSTR),
213 ('hIconSm', HICON),
215 LPWNDCLASSEX = PWNDCLASSEX = POINTER(WNDCLASSEX)
217 get_aw_symbols(globals(), _user32, ['CallWindowProc', 'DefWindowProc', 'GetClassInfo', 'GetClassInfoEx', 'GetClassLong', 'GetClassName', 'GetWindowLong', 'RegisterClass', 'RegisterClassEx', 'SetClassLong', 'SetWindowLong', 'SetClassLong', 'UnregisterClass', 'CreateWindowEx'])
219 CallWindowProc.argtypes = [WNDPROC,HWND,UINT,WPARAM,LPARAM]
221 DefWindowProc.argtypes = [HWND,UINT,WPARAM,LPARAM]
223 _GetClassInfo = GetClassInfo
224 _GetClassInfo.argtypes = [HINSTANCE, LPCTSTR, LPWNDCLASS]
225 _GetClassInfo.restype = NONZERO
226 def GetClassInfo(instance, classname):
227 result = WNDCLASS()
228 _GetClassInfo(instance, classname, byref(result))
229 return result
231 _GetClassInfoEx = GetClassInfoEx
232 _GetClassInfoEx.argtypes = [HINSTANCE, LPCTSTR, LPWNDCLASSEX]
233 _GetClassInfoEx.restype = NONZERO
234 def GetClassInfoEx(instance, classname):
235 result = WNDCLASSEX()
236 _GetClassInfoEx(instance, classname, byref(result))
237 return result
239 GetClassLong.argtypes = [HWND, INT]
240 GetClassLong.restype = NONZERO
242 GetClassLongPtr = GetClassLong
244 _GetClassName = GetClassName
245 _GetClassName.argtypes = [HWND, LPTSTR, INT]
246 _GetClassName.restype = NONZERO
247 def GetClassName(hwnd, maxlength=256):
248 result = create_tchar_buffer(maxlength)
249 _GetClassName(hwnd, byref(result), len(result))
250 return result.value
252 GetClassWord = _user32.GetClassWord
253 GetClassWord.argtypes = [HWND, INT]
254 GetClassWord.restype = NONZERO
256 GetWindowLong.argtypes = [HWND, INT]
257 GetWindowLong.restype = NONZERO
259 GetWindowLongPtr = GetWindowLong
261 RegisterClass.argtypes = [LPWNDCLASS]
262 RegisterClass.restype = NONZERO
264 RegisterClassEx.argtypes = [LPWNDCLASSEX]
265 RegisterClassEx.restype = NONZERO
267 SetClassLong.argtypes = [HWND,INT,LONG]
268 SetClassLong.restype = NONZERO
270 SetClassLongPtr = SetClassLong
272 SetClassWord = _user32.SetClassWord
273 SetClassWord.argtypes = [HWND,INT,WORD]
274 SetClassWord.restype = NONZERO
276 SetWindowLong.argtypes = [HWND,INT,LONG]
277 SetWindowLong.restype = NONZERO
279 SetWindowLongPtr = SetWindowLong
281 UnregisterClass.argtypes = [LPCTSTR,HINSTANCE]
282 UnregisterClass.restype = NONZERO
284 CreateWindowEx.argtypes = [DWORD,LPCTSTR,LPCTSTR,DWORD,INT,INT,INT,INT,HWND,HMENU,HINSTANCE,LPVOID]
285 CreateWindowEx.restype = NONZERO
287 GetDesktopWindow = _user32.GetDesktopWindow
288 GetDesktopWindow.argtypes = []
289 GetDesktopWindow.restype = NONZERO
291 ShowWindow = _user32.ShowWindow
292 ShowWindow.argtypes = [HWND, INT]
294 MoveWindow = _user32.MoveWindow
295 MoveWindow.argtypes = [HWND, INT, INT, INT, INT, BOOL]
296 MoveWindow.restype = NONZERO
298 window_instances = {}
299 window_instance_lock = _thread.allocate_lock()
301 class WindowClass(type):
302 """WindowClass - the type of all window classes
304 Don't use WindowClass directly. Subclass Window instead."""
306 classcount = 0
307 classcount_lock = _thread.allocate_lock()
309 def __init__(self, name, bases, dict):
310 if bases[0] is BaseWindow or bases[0] is _WindowMetaClassInstanceBase:
311 #WindowClass is an abstract base type. None of the magic
312 #things we do to initialize a real window type should be
313 #done for WindowClass
314 return type.__init__(self, name, bases, dict)
316 #if internal is undefined, default to True
317 if 'internal' not in dict:
318 self.internal = True
320 if 'subclass' not in dict:
321 #if the superclass has the same name as this class, this is a subclass
322 self.subclass = bases[0].classname is not None and bases[0].classname != self.classname
324 self._superclass_wndproc = bases[0]._wndproc_ptr
326 self.message_handlers = {}
327 for name in dict:
328 if name.startswith('DO_'):
329 msgname = name[3:]
330 if msgname.startswith('0x'):
331 msg = int(msgname[2:], 16)
332 elif msgname.startswith('R_'):
333 import message
334 msg = message.RegisterWindowMessage(msgname[2:])
335 else:
336 try:
337 msg = int(msgname)
338 except ValueError:
339 msg = getattr(__import__('message'), msgname)
340 self.message_handlers[msg] = dict[name]
342 type.__init__(self, name, bases, dict)
344 if self.internal or self.subclass:
345 self._wndproc_ptr = WNDPROC(self._wndproc)
346 else:
347 info = GetClassInfoEx(0, self.classname)
348 self._wndproc_ptr = info.lpfnWndProc
349 self._class_extrabytes = info.cbClsExtra
350 self._wnd_extrabytes = info.cbWndExtra
351 #self._class_instance = info.hInstance
352 self.icon = info.hIcon
353 self.cursor = info.hCursor
354 if info.hbrBackground and info.hbrBackground <= COLOR_BTNHIGHLIGHT + 1:
355 self.background = info.hbrBackground - 1
356 else:
357 self.background = info.hbrBackground
358 self._class_menuname = info.lpszMenuName
359 self.smallicon = info.hIconSm
361 if self.internal and not self.subclass:
362 if self.classname is None or self.classname == bases[0].classname:
363 WindowClass.classcount_lock.acquire()
364 self.classname = 'PyWinLite%s' % WindowClass.classcount
365 WindowClass.classcount += 1
366 WindowClass.classcount_lock.release()
367 wce = WNDCLASSEX()
368 wce.cbSize = sizeof(wce)
369 wce.lpfnWndProc = self._wndproc_ptr
370 wce.cbClsExtra = self._class_extrabytes
371 wce.cbWndExtra = self._wnd_extrabytes
372 wce.hInstance = self._class_instance
373 wce.hIcon = self.icon
374 if self.cursor is None:
375 import cursor
376 wce.hCursor = cursor.LoadCursor(0, cursor.IDC_ARROW)
377 else:
378 wce.hCursor = self.cursor
379 if isinstance(self.background, int) and self.background <= 0xFFFF:
380 wce.hbrBackground = self.background + 1
381 else:
382 wce.hbrBackground = self.background
383 wce.lpszMenuName = self._class_menuname
384 wce.lpszClassName = create_tchar_buffer(self.classname)
385 wce.hIconSm = self.smallicon
386 RegisterClassEx(byref(wce))
388 def __call__(self, *args, **kwargs):
389 hwnd = self.__create__(*args, **kwargs)
390 result = window_from_hwnd(hwnd, self)
391 result.__setup__(*args, **kwargs)
392 return window_from_hwnd(hwnd)
394 def __del__(self):
395 pass # if internal, unregisterclass
397 class BaseWindow(object):
398 def __init__(self):
399 if type(self) is BaseWindow:
400 raise TypeError("BaseWindow is an abstract base class")
402 class ExternalWindow(BaseWindow):
403 pass
405 # Needed because 3.0 added new syntax for metaclasses and removed the old syntax.
406 _WindowMetaClassInstanceBase = WindowClass('_WindowMetaClassBase', (BaseWindow,), {})
408 class Window(_WindowMetaClassInstanceBase):
409 classname = None
410 internal = False
411 subclass = False
412 _superclass_wndproc = None
414 #class creation settings
415 _class_extrabytes = 0
416 _wnd_extrabytes = 0
417 _class_instance = 0
418 icon = 0
419 cursor = None # None is a special value that loads a default cursor; use 0 for no cursor
420 background = COLOR_WINDOW
421 _class_menuname = None
422 smallicon = 0
424 #instance creation settings
425 style = WS_TILEDWINDOW
426 exstyle = 0
427 windowname = "PyWinLite"
428 x = y = width = height = CW_USEDEFAULT
429 parent = None
430 menu = 0
431 instance = 0
432 lparam = 0
434 @classmethod
435 def super_on_msg(klass, hwnd, msg, wparam, lparam):
436 if klass._superclass_wndproc is None:
437 return DefWindowProc(hwnd, msg, wparam, lparam)
438 else:
439 return CallWindowProc(klass._superclass_wndproc, hwnd, msg, wparam, lparam)
441 @classmethod
442 def _wndproc(klass, hwnd, msg, wparam, lparam):
443 wnd = window_from_hwnd(hwnd, klass)
444 try:
445 result = klass.on_msg(wnd, hwnd, msg, wparam, lparam)
446 if result == None:
447 return 0
448 else:
449 return result
450 except NotImplementedError:
451 return klass.super_on_msg(hwnd, msg, wparam, lparam)
453 _wndproc_ptr = None
455 def _on_msg(klass, wnd, hwnd, msg, wparam, lparam):
456 try:
457 handler = klass.message_handlers[msg]
458 except KeyError:
459 raise NotImplementedError
460 return handler(wnd, hwnd, msg, wparam, lparam)
462 on_msg = classmethod(_on_msg)
463 _on_msg = staticmethod(_on_msg)
465 @classmethod
466 def __create__(klass, exstyle=None, classname=None, windowname=None, style=None, x=None, y=None, width=None, height=None, parent=None, menu=None, instance=None, lparam=None):
467 """__create__(klass, exstyle=None, classname=None, windowname=None, style=None, x=None, y=None, width=None, height=None, parent=None, menu=None, instance=None, lparam=None)
468 This function calls CreateWindowEx and returns an hwnd. It is called with the arguments used to create the instance."""
469 if exstyle is None: exstyle = klass.exstyle
470 if classname is None: classname = klass.classname
471 if windowname is None: windowname = klass.windowname
472 if style is None: style = klass.style
473 if x is None: x = klass.x
474 if y is None: y = klass.y
475 if width is None: width = klass.width
476 if height is None: height = klass.height
477 if parent is None: parent = klass.parent
478 if menu is None: menu = klass.menu
479 if instance is None: instance = klass.instance
480 if lparam is None: lparam = klass.lparam
481 return CreateWindowEx(exstyle, classname, windowname, style, x, y, width, height, parent, menu, instance, lparam)
483 def __setup__(self, *args, **kwargs):
484 """__setup__(self, *args, **kwargs)
485 Called after CreateWindowEx returns with arguments used to create the instance. Note that, because of how CreateWindow works, the instance will have already processed some messages by this time."""
487 def __init__(self, hwnd):
488 """__init__(self, hwnd)
489 Called before processing the first message, always with the hwnd of this window. The arguments used to create the instance cannot be automatically forwarded to this function. If you REALLY need them now, use lparam. Otherwise, process them in __setup__."""
490 BaseWindow.__init__(self)
492 if type(self) is Window:
493 raise TypeError("Window is an abstract base class")
495 self._as_parameter_ = self.hwnd = hwnd
497 if self.parent is None:
498 self.parent = GetDesktopWindow()
500 if type(self).subclass:
501 SetWindowLongPtr(hwnd, GWLP_WNDPROC, type(self)._wndproc_ptr)
503 @classmethod
504 def from_hwnd(klass, hwnd):
505 result = klass.__new__(klass, hwnd)
506 result.__init__(hwnd)
507 return result
509 def __del__(self):
510 pass #DestroyWindow(self.hwnd)
512 def window_from_hwnd(hwnd, klass=ExternalWindow):
513 try:
514 return window_instances[hwnd]
515 except KeyError:
516 window_instance_lock.acquire()
517 try:
518 return window_instances[hwnd]
519 except KeyError:
520 window_instances[hwnd] = klass.from_hwnd(hwnd) #TODO: implement from_hwnd for ExternalWindow
521 return window_instances[hwnd]
522 finally:
523 window_instance_lock.release()