internal class X11ThreadQueue {
- XQueue xqueue;
+ XEventQueue xqueue;
PaintQueue paint_queue;
+ ConfigureQueue configure_queue;
ArrayList timer_list;
Thread thread;
bool quit_posted;
bool need_dispatch_idle = true;
object lockobj = new object ();
- static readonly int InitialXEventSize = 100;
- static readonly int InitialPaintSize = 50;
+ static readonly int InitialXEventQueueSize = 128;
+ static readonly int InitialHwndQueueSize = 50;
public X11ThreadQueue (Thread thread)
{
- xqueue = new XQueue (InitialXEventSize);
- paint_queue = new PaintQueue(InitialPaintSize);
+ xqueue = new XEventQueue (InitialXEventQueueSize);
+ paint_queue = new PaintQueue (InitialHwndQueueSize);
+ configure_queue = new ConfigureQueue (InitialHwndQueueSize);
timer_list = new ArrayList ();
this.thread = thread;
this.quit_posted = false;
public void EnqueueUnlocked (XEvent xevent)
{
+ switch (xevent.type) {
+ case XEventName.KeyPress:
+ case XEventName.KeyRelease:
+ case XEventName.ButtonPress:
+ case XEventName.ButtonRelease:
+ NeedDispatchIdle = true;
+ break;
+ case XEventName.MotionNotify:
+ if (xqueue.Count > 0) {
+ XEvent peek = xqueue.Peek ();
+ if (peek.AnyEvent.type == XEventName.MotionNotify)
+ return; // we've already got a pending motion notify.
+ }
+
+ // otherwise fall through and enqueue
+ // the event.
+ break;
+ }
+
xqueue.Enqueue (xevent);
// wake up any thread blocking in DequeueUnlocked
Monitor.PulseAll (lockobj);
}
}
- public bool DequeueUnlocked (out XEvent xevent)
+ public bool Dequeue (out XEvent xevent)
{
- try_again:
- if (xqueue.Count > 0) {
- xevent = xqueue.Dequeue ();
- return true;
- }
+ StartOver:
+ bool got_xevent = false;
- if (paint_queue.Count > 0) {
- xevent = paint_queue.Dequeue ();
- return true;
+ lock (lockobj) {
+ if (xqueue.Count > 0) {
+ got_xevent = true;
+ xevent = xqueue.Dequeue ();
+ }
+ else
+ xevent = new XEvent (); /* not strictly needed, but mcs complains */
}
- // both queues are empty. go to sleep until NextTimeout
- // (or until there's an event to handle).
+ if (got_xevent) {
+ if (xevent.AnyEvent.type == XEventName.Expose) {
+#if spew
+ Console.Write ("E");
+ Console.Out.Flush ();
+#endif
+ X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
+ hwnd.AddExpose (xevent.AnyEvent.window == hwnd.ClientWindow,
+ xevent.ExposeEvent.x, xevent.ExposeEvent.y,
+ xevent.ExposeEvent.width, xevent.ExposeEvent.height);
+ goto StartOver;
+ }
+ else if (xevent.AnyEvent.type == XEventName.ConfigureNotify) {
+#if spew
+ Console.Write ("C");
+ Console.Out.Flush ();
+#endif
+ X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
+ hwnd.AddConfigureNotify (xevent);
+ goto StartOver;
+ }
+ else {
+#if spew
+ Console.Write ("X");
+ Console.Out.Flush ();
+#endif
+ /* it was an event we can deal with directly, return it */
+ return true;
+ }
+ }
+ else {
+ if (paint_queue.Count > 0) {
+ xevent = paint_queue.Dequeue ();
+#if spew
+ Console.Write ("e");
+ Console.Out.Flush ();
+#endif
+ return true;
+ }
+ else if (configure_queue.Count > 0) {
+ xevent = configure_queue.Dequeue ();
+#if spew
+ Console.Write ("c");
+ Console.Out.Flush ();
+#endif
+ return true;
+ }
+ }
if (dispatch_idle && need_dispatch_idle) {
OnIdle (EventArgs.Empty);
need_dispatch_idle = false;
}
- if (Monitor.Wait (lockobj, NextTimeout (), true)) {
- /* the lock was reaquired before timeout.
- i.e. we have an event now */
- goto try_again;
- }
- else {
- xevent = new XEvent ();
- return false;
- }
- }
-
- public bool Dequeue (out XEvent xevent)
- {
lock (lockobj) {
- return DequeueUnlocked (out xevent);
+ if (CountUnlocked > 0)
+ goto StartOver;
+
+ if (Monitor.Wait (lockobj, NextTimeout (), true)) {
+ // the lock was reaquired before the
+ // timeout. meaning an event was
+ // enqueued by X11Display.XEventThread.
+ goto StartOver;
+ }
+ else {
+ CheckTimers ();
+ return false;
+ }
}
}
public void RemovePaint (Hwnd hwnd)
{
- lock (lockobj) {
- paint_queue.Remove (hwnd);
- }
+ paint_queue.Remove (hwnd);
}
public void AddPaint (Hwnd hwnd)
{
- lock (lockobj) {
- Console.WriteLine ("adding paint event");
- paint_queue.Enqueue (hwnd);
- // wake up any thread blocking in DequeueUnlocked
- Monitor.PulseAll (lockobj);
- }
+ paint_queue.Enqueue (hwnd);
+ }
+
+ public void AddConfigure (Hwnd hwnd)
+ {
+ configure_queue.Enqueue (hwnd);
+ }
+
+ public ConfigureQueue Configure {
+ get { return configure_queue; }
+ }
+
+ public PaintQueue Paint {
+ get { return paint_queue; }
}
public void Lock ()
foreach (Timer timer in timer_list) {
int next = (int) (timer.Expires - now).TotalMilliseconds;
- if (next < 0) {
+ if (next < 0)
return 0; // Have a timer that has already expired
- }
- if (next < timeout) {
+ if (next < timeout)
timeout = next;
- }
}
+
if (timeout < Timer.Minimum) {
timeout = Timer.Minimum;
}
-#if false
- if (timeout > 1000)
- timeout = 1000;
-#endif
+ if (timeout == Int32.MaxValue)
+ timeout = Timeout.Infinite;
+
return timeout;
}
set { quit_posted = value; }
}
- public class PaintQueue {
-
- private ArrayList hwnds;
-
- public PaintQueue (int size) {
- hwnds = new ArrayList(size);
+ public abstract class HwndEventQueue {
+ protected ArrayList hwnds;
+#if DebugHwndEventQueue
+ protected ArrayList stacks;
+#endif
+ public HwndEventQueue (int size)
+ {
+ hwnds = new ArrayList (size);
+#if DebugHwndEventQueue
+ stacks = new ArrayList (size);
+#endif
}
public int Count {
public void Enqueue (Hwnd hwnd)
{
+ if (hwnds.Contains (hwnd)) {
+#if DebugHwndEventQueue
+ Console.WriteLine ("hwnds can only appear in the queue once.");
+ Console.WriteLine (Environment.StackTrace);
+ Console.WriteLine ("originally added here:");
+ Console.WriteLine (stacks[hwnds.IndexOf (hwnd)]);
+#endif
+
+ return;
+ }
hwnds.Add(hwnd);
+#if DebugHwndEventQueue
+ stacks.Add(Environment.StackTrace);
+#endif
}
public void Remove(Hwnd hwnd)
{
- if (!hwnd.expose_pending && !hwnd.nc_expose_pending) {
- hwnds.Remove(hwnd);
- }
+#if DebugHwndEventQueue
+ int index = hwnds.IndexOf(hwnd);
+ if (index != -1)
+ stacks.RemoveAt(index);
+#endif
+ hwnds.Remove(hwnd);
}
- public XEvent Peek ()
+ protected abstract XEvent Peek ();
+
+ public virtual XEvent Dequeue ()
{
if (hwnds.Count == 0)
throw new Exception ("Attempt to dequeue empty queue.");
- Hwnd hwnd = (Hwnd)hwnds[0];
+ return Peek ();
+ }
+ }
+
+
+ public class ConfigureQueue : HwndEventQueue
+ {
+ public ConfigureQueue (int size) : base (size)
+ {
+ }
+
+ protected override XEvent Peek ()
+ {
+ X11Hwnd hwnd = (X11Hwnd)hwnds[0];
XEvent xevent = new XEvent ();
+ xevent.AnyEvent.type = XEventName.ConfigureNotify;
+
+ xevent.ConfigureEvent.window = hwnd.ClientWindow;
+ xevent.ConfigureEvent.x = hwnd.X;
+ xevent.ConfigureEvent.y = hwnd.Y;
+ xevent.ConfigureEvent.width = hwnd.Width;
+ xevent.ConfigureEvent.height = hwnd.Height;
+
+ return xevent;
+ }
+
+ public override XEvent Dequeue ()
+ {
+ XEvent xev = base.Dequeue ();
+
+
+ hwnds.RemoveAt(0);
+#if DebugHwndEventQueue
+ stacks.RemoveAt(0);
+#endif
+
+ return xev;
+ }
+ }
+
+ public class PaintQueue : HwndEventQueue
+ {
+ public PaintQueue (int size) : base (size)
+ {
+ }
+
+ protected override XEvent Peek ()
+ {
+ X11Hwnd hwnd = (X11Hwnd)hwnds[0];
+
+ XEvent xevent = new XEvent ();
+
xevent.AnyEvent.type = XEventName.Expose;
- if (hwnd.expose_pending) {
- xevent.ExposeEvent.window = hwnd.client_window;
- return xevent;
+ if (hwnd.PendingExpose) {
+ xevent.ExposeEvent.window = hwnd.ClientWindow;
} else {
- xevent.ExposeEvent.window = hwnd.whole_window;
+ xevent.ExposeEvent.window = hwnd.WholeWindow;
xevent.ExposeEvent.x = hwnd.nc_invalid.X;
xevent.ExposeEvent.y = hwnd.nc_invalid.Y;
xevent.ExposeEvent.width = hwnd.nc_invalid.Width;
xevent.ExposeEvent.height = hwnd.nc_invalid.Height;
- return xevent;
}
- }
-
- public XEvent Dequeue ()
- {
- if (hwnds.Count == 0)
- throw new Exception ("Attempt to dequeue empty queue.");
-
- // populate the xevent
- XEvent xevent = Peek ();
-
- Hwnd hwnd = (Hwnd)hwnds[0];
-
- // We only remove the event from the queue if we have one expose left since
- // a single entry in our queue may be for both NC and Client exposed
- if ( !(hwnd.nc_expose_pending && hwnd.expose_pending))
- hwnds.RemoveAt(0);
return xevent;
}
+
+ // don't override Dequeue like ConfigureQueue does.
}
- private class XQueue {
+ /* a circular queue for holding X events for processing by GetMessage */
+ private class XEventQueue {
- private XEvent [] xevents;
- private int head;
- private int tail;
- private int size;
+ XEvent[] xevents;
+ int head;
+ int tail;
+ int size;
- public XQueue (int initial_size)
+ public XEventQueue (int initial_size)
{
+ if (initial_size % 2 != 0)
+ throw new Exception ("XEventQueue must be a power of 2 size");
+
xevents = new XEvent [initial_size];
}
{
if (size == xevents.Length)
Grow ();
-
+
xevents [tail] = xevent;
- tail = (tail + 1) % xevents.Length;
+ tail = (tail + 1) & (xevents.Length - 1);
size++;
}
throw new Exception ("Attempt to dequeue empty queue.");
XEvent res = xevents [head];
- head = (head + 1) % xevents.Length;
+ head = (head + 1) & (xevents.Length - 1);
size--;
return res;
}
{
int newcap = (xevents.Length * 2);
XEvent [] na = new XEvent [newcap];
- xevents.CopyTo (na, 0);
+
+ if (head + size > xevents.Length) {
+ Array.Copy (xevents, head, na, 0, xevents.Length - head);
+ Array.Copy (xevents, 0, na, xevents.Length - head, head + size - xevents.Length);
+ }
+ else {
+ Array.Copy (xevents, head, na, 0, size);
+ }
+
xevents = na;
head = 0;
tail = head + size;