void UpdateMessageQueue (XEventQueue queue, bool allowIdle) { DateTime now; int pending; Hwnd hwnd; now = DateTime.UtcNow; lock (XlibLock) { pending = XPending (DisplayHandle); } if (pending == 0 && allowIdle) { if ((queue == null || queue.DispatchIdle) && Idle != null) { Idle (this, EventArgs.Empty); } lock (XlibLock) { pending = XPending (DisplayHandle); } } if (pending == 0) { int timeout = 0; if (queue != null) { if (queue.Paint.Count > 0) return; timeout = NextTimeout (queue.timer_list, now); } if (timeout > 0) { #if __MonoCS__ int length = pollfds.Length - 1; lock (wake_waiting_lock) { if (wake_waiting == false) { length ++; wake_waiting = true; } } Syscall.poll (pollfds, (uint)length, timeout); // Clean out buffer, so we're not busy-looping on the same data if (length == pollfds.Length) { if (pollfds[1].revents != 0) wake_receive.Receive(network_buffer, 0, 1, SocketFlags.None); lock (wake_waiting_lock) { wake_waiting = false; } } #endif lock (XlibLock) { pending = XPending (DisplayHandle); } } } if (queue != null) CheckTimers (queue.timer_list, now); while (true) { XEvent xevent = new XEvent (); lock (XlibLock) { if (XPending (DisplayHandle) == 0) break; XNextEvent (DisplayHandle, ref xevent); if (xevent.AnyEvent.type == XEventName.KeyPress || xevent.AnyEvent.type == XEventName.KeyRelease) { // PreFilter() handles "shift key state updates. Keyboard.PreFilter (xevent); if (XFilterEvent (ref xevent, Keyboard.ClientWindow)) { // probably here we could raise WM_IME_KEYDOWN and // WM_IME_KEYUP, but I'm not sure it is worthy. continue; } } else if (XFilterEvent (ref xevent, IntPtr.Zero)) continue; } hwnd = Hwnd.GetObjectFromWindow(xevent.AnyEvent.window); if (hwnd == null) continue; DebugHelper.WriteLine ("UpdateMessageQueue got Event: " + xevent.ToString ()); switch (xevent.type) { case XEventName.Expose: AddExpose (hwnd, xevent.ExposeEvent.window == hwnd.ClientWindow, xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); break; case XEventName.SelectionClear: { // Should we do something? break; } case XEventName.SelectionRequest: { if (Dnd.HandleSelectionRequestEvent (ref xevent)) break; XEvent sel_event; sel_event = new XEvent(); sel_event.SelectionEvent.type = XEventName.SelectionNotify; sel_event.SelectionEvent.send_event = true; sel_event.SelectionEvent.display = DisplayHandle; sel_event.SelectionEvent.selection = xevent.SelectionRequestEvent.selection; sel_event.SelectionEvent.target = xevent.SelectionRequestEvent.target; sel_event.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor; sel_event.SelectionEvent.time = xevent.SelectionRequestEvent.time; sel_event.SelectionEvent.property = IntPtr.Zero; IntPtr format_atom = xevent.SelectionRequestEvent.target; // Seems that some apps support asking for supported types if (format_atom == TARGETS) { int[] atoms; int atom_count; atoms = new int[5]; atom_count = 0; if (Clipboard.IsSourceText) { atoms[atom_count++] = (int)Atom.XA_STRING; atoms[atom_count++] = (int)OEMTEXT; atoms[atom_count++] = (int)UTF8_STRING; atoms[atom_count++] = (int)UTF16_STRING; atoms[atom_count++] = (int)RICHTEXTFORMAT; } else if (Clipboard.IsSourceImage) { atoms[atom_count++] = (int)Atom.XA_PIXMAP; atoms[atom_count++] = (int)Atom.XA_BITMAP; } else { // FIXME - handle other types } XChangeProperty(DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, (IntPtr)xevent.SelectionRequestEvent.target, 32, PropertyMode.Replace, atoms, atom_count); sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; } else if (format_atom == (IntPtr)RICHTEXTFORMAT) { string rtf_text = Clipboard.GetRtfText (); if (rtf_text != null) { // The RTF spec mentions that ascii is enough to contain it Byte [] bytes = Encoding.ASCII.GetBytes (rtf_text); int buflen = bytes.Length; IntPtr buffer = Marshal.AllocHGlobal (buflen); for (int i = 0; i < buflen; i++) Marshal.WriteByte (buffer, i, bytes[i]); XChangeProperty(DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, (IntPtr)xevent.SelectionRequestEvent.target, 8, PropertyMode.Replace, buffer, buflen); sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; Marshal.FreeHGlobal(buffer); } } else if (Clipboard.IsSourceText && (format_atom == (IntPtr)Atom.XA_STRING || format_atom == OEMTEXT || format_atom == UTF16_STRING || format_atom == UTF8_STRING)) { IntPtr buffer = IntPtr.Zero; int buflen; Encoding encoding = null; buflen = 0; // Select an encoding depending on the target IntPtr target_atom = xevent.SelectionRequestEvent.target; if (target_atom == (IntPtr)Atom.XA_STRING || target_atom == OEMTEXT) // FIXME - EOMTEXT should encode into ISO2022 encoding = Encoding.ASCII; else if (target_atom == UTF16_STRING) encoding = Encoding.Unicode; else if (target_atom == UTF8_STRING) encoding = Encoding.UTF8; Byte [] bytes; bytes = encoding.GetBytes (Clipboard.GetPlainText ()); buffer = Marshal.AllocHGlobal (bytes.Length); buflen = bytes.Length; for (int i = 0; i < buflen; i++) Marshal.WriteByte (buffer, i, bytes [i]); if (buffer != IntPtr.Zero) { XChangeProperty(DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, (IntPtr)xevent.SelectionRequestEvent.target, 8, PropertyMode.Replace, buffer, buflen); sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; Marshal.FreeHGlobal(buffer); } } else if (Clipboard.GetSource (format_atom.ToInt32 ()) != null) { // check if we have an available value of this format if (DataFormats.GetFormat (format_atom.ToInt32 ()).is_serializable) { object serializable = Clipboard.GetSource (format_atom.ToInt32 ()); BinaryFormatter formatter = new BinaryFormatter (); MemoryStream memory_stream = new MemoryStream (); formatter.Serialize (memory_stream, serializable); int buflen = (int)memory_stream.Length; IntPtr buffer = Marshal.AllocHGlobal (buflen); memory_stream.Position = 0; for (int i = 0; i < buflen; i++) Marshal.WriteByte (buffer, i, (byte)memory_stream.ReadByte ()); memory_stream.Close (); XChangeProperty (DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, (IntPtr)xevent.SelectionRequestEvent.target, 8, PropertyMode.Replace, buffer, buflen); sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; Marshal.FreeHGlobal (buffer); } } else if (Clipboard.IsSourceImage) { if (xevent.SelectionEvent.target == (IntPtr)Atom.XA_PIXMAP) { // FIXME - convert image and store as property } else if (xevent.SelectionEvent.target == (IntPtr)Atom.XA_PIXMAP) { // FIXME - convert image and store as property } } XSendEvent(DisplayHandle, xevent.SelectionRequestEvent.requestor, false, new IntPtr ((int)EventMask.NoEventMask), ref sel_event); break; } case XEventName.SelectionNotify: { if (Clipboard.Enumerating) { Clipboard.Enumerating = false; if (xevent.SelectionEvent.property != IntPtr.Zero) { XDeleteProperty(DisplayHandle, FosterParent, (IntPtr)xevent.SelectionEvent.property); if (!Clipboard.Formats.Contains(xevent.SelectionEvent.property)) { Clipboard.Formats.Add(xevent.SelectionEvent.property); DriverDebug("Got supported clipboard atom format: {0}", xevent.SelectionEvent.property); } } } else if (Clipboard.Retrieving) { Clipboard.Retrieving = false; if (xevent.SelectionEvent.property != IntPtr.Zero) { TranslatePropertyToClipboard(xevent.SelectionEvent.property); } else { Clipboard.ClearSources (); Clipboard.Item = null; } } else { Dnd.HandleSelectionNotifyEvent (ref xevent); } break; } case XEventName.KeyRelease: if (!detectable_key_auto_repeat && XPending (DisplayHandle) != 0) { XEvent nextevent = new XEvent (); XPeekEvent (DisplayHandle, ref nextevent); if (nextevent.type == XEventName.KeyPress && nextevent.KeyEvent.keycode == xevent.KeyEvent.keycode && nextevent.KeyEvent.time == xevent.KeyEvent.time) { continue; } } goto case XEventName.KeyPress; case XEventName.MotionNotify: { XEvent peek; /* we can't do motion compression across threads, so just punt if we don't match up */ if (Thread.CurrentThread == hwnd.Queue.Thread && hwnd.Queue.Count > 0) { peek = hwnd.Queue.Peek(); if (peek.AnyEvent.type == XEventName.MotionNotify) { continue; } } goto case XEventName.KeyPress; } case XEventName.KeyPress: hwnd.Queue.EnqueueLocked (xevent); /* Process KeyPresses immediately. Otherwise multiple Compose messages as a result of a * single physical keypress are not processed correctly */ return; case XEventName.ButtonPress: case XEventName.ButtonRelease: case XEventName.EnterNotify: case XEventName.LeaveNotify: case XEventName.CreateNotify: case XEventName.DestroyNotify: case XEventName.FocusIn: case XEventName.FocusOut: case XEventName.ClientMessage: case XEventName.ReparentNotify: case XEventName.MapNotify: case XEventName.UnmapNotify: hwnd.Queue.EnqueueLocked (xevent); break; case XEventName.ConfigureNotify: AddConfigureNotify(xevent); break; case XEventName.PropertyNotify: DriverDebug ("UpdateMessageQueue (), got Event: {0}", xevent.ToString ()); if (xevent.PropertyEvent.atom == _NET_ACTIVE_WINDOW) { IntPtr actual_atom; int actual_format; IntPtr nitems; IntPtr bytes_after; IntPtr prop = IntPtr.Zero; IntPtr prev_active; prev_active = ActiveWindow; XGetWindowProperty(DisplayHandle, RootWindow, _NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false, (IntPtr)Atom.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); if (((long)nitems > 0) && (prop != IntPtr.Zero)) { ActiveWindow = Hwnd.GetHandleFromWindow((IntPtr)Marshal.ReadInt32(prop)); XFree(prop); DebugHelper.WriteLine ("PropertyNotify: _NET_ACTIVE_WINDOW: previous = 0x{0:x}, new = 0x{1:x}", prev_active.ToInt32 (), ActiveWindow.ToInt32 ()); if (prev_active != ActiveWindow) { if (prev_active != IntPtr.Zero) { PostMessage(prev_active, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); } if (ActiveWindow != IntPtr.Zero) { PostMessage(ActiveWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_ACTIVE, IntPtr.Zero); } } if (ModalWindows.Count == 0) { break; } else { // Modality Handling // // If there is a modal window on the stack and the new active // window is MWF window, but not the modal one and not a non-modal // child of the modal one, switch back to the modal window. // // To identify if a non-modal form is child of a modal form // we match their ApplicationContexts, which will be the same. // This is because each modal form runs the loop with a // new ApplicationContext, which is inherited by the non-modal // forms. Form activeForm = Control.FromHandle (ActiveWindow) as Form; if (activeForm != null) { Form modalForm = Control.FromHandle ((IntPtr)ModalWindows.Peek()) as Form; if (ActiveWindow != (IntPtr)ModalWindows.Peek() && (modalForm == null || activeForm.context == modalForm.context)) { Activate((IntPtr)ModalWindows.Peek()); } } break; } } } else if (xevent.PropertyEvent.atom == _NET_WM_STATE) { // invalidate our cache - we'll query again the next time someone does GetWindowState. hwnd.cached_window_state = (FormWindowState)(-1); PostMessage (hwnd.Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); } break; } } }
XEventQueue ThreadQueue(Thread thread) { XEventQueue queue; queue = (XEventQueue)MessageQueues[thread]; if (queue == null) { queue = new XEventQueue(thread); MessageQueues[thread] = queue; } return queue; }
void UpdateMessageQueue (XEventQueue queue) { UpdateMessageQueue(queue, true); }
private XplatUIX11GTK () { Console.WriteLine ("XplatUIX11GTK ctor..."); // Handle singleton stuff first RefCount = 0; // init gdk gdk_init_check (IntPtr.Zero, IntPtr.Zero); // Now regular initialization XlibLock = new object (); MessageQueue = new XEventQueue (); TimerList = new ArrayList (); XInitThreads (); ErrorExceptions = false; // X11 Initialization SetDisplay (gdk_x11_display_get_xdisplay (gdk_display_get_default ())); X11DesktopColors.Initialize (); // Handle any upcoming errors; we re-set it here, X11DesktopColor stuff might have stolen it (gtk does) ErrorHandler = new XErrorHandler (HandleError); XSetErrorHandler (ErrorHandler); }