aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Internal/X11ThreadQueue.cs
diff options
context:
space:
mode:
authorMichael VanOverbeek <[email protected]>2016-07-25 12:57:52 -0400
committerGitHub <[email protected]>2016-07-25 12:57:52 -0400
commit46c1c31302f111a1f3ec23a70e6f3986a9aa2a27 (patch)
treef00af7ea3f6ad2641fb26fa1d310fd8b7179b39c /source/ShiftUI/Internal/X11ThreadQueue.cs
parentaf48e774189596b8d7a058c564a7d6d75205ca03 (diff)
parent6fa16209519896de09949a27425dff00ebf2970a (diff)
downloadshiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.tar.gz
shiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.tar.bz2
shiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.zip
Merge pull request #17 from MichaelTheShifter/shiftui_integration
Shiftui integration
Diffstat (limited to 'source/ShiftUI/Internal/X11ThreadQueue.cs')
-rw-r--r--source/ShiftUI/Internal/X11ThreadQueue.cs502
1 files changed, 502 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/X11ThreadQueue.cs b/source/ShiftUI/Internal/X11ThreadQueue.cs
new file mode 100644
index 0000000..b68d74c
--- /dev/null
+++ b/source/ShiftUI/Internal/X11ThreadQueue.cs
@@ -0,0 +1,502 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2004-2006 Novell, Inc.
+//
+// Authors:
+// Jackson Harper ([email protected])
+// Peter Dennis Bartok ([email protected])
+// Chris Toshok ([email protected])
+//
+
+using System;
+using System.Threading;
+using System.Collections;
+
+namespace ShiftUI.X11Internal {
+
+ internal class X11ThreadQueue {
+
+ 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 InitialXEventQueueSize = 128;
+ static readonly int InitialHwndQueueSize = 50;
+
+ public X11ThreadQueue (Thread thread)
+ {
+ 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;
+ this.dispatch_idle = true;
+ }
+
+ public int CountUnlocked {
+ get { return xqueue.Count + paint_queue.Count; }
+ }
+
+ public Thread Thread {
+ get { return thread; }
+ }
+
+ 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 void Enqueue (XEvent xevent)
+ {
+ lock (lockobj) {
+ EnqueueUnlocked (xevent);
+ }
+ }
+
+ public bool Dequeue (out XEvent xevent)
+ {
+ StartOver:
+ bool got_xevent = false;
+
+ lock (lockobj) {
+ if (xqueue.Count > 0) {
+ got_xevent = true;
+ xevent = xqueue.Dequeue ();
+ }
+ else
+ xevent = new XEvent (); /* not strictly needed, but mcs complains */
+ }
+
+ 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;
+ }
+
+ lock (lockobj) {
+ 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)
+ {
+ paint_queue.Remove (hwnd);
+ }
+
+ public void AddPaint (Hwnd hwnd)
+ {
+ 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 ()
+ {
+ Monitor.Enter (lockobj);
+ }
+
+ public void Unlock ()
+ {
+ Monitor.Exit (lockobj);
+ }
+
+ private int NextTimeout ()
+ {
+ int timeout = Int32.MaxValue;
+ DateTime now = DateTime.UtcNow;
+
+ foreach (Timer timer in timer_list) {
+ int next = (int) (timer.Expires - now).TotalMilliseconds;
+ if (next < 0)
+ return 0; // Have a timer that has already expired
+
+ if (next < timeout)
+ timeout = next;
+ }
+
+ if (timeout < Timer.Minimum) {
+ timeout = Timer.Minimum;
+ }
+
+ if (timeout == Int32.MaxValue)
+ timeout = Timeout.Infinite;
+
+ return timeout;
+ }
+
+ public void CheckTimers ()
+ {
+ int count;
+ DateTime now = DateTime.UtcNow;
+
+ count = timer_list.Count;
+
+ if (count == 0)
+ return;
+
+ for (int i = 0; i < timer_list.Count; i++) {
+ Timer timer;
+
+ timer = (Timer) timer_list [i];
+
+ if (timer.Enabled && timer.Expires <= now) {
+ timer.Update (now);
+ timer.FireTick ();
+ }
+ }
+ }
+
+ public void SetTimer (Timer timer)
+ {
+ lock (lockobj) {
+ timer_list.Add (timer);
+
+ // we need to wake up any thread waiting in DequeueUnlocked,
+ // since it might need to wait for a different amount of time.
+ Monitor.PulseAll (lockobj);
+ }
+
+ }
+
+ public void KillTimer (Timer timer)
+ {
+ lock (lockobj) {
+ timer_list.Remove (timer);
+
+ // we need to wake up any thread waiting in DequeueUnlocked,
+ // since it might need to wait for a different amount of time.
+ Monitor.PulseAll (lockobj);
+ }
+ }
+
+ 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; }
+ }
+
+ public bool PostQuitState {
+ get { return quit_posted; }
+ set { quit_posted = value; }
+ }
+
+ 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 {
+ get { return hwnds.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 DebugHwndEventQueue
+ int index = hwnds.IndexOf(hwnd);
+ if (index != -1)
+ stacks.RemoveAt(index);
+#endif
+ hwnds.Remove(hwnd);
+ }
+
+ protected abstract XEvent Peek ();
+
+ public virtual XEvent Dequeue ()
+ {
+ if (hwnds.Count == 0)
+ throw new Exception ("Attempt to dequeue empty queue.");
+
+ 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.PendingExpose) {
+ xevent.ExposeEvent.window = hwnd.ClientWindow;
+ } else {
+ 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;
+ }
+
+ // don't override Dequeue like ConfigureQueue does.
+ }
+
+ /* a circular queue for holding X events for processing by GetMessage */
+ private class XEventQueue {
+
+ XEvent[] xevents;
+ int head;
+ int tail;
+ int 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];
+ }
+
+ public int Count {
+ get { return size; }
+ }
+
+ public void Enqueue (XEvent xevent)
+ {
+ if (size == xevents.Length)
+ Grow ();
+
+ xevents [tail] = xevent;
+ tail = (tail + 1) & (xevents.Length - 1);
+ size++;
+ }
+
+ public XEvent Dequeue ()
+ {
+ if (size < 1)
+ throw new Exception ("Attempt to dequeue empty queue.");
+
+ XEvent res = xevents [head];
+ head = (head + 1) & (xevents.Length - 1);
+ size--;
+ return res;
+ }
+
+ public XEvent Peek()
+ {
+ if (size < 1)
+ throw new Exception ("Attempt to peek at empty queue.");
+
+ return xevents[head];
+ }
+
+ private void Grow ()
+ {
+ int newcap = (xevents.Length * 2);
+ XEvent [] na = new XEvent [newcap];
+
+ 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;
+ }
+ }
+ }
+}
+