Win32 - handling mainloop while showing modal comon controls



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]