From d40fed5ce2bc806a91245adb18039634eac13ed0 Mon Sep 17 00:00:00 2001 From: MichaelTheShifter Date: Wed, 20 Jul 2016 09:40:36 -0400 Subject: Move ShiftUI source code to ShiftOS This'll be a lot easier to work on. --- source/ShiftUI/Internal/X11ThreadQueue.cs | 502 ++++++++++++++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 source/ShiftUI/Internal/X11ThreadQueue.cs (limited to 'source/ShiftUI/Internal/X11ThreadQueue.cs') 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 (jackson@ximian.com) +// Peter Dennis Bartok (pbartok@novell.com) +// Chris Toshok (toshok@ximian.com) +// + +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; + } + } + } +} + -- cgit v1.2.3