Win32 - handling mainloop while showing modal comon controls
- From: Alexander Larsson <alexl redhat com>
- To: gtk-devel-list gnome org
- Subject: Win32 - handling mainloop while showing modal comon controls
- Date: Tue, 11 Apr 2006 10:59:03 -0400 (EDT)
I'm working on the win32 printing backend again, and I'm now looking at
the "windows don't redraw while print dialog is up" issue. The issue is of
course that we don't run the gtk mainloop while the common dialogs run.
Previous proposals where running the dialog in a new thread or calling th
mainlop from a SetTimer callback. Neither of these approaches is very
nice, and I have figured out another approach that I'd like some feedback
on.
First I'd like to outline the problems with the suggested approaches:
Threads:
Gtk+ on windows doesn't really handle threads well due to the threading
model of windows. Also, you can't always be sure that gtk+ apps initialize
threading. This is extra problematic since we really want to try to embed
gtk+ widgets into the print dialogs.
Timeouts:
Pumping the mainloop in timeouts can easily cause stutter or flashing,
since we don't immediately respond to WM_PAINT messages. Also, iterating
the gtk+ mainloop will cause gdk-win32 to pump the win32 mainloop
(i.e. call DispatchMessage), something that the dialog mainloop is also
doing. This could cause all sorts of weird recursion.
So, what happens when you run the printing dialog? Its got its own win32
mainloop, so it will be calling GetMessage() and DispatchMessage(), which
will cause the gdk Window Process to get called. This will convert the
Win32 events to Gdk events and put them on the gdk event queue. However,
we will never actually handle the events in the gdk queue, because we're
not iterating the mainloop.
My approach is to iterate the gtk+ mainloop from the win32 mainloop when
there are messages in the gdk queue, but limit the gtk+ mainloop so that
it only looks at events in the gdk event queue and touch the Win32
message queue. The way we do this is that whenever there is a modal win32
dialog (with its own mainloop) up we set a special variable in gdk that
causes it to not get messages from win32, and to send a message to a
specified window whenever we put an event in the gdk event queue. We then
use special hooks in the print dialog functions to receive this message
and pump the gtk+ mainloop.
The attached patch does this, and it seems to work fine for me. Its not an
ideal solution, since we only handle events generated from windows, not
e.g. glib timeouts, or other fd:s added to the gtk+ mainloop. However, i'm
not sure that is an enourmous problem.
I'd like to get some feedback on this approach and patch from the win32
developers though.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Alexander Larsson Red Hat, Inc
alexl redhat com alla lysator liu se
He's a war-weary bohemian househusband with a winning smile and a way with the
ladies. She's a violent Buddhist research scientist trying to make a
difference in a man's world. They fight crime!
Index: gdk/gdk.symbols
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdk.symbols,v
retrieving revision 1.35
diff -u -r1.35 gdk.symbols
--- gdk/gdk.symbols 8 Mar 2006 17:02:32 -0000 1.35
+++ gdk/gdk.symbols 11 Apr 2006 14:17:00 -0000
@@ -1110,6 +1110,12 @@
#endif
#if IN_HEADER(__GDK_WIN32_H__)
+#if IN_FILE(__GDK_EVENTS_WIN32_C__)
+gdk_win32_set_modal_dialog
+#endif
+#endif
+
+#if IN_HEADER(__GDK_WIN32_H__)
#if IN_FILE(__GDK_GC_WIN32_C__)
gdk_win32_hdc_get
gdk_win32_hdc_release
Index: gdk/win32/gdkevents-win32.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkevents-win32.c,v
retrieving revision 1.164
diff -u -r1.164 gdkevents-win32.c
--- gdk/win32/gdkevents-win32.c 9 Feb 2006 05:49:55 -0000 1.164
+++ gdk/win32/gdkevents-win32.c 11 Apr 2006 14:17:01 -0000
@@ -144,6 +144,9 @@
static UINT msh_mousewheel;
static UINT client_message;
+static UINT got_gdk_events_message;
+static HWND modal_win32_dialog = NULL;
+
#ifdef HAVE_DIMM_H
static IActiveIMMApp *active_imm_app = NULL;
static IActiveIMMMessagePumpOwner *active_imm_msgpump_owner = NULL;
@@ -281,6 +284,9 @@
/* If gdk_event_translate() returns TRUE, we return ret_val from
* the window procedure.
*/
+ if (modal_win32_dialog)
+ PostMessage (modal_win32_dialog, got_gdk_events_message,
+ (WPARAM) 1, 0);
return ret_val;
}
else
@@ -379,6 +385,7 @@
msh_mousewheel = RegisterWindowMessage ("MSWHEEL_ROLLMSG");
client_message = RegisterWindowMessage ("GDK_WIN32_CLIENT_MESSAGE");
+ got_gdk_events_message = RegisterWindowMessage ("GDK_WIN32_GOT_EVENTS");
#if 0
/* Check if we have some input locale identifier loaded that uses a
@@ -463,7 +470,8 @@
{
MSG msg;
return (_gdk_event_queue_find_first (_gdk_display) ||
- PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE));
+ (modal_win32_dialog == NULL &&
+ PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)));
}
GdkEvent*
@@ -3516,6 +3524,9 @@
{
MSG msg;
+ if (modal_win32_dialog != NULL)
+ return;
+
while (!_gdk_event_queue_find_first (display) &&
PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
@@ -3536,7 +3547,8 @@
*timeout = -1;
retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
- PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE));
+ (modal_win32_dialog == NULL &&
+ PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)));
GDK_THREADS_LEAVE ();
@@ -3553,7 +3565,8 @@
if (event_poll_fd.revents & G_IO_IN)
retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
- PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE));
+ (modal_win32_dialog == NULL &&
+ PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)));
else
retval = FALSE;
@@ -3585,6 +3598,12 @@
GDK_THREADS_LEAVE ();
return TRUE;
+}
+
+void
+gdk_win32_set_modal_dialog (HWND window)
+{
+ modal_win32_dialog = window;
}
static void
Index: gdk/win32/gdkwin32.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkwin32.h,v
retrieving revision 1.35
diff -u -r1.35 gdkwin32.h
--- gdk/win32/gdkwin32.h 18 Sep 2005 17:46:55 -0000 1.35
+++ gdk/win32/gdkwin32.h 11 Apr 2006 14:17:01 -0000
@@ -88,6 +88,8 @@
GdkPixbuf * gdk_win32_icon_to_pixbuf_libgtk_only (HICON hicon);
HICON gdk_win32_pixbuf_to_hicon_libgtk_only (GdkPixbuf *pixbuf);
+void gdk_win32_set_modal_dialog (HWND window);
+
G_END_DECLS
#endif /* __GDK_WIN32_H__ */
Index: gtk/gtkprintoperation-win32.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/Attic/gtkprintoperation-win32.c,v
retrieving revision 1.1.2.8
diff -u -r1.1.2.8 gtkprintoperation-win32.c
--- gtk/gtkprintoperation-win32.c 10 Apr 2006 15:46:39 -0000 1.1.2.8
+++ gtk/gtkprintoperation-win32.c 11 Apr 2006 14:17:01 -0000
@@ -23,6 +23,7 @@
#define WINVER _WIN32_WINNT
#endif
+#define COBJMACROS
#include "config.h"
#include <math.h>
#include <stdlib.h>
@@ -56,6 +57,39 @@
static void win32_poll_status (GtkPrintOperation *op);
+static const GUID myIID_IPrintDialogCallback = {0x5852a2c3,0x6530,0x11d1,{0xb6,0xa3,0x0,0x0,0xf8,0x75,0x7b,0xf9}};
+
+#undef INTERFACE
+#define INTERFACE IPrintDialogCallback
+DECLARE_INTERFACE_ (IPrintDialogCallback, IUnknown)
+{
+ STDMETHOD (QueryInterface)(THIS_ REFIID,LPVOID*) PURE;
+ STDMETHOD_ (ULONG, AddRef)(THIS) PURE;
+ STDMETHOD_ (ULONG, Release)(THIS) PURE;
+ STDMETHOD (InitDone)(THIS) PURE;
+ STDMETHOD (SelectionChange)(THIS) PURE;
+ STDMETHOD (HandleMessage)(THIS_ HWND,UINT,WPARAM,LPARAM,LRESULT*) PURE;
+};
+
+static UINT got_gdk_events_message;
+
+UINT_PTR CALLBACK
+run_mainloop_hook (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uiMsg == WM_INITDIALOG)
+ {
+ gdk_win32_set_modal_dialog (hdlg);
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ }
+ else if (uiMsg == got_gdk_events_message)
+ {
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ return 1;
+ }
+ return 0;
+}
static GtkPageOrientation
orientation_from_win32 (short orientation)
@@ -1120,6 +1154,114 @@
op->priv->default_page_setup);
}
+typedef struct {
+ IPrintDialogCallback iPrintDialogCallback;
+ gboolean set_hwnd;
+ int ref_count;
+} PrintDialogCallback;
+
+
+static ULONG STDMETHODCALLTYPE
+iprintdialogcallback_addref (IPrintDialogCallback *This)
+{
+ PrintDialogCallback *callback = (PrintDialogCallback *)This;
+ return ++callback->ref_count;
+}
+
+static ULONG STDMETHODCALLTYPE
+iprintdialogcallback_release (IPrintDialogCallback *This)
+{
+ PrintDialogCallback *callback = (PrintDialogCallback *)This;
+ int ref_count = --callback->ref_count;
+
+ if (ref_count == 0)
+ g_free (This);
+
+ return ref_count;
+}
+
+static HRESULT STDMETHODCALLTYPE
+iprintdialogcallback_queryinterface (IPrintDialogCallback *This,
+ REFIID riid,
+ LPVOID *ppvObject)
+{
+ if (IsEqualIID (riid, &IID_IUnknown) ||
+ IsEqualIID (riid, &myIID_IPrintDialogCallback))
+ {
+ *ppvObject = This;
+ IUnknown_AddRef ((IUnknown *)This);
+ return NOERROR;
+ }
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+static HRESULT STDMETHODCALLTYPE
+iprintdialogcallback_initdone (IPrintDialogCallback *This)
+{
+ return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE
+iprintdialogcallback_selectionchange (IPrintDialogCallback *This)
+{
+ return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE
+iprintdialogcallback_handlemessage (IPrintDialogCallback *This,
+ HWND hDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam,
+ LRESULT *pResult)
+{
+ PrintDialogCallback *callback = (PrintDialogCallback *)This;
+
+ if (!callback->set_hwnd)
+ {
+ gdk_win32_set_modal_dialog (hDlg);
+ callback->set_hwnd = TRUE;
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ }
+ else if (uMsg == got_gdk_events_message)
+ {
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ *pResult = TRUE;
+ return S_OK;
+ }
+
+ *pResult = 0;
+ return S_FALSE;
+}
+
+static IPrintDialogCallbackVtbl ipdc_vtbl = {
+ iprintdialogcallback_queryinterface,
+ iprintdialogcallback_addref,
+ iprintdialogcallback_release,
+ iprintdialogcallback_initdone,
+ iprintdialogcallback_selectionchange,
+ iprintdialogcallback_handlemessage
+};
+
+static IPrintDialogCallback *
+print_callback_new (void)
+{
+ PrintDialogCallback *callback;
+
+ callback = g_new0 (PrintDialogCallback, 1);
+ callback->iPrintDialogCallback.lpVtbl = &ipdc_vtbl;
+ callback->ref_count = 1;
+ callback->set_hwnd = FALSE;
+
+ return &callback->iPrintDialogCallback;
+}
+
GtkPrintOperationResult
_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
GtkWindow *parent,
@@ -1133,6 +1275,7 @@
GtkWidget *invisible = NULL;
GtkPrintOperationResult result;
GtkPrintOperationWin32 *op_win32;
+ IPrintDialogCallback *callback;
*do_print = FALSE;
@@ -1197,8 +1340,13 @@
dialog_from_print_settings (op, printdlgex);
- /* TODO: We should do this in a thread to avoid blocking the mainloop */
+ callback = print_callback_new ();
+ printdlgex->lpCallback = (IUnknown *)callback;
+ got_gdk_events_message = RegisterWindowMessage ("GDK_WIN32_GOT_EVENTS");
+
hResult = PrintDlgExW(printdlgex);
+ IUnknown_Release ((IUnknown *)callback);
+ gdk_win32_set_modal_dialog (NULL);
if (hResult != S_OK)
{
@@ -1390,8 +1538,13 @@
floor (gtk_page_setup_get_top_margin (page_setup, unit) * scale + 0.5);
pagesetupdlg->rtMargin.bottom =
floor (gtk_page_setup_get_bottom_margin (page_setup, unit) * scale + 0.5);
+
+ pagesetupdlg->Flags |= PSD_ENABLEPAGESETUPHOOK;
+ pagesetupdlg->lpfnPageSetupHook = run_mainloop_hook;
+ got_gdk_events_message = RegisterWindowMessage ("GDK_WIN32_GOT_EVENTS");
res = PageSetupDlgW (pagesetupdlg);
+ gdk_win32_set_modal_dialog (NULL);
if (res)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]