private int interval = 100;
private DateTime expires;
+ internal static readonly int Minimum = 15;
+
public Timer ()
{
enabled = false;
set {
interval = value;
// Use AddTicks so we get some rounding
- expires = DateTime.Now.AddMilliseconds (interval);
+ expires = DateTime.Now.AddMilliseconds (interval > Minimum ? interval : Minimum);
}
}
return base.ToString () + ", Interval: " + Interval;
}
- internal void Update ()
+ internal void Update (DateTime update)
{
- expires = DateTime.Now.AddMilliseconds (interval);
+ expires = update.AddMilliseconds (interval > Minimum ? interval : Minimum);
}
internal void FireTick ()
{
OnTick (EventArgs.Empty);
+ Update (DateTime.Now);
}
protected virtual void OnTick (EventArgs e)
{
- lock (this) {
- if (Tick != null)
- Tick (this, e);
- }
+ if (Tick != null)
+ Tick (this, e);
}
protected override void Dispose (bool disposing)
Enabled = false;
}
- private void TickHandler (object sender, EventArgs e)
+ private bool has_last_fire = false;
+ private DateTime last_fire;
+
+ internal void TickHandler (object sender, EventArgs e)
{
OnTick (e);
}
// Peter Bartok pbartok@novell.com
//
//
-// $Revision: 1.10 $
+// $Revision: 1.11 $
// $Modtime: $
// $Log: X11Structs.cs,v $
+// Revision 1.11 2004/09/14 00:13:29 jackson
+// Timers are now handled in a second thread and post messages into the main threads message queue. This makes timing much more consistent. Both win2K and XP have a minimum timer value of 15 milliseconds, so we now do this too.
+//
// Revision 1.10 2004/09/13 21:18:32 pbartok
// - Added Z-Ordering methods
//
internal byte minor_code;
}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct XTimerNotifyEvent {
+ internal XEventName type;
+ internal EventHandler handler;
+ }
+
[StructLayout(LayoutKind.Sequential)]
internal struct XEventPad {
internal int pad0;
[ FieldOffset(0) ] internal XMappingEvent MappingEvent;
[ FieldOffset(0) ] internal XErrorEvent ErrorEvent;
[ FieldOffset(0) ] internal XKeymapEvent KeymapEvent;
+ [ FieldOffset(0) ] internal XTimerNotifyEvent TimerNotifyEvent;
//[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=24)]
//[ FieldOffset(0) ] internal int[] pad;
SelectionRequest = 30,
SelectionNotify = 31,
ColormapNotify = 32,
- ClientMessage = 33,
- MappingNotify = 34,
- LASTEvent = 35
+ ClientMessage = 33,
+ MappingNotify = 34,
+ TimerNotify = 100,
+
+ LASTEvent
}
internal enum XWindowAttribute {
private static Hashtable handle_data;
private Queue message_queue;
+
private ArrayList timer_list;
+ private Thread timer_thread;
+ private AutoResetEvent timer_wait;
private static readonly EventMask SelectInputMask = EventMask.ButtonPressMask |
EventMask.ButtonReleaseMask |
ref_count=0;
message_queue = new Queue ();
- timer_list = new ArrayList ();
+ ArrayList timers = new ArrayList ();
+ timer_list = ArrayList.Synchronized (timers);
// Now regular initialization
SetDisplay(XOpenDisplay(IntPtr.Zero));
return (IntPtr)result;
}
+ // The message_queue should be locked when this is called
private void UpdateMessageQueue ()
{
- lock (timer_list) {
- for (int i = 0; i < timer_list.Count; i++) {
- Timer timer = (Timer) timer_list [i];
- if (timer.Enabled && timer.Expires <= DateTime.Now) {
- timer.FireTick ();
- timer.Update ();
- }
- }
+ int pending;
+ lock (this) {
+ pending = XPending (DisplayHandle);
}
-
- while (XPending (DisplayHandle) > 0) {
+
+ while (pending > 0) {
XEvent xevent = new XEvent ();
- XNextEvent (DisplayHandle, ref xevent);
+ lock (this) {
+ XNextEvent (DisplayHandle, ref xevent);
+ }
switch (xevent.type) {
case XEventName.Expose:
message_queue.Enqueue (xevent);
break;
}
+
+ lock (this) {
+ pending = XPending (DisplayHandle);
+ }
}
+
}
internal override bool GetMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) {
- XEvent xevent = new XEvent();
+ XEvent xevent;
lock (message_queue) {
if (message_queue.Count > 0) {
if (message_queue.Count > 0) {
xevent = (XEvent) message_queue.Dequeue ();
} else {
+ msg.hwnd= IntPtr.Zero;
msg.message = Msg.WM_ENTERIDLE;
return true;
}
}
break;
}
- default: {
+
+ case XEventName.TimerNotify: {
+ xevent.TimerNotifyEvent.handler (this, EventArgs.Empty);
+ break;
+ }
+
+ default: {
msg.message = Msg.WM_NULL;
break;
}
}
}
+ internal void TimerProc ()
+ {
+ while (true) {
+ int next_fire = -1;
+
+ ArrayList fire_list = new ArrayList ();
+ DateTime now = DateTime.Now;
+ for (int i = 0; i < timer_list.Count; i++) {
+ Timer timer = (Timer) timer_list [i];
+
+ if (timer.Enabled && timer.Expires <= now) {
+ XEvent xevent = new XEvent ();
+ xevent.type = XEventName.TimerNotify;
+ xevent.AnyEvent.window = IntPtr.Zero;
+ xevent.TimerNotifyEvent.handler = new EventHandler (timer.TickHandler);
+
+ fire_list.Add (xevent);
+ }
+ int next = (int) (timer.Expires.AddMilliseconds (timer.Interval) - now).TotalMilliseconds;
+ if (i == 0 || next < next_fire)
+ next_fire = next;
+ }
+
+ // Everything is copied to a list and then to the queue so we
+ // don't lock the queue while calculating all the times
+ if (fire_list.Count > 0) {
+ lock (message_queue) {
+ foreach (object obj in fire_list) {
+ message_queue.Enqueue (obj);
+ }
+ }
+ }
+ if (next_fire < Timer.Minimum)
+ next_fire = Timer.Minimum;
+ timer_wait.WaitOne (next_fire, true);
+ }
+ }
+
internal override void SetTimer (Timer timer)
{
- lock (timer_list) {
- timer_list.Add (timer);
+ timer_list.Add (timer);
+
+ if (timer_thread == null) {
+ ThreadStart thread_start = new ThreadStart (TimerProc);
+ timer_thread = new Thread (thread_start);
+ timer_thread.IsBackground = true;
+
+ timer_wait = new AutoResetEvent (true);
+ timer_thread.Start();
}
}
internal override void KillTimer (Timer timer)
{
- lock (timer_list) {
- timer_list.Remove (timer);
- }
+ timer_list.Remove (timer);
}
// Santa's little helper