2014-08-14 09:42:10 -05:00
// 17 july 2014
2014-07-17 18:36:24 -05:00
# include "winapi_windows.h"
# include "_cgo_export.h"
2014-08-19 12:25:52 -05:00
# include "modalqueue.h"
2014-07-17 18:36:24 -05:00
2014-08-14 09:42:10 -05:00
// note that this includes the terminating '\0'
2014-08-14 10:30:48 -05:00
// this also assumes WC_TABCONTROL is longer than areaWindowClass
# define NCLASSNAME (sizeof WC_TABCONTROL / sizeof WC_TABCONTROL[0])
2014-08-13 09:41:27 -05:00
2014-08-21 04:37:24 -05:00
void uimsgloop_area ( HWND active , HWND focus , MSG * msg )
2014-08-20 22:38:22 -05:00
{
2014-08-21 04:37:24 -05:00
MSG copy ;
copy = * msg ;
switch ( copy . message ) {
case WM_KEYDOWN :
case WM_SYSKEYDOWN : // Alt+[anything] and F10 send these instead
copy . message = msgAreaKeyDown ;
break ;
case WM_KEYUP :
case WM_SYSKEYUP :
copy . message = msgAreaKeyUp ;
break ;
default :
goto notkey ;
}
// if we handled the key, don't do the default behavior
2014-08-20 22:38:22 -05:00
// don't call TranslateMessage(); we do our own keyboard handling
2014-08-21 04:37:24 -05:00
if ( DispatchMessage ( & copy ) ! = FALSE )
return ;
// TODO move this under notkey?
if ( IsDialogMessage ( active , msg ) ! = 0 )
return ;
notkey :
2014-08-20 22:38:22 -05:00
DispatchMessage ( msg ) ;
}
void uimsgloop_tab ( HWND active , HWND focus , MSG * msg )
{
BOOL hasChildren ;
BOOL idm ;
// THIS BIT IS IMPORTANT: if the current tab has no children, then there will be no children left in the dialog to tab to, and IsDialogMessageW() will loop forever
hasChildren = SendMessageW ( focus , msgTabCurrentTabHasChildren , 0 , 0 ) ;
if ( hasChildren )
tabEnterChildren ( focus ) ;
idm = IsDialogMessageW ( active , msg ) ;
if ( hasChildren )
tabLeaveChildren ( focus ) ;
if ( idm ! = 0 )
return ;
TranslateMessage ( msg ) ;
DispatchMessage ( msg ) ;
}
void uimsgloop_else ( MSG * msg )
{
TranslateMessage ( msg ) ;
DispatchMessage ( msg ) ;
}
2014-07-17 18:36:24 -05:00
void uimsgloop ( void )
{
MSG msg ;
int res ;
2014-08-14 10:30:48 -05:00
HWND active , focus ;
WCHAR classchk [ NCLASSNAME ] ;
2014-08-13 09:41:27 -05:00
BOOL dodlgmessage ;
2014-07-17 18:36:24 -05:00
for ( ; ; ) {
SetLastError ( 0 ) ;
2014-07-17 22:14:22 -05:00
res = GetMessageW ( & msg , NULL , 0 , 0 ) ;
2014-07-17 18:36:24 -05:00
if ( res < 0 )
xpanic ( " error calling GetMessage() " , GetLastError ( ) ) ;
2014-08-14 09:42:10 -05:00
if ( res = = 0 ) // WM_QUIT
2014-07-17 18:36:24 -05:00
break ;
2014-08-12 16:18:45 -05:00
active = GetActiveWindow ( ) ;
2014-08-20 22:38:22 -05:00
if ( active = = NULL ) {
uimsgloop_else ( & msg ) ;
continue ;
}
// bit of logic involved here:
// we don't want dialog messages passed into Areas, so we don't call IsDialogMessageW() there
// as for Tabs, we can't have both WS_TABSTOP and WS_EX_CONTROLPARENT set at the same time, so we hotswap the two styles to get the behavior we want
// theoretically we could use the class atom to avoid a wcscmp()
// however, raymond chen advises against this - http://blogs.msdn.com/b/oldnewthing/archive/2004/10/11/240744.aspx (and we're not in control of the Tab class, before you say anything)
// we could also theoretically just send msgAreaDefocuses directly, but what DefWindowProc() does to a WM_APP message is undocumented
focus = GetFocus ( ) ;
if ( focus ! = NULL ) {
if ( GetClassNameW ( focus , classchk , NCLASSNAME ) = = 0 )
xpanic ( " error getting name of focused window class for Area check " , GetLastError ( ) ) ;
if ( wcscmp ( classchk , areaWindowClass ) = = 0 ) {
2014-08-21 04:37:24 -05:00
uimsgloop_area ( active , focus , & msg ) ;
2014-08-20 22:38:22 -05:00
continue ;
} else if ( wcscmp ( classchk , WC_TABCONTROL ) = = 0 ) {
uimsgloop_tab ( active , focus , & msg ) ;
continue ;
2014-08-14 12:05:31 -05:00
}
2014-08-20 22:38:22 -05:00
// else fall through
2014-08-13 09:41:27 -05:00
}
2014-08-20 22:38:22 -05:00
if ( IsDialogMessage ( active , & msg ) ! = 0 )
continue ;
uimsgloop_else ( & msg ) ;
2014-07-17 18:36:24 -05:00
}
}
void issue ( void * request )
{
SetLastError ( 0 ) ;
2014-07-17 23:22:21 -05:00
if ( PostMessageW ( msgwin , msgRequest , 0 , ( LPARAM ) request ) = = 0 )
2014-07-17 18:36:24 -05:00
xpanic ( " error issuing request " , GetLastError ( ) ) ;
}
HWND msgwin ;
# define msgwinclass L"gouimsgwin"
2014-08-19 02:31:53 -05:00
static BOOL CALLBACK beginEndModalAll ( HWND hwnd , LPARAM lParam )
{
if ( hwnd ! = msgwin )
SendMessageW ( hwnd , ( UINT ) lParam , 0 , 0 ) ;
return TRUE ;
}
2014-07-17 18:36:24 -05:00
static LRESULT CALLBACK msgwinproc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
2014-08-14 15:00:31 -05:00
LRESULT shared ;
2014-08-19 02:56:04 -05:00
size_t i ;
2014-08-14 15:00:31 -05:00
if ( sharedWndProc ( hwnd , uMsg , wParam , lParam , & shared ) )
return shared ;
2014-07-17 18:36:24 -05:00
switch ( uMsg ) {
2014-08-17 12:34:06 -05:00
case WM_CREATE :
// initial
makeCheckboxImageList ( hwnd ) ;
return 0 ;
2014-08-17 14:30:10 -05:00
// TODO respond to WM_THEMECHANGED
2014-07-17 23:22:21 -05:00
case msgRequest :
2014-08-19 02:31:53 -05:00
// in modal?
2014-08-19 12:25:52 -05:00
if ( ! queueIfModal ( ( void * ) lParam ) )
// nope, we can run now
doissue ( ( void * ) lParam ) ;
2014-07-17 18:36:24 -05:00
return 0 ;
2014-08-19 02:31:53 -05:00
case msgBeginModal :
2014-08-19 12:25:52 -05:00
beginModal ( ) ;
2014-08-19 02:31:53 -05:00
EnumThreadWindows ( GetCurrentThreadId ( ) , beginEndModalAll , msgBeginModal ) ;
return 0 ;
case msgEndModal :
2014-08-19 12:25:52 -05:00
endModal ( ) ;
2014-08-19 02:31:53 -05:00
EnumThreadWindows ( GetCurrentThreadId ( ) , beginEndModalAll , msgEndModal ) ;
return 0 ;
2014-07-17 18:36:24 -05:00
default :
2014-07-17 22:14:22 -05:00
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
2014-07-17 18:36:24 -05:00
}
xmissedmsg ( " message-only " , " msgwinproc() " , uMsg ) ;
2014-08-14 09:42:10 -05:00
return 0 ; // unreachable
2014-07-17 18:36:24 -05:00
}
DWORD makemsgwin ( char * * errmsg )
{
2014-07-17 22:14:22 -05:00
WNDCLASSW wc ;
2014-07-17 18:36:24 -05:00
2014-07-17 22:14:22 -05:00
ZeroMemory ( & wc , sizeof ( WNDCLASSW ) ) ;
2014-07-17 18:36:24 -05:00
wc . lpfnWndProc = msgwinproc ;
wc . hInstance = hInstance ;
wc . hIcon = hDefaultIcon ;
wc . hCursor = hArrowCursor ;
wc . hbrBackground = ( HBRUSH ) ( COLOR_BTNFACE + 1 ) ;
wc . lpszClassName = msgwinclass ;
2014-07-17 22:14:22 -05:00
if ( RegisterClassW ( & wc ) = = 0 ) {
2014-07-17 18:36:24 -05:00
* errmsg = " error registering message-only window classs " ;
return GetLastError ( ) ;
}
2014-07-17 22:14:22 -05:00
msgwin = CreateWindowExW (
2014-07-17 18:36:24 -05:00
0 ,
msgwinclass , L " package ui message-only window " ,
0 ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
HWND_MESSAGE , NULL , hInstance , NULL ) ;
if ( msgwin = = NULL ) {
* errmsg = " error creating message-only window " ;
return GetLastError ( ) ;
}
return 0 ;
}