[gtk+] GDK W32: custom (non-WM) drag-move and drag-resize code



commit e03946bd285d860fc9011a2fbb5363c2a176aaab
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Sat Feb 6 12:06:41 2016 +0000

    GDK W32: custom (non-WM) drag-move and drag-resize code
    
    Normally works only on CSD windows, non-CSD windows continue
    to use WM modal loop for drag-resizing and drag-moving. However,
    if it is activated on non-CSD windows, it does work.
    
    Has the advantage of being completely immune to AeroSnap.
    AeroSnap only worked partially on CSD windows, with the only part
    that worked being "don't let users drag window titlebar outside of
    the desktop". Now AeroSnap doesn't work on windows moved by
    this code at all, which is good, since they currently don't work
    well with it due to the way shadows are drawn.
    
    It's possible to also re-implement AeroSnap (or something similar),
    but that is a story for another commit.
    
    This code was originally intended to fix the problem of window
    size and window contents desynchronization, but failed to achieve
    that result in the end. Nevertheless, it serves as a foundation for
    other changes to the way window resizing works.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=761629

 gdk/win32/gdkevents-win32.c  |  223 +++++++++++++++----------
 gdk/win32/gdkprivate-win32.h |   12 ++
 gdk/win32/gdkwindow-win32.c  |  388 +++++++++++++++++++++++++++++++++++-------
 gdk/win32/gdkwindow-win32.h  |   49 ++++++
 4 files changed, 523 insertions(+), 149 deletions(-)
---
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index b6a0b61..da21e9f 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -1408,8 +1408,14 @@ doesnt_want_char (gint mask,
   return !(mask & (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK));
 }
 
-void
-_gdk_win32_emit_configure_event (GdkWindow *window)
+/* Acquires actual client area size of the underlying native window.
+ * Rectangle is in GDK screen coordinates (_gdk_offset_* is added).
+ * Returns FALSE if configure events should be inhibited,
+ * TRUE otherwise.
+ */
+gboolean
+_gdk_win32_get_window_rect (GdkWindow *window,
+                            RECT      *rect)
 {
   GdkWindowImplWin32 *window_impl;
   RECT client_rect;
@@ -1417,8 +1423,6 @@ _gdk_win32_emit_configure_event (GdkWindow *window)
   HWND hwnd;
 
   window_impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
-  if (window_impl->inhibit_configure)
-    return;
 
   hwnd = GDK_WINDOW_HWND (window);
 
@@ -1434,11 +1438,23 @@ _gdk_win32_emit_configure_event (GdkWindow *window)
       point.y += _gdk_offset_y;
     }
 
-  window->width = client_rect.right - client_rect.left;
-  window->height = client_rect.bottom - client_rect.top;
+  rect->left = point.x;
+  rect->top = point.y;
+  rect->right = point.x + client_rect.right - client_rect.left;
+  rect->bottom = point.y + client_rect.bottom - client_rect.top;
+
+  return !window_impl->inhibit_configure;
+}
+
+void
+_gdk_win32_do_emit_configure_event (GdkWindow *window,
+                                    RECT       rect)
+{
+  window->width = rect.right - rect.left;
+  window->height = rect.bottom - rect.top;
 
-  window->x = point.x;
-  window->y = point.y;
+  window->x = rect.left;
+  window->y = rect.top;
 
   _gdk_window_update_size (window);
 
@@ -1448,16 +1464,27 @@ _gdk_win32_emit_configure_event (GdkWindow *window)
 
       event->configure.window = window;
 
-      event->configure.width = client_rect.right - client_rect.left;
-      event->configure.height = client_rect.bottom - client_rect.top;
+      event->configure.width = rect.right - rect.left;
+      event->configure.height = rect.bottom - rect.top;
 
-      event->configure.x = point.x;
-      event->configure.y = point.y;
+      event->configure.x = rect.left;
+      event->configure.y = rect.top;
 
       _gdk_win32_append_event (event);
     }
 }
 
+void
+_gdk_win32_emit_configure_event (GdkWindow *window)
+{
+  RECT rect;
+
+  if (!_gdk_win32_get_window_rect (window, &rect))
+    return;
+
+  _gdk_win32_do_emit_configure_event (window, rect);
+}
+
 cairo_region_t *
 _gdk_win32_hrgn_to_region (HRGN hrgn)
 {
@@ -1874,6 +1901,76 @@ ensure_stacking_on_activate_app (MSG       *msg,
     }
 }
 
+gboolean
+_gdk_win32_window_fill_min_max_info (GdkWindow  *window,
+                                     MINMAXINFO *mmi)
+{
+  GdkWindowImplWin32 *impl;
+  RECT rect;
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return FALSE;
+
+  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+  if (impl->hint_flags & GDK_HINT_MIN_SIZE)
+    {
+      rect.left = rect.top = 0;
+      rect.right = impl->hints.min_width;
+      rect.bottom = impl->hints.min_height;
+
+      _gdk_win32_adjust_client_rect (window, &rect);
+
+      mmi->ptMinTrackSize.x = rect.right - rect.left;
+      mmi->ptMinTrackSize.y = rect.bottom - rect.top;
+    }
+
+  if (impl->hint_flags & GDK_HINT_MAX_SIZE)
+    {
+      int maxw, maxh;
+
+      rect.left = rect.top = 0;
+      rect.right = impl->hints.max_width;
+      rect.bottom = impl->hints.max_height;
+
+      _gdk_win32_adjust_client_rect (window, &rect);
+
+      /* at least on win9x we have the 16 bit trouble */
+      maxw = rect.right - rect.left;
+      maxh = rect.bottom - rect.top;
+      mmi->ptMaxTrackSize.x = maxw > 0 && maxw < G_MAXSHORT ? maxw : G_MAXSHORT;
+      mmi->ptMaxTrackSize.y = maxh > 0 && maxh < G_MAXSHORT ? maxh : G_MAXSHORT;
+    }
+  else
+    {
+      HMONITOR nearest_monitor;
+      MONITORINFO nearest_info;
+
+      nearest_monitor = MonitorFromWindow (GDK_WINDOW_HWND (window), MONITOR_DEFAULTTONEAREST);
+      nearest_info.cbSize = sizeof (nearest_info);
+
+      if (GetMonitorInfoA (nearest_monitor, &nearest_info))
+        {
+          /* MSDN says that we must specify maximized window
+           * size as if it was located on the primary monitor.
+           * However, we still need to account for a taskbar
+           * that might or might not be on the nearest monitor where
+           * window will actually end up.
+           * "0" here is the top-left corner of the primary monitor.
+           */
+          mmi->ptMaxPosition.x = 0 + (nearest_info.rcWork.left - nearest_info.rcMonitor.left);
+          mmi->ptMaxPosition.y = 0 + (nearest_info.rcWork.top - nearest_info.rcMonitor.top);
+          mmi->ptMaxSize.x = nearest_info.rcWork.right - nearest_info.rcWork.left;
+          mmi->ptMaxSize.y = nearest_info.rcWork.bottom - nearest_info.rcWork.top;
+        }
+
+      mmi->ptMaxTrackSize.x = GetSystemMetrics (SM_CXMAXTRACK);
+      mmi->ptMaxTrackSize.y = GetSystemMetrics (SM_CYMAXTRACK);
+    }
+
+  return TRUE;
+}
+
 #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \
                             GDK_BUTTON2_MASK | \
                             GDK_BUTTON3_MASK | \
@@ -1887,7 +1984,6 @@ gdk_event_translate (MSG  *msg,
   RECT rect, *drag, orig_drag;
   POINT point;
   MINMAXINFO *mmi;
-  LONG style;
   HWND hwnd;
   GdkCursor *cursor;
   BYTE key_state[256];
@@ -2469,7 +2565,13 @@ gdk_event_translate (MSG  *msg,
       current_root_x = msg->pt.x + _gdk_offset_x;
       current_root_y = msg->pt.y + _gdk_offset_y;
 
-      if (!_gdk_input_ignore_core)
+      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+      if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
+        {
+          gdk_win32_window_do_move_resize_drag (window, current_root_x, current_root_y);
+        }
+      else if (!_gdk_input_ignore_core)
        {
          event = gdk_event_new (GDK_MOTION_NOTIFY);
          event->motion.window = window;
@@ -2775,6 +2877,11 @@ gdk_event_translate (MSG  *msg,
          _modal_move_resize_window = NULL;
          _gdk_win32_end_modal_call ();
        }
+
+      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+      if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
+        gdk_win32_window_end_move_resize_drag (window);
       break;
 
     case WM_WINDOWPOSCHANGING:
@@ -3130,11 +3237,8 @@ gdk_event_translate (MSG  *msg,
       break;
 
     case WM_GETMINMAXINFO:
-      if (GDK_WINDOW_DESTROYED (window))
-       break;
-
-      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
       mmi = (MINMAXINFO*) msg->lParam;
+
       GDK_NOTE (EVENTS, g_print (" (mintrack:%ldx%ld maxtrack:%ldx%ld "
                                 "maxpos:%+ld%+ld maxsize:%ldx%ld)",
                                 mmi->ptMinTrackSize.x, mmi->ptMinTrackSize.y,
@@ -3142,79 +3246,20 @@ gdk_event_translate (MSG  *msg,
                                 mmi->ptMaxPosition.x, mmi->ptMaxPosition.y,
                                 mmi->ptMaxSize.x, mmi->ptMaxSize.y));
 
-      style = GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE);
-
-      if (impl->hint_flags & GDK_HINT_MIN_SIZE)
-       {
-         rect.left = rect.top = 0;
-         rect.right = impl->hints.min_width;
-         rect.bottom = impl->hints.min_height;
-
-         _gdk_win32_adjust_client_rect (window, &rect);
-
-         mmi->ptMinTrackSize.x = rect.right - rect.left;
-         mmi->ptMinTrackSize.y = rect.bottom - rect.top;
-       }
-
-      if (impl->hint_flags & GDK_HINT_MAX_SIZE)
-       {
-         int maxw, maxh;
-
-         rect.left = rect.top = 0;
-         rect.right = impl->hints.max_width;
-         rect.bottom = impl->hints.max_height;
-
-         _gdk_win32_adjust_client_rect (window, &rect);
-
-         /* at least on win9x we have the 16 bit trouble */
-         maxw = rect.right - rect.left;
-         maxh = rect.bottom - rect.top;
-         mmi->ptMaxTrackSize.x = maxw > 0 && maxw < G_MAXSHORT ? maxw : G_MAXSHORT;
-         mmi->ptMaxTrackSize.y = maxh > 0 && maxh < G_MAXSHORT ? maxh : G_MAXSHORT;
-       }
-      /* Assume that these styles are incompatible with CSD,
-       * so there's no reason for us to override the defaults.
-       */
-      else if ((style & (WS_BORDER | WS_THICKFRAME)) == 0)
-       {
-         HMONITOR nearest_monitor;
-         MONITORINFO nearest_info;
-
-         nearest_monitor = MonitorFromWindow (GDK_WINDOW_HWND (window), MONITOR_DEFAULTTONEAREST);
-         nearest_info.cbSize = sizeof (nearest_info);
-
-         if (GetMonitorInfoA (nearest_monitor, &nearest_info))
-           {
-             /* MSDN says that we must specify maximized window
-              * size as if it was located on the primary monitor.
-              * However, we still need to account for a taskbar
-              * that might or might not be on the nearest monitor where
-              * window will actually end up.
-               * "0" here is the top-left corner of the primary monitor.
-              */
-             mmi->ptMaxPosition.x = 0 + (nearest_info.rcWork.left - nearest_info.rcMonitor.left);
-             mmi->ptMaxPosition.y = 0 + (nearest_info.rcWork.top - nearest_info.rcMonitor.top);
-             mmi->ptMaxSize.x = nearest_info.rcWork.right - nearest_info.rcWork.left;
-             mmi->ptMaxSize.y = nearest_info.rcWork.bottom - nearest_info.rcWork.top;
-           }
-
-         mmi->ptMaxTrackSize.x = GetSystemMetrics (SM_CXMAXTRACK);
-         mmi->ptMaxTrackSize.y = GetSystemMetrics (SM_CYMAXTRACK);
-       }
+      if (_gdk_win32_window_fill_min_max_info (window, mmi))
+        {
+          /* Don't call DefWindowProcW() */
+          GDK_NOTE (EVENTS,
+                    g_print (" (handled, mintrack:%ldx%ld maxtrack:%ldx%ld "
+                             "maxpos:%+ld%+ld maxsize:%ldx%ld)",
+                             mmi->ptMinTrackSize.x, mmi->ptMinTrackSize.y,
+                             mmi->ptMaxTrackSize.x, mmi->ptMaxTrackSize.y,
+                             mmi->ptMaxPosition.x, mmi->ptMaxPosition.y,
+                             mmi->ptMaxSize.x, mmi->ptMaxSize.y));
 
-      if (impl->hint_flags & (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE))
-       {
-         /* Don't call DefWindowProcW() */
-         GDK_NOTE (EVENTS, g_print (" (handled, mintrack:%ldx%ld maxtrack:%ldx%ld "
-                                    "maxpos:%+ld%+ld maxsize:%ldx%ld)",
-                                    mmi->ptMinTrackSize.x, mmi->ptMinTrackSize.y,
-                                    mmi->ptMaxTrackSize.x, mmi->ptMaxTrackSize.y,
-                                    mmi->ptMaxPosition.x, mmi->ptMaxPosition.y,
-                                    mmi->ptMaxSize.x, mmi->ptMaxSize.y));
-         return_val = TRUE;
-       }
+          return_val = TRUE;
+        }
 
-      return_val = TRUE;
       break;
 
     case WM_CLOSE:
diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h
index f590ff7..ba79a1b 100644
--- a/gdk/win32/gdkprivate-win32.h
+++ b/gdk/win32/gdkprivate-win32.h
@@ -521,8 +521,20 @@ gchar *_gdk_win32_display_manager_get_atom_name (GdkDisplayManager *manager,
 void _gdk_win32_append_event (GdkEvent *event);
 void _gdk_win32_emit_configure_event (GdkWindow *window);
 
+
 guint32 _gdk_win32_keymap_get_decimal_mark (void);
 
+gboolean _gdk_win32_get_window_rect             (GdkWindow  *window,
+                                                 RECT       *rect);
+void     _gdk_win32_do_emit_configure_event     (GdkWindow  *window,
+                                                 RECT        rect);
+void      gdk_win32_window_do_move_resize_drag  (GdkWindow  *window,
+                                                 gint        x,
+                                                 gint        y);
+void      gdk_win32_window_end_move_resize_drag (GdkWindow  *window);
+gboolean _gdk_win32_window_fill_min_max_info    (GdkWindow  *window,
+                                                 MINMAXINFO *mmi);
+
 /* Initialization */
 void _gdk_win32_windowing_init (void);
 void _gdk_dnd_init    (void);
diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c
index bd64753..513a3bf 100644
--- a/gdk/win32/gdkwindow-win32.c
+++ b/gdk/win32/gdkwindow-win32.c
@@ -184,6 +184,50 @@ gdk_window_impl_win32_finalize (GObject *object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+static void
+gdk_win32_window_end_paint (GdkWindow *window)
+{
+  GdkWindowImplWin32 *impl;
+  RECT window_rect;
+  gint x, y;
+
+  if (window == NULL || GDK_WINDOW_DESTROYED (window))
+    return;
+
+  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+  if (!impl->drag_move_resize_context.native_move_resize_pending)
+    return;
+
+  impl->drag_move_resize_context.native_move_resize_pending = FALSE;
+
+  gdk_window_get_position (window, &x, &y);
+  window_rect.left = x;
+  window_rect.top = y;
+  window_rect.right = window_rect.left + gdk_window_get_width (window);
+  window_rect.bottom = window_rect.top + gdk_window_get_height (window);
+
+  /* Turn client area into window area */
+  _gdk_win32_adjust_client_rect (window, &window_rect);
+
+  /* Convert GDK screen coordinates to W32 desktop coordinates */
+  window_rect.left -= _gdk_offset_x;
+  window_rect.right -= _gdk_offset_x;
+  window_rect.top -= _gdk_offset_y;
+  window_rect.bottom -= _gdk_offset_y;
+
+  GDK_NOTE (EVENTS, g_print ("Setting window position ... "));
+
+  API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window),
+                           SWP_NOZORDER_SPECIFIED,
+                          window_rect.left, window_rect.top,
+                           window_rect.right - window_rect.left,
+                           window_rect.bottom - window_rect.top,
+                          SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW));
+
+  GDK_NOTE (EVENTS, g_print (" ... set window position\n"));
+}
+
 void
 _gdk_win32_adjust_client_rect (GdkWindow *window,
                               RECT      *rect)
@@ -2639,86 +2683,308 @@ _gdk_window_get_functions (GdkWindow     *window,
 }
 
 static void
-gdk_win32_window_begin_resize_drag (GdkWindow     *window,
-                              GdkWindowEdge  edge,
-                              GdkDevice     *device,
-                              gint           button,
-                              gint           root_x,
-                              gint           root_y,
-                              guint32        timestamp)
+setup_drag_move_resize_context (GdkWindow                   *window,
+                                GdkW32DragMoveResizeContext *context,
+                                GdkW32WindowDragOp           op,
+                                GdkWindowEdge                edge,
+                                GdkDevice                   *device,
+                                gint                         button,
+                                gint                         root_x,
+                                gint                         root_y,
+                                guint32                      timestamp)
 {
-  WPARAM winedge;
+  RECT rect;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  _gdk_win32_get_window_rect (window, &rect);
 
-  if (GDK_WINDOW_DESTROYED (window))
-    return;
+  context->op = op;
+  context->edge = edge;
+  context->device = device;
+  context->button = button;
+  context->start_root_x = root_x;
+  context->start_root_y = root_y;
+  context->timestamp = timestamp;
+  context->start_rect = rect;
 
-  /* Tell Windows to start interactively resizing the window by pretending that
-   * the left pointer button was clicked in the suitable edge or corner. This
-   * will only work if the button is down when this function is called, and
-   * will only work with button 1 (left), since Windows only allows window
-   * dragging using the left mouse button.
-   */
-  if (button != 1)
+  GDK_NOTE (EVENTS,
+            g_print ("begin drag moveresize: "
+                     "op %u, edge %d, device %p, "
+                     "button %d, coord %d:%d, time %u\n",
+                     context->op, context->edge, context->device,
+                     context->button, context->start_root_x,
+                     context->start_root_y, context->timestamp));
+}
+
+void
+gdk_win32_window_end_move_resize_drag (GdkWindow *window)
+{
+  GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+  GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context;
+
+  context->op = GDK_WIN32_DRAGOP_NONE;
+  GDK_NOTE (EVENTS,
+            g_print ("end drag moveresize: "
+                     "op %u, edge %d, device %p, "
+                     "button %d, coord %d:%d, time %u\n",
+                     context->op, context->edge, context->device,
+                     context->button, context->start_root_x,
+                     context->start_root_y, context->timestamp));
+}
+
+void
+gdk_win32_window_do_move_resize_drag (GdkWindow *window,
+                                      gint       x,
+                                      gint       y)
+{
+  RECT rect;
+  RECT new_rect;
+  gint diffy, diffx;
+  MINMAXINFO mmi;
+  GdkWindowImplWin32 *impl;
+  GdkW32DragMoveResizeContext *context;
+  gint width;
+  gint height;
+
+  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+  context = &impl->drag_move_resize_context;
+
+  if (!_gdk_win32_get_window_rect (window, &rect))
     return;
 
-  /* Must break the automatic grab that occured when the button was
-   * pressed, otherwise it won't work.
-   */
-  gdk_device_ungrab (device, 0);
+  new_rect = context->start_rect;
+  diffx = x - context->start_root_x;
+  diffy = y - context->start_root_y;
 
-  switch (edge)
+  switch (context->op)
     {
-    case GDK_WINDOW_EDGE_NORTH_WEST:
-      winedge = HTTOPLEFT;
-      break;
+    case GDK_WIN32_DRAGOP_RESIZE:
+      switch (context->edge)
+        {
+        case GDK_WINDOW_EDGE_NORTH_WEST:
+          new_rect.left += diffx;
+          new_rect.top += diffy;
+          break;
+
+        case GDK_WINDOW_EDGE_NORTH:
+          new_rect.top += diffy;
+          break;
+
+        case GDK_WINDOW_EDGE_NORTH_EAST:
+          new_rect.right += diffx;
+          new_rect.top += diffy;
+          break;
+
+        case GDK_WINDOW_EDGE_WEST:
+          new_rect.left += diffx;
+          break;
+
+        case GDK_WINDOW_EDGE_EAST:
+          new_rect.right += diffx;
+          break;
+
+        case GDK_WINDOW_EDGE_SOUTH_WEST:
+          new_rect.left += diffx;
+          new_rect.bottom += diffy;
+          break;
+
+        case GDK_WINDOW_EDGE_SOUTH:
+          new_rect.bottom += diffy;
+          break;
+
+        case GDK_WINDOW_EDGE_SOUTH_EAST:
+        default:
+          new_rect.right += diffx;
+          new_rect.bottom += diffy;
+          break;
+        }
 
-    case GDK_WINDOW_EDGE_NORTH:
-      winedge = HTTOP;
-      break;
+      /* When handling WM_GETMINMAXINFO, mmi is already populated
+       * by W32 WM and we apply our stuff on top of that.
+       * Here it isn't, so we should at least clear it.
+       */
+      memset (&mmi, 0, sizeof (mmi));
 
-    case GDK_WINDOW_EDGE_NORTH_EAST:
-      winedge = HTTOPRIGHT;
-      break;
+      if (!_gdk_win32_window_fill_min_max_info (window, &mmi))
+        break;
 
-    case GDK_WINDOW_EDGE_WEST:
-      winedge = HTLEFT;
-      break;
+      width = new_rect.right - new_rect.left;
+      height = new_rect.bottom - new_rect.top;
 
-    case GDK_WINDOW_EDGE_EAST:
-      winedge = HTRIGHT;
-      break;
+      if (width > mmi.ptMaxTrackSize.x)
+        {
+          switch (context->edge)
+            {
+            case GDK_WINDOW_EDGE_NORTH_WEST:
+            case GDK_WINDOW_EDGE_WEST:
+            case GDK_WINDOW_EDGE_SOUTH_WEST:
+              new_rect.left = new_rect.right - mmi.ptMaxTrackSize.x;
+              break;
+
+            case GDK_WINDOW_EDGE_NORTH_EAST:
+            case GDK_WINDOW_EDGE_EAST:
+            case GDK_WINDOW_EDGE_SOUTH_EAST:
+            default:
+              new_rect.right = new_rect.left + mmi.ptMaxTrackSize.x;
+              break;
+            }
+        }
+      else if (width < mmi.ptMinTrackSize.x)
+        {
+          switch (context->edge)
+            {
+            case GDK_WINDOW_EDGE_NORTH_WEST:
+            case GDK_WINDOW_EDGE_WEST:
+            case GDK_WINDOW_EDGE_SOUTH_WEST:
+              new_rect.left = new_rect.right - mmi.ptMinTrackSize.x;
+              break;
+
+            case GDK_WINDOW_EDGE_NORTH_EAST:
+            case GDK_WINDOW_EDGE_EAST:
+            case GDK_WINDOW_EDGE_SOUTH_EAST:
+            default:
+              new_rect.right = new_rect.left + mmi.ptMinTrackSize.x;
+              break;
+            }
+        }
 
-    case GDK_WINDOW_EDGE_SOUTH_WEST:
-      winedge = HTBOTTOMLEFT;
-      break;
+      if (height > mmi.ptMaxTrackSize.y)
+        {
+          switch (context->edge)
+            {
+            case GDK_WINDOW_EDGE_NORTH_WEST:
+            case GDK_WINDOW_EDGE_NORTH:
+            case GDK_WINDOW_EDGE_NORTH_EAST:
+              new_rect.top = new_rect.bottom - mmi.ptMaxTrackSize.y;
+
+            case GDK_WINDOW_EDGE_SOUTH_WEST:
+            case GDK_WINDOW_EDGE_SOUTH:
+            case GDK_WINDOW_EDGE_SOUTH_EAST:
+            default:
+              new_rect.bottom = new_rect.top + mmi.ptMaxTrackSize.y;
+              break;
+            }
+        }
+      else if (height < mmi.ptMinTrackSize.y)
+        {
+          switch (context->edge)
+            {
+            case GDK_WINDOW_EDGE_NORTH_WEST:
+            case GDK_WINDOW_EDGE_NORTH:
+            case GDK_WINDOW_EDGE_NORTH_EAST:
+              new_rect.top = new_rect.bottom - mmi.ptMinTrackSize.y;
+
+            case GDK_WINDOW_EDGE_SOUTH_WEST:
+            case GDK_WINDOW_EDGE_SOUTH:
+            case GDK_WINDOW_EDGE_SOUTH_EAST:
+            default:
+              new_rect.bottom = new_rect.top + mmi.ptMinTrackSize.y;
+              break;
+            }
+        }
 
-    case GDK_WINDOW_EDGE_SOUTH:
-      winedge = HTBOTTOM;
       break;
-
-    case GDK_WINDOW_EDGE_SOUTH_EAST:
+    case GDK_WIN32_DRAGOP_MOVE:
+      new_rect.left += diffx;
+      new_rect.top += diffy;
+      new_rect.right += diffx;
+      new_rect.bottom += diffy;
+      break;
     default:
-      winedge = HTBOTTOMRIGHT;
       break;
     }
 
-  DefWindowProcW (GDK_WINDOW_HWND (window), WM_NCLBUTTONDOWN, winedge,
-                 MAKELPARAM (root_x - _gdk_offset_x, root_y - _gdk_offset_y));
+  if (context->op == GDK_WIN32_DRAGOP_RESIZE &&
+      (rect.left != new_rect.left ||
+       rect.right != new_rect.right ||
+       rect.top != new_rect.top ||
+       rect.bottom != new_rect.bottom))
+    {
+      context->native_move_resize_pending = TRUE;
+      _gdk_win32_do_emit_configure_event (window, new_rect);
+    }
+  else if (context->op == GDK_WIN32_DRAGOP_MOVE &&
+           (rect.left != new_rect.left ||
+            rect.top != new_rect.top))
+    {
+      POINT window_position;
+
+      context->native_move_resize_pending = FALSE;
+
+      _gdk_win32_do_emit_configure_event (window, new_rect);
+
+      /* Turn client area into window area */
+      _gdk_win32_adjust_client_rect (window, &new_rect);
+
+      /* Convert GDK screen coordinates to W32 desktop coordinates */
+      new_rect.left -= _gdk_offset_x;
+      new_rect.right -= _gdk_offset_x;
+      new_rect.top -= _gdk_offset_y;
+      new_rect.bottom -= _gdk_offset_y;
+
+      window_position.x = new_rect.left;
+      window_position.y = new_rect.top;
+
+      /* Move immediately, no need to wait for redraw */
+      API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window),
+                               SWP_NOZORDER_SPECIFIED,
+                               window_position.x, window_position.y,
+                               0, 0,
+                               SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE));
+    }
+}
+
+static void
+gdk_win32_window_begin_resize_drag (GdkWindow     *window,
+                                    GdkWindowEdge  edge,
+                                    GdkDevice     *device,
+                                    gint           button,
+                                    gint           root_x,
+                                    gint           root_y,
+                                    guint32        timestamp)
+{
+  GdkWindowImplWin32 *impl;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window) ||
+      GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD ||
+      IsIconic (GDK_WINDOW_HWND (window)))
+    return;
+
+  /* Tell Windows to start interactively resizing the window by pretending that
+   * the left pointer button was clicked in the suitable edge or corner. This
+   * will only work if the button is down when this function is called, and
+   * will only work with button 1 (left), since Windows only allows window
+   * dragging using the left mouse button.
+   */
+  if (button != 1)
+    return;
+
+  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+  if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
+    gdk_win32_window_end_move_resize_drag (window);
+
+  setup_drag_move_resize_context (window, &impl->drag_move_resize_context,
+                                  GDK_WIN32_DRAGOP_RESIZE, edge, device,
+                                  button, root_x, root_y, timestamp);
 }
 
 static void
 gdk_win32_window_begin_move_drag (GdkWindow *window,
-                            GdkDevice *device,
-                            gint       button,
-                            gint       root_x,
-                            gint       root_y,
-                            guint32    timestamp)
+                                  GdkDevice *device,
+                                  gint       button,
+                                  gint       root_x,
+                                  gint       root_y,
+                                  guint32    timestamp)
 {
+  GdkWindowImplWin32 *impl;
+
   g_return_if_fail (GDK_IS_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) ||
+      GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD ||
+      IsIconic (GDK_WINDOW_HWND (window)))
     return;
 
   /* Tell Windows to start interactively moving the window by pretending that
@@ -2730,13 +2996,14 @@ gdk_win32_window_begin_move_drag (GdkWindow *window,
   if (button != 1)
     return;
 
-  /* Must break the automatic grab that occured when the button was pressed,
-   * otherwise it won't work.
-   */
-  gdk_device_ungrab (device, 0);
+  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+  if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
+    gdk_win32_window_end_move_resize_drag (window);
 
-  DefWindowProcW (GDK_WINDOW_HWND (window), WM_NCLBUTTONDOWN, HTCAPTION,
-                 MAKELPARAM (root_x - _gdk_offset_x, root_y - _gdk_offset_y));
+  setup_drag_move_resize_context (window, &impl->drag_move_resize_context,
+                                  GDK_WIN32_DRAGOP_MOVE, GDK_WINDOW_EDGE_NORTH_WEST,
+                                  device, button, root_x, root_y, timestamp);
 }
 
 
@@ -3447,6 +3714,7 @@ gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass)
   impl_class->destroy_foreign = gdk_win32_window_destroy_foreign;
   impl_class->get_shape = gdk_win32_window_get_shape;
   //FIXME?: impl_class->get_input_shape = gdk_win32_window_get_input_shape;
+  impl_class->end_paint = gdk_win32_window_end_paint;
 
   //impl_class->beep = gdk_x11_window_beep;
 
diff --git a/gdk/win32/gdkwindow-win32.h b/gdk/win32/gdkwindow-win32.h
index 8f17ead..b7389f1 100644
--- a/gdk/win32/gdkwindow-win32.h
+++ b/gdk/win32/gdkwindow-win32.h
@@ -46,6 +46,53 @@ 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 _GdkW32WindowDragOp
+{
+  GDK_WIN32_DRAGOP_NONE = 0,
+  GDK_WIN32_DRAGOP_RESIZE,
+  GDK_WIN32_DRAGOP_MOVE,
+  GDK_WIN32_DRAGOP_COUNT
+};
+
+typedef enum _GdkW32WindowDragOp GdkW32WindowDragOp;
+
+struct _GdkW32DragMoveResizeContext
+{
+  /* The kind of drag-operation going on. */
+  GdkW32WindowDragOp op;
+
+  /* The edge that was grabbed for resizing. Not used for moving. */
+  GdkWindowEdge      edge;
+
+  /* Not used */
+  GdkDevice         *device;
+
+  /* Not used */
+  gint               button;
+
+  /* Initial cursor position when the operation began.
+   * Current cursor position is subtracted from it to find how far
+   * to move window border(s).
+   */
+  gint               start_root_x;
+  gint               start_root_y;
+
+  /* Initial window rectangle (position and size).
+   * The window is resized/moved relative to this (see start_root_*).
+   */
+  RECT               start_rect;
+
+  /* Not used */
+  guint32            timestamp;
+
+  /* TRUE if during the next redraw we should call SetWindowPos() to push
+   * the window size and poistion to the native window.
+   */
+  gboolean           native_move_resize_pending;
+};
+
+typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext;
+
 struct _GdkWindowImplWin32
 {
   GdkWindowImpl parent_instance;
@@ -83,6 +130,8 @@ struct _GdkWindowImplWin32
   HDC              hdc;
   int              hdc_count;
   HBITMAP          saved_dc_bitmap; /* Original bitmap for dc */
+
+  GdkW32DragMoveResizeContext drag_move_resize_context;
 };
 
 struct _GdkWindowImplWin32Class


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]