From 87103b9a1401bb9d28ee77c39310833f98d9e836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Fri, 11 Aug 2017 07:00:41 +0000 Subject: [PATCH] GDK W32: More flexible modal operation mode Instead of using a boolean to indicate a modal operation being in progress, use a set of flags, and allow these to be set and unset independently. Specifically, this allows WM_CAPTURECHANGED handler to only act when a drag-move or drag-resize modal operation is in progress, and ignore DND (which can also cause WM_CAPTURECHANGED to be posted). This avoids a crash due to assertion failure when OLE2 DND code tries to end a modal operation that was already ended by the WM_CAPTURECHANGED handler. https://bugzilla.gnome.org/show_bug.cgi?id=786121 --- gdk/win32/gdkdevicemanager-win32.c | 2 +- gdk/win32/gdkdnd-win32.c | 4 +- gdk/win32/gdkevents-win32.c | 59 ++++++++++++++++++------------ gdk/win32/gdkglobals-win32.c | 2 +- gdk/win32/gdkprivate-win32.h | 24 ++++++++---- 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/gdk/win32/gdkdevicemanager-win32.c b/gdk/win32/gdkdevicemanager-win32.c index c4740747ac..830f5131c6 100644 --- a/gdk/win32/gdkdevicemanager-win32.c +++ b/gdk/win32/gdkdevicemanager-win32.c @@ -1041,7 +1041,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS; /* Don't produce any button or motion events while a window is being * moved or resized, see bug #151090. */ - if (_modal_operation_in_progress) + if (_modal_operation_in_progress & GDK_WIN32_MODAL_OP_SIZEMOVE_MASK) { GDK_NOTE (EVENTS_OR_INPUT, g_print ("... ignored when moving/sizing\n")); return FALSE; diff --git a/gdk/win32/gdkdnd-win32.c b/gdk/win32/gdkdnd-win32.c index cec3dcc763..e2cd1ba75e 100644 --- a/gdk/win32/gdkdnd-win32.c +++ b/gdk/win32/gdkdnd-win32.c @@ -1896,11 +1896,11 @@ _gdk_win32_dnd_do_dragdrop (void) GDK_NOTE (DND, g_print ("Calling DoDragDrop\n")); - _gdk_win32_begin_modal_call (); + _gdk_win32_begin_modal_call (GDK_WIN32_MODAL_OP_DND); hr = DoDragDrop (&dobj->ido, &pending_src_context->ids, DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect); - _gdk_win32_end_modal_call (); + _gdk_win32_end_modal_call (GDK_WIN32_MODAL_OP_DND); GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n", (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" : diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 9f77a28e30..8066fabec1 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -1736,36 +1736,42 @@ modal_timer_proc (HWND hwnd, { int arbitrary_limit = 10; - while (_modal_operation_in_progress && + while (_modal_operation_in_progress != GDK_WIN32_MODAL_OP_NONE && g_main_context_pending (NULL) && arbitrary_limit--) g_main_context_iteration (NULL, FALSE); } void -_gdk_win32_begin_modal_call (void) +_gdk_win32_begin_modal_call (GdkWin32ModalOpKind kind) { - g_assert (!_modal_operation_in_progress); + GdkWin32ModalOpKind was = _modal_operation_in_progress; + g_assert (!(_modal_operation_in_progress & kind)); - _modal_operation_in_progress = TRUE; + _modal_operation_in_progress |= kind; - modal_timer = SetTimer (NULL, 0, 10, modal_timer_proc); - if (modal_timer == 0) - WIN32_API_FAILED ("SetTimer"); + if (was == GDK_WIN32_MODAL_OP_NONE) + { + modal_timer = SetTimer (NULL, 0, 10, modal_timer_proc); + + if (modal_timer == 0) + WIN32_API_FAILED ("SetTimer"); + } } void -_gdk_win32_end_modal_call (void) +_gdk_win32_end_modal_call (GdkWin32ModalOpKind kind) { - g_assert (_modal_operation_in_progress); + g_assert (_modal_operation_in_progress & kind); - _modal_operation_in_progress = FALSE; + _modal_operation_in_progress &= ~kind; - if (modal_timer != 0) + if (_modal_operation_in_progress == GDK_WIN32_MODAL_OP_NONE && + modal_timer != 0) { API_CALL (KillTimer, (NULL, modal_timer)); modal_timer = 0; - } + } } static VOID CALLBACK @@ -3246,30 +3252,37 @@ gdk_event_translate (MSG *msg, break; case WM_ENTERSIZEMOVE: - case WM_ENTERMENULOOP: - if (msg->message == WM_ENTERSIZEMOVE) - _modal_move_resize_window = msg->hwnd; - - _gdk_win32_begin_modal_call (); + _modal_move_resize_window = msg->hwnd; + _gdk_win32_begin_modal_call (GDK_WIN32_MODAL_OP_SIZEMOVE_MASK); break; case WM_EXITSIZEMOVE: - case WM_EXITMENULOOP: - if (_modal_operation_in_progress) + if (_modal_operation_in_progress & GDK_WIN32_MODAL_OP_SIZEMOVE_MASK) { _modal_move_resize_window = NULL; - _gdk_win32_end_modal_call (); + _gdk_win32_end_modal_call (GDK_WIN32_MODAL_OP_SIZEMOVE_MASK); } break; + case WM_ENTERMENULOOP: + _gdk_win32_begin_modal_call (GDK_WIN32_MODAL_OP_MENU); + break; + + case WM_EXITMENULOOP: + if (_modal_operation_in_progress & GDK_WIN32_MODAL_OP_MENU) + _gdk_win32_end_modal_call (GDK_WIN32_MODAL_OP_MENU); + break; + + break; + case WM_CAPTURECHANGED: /* Sometimes we don't get WM_EXITSIZEMOVE, for instance when you select move/size in the menu and then click somewhere without moving/resizing. We work around this using WM_CAPTURECHANGED. */ - if (_modal_operation_in_progress) + if (_modal_operation_in_progress & GDK_WIN32_MODAL_OP_SIZEMOVE_MASK) { _modal_move_resize_window = NULL; - _gdk_win32_end_modal_call (); + _gdk_win32_end_modal_call (GDK_WIN32_MODAL_OP_SIZEMOVE_MASK); } impl = GDK_WINDOW_IMPL_WIN32 (window->impl); @@ -3425,7 +3438,7 @@ gdk_event_translate (MSG *msg, } /* Call modal timer immediate so that we repaint faster after a resize. */ - if (_modal_operation_in_progress) + if (_modal_operation_in_progress & GDK_WIN32_MODAL_OP_SIZEMOVE_MASK) modal_timer_proc (0,0,0,0); /* Claim as handled, so that WM_SIZE and WM_MOVE are avoided */ diff --git a/gdk/win32/gdkglobals-win32.c b/gdk/win32/gdkglobals-win32.c index cb352a4198..f16ac48dcc 100644 --- a/gdk/win32/gdkglobals-win32.c +++ b/gdk/win32/gdkglobals-win32.c @@ -73,7 +73,7 @@ GdkWin32DndState _dnd_source_state = GDK_WIN32_DND_NONE; gint _gdk_input_ignore_wintab = FALSE; gint _gdk_max_colors = 0; -gboolean _modal_operation_in_progress = FALSE; +GdkWin32ModalOpKind _modal_operation_in_progress = GDK_WIN32_MODAL_OP_NONE; HWND _modal_move_resize_window = NULL; gboolean _ignore_destroy_clipboard = FALSE; diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 27aec8ebfa..8b3310a0ac 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -343,8 +343,23 @@ void _gdk_win32_ole2_dnd_property_change (GdkAtom type, const guchar *data, gint nelements); -void _gdk_win32_begin_modal_call (void); -void _gdk_win32_end_modal_call (void); +typedef enum { + GDK_WIN32_MODAL_OP_NONE = 0x0, + GDK_WIN32_MODAL_OP_SIZE = 0x1 << 0, + GDK_WIN32_MODAL_OP_MOVE = 0x1 << 1, + GDK_WIN32_MODAL_OP_MENU = 0x1 << 2, + GDK_WIN32_MODAL_OP_DND = 0x1 << 3 +} GdkWin32ModalOpKind; + +#define GDK_WIN32_MODAL_OP_SIZEMOVE_MASK (GDK_WIN32_MODAL_OP_SIZE | GDK_WIN32_MODAL_OP_MOVE) + +/* Non-zero while a modal sizing, moving, or dnd operation is in progress */ +extern GdkWin32ModalOpKind _modal_operation_in_progress; + +extern HWND _modal_move_resize_window; + +void _gdk_win32_begin_modal_call (GdkWin32ModalOpKind kind); +void _gdk_win32_end_modal_call (GdkWin32ModalOpKind kind); /* Options */ @@ -353,11 +368,6 @@ extern gint _gdk_max_colors; #define GDK_WIN32_COLORMAP_DATA(cmap) ((GdkColormapPrivateWin32 *) GDK_COLORMAP (cmap)->windowing_data) -/* TRUE while a modal sizing, moving, or dnd operation is in progress */ -extern gboolean _modal_operation_in_progress; - -extern HWND _modal_move_resize_window; - /* TRUE when we are emptying the clipboard ourselves */ extern gboolean _ignore_destroy_clipboard;