GDK W32: Re-implement AeroSnap for CSD windows

It works exactly like AeroSnap.
Except for shift+win+left/right, which is left for AeroSnap
to handle (AeroSnap takes action before we get the message,
so there's no way for us to override it).
The only thing that doesn't work is shift+win+left/right on
a maximized window, for reasons unknown at the moment.

This only implements winkey+stuff behaviour of AeroSnap,
not the drag-to-the-edge-and-something-funny-happens one.

https://bugzilla.gnome.org/show_bug.cgi?id=763013
This commit is contained in:
Руслан Ижбулатов 2016-03-08 02:33:47 +00:00
parent 27a1b50bc6
commit 0ce217cf94
4 changed files with 369 additions and 0 deletions

View File

@ -2288,6 +2288,48 @@ gdk_event_translate (MSG *msg,
if (GDK_WINDOW_DESTROYED (window))
break;
if (msg->message == WM_KEYUP &&
!GDK_WINDOW_DESTROYED (gdk_window_get_toplevel (window)) &&
_gdk_win32_window_lacks_wm_decorations (gdk_window_get_toplevel (window)) && /* For CSD only */
((GetKeyState (VK_LWIN) & 0x8000) ||
(GetKeyState (VK_RWIN) & 0x8000)))
{
GdkWin32AeroSnapCombo combo = GDK_WIN32_AEROSNAP_COMBO_NOTHING;
gboolean lshiftdown = GetKeyState (VK_LSHIFT) & 0x8000;
gboolean rshiftdown = GetKeyState (VK_RSHIFT) & 0x8000;
gboolean oneshiftdown = (lshiftdown || rshiftdown) && !(lshiftdown && rshiftdown);
switch (msg->wParam)
{
case VK_UP:
combo = GDK_WIN32_AEROSNAP_COMBO_UP;
break;
case VK_DOWN:
combo = GDK_WIN32_AEROSNAP_COMBO_DOWN;
break;
case VK_LEFT:
combo = GDK_WIN32_AEROSNAP_COMBO_LEFT;
break;
case VK_RIGHT:
combo = GDK_WIN32_AEROSNAP_COMBO_RIGHT;
break;
}
if (oneshiftdown && combo != GDK_WIN32_AEROSNAP_COMBO_NOTHING)
combo += 4;
/* These are the only combos that Windows WM does handle for us */
if (combo == GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT ||
combo == GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT)
combo = GDK_WIN32_AEROSNAP_COMBO_NOTHING;
if (combo != GDK_WIN32_AEROSNAP_COMBO_NOTHING)
{
_gdk_win32_window_handle_aerosnap (gdk_window_get_toplevel (window), combo);
break;
}
}
event = gdk_event_new ((msg->message == WM_KEYDOWN ||
msg->message == WM_SYSKEYDOWN) ?
GDK_KEY_PRESS : GDK_KEY_RELEASE);

View File

@ -536,6 +536,9 @@ void _gdk_win32_emit_configure_event (GdkWindow *window);
guint32 _gdk_win32_keymap_get_decimal_mark (void);
void _gdk_win32_window_handle_aerosnap (GdkWindow *window,
GdkWin32AeroSnapCombo combo);
gboolean _gdk_win32_get_window_rect (GdkWindow *window,
RECT *rect);
void _gdk_win32_do_emit_configure_event (GdkWindow *window,

View File

@ -42,6 +42,8 @@
#include <cairo-win32.h>
#include <dwmapi.h>
#include <math.h>
#include "fallback-c89.c"
static void gdk_window_impl_win32_init (GdkWindowImplWin32 *window);
static void gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass);
@ -2919,6 +2921,277 @@ _gdk_window_get_functions (GdkWindow *window,
return (functions_set != NULL);
}
static void
unsnap (GdkWindow *window,
GdkScreen *screen,
gint monitor)
{
GdkWindowImplWin32 *impl;
GdkRectangle rect;
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
if (impl->snap_stash == NULL)
return;
gdk_screen_get_monitor_workarea (screen, monitor, &rect);
GDK_NOTE (MISC, g_print ("Monitor work area %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y));
/* Calculate actual unsnapped window size based on its
* old relative size. Same for position.
*/
rect.x += round (rect.width * impl->snap_stash->x);
rect.y += round (rect.height * impl->snap_stash->y);
rect.width = round (rect.width * impl->snap_stash->width);
rect.height = round (rect.height * impl->snap_stash->height);
GDK_NOTE (MISC, g_print ("Unsnapped window size %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y));
gdk_window_move_resize (window, rect.x, rect.y,
rect.width, rect.height);
g_clear_pointer (&impl->snap_stash, g_free);
}
static void
stash_window (GdkWindow *window,
GdkWindowImplWin32 *impl,
GdkScreen *screen,
gint monitor)
{
gint x, y;
gint width;
gint height;
WINDOWPLACEMENT placement;
HMONITOR hmonitor;
MONITORINFO hmonitor_info;
placement.length = sizeof(WINDOWPLACEMENT);
/* Use W32 API to get unmaximized window size, which GDK doesn't remember */
if (!GetWindowPlacement (GDK_WINDOW_HWND (window), &placement))
return;
/* MSDN is very vague, but in practice rcNormalPosition is the same as GetWindowRect(),
* only with adjustments for toolbars (which creates rather weird coodinate space issues).
* We need to get monitor info and apply workarea vs monitorarea diff to turn
* these into screen coordinates proper.
*/
hmonitor = MonitorFromWindow (GDK_WINDOW_HWND (window), MONITOR_DEFAULTTONEAREST);
hmonitor_info.cbSize = sizeof (hmonitor_info);
if (!GetMonitorInfoA (hmonitor, &hmonitor_info))
return;
if (impl->snap_stash == NULL)
impl->snap_stash = g_new0 (GdkRectangleDouble, 1);
GDK_NOTE (MISC, g_print ("monitor work area %ld x %ld @ %ld : %ld\n",
hmonitor_info.rcWork.right - hmonitor_info.rcWork.left,
hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top,
hmonitor_info.rcWork.left,
hmonitor_info.rcWork.top));
GDK_NOTE (MISC, g_print ("monitor area %ld x %ld @ %ld : %ld\n",
hmonitor_info.rcMonitor.right - hmonitor_info.rcMonitor.left,
hmonitor_info.rcMonitor.bottom - hmonitor_info.rcMonitor.top,
hmonitor_info.rcMonitor.left,
hmonitor_info.rcMonitor.top));
GDK_NOTE (MISC, g_print ("window work place %ld x %ld @ %ld : %ld\n",
placement.rcNormalPosition.right - placement.rcNormalPosition.left,
placement.rcNormalPosition.bottom - placement.rcNormalPosition.top,
placement.rcNormalPosition.left,
placement.rcNormalPosition.top));
width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
x = placement.rcNormalPosition.left - hmonitor_info.rcMonitor.left;
y = placement.rcNormalPosition.top - hmonitor_info.rcMonitor.top;
impl->snap_stash->x = (gdouble) (x) / (gdouble) (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left);
impl->snap_stash->y = (gdouble) (y) / (gdouble) (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top);
impl->snap_stash->width = (gdouble) width / (gdouble) (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left);
impl->snap_stash->height = (gdouble) height / (gdouble) (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top);
GDK_NOTE (MISC, g_print ("Stashed window %d x %d @ %d : %d as %f x %f @ %f : %f\n",
width, height, x, y,
impl->snap_stash->width, impl->snap_stash->height, impl->snap_stash->x, impl->snap_stash->y));
}
static void
snap_up (GdkWindow *window,
GdkScreen *screen,
gint monitor)
{
SHORT maxysize;
gint x, y;
gint width;
GdkWindowImplWin32 *impl;
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP;
maxysize = GetSystemMetrics (SM_CYMAXTRACK);
gdk_window_get_position (window, &x, &y);
width = gdk_window_get_width (window);
stash_window (window, impl, screen, monitor);
gdk_window_move_resize (window, x, 0, width, maxysize);
}
static void
snap_left (GdkWindow *window,
GdkScreen *screen,
gint monitor,
gint snap_monitor)
{
GdkRectangle rect;
GdkWindowImplWin32 *impl;
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFLEFT;
gdk_screen_get_monitor_workarea (screen, snap_monitor, &rect);
stash_window (window, impl, screen, monitor);
rect.width = rect.width / 2;
gdk_window_move_resize (window, rect.x, rect.y, rect.width, rect.height);
}
static void
snap_right (GdkWindow *window,
GdkScreen *screen,
gint monitor,
gint snap_monitor)
{
GdkRectangle rect;
GdkWindowImplWin32 *impl;
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT;
gdk_screen_get_monitor_workarea (screen, snap_monitor, &rect);
stash_window (window, impl, screen, monitor);
rect.width /= 2;
rect.x += rect.width;
gdk_window_move_resize (window, rect.x, rect.y, rect.width, rect.height);
}
void
_gdk_win32_window_handle_aerosnap (GdkWindow *window,
GdkWin32AeroSnapCombo combo)
{
GdkWindowImplWin32 *impl;
GdkDisplay *display;
GdkScreen *screen;
gint n_monitors, monitor;
GdkWindowState window_state = gdk_window_get_state (window);
gboolean minimized = window_state & GDK_WINDOW_STATE_ICONIFIED;
gboolean maximized = window_state & GDK_WINDOW_STATE_MAXIMIZED;
gboolean halfsnapped;
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
display = gdk_window_get_display (window);
screen = gdk_display_get_default_screen (display);
n_monitors = gdk_screen_get_n_monitors (screen);
monitor = gdk_screen_get_monitor_at_window (screen, window);
if (minimized && maximized)
minimized = FALSE;
halfsnapped = (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT ||
impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT ||
impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP);
switch (combo)
{
case GDK_WIN32_AEROSNAP_COMBO_NOTHING:
/* Do nothing */
break;
case GDK_WIN32_AEROSNAP_COMBO_UP:
if (!maximized)
{
unsnap (window, screen, monitor);
gdk_window_maximize (window);
}
break;
case GDK_WIN32_AEROSNAP_COMBO_DOWN:
case GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN:
if (maximized)
{
gdk_window_unmaximize (window);
unsnap (window, screen, monitor);
}
else if (halfsnapped)
unsnap (window, screen, monitor);
else if (!minimized)
gdk_window_iconify (window);
break;
case GDK_WIN32_AEROSNAP_COMBO_LEFT:
if (maximized)
gdk_window_unmaximize (window);
if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED ||
impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)
{
unsnap (window, screen, monitor);
snap_left (window, screen, monitor, monitor);
}
else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT)
{
unsnap (window, screen, monitor);
snap_right (window, screen, monitor, monitor - 1 >= 0 ? monitor - 1 : n_monitors - 1);
}
else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT)
{
unsnap (window, screen, monitor);
}
break;
case GDK_WIN32_AEROSNAP_COMBO_RIGHT:
if (maximized)
gdk_window_unmaximize (window);
if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED ||
impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)
{
unsnap (window, screen, monitor);
snap_right (window, screen, monitor, monitor);
}
else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT)
{
unsnap (window, screen, monitor);
}
else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT)
{
unsnap (window, screen, monitor);
snap_left (window, screen, monitor, monitor + 1 < n_monitors ? monitor + 1 : 0);
}
break;
case GDK_WIN32_AEROSNAP_COMBO_SHIFTUP:
if (!maximized &&
impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED)
{
snap_up (window, screen, monitor);
}
break;
case GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT:
case GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT:
/* No implementation needed at the moment */
break;
}
}
static const gchar *
get_cursor_name_from_op (GdkW32WindowDragOp op,
GdkWindowEdge edge)

View File

@ -46,6 +46,44 @@ typedef struct _GdkWindowImplWin32Class GdkWindowImplWin32Class;
#define GDK_IS_WINDOW_IMPL_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WINDOW_IMPL_WIN32))
#define GDK_WINDOW_IMPL_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WINDOW_IMPL_WIN32, GdkWindowImplWin32Class))
enum _GdkWin32AeroSnapCombo
{
GDK_WIN32_AEROSNAP_COMBO_NOTHING = 0,
GDK_WIN32_AEROSNAP_COMBO_UP,
GDK_WIN32_AEROSNAP_COMBO_DOWN,
GDK_WIN32_AEROSNAP_COMBO_LEFT,
GDK_WIN32_AEROSNAP_COMBO_RIGHT,
/* Same order as non-shift variants. We use it to do things like:
* AEROSNAP_UP + 4 = AEROSNAP_SHIFTUP
*/
GDK_WIN32_AEROSNAP_COMBO_SHIFTUP,
GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN,
GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT,
GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT
};
typedef enum _GdkWin32AeroSnapCombo GdkWin32AeroSnapCombo;
enum _GdkWin32AeroSnapState
{
GDK_WIN32_AEROSNAP_STATE_UNDETERMINED = 0,
GDK_WIN32_AEROSNAP_STATE_HALFLEFT,
GDK_WIN32_AEROSNAP_STATE_HALFRIGHT,
GDK_WIN32_AEROSNAP_STATE_FULLUP,
};
typedef enum _GdkWin32AeroSnapState GdkWin32AeroSnapState;
struct _GdkRectangleDouble
{
gdouble x;
gdouble y;
gdouble width;
gdouble height;
};
typedef struct _GdkRectangleDouble GdkRectangleDouble;
enum _GdkW32WindowDragOp
{
GDK_WIN32_DRAGOP_NONE = 0,
@ -173,6 +211,19 @@ struct _GdkWindowImplWin32
GdkW32DragMoveResizeContext drag_move_resize_context;
/* Remembers where the window was snapped.
* Some snap operations change their meaning if
* the window is already snapped.
*/
GdkWin32AeroSnapState snap_state;
/* Remembers window position before it was snapped.
* This is used to unsnap it.
* Position and size are percentages of the workarea
* of the monitor on which the window was before it was snapped.
*/
GdkRectangleDouble *snap_stash;
/* Decorations set by gdk_window_set_decorations() or NULL if unset */
GdkWMDecoration* decorations;