GDK W32: Implement show_window_menu()

This is achieved by sending undocumented message WM_SYSMENU
to the window.
Before doing that, the window is given WS_SYSMENU style
(to enable window menu) and some combination of
WS_MAXIMIZEBOX (for "Mazimize" item)
WS_MINIMIZEBOX (for "Minimize" item)
WS_SIZEBOX (for "Size" item)
depending on which operations are currently permissible.

WM_SYSMENU is processed by DefWindowProc(), which results
in showing the window menu. We remove extra styles
at the first opportunity (WM_INITMENU message), as they
alter the way our window is rendered.

https://bugzilla.gnome.org/show_bug.cgi?id=763851
This commit is contained in:
Руслан Ижбулатов
2016-03-18 04:51:52 +00:00
parent 38f94194a6
commit cea8c29a26
4 changed files with 147 additions and 0 deletions

View File

@ -1910,6 +1910,77 @@ ensure_stacking_on_activate_app (MSG *msg,
}
}
static gboolean
handle_wm_sysmenu (GdkWindow *window, MSG *msg, gint *ret_valp)
{
GdkWindowImplWin32 *impl;
LONG_PTR style, tmp_style;
gboolean maximized, minimized;
LONG_PTR additional_styles;
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
style = GetWindowLongPtr (msg->hwnd, GWL_STYLE);
maximized = IsZoomed (msg->hwnd);
minimized = IsIconic (msg->hwnd);
additional_styles = 0;
if (!(style & WS_SYSMENU))
additional_styles |= WS_SYSMENU;
if (!maximized && !(style & WS_MAXIMIZEBOX))
additional_styles |= WS_MAXIMIZEBOX;
if (!minimized && !(style & WS_MINIMIZEBOX))
additional_styles |= WS_MINIMIZEBOX;
if (!minimized && !maximized && !(style & WS_SIZEBOX))
additional_styles |= WS_SIZEBOX;
if (additional_styles == 0)
/* The caller will eventually pass this to DefWindowProc (),
* only without the style dance, which isn't needed, as it turns out.
*/
return FALSE;
/* Note: This code will enable resizing, maximizing and minimizing windows
* via window menu even if these are non-CSD windows that were explicitly
* forbidden from doing this by removing the appropriate styles,
* or if these are CSD windows that were explicitly forbidden from doing
* this by removing appropriate decorations from the headerbar and/or
* changing hints or properties.
*
* If doing this for non-CSD windows is not desired,
* do a _gdk_win32_window_lacks_wm_decorations() check and return FALSE
* if it doesn't pass.
*
* If doing this for CSD windows with disabled decorations is not desired,
* tough luck - GDK can't know which CSD decorations are enabled, and which
* are not.
*
* If doing this for CSD windows with particular hints is not desired,
* check window hints here and return FALSE (DefWindowProc() will return
* FALSE later) or set *ret_valp to 0 and return TRUE.
*/
tmp_style = style | additional_styles;
GDK_NOTE (EVENTS, g_print (" Handling WM_SYSMENU: style 0x%lx -> 0x%lx\n", style, tmp_style));
impl->have_temp_styles = TRUE;
impl->temp_styles = additional_styles;
SetWindowLongPtr (msg->hwnd, GWL_STYLE, tmp_style);
*ret_valp = DefWindowProc (msg->hwnd, msg->message, msg->wParam, msg->lParam);
tmp_style = GetWindowLongPtr (msg->hwnd, GWL_STYLE);
style = tmp_style & ~additional_styles;
GDK_NOTE (EVENTS, g_print (" Handling WM_SYSMENU: style 0x%lx <- 0x%lx\n", style, tmp_style));
SetWindowLongPtr (msg->hwnd, GWL_STYLE, style);
impl->have_temp_styles = FALSE;
return TRUE;
}
gboolean
_gdk_win32_window_fill_min_max_info (GdkWindow *window,
MINMAXINFO *mmi)
@ -2859,6 +2930,30 @@ gdk_event_translate (MSG *msg,
}
break;
case WM_SYSMENU:
return_val = handle_wm_sysmenu (window, msg, ret_valp);
break;
case WM_INITMENU:
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
if (impl->have_temp_styles)
{
LONG_PTR window_style;
window_style = GetWindowLongPtr (GDK_WINDOW_HWND (window),
GWL_STYLE);
/* Handling WM_SYSMENU added extra styles to this window,
* remove them now.
*/
window_style &= ~impl->temp_styles;
SetWindowLongPtr (GDK_WINDOW_HWND (window),
GWL_STYLE,
window_style);
}
break;
case WM_SYSCOMMAND:
switch (msg->wParam)
{

View File

@ -97,6 +97,18 @@
#define WM_MOUSEHWHEEL 0x20E
#endif
/* According to
* http://blog.airesoft.co.uk/2009/11/wm_messages/
* this is the actual internal name MS uses for this undocumented message.
* According to
* https://bugs.winehq.org/show_bug.cgi?id=15055
* wParam is 0
* lParam is a pair of virtual desktop coordinates for the popup
*/
#ifndef WM_SYSMENU
#define WM_SYSMENU 0x313
#endif
#ifndef CF_DIBV5
#define CF_DIBV5 17
#endif

View File

@ -3968,6 +3968,33 @@ gdk_win32_window_is_win32 (GdkWindow *window)
return GDK_WINDOW_IS_WIN32 (window);
}
static gboolean
gdk_win32_window_show_window_menu (GdkWindow *window,
GdkEvent *event)
{
double event_x, event_y;
gint x, y;
switch (event->type)
{
case GDK_BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_END:
break;
default:
return FALSE;
}
gdk_event_get_root_coords (event, &event_x, &event_y);
x = event_x - _gdk_offset_x;
y = event_y - _gdk_offset_y;
SendMessage (GDK_WINDOW_HWND (window), WM_SYSMENU, 0, MAKELPARAM (x, y));
return TRUE;
}
/**
* _gdk_win32_acquire_dc
* @impl: a Win32 #GdkWindowImplWin32 implementation
@ -4197,6 +4224,8 @@ gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass)
//impl_class->beep = gdk_x11_window_beep;
impl_class->show_window_menu = gdk_win32_window_show_window_menu;
impl_class->focus = gdk_win32_window_focus;
impl_class->set_type_hint = gdk_win32_window_set_type_hint;
impl_class->get_type_hint = gdk_win32_window_get_type_hint;

View File

@ -141,6 +141,11 @@ struct _GdkWindowImplWin32
*/
guint layered : 1;
/* If TRUE, the @temp_styles is set to the styles that were temporarily
* added to this window.
*/
guint have_temp_styles : 1;
/* GDK does not keep window contents around, it just draws new
* stuff over the window where changes occurred.
* cache_surface retains old window contents, because
@ -173,6 +178,12 @@ struct _GdkWindowImplWin32
/* No. of windows to force layered windows off */
guint suppress_layered;
/* Temporary styles that this window got for the purpose of
* handling WM_SYSMENU.
* They are removed at the first opportunity (usually WM_INITMENU).
*/
LONG_PTR temp_styles;
};
struct _GdkWindowImplWin32Class