private void UpdateMessageQueue (XEventQueue queue) {
DateTime now;
int pending;
Hwnd hwnd;
now = DateTime.UtcNow;
lock (XlibLock) {
pending = XPending (DisplayHandle);
}
if (pending == 0) {
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;
#if debug
Console.WriteLine ("UpdateMessageQueue (), got Event: {0}", xevent.ToString ());
#else
DebugHelper.WriteLine ("UpdateMessageQueue got Event: " + xevent.ToString ());
#endif
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);
#if DriverDebugExtra
Console.WriteLine("Got supported clipboard atom format: {0}", xevent.SelectionEvent.property);
#endif
}
}
} 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:
#if debug
Console.WriteLine ("UpdateMessageQueue (), got Event: {0}", xevent.ToString ());
#endif
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;
}
}
}