Merge pull request #530 from jmp75/visualstudio-build
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.X11Internal / X11ThreadQueue.cs
index 79ac89e9addcec64c26fd0104448d2d4f30369cb..cf07045de7b753f157749512ec9ae62043c460b8 100644 (file)
@@ -33,21 +33,24 @@ namespace System.Windows.Forms.X11Internal {
 
        internal class X11ThreadQueue {
 
-               XQueue xqueue;
+               XEventQueue xqueue;
                PaintQueue paint_queue;
+               ConfigureQueue configure_queue;
                ArrayList timer_list;
                Thread thread;
                bool quit_posted;
                bool dispatch_idle;
+               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;
@@ -64,6 +67,25 @@ namespace System.Windows.Forms.X11Internal {
 
                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);
@@ -76,55 +98,112 @@ namespace System.Windows.Forms.X11Internal {
                        }
                }
 
-               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 (Monitor.Wait (lockobj, NextTimeout (), true)) {
-                               /* the lock was reaquired before timeout.
-                                  i.e. we have an event now */
-                               goto try_again;
+                       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 {
-                               xevent = new XEvent ();
-                               return false;
+                               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;
                        }
-               }
 
-               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 ()
@@ -140,26 +219,24 @@ namespace System.Windows.Forms.X11Internal {
                private int NextTimeout ()
                {
                        int timeout = Int32.MaxValue; 
-                       DateTime now = DateTime.Now;
+                       DateTime now = DateTime.UtcNow;
 
                        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;
                }
 
@@ -208,6 +285,18 @@ namespace System.Windows.Forms.X11Internal {
                        }
                }
 
+               public event EventHandler Idle;
+               public void OnIdle (EventArgs e)
+               {
+                       if (Idle != null)
+                               Idle (thread, e);
+               }
+
+               public bool NeedDispatchIdle {
+                       get { return need_dispatch_idle; }
+                       set { need_dispatch_idle = value; }
+               }
+
                public bool DispatchIdle {
                        get { return dispatch_idle; }
                        set { dispatch_idle = value; }
@@ -218,12 +307,17 @@ namespace System.Windows.Forms.X11Internal {
                        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 {
@@ -232,67 +326,123 @@ namespace System.Windows.Forms.X11Internal {
 
                        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];
                        }
 
@@ -304,9 +454,9 @@ namespace System.Windows.Forms.X11Internal {
                        {
                                if (size == xevents.Length)
                                        Grow ();
-                               
+
                                xevents [tail] = xevent;
-                               tail = (tail + 1) % xevents.Length;
+                               tail = (tail + 1) & (xevents.Length - 1);
                                size++;
                        }
 
@@ -316,7 +466,7 @@ namespace System.Windows.Forms.X11Internal {
                                        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;
                        }
@@ -333,7 +483,15 @@ namespace System.Windows.Forms.X11Internal {
                        {
                                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;