internal static void RunLoop(bool Modal, ApplicationContext context) { Queue toplevels; MSG msg; Object queue_id; MWFThread thread; ApplicationContext previous_thread_context; thread = MWFThread.Current; /* * There is a NotWorking test for this, but since we are using this method both for Form.ShowDialog as for ApplicationContexts we'll * fail on nested ShowDialogs, so disable the check for the moment. */ //if (thread.MessageLoop) { // throw new InvalidOperationException ("Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead."); //} msg = new MSG(); if (context == null) { context = new ApplicationContext(); } previous_thread_context = thread.Context; thread.Context = context; if (context.MainForm != null) { context.MainForm.is_modal = Modal; context.MainForm.context = context; context.MainForm.closing = false; context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus // XXX the above line can be used to close the form. another problem with our handling of Show/Activate. if (context.MainForm != null) { context.MainForm.Activate(); } } #if DebugRunLoop Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL"); #endif if (Modal) { toplevels = new Queue(); DisableFormsForModalLoop(toplevels, context); // FIXME - need activate? /* make sure the MainForm is enabled */ if (context.MainForm != null) { XplatUI.EnableWindow(context.MainForm.Handle, true); XplatUI.SetModal(context.MainForm.Handle, true); } } else { toplevels = null; } queue_id = XplatUI.StartLoop(Thread.CurrentThread); thread.MessageLoop = true; bool quit = false; while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) { Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam); if (Application.FilterMessage(ref m)) { continue; } switch ((Msg)msg.message) { case Msg.WM_KEYDOWN: case Msg.WM_SYSKEYDOWN: case Msg.WM_CHAR: case Msg.WM_SYSCHAR: case Msg.WM_KEYUP: case Msg.WM_SYSKEYUP: Control c = Control.FromHandle(msg.hwnd); // If we have a control with keyboard capture (usually a *Strip) // give it the message, and then drop the message if (keyboard_capture != null) { // WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here if ((Msg)m.Msg == Msg.WM_SYSKEYDOWN) { if (m.WParam.ToInt32() == (int)Keys.Menu) { keyboard_capture.GetTopLevelToolStrip().Dismiss(ToolStripDropDownCloseReason.Keyboard); continue; } } m.HWnd = keyboard_capture.Handle; switch (keyboard_capture.PreProcessControlMessageInternal(ref m)) { case PreProcessControlState.MessageProcessed: continue; case PreProcessControlState.MessageNeeded: case PreProcessControlState.MessageNotNeeded: if (((m.Msg == (int)Msg.WM_KEYDOWN || m.Msg == (int)Msg.WM_CHAR) && !keyboard_capture.ProcessControlMnemonic((char)m.WParam))) { if (c == null || !ControlOnToolStrip(c)) { continue; } else { m.HWnd = msg.hwnd; } } else { continue; } break; } } if (((c != null) && c.PreProcessControlMessageInternal(ref m) != PreProcessControlState.MessageProcessed) || (c == null)) { goto default; } break; case Msg.WM_LBUTTONDOWN: case Msg.WM_MBUTTONDOWN: case Msg.WM_RBUTTONDOWN: if (keyboard_capture != null) { Control c2 = Control.FromHandle(msg.hwnd); var contextMenuStrip = keyboard_capture.GetTopLevelToolStrip() as ContextMenuStrip; // The target is not a winforms control (an embedded control, perhaps), so // release everything if (c2 == null) { ToolStripManager.FireAppClicked(); goto default; } // Skip clicks on owner windows, eg. expanded ComboBox if (Control.IsChild(keyboard_capture.Handle, msg.hwnd)) { goto default; } // Close any active toolstrips drop-downs if we click outside of them, // but also don't close them all if we click outside of the top-most // one, but into its owner. Point c2_point = c2.PointToScreen(new Point( (int)(short)(m.LParam.ToInt32() & 0xffff), (int)(short)(m.LParam.ToInt32() >> 16))); while (keyboard_capture != null && !keyboard_capture.ClientRectangle.Contains(keyboard_capture.PointToClient(c2_point))) { keyboard_capture.Dismiss(); } var iter_OwnerItem = (c2 as ToolStripDropDown)?.OwnerItem; while (iter_OwnerItem != null && iter_OwnerItem.Owner != contextMenuStrip) { iter_OwnerItem = iter_OwnerItem.OwnerItem; } var contextMenuStripIsOwnerOf_c2 = (iter_OwnerItem != null); if (c2 != contextMenuStrip && !contextMenuStripIsOwnerOf_c2) { contextMenuStrip.Dismiss(); } } goto default; case Msg.WM_QUIT: quit = true; // make sure we exit break; default: XplatUI.TranslateMessage(ref msg); XplatUI.DispatchMessage(ref msg); break; } // If our Form doesn't have a handle anymore, it means it was destroyed and we need to *wait* for WM_QUIT. if ((context.MainForm != null) && (!context.MainForm.IsHandleCreated)) { continue; } // Handle exit, Form might have received WM_CLOSE and set 'closing' in response. if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) { if (!Modal) { XplatUI.PostQuitMessage(0); } else { break; } } } #if DebugRunLoop Console.WriteLine(" RunLoop loop left"); #endif thread.MessageLoop = false; XplatUI.EndLoop(Thread.CurrentThread); if (Modal) { Form old = context.MainForm; context.MainForm = null; EnableFormsForModalLoop(toplevels, context); if (old != null && old.IsHandleCreated) { XplatUI.SetModal(old.Handle, false); } #if DebugRunLoop Console.WriteLine(" Done with the SetModal"); #endif old.RaiseCloseEvents(true, false); old.is_modal = false; } #if DebugRunLoop Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL"); #endif if (context.MainForm != null) { context.MainForm.context = null; context.MainForm = null; } thread.Context = previous_thread_context; if (!Modal) { thread.Exit(); } }
internal static void RunLoop(bool Modal, ApplicationContext context) { MWFThread thread = MWFThread.Current; // There is a NotWorking test for this, but since we are using this method both for Form.ShowDialog as for ApplicationContexts we'll // fail on nested ShowDialogs, so disable the check for the moment. //if (thread.MessageLoop) { // throw new InvalidOperationException ("Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead."); //} if (context == null) { context = new ApplicationContext(); } ApplicationContext previous_thread_context = thread.Context; thread.Context = context; if (context.MainForm != null) { context.MainForm.is_modal = Modal; context.MainForm.context = context; context.MainForm.closing = false; context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus // XXX the above line can be used to close the form. another problem with our handling of Show/Activate. if (context.MainForm != null) { context.MainForm.Activate(); } } #if DebugRunLoop Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL"); #endif Queue toplevels = new Queue(); if (Modal) { DisableFormsForModalLoop(toplevels, context); // FIXME - need activate? // make sure the MainForm is enabled if (context.MainForm != null) { XplatUI.EnableWindow(context.MainForm.Handle, true); XplatUI.SetModal(context.MainForm.Handle, true); } } Object queue_id = XplatUI.StartLoop(Thread.CurrentThread); var prevMessageLoop = thread.MessageLoop; thread.MessageLoop = true; bool quit = false, drop = false; MSG msg = new MSG(); while (!quit) { using (var cleanup = XplatUI.StartCycle(queue_id)) { if ((quit = !XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0))) { continue; } if (msg.message != Msg.WM_NULL) { Application.SendMessage(ref msg, out drop, out quit); } if (drop) { continue; } var mainForm = context.MainForm; if (mainForm == null) { continue; } // If our Form doesn't have a handle anymore, it means it was destroyed and we need to *wait* for WM_QUIT. if (!mainForm.IsHandleCreated) { continue; } // Handle exit, Form might have received WM_CLOSE and set 'closing' in response. if (mainForm.closing || (Modal && !mainForm.Visible)) { if (!Modal) { XplatUI.PostQuitMessage(0); } else { break; } } } } #if DebugRunLoop Console.WriteLine(" RunLoop loop left"); #endif thread.MessageLoop = prevMessageLoop; XplatUI.EndLoop(Thread.CurrentThread); if (Modal) { Form old = context.MainForm; context.MainForm = null; EnableFormsForModalLoop(toplevels, context); if (old != null && old.IsHandleCreated) { XplatUI.SetModal(old.Handle, false); } #if DebugRunLoop Console.WriteLine(" Done with the SetModal"); #endif old.RaiseCloseEvents(true, false); old.is_modal = false; } #if DebugRunLoop Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL"); #endif if (context.MainForm != null) { context.MainForm.context = null; context.MainForm = null; } thread.Context = previous_thread_context; if (!Modal) { thread.Exit(); } }