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
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
30 import thread
as _thread
# this module was renamed between 2.x and 3.x
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
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
53 CS_DROPSHADOW
= 0x00020000
56 GCL_HBRBACKGROUND
= -10
67 GCLP_HBRBACKGROUND
= -10
85 # special COLOR_* values copied here to avoid importing brushes
88 COLOR_ACTIVECAPTION
= 2
89 COLOR_INACTIVECAPTION
= 3
96 COLOR_ACTIVEBORDER
= 10
97 COLOR_INACTIVEBORDER
= 11
98 COLOR_APPWORKSPACE
= 12
100 COLOR_HIGHLIGHTTEXT
= 14
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
)
173 SW_SHOWNOACTIVATE
= 4
176 SW_SHOWMINNOACTIVE
= 7
180 SW_FORCEMINIMIZE
= 11
183 WNDPROC
= WINFUNCTYPE(LRESULT
, HWND
, UINT
, WPARAM
, LPARAM
)
185 class WNDCLASS(Structure
):
188 ('lpfnWndProc', WNDPROC
),
191 ('hInstance', HINSTANCE
),
193 ('hCursor', HCURSOR
),
194 ('hbrBackground', HBRUSH
),
195 ('lpszMenuName', LPCTSTR
),
196 ('lpszClassName', LPCTSTR
),
198 LPWNDCLASS
= PWNDCLASS
= POINTER(WNDCLASS
)
200 class WNDCLASSEX(Structure
):
204 ('lpfnWndProc', WNDPROC
),
207 ('hInstance', HINSTANCE
),
209 ('hCursor', HCURSOR
),
210 ('hbrBackground', HBRUSH
),
211 ('lpszMenuName', LPCTSTR
),
212 ('lpszClassName', LPCTSTR
),
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
):
228 _GetClassInfo(instance
, classname
, byref(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
))
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
))
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."""
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:
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
= {}
328 if name
.startswith('DO_'):
330 if msgname
.startswith('0x'):
331 msg
= int(msgname
[2:], 16)
332 elif msgname
.startswith('R_'):
334 msg
= message
.RegisterWindowMessage(msgname
[2:])
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
)
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
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()
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:
376 wce
.hCursor
= cursor
.LoadCursor(0, cursor
.IDC_ARROW
)
378 wce
.hCursor
= self
.cursor
379 if isinstance(self
.background
, int) and self
.background
<= 0xFFFF:
380 wce
.hbrBackground
= self
.background
+ 1
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
)
395 pass # if internal, unregisterclass
397 class BaseWindow(object):
399 if type(self
) is BaseWindow
:
400 raise TypeError("BaseWindow is an abstract base class")
402 class ExternalWindow(BaseWindow
):
405 # Needed because 3.0 added new syntax for metaclasses and removed the old syntax.
406 _WindowMetaClassInstanceBase
= WindowClass('_WindowMetaClassBase', (BaseWindow
,), {})
408 class Window(_WindowMetaClassInstanceBase
):
412 _superclass_wndproc
= None
414 #class creation settings
415 _class_extrabytes
= 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
424 #instance creation settings
425 style
= WS_TILEDWINDOW
427 windowname
= "PyWinLite"
428 x
= y
= width
= height
= CW_USEDEFAULT
435 def super_on_msg(klass
, hwnd
, msg
, wparam
, lparam
):
436 if klass
._superclass
_wndproc
is None:
437 return DefWindowProc(hwnd
, msg
, wparam
, lparam
)
439 return CallWindowProc(klass
._superclass
_wndproc
, hwnd
, msg
, wparam
, lparam
)
442 def _wndproc(klass
, hwnd
, msg
, wparam
, lparam
):
443 wnd
= window_from_hwnd(hwnd
, klass
)
445 result
= klass
.on_msg(wnd
, hwnd
, msg
, wparam
, lparam
)
450 except NotImplementedError:
451 return klass
.super_on_msg(hwnd
, msg
, wparam
, lparam
)
455 def _on_msg(klass
, wnd
, hwnd
, msg
, wparam
, lparam
):
457 handler
= klass
.message_handlers
[msg
]
459 raise NotImplementedError
460 return handler(wnd
, hwnd
, msg
, wparam
, lparam
)
462 on_msg
= classmethod(_on_msg
)
463 _on_msg
= staticmethod(_on_msg
)
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
)
504 def from_hwnd(klass
, hwnd
):
505 result
= klass
.__new
__(klass
, hwnd
)
506 result
.__init
__(hwnd
)
510 pass #DestroyWindow(self.hwnd)
512 def window_from_hwnd(hwnd
, klass
=ExternalWindow
):
514 return window_instances
[hwnd
]
516 window_instance_lock
.acquire()
518 return window_instances
[hwnd
]
520 window_instances
[hwnd
] = klass
.from_hwnd(hwnd
) #TODO: implement from_hwnd for ExternalWindow
521 return window_instances
[hwnd
]
523 window_instance_lock
.release()