1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2006 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
24 // Peter Dennis Bartok (pbartok@novell.com)
25 // Chris Toshok (toshok@ximian.com)
29 using System.Threading;
30 using System.Collections;
32 namespace System.Windows.Forms.X11Internal {
34 internal class X11ThreadQueue {
37 PaintQueue paint_queue;
38 ConfigureQueue configure_queue;
43 bool need_dispatch_idle = true;
44 object lockobj = new object ();
46 static readonly int InitialXEventQueueSize = 128;
47 static readonly int InitialHwndQueueSize = 50;
49 public X11ThreadQueue (Thread thread)
51 xqueue = new XEventQueue (InitialXEventQueueSize);
52 paint_queue = new PaintQueue (InitialHwndQueueSize);
53 configure_queue = new ConfigureQueue (InitialHwndQueueSize);
54 timer_list = new ArrayList ();
56 this.quit_posted = false;
57 this.dispatch_idle = true;
60 public int CountUnlocked {
61 get { return xqueue.Count + paint_queue.Count; }
64 public Thread Thread {
65 get { return thread; }
68 public void EnqueueUnlocked (XEvent xevent)
70 switch (xevent.type) {
71 case XEventName.KeyPress:
72 case XEventName.KeyRelease:
73 case XEventName.ButtonPress:
74 case XEventName.ButtonRelease:
75 NeedDispatchIdle = true;
77 case XEventName.MotionNotify:
78 if (xqueue.Count > 0) {
79 XEvent peek = xqueue.Peek ();
80 if (peek.AnyEvent.type == XEventName.MotionNotify)
81 return; // we've already got a pending motion notify.
84 // otherwise fall through and enqueue
89 xqueue.Enqueue (xevent);
90 // wake up any thread blocking in DequeueUnlocked
91 Monitor.PulseAll (lockobj);
94 public void Enqueue (XEvent xevent)
97 EnqueueUnlocked (xevent);
101 public bool Dequeue (out XEvent xevent)
104 bool got_xevent = false;
107 if (xqueue.Count > 0) {
109 xevent = xqueue.Dequeue ();
112 xevent = new XEvent (); /* not strictly needed, but mcs complains */
116 if (xevent.AnyEvent.type == XEventName.Expose) {
119 Console.Out.Flush ();
121 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
122 hwnd.AddExpose (xevent.AnyEvent.window == hwnd.ClientWindow,
123 xevent.ExposeEvent.x, xevent.ExposeEvent.y,
124 xevent.ExposeEvent.width, xevent.ExposeEvent.height);
127 else if (xevent.AnyEvent.type == XEventName.ConfigureNotify) {
130 Console.Out.Flush ();
132 X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
133 hwnd.AddConfigureNotify (xevent);
139 Console.Out.Flush ();
141 /* it was an event we can deal with directly, return it */
146 if (paint_queue.Count > 0) {
147 xevent = paint_queue.Dequeue ();
150 Console.Out.Flush ();
154 else if (configure_queue.Count > 0) {
155 xevent = configure_queue.Dequeue ();
158 Console.Out.Flush ();
164 if (dispatch_idle && need_dispatch_idle) {
165 OnIdle (EventArgs.Empty);
166 need_dispatch_idle = false;
170 if (CountUnlocked > 0)
173 if (Monitor.Wait (lockobj, NextTimeout (), true)) {
174 // the lock was reaquired before the
175 // timeout. meaning an event was
176 // enqueued by X11Display.XEventThread.
186 public void RemovePaint (Hwnd hwnd)
188 paint_queue.Remove (hwnd);
191 public void AddPaint (Hwnd hwnd)
193 paint_queue.Enqueue (hwnd);
196 public void AddConfigure (Hwnd hwnd)
198 configure_queue.Enqueue (hwnd);
201 public ConfigureQueue Configure {
202 get { return configure_queue; }
205 public PaintQueue Paint {
206 get { return paint_queue; }
211 Monitor.Enter (lockobj);
214 public void Unlock ()
216 Monitor.Exit (lockobj);
219 private int NextTimeout ()
221 int timeout = Int32.MaxValue;
222 DateTime now = DateTime.UtcNow;
224 foreach (Timer timer in timer_list) {
225 int next = (int) (timer.Expires - now).TotalMilliseconds;
227 return 0; // Have a timer that has already expired
233 if (timeout < Timer.Minimum) {
234 timeout = Timer.Minimum;
237 if (timeout == Int32.MaxValue)
238 timeout = Timeout.Infinite;
243 public void CheckTimers ()
246 DateTime now = DateTime.UtcNow;
248 count = timer_list.Count;
253 for (int i = 0; i < timer_list.Count; i++) {
256 timer = (Timer) timer_list [i];
258 if (timer.Enabled && timer.Expires <= now) {
265 public void SetTimer (Timer timer)
268 timer_list.Add (timer);
270 // we need to wake up any thread waiting in DequeueUnlocked,
271 // since it might need to wait for a different amount of time.
272 Monitor.PulseAll (lockobj);
277 public void KillTimer (Timer timer)
280 timer_list.Remove (timer);
282 // we need to wake up any thread waiting in DequeueUnlocked,
283 // since it might need to wait for a different amount of time.
284 Monitor.PulseAll (lockobj);
288 public event EventHandler Idle;
289 public void OnIdle (EventArgs e)
295 public bool NeedDispatchIdle {
296 get { return need_dispatch_idle; }
297 set { need_dispatch_idle = value; }
300 public bool DispatchIdle {
301 get { return dispatch_idle; }
302 set { dispatch_idle = value; }
305 public bool PostQuitState {
306 get { return quit_posted; }
307 set { quit_posted = value; }
310 public abstract class HwndEventQueue {
311 protected ArrayList hwnds;
312 #if DebugHwndEventQueue
313 protected ArrayList stacks;
315 public HwndEventQueue (int size)
317 hwnds = new ArrayList (size);
318 #if DebugHwndEventQueue
319 stacks = new ArrayList (size);
324 get { return hwnds.Count; }
327 public void Enqueue (Hwnd hwnd)
329 if (hwnds.Contains (hwnd)) {
330 #if DebugHwndEventQueue
331 Console.WriteLine ("hwnds can only appear in the queue once.");
332 Console.WriteLine (Environment.StackTrace);
333 Console.WriteLine ("originally added here:");
334 Console.WriteLine (stacks[hwnds.IndexOf (hwnd)]);
340 #if DebugHwndEventQueue
341 stacks.Add(Environment.StackTrace);
345 public void Remove(Hwnd hwnd)
347 #if DebugHwndEventQueue
348 int index = hwnds.IndexOf(hwnd);
350 stacks.RemoveAt(index);
355 protected abstract XEvent Peek ();
357 public virtual XEvent Dequeue ()
359 if (hwnds.Count == 0)
360 throw new Exception ("Attempt to dequeue empty queue.");
367 public class ConfigureQueue : HwndEventQueue
369 public ConfigureQueue (int size) : base (size)
373 protected override XEvent Peek ()
375 X11Hwnd hwnd = (X11Hwnd)hwnds[0];
377 XEvent xevent = new XEvent ();
378 xevent.AnyEvent.type = XEventName.ConfigureNotify;
380 xevent.ConfigureEvent.window = hwnd.ClientWindow;
381 xevent.ConfigureEvent.x = hwnd.X;
382 xevent.ConfigureEvent.y = hwnd.Y;
383 xevent.ConfigureEvent.width = hwnd.Width;
384 xevent.ConfigureEvent.height = hwnd.Height;
389 public override XEvent Dequeue ()
391 XEvent xev = base.Dequeue ();
395 #if DebugHwndEventQueue
403 public class PaintQueue : HwndEventQueue
405 public PaintQueue (int size) : base (size)
409 protected override XEvent Peek ()
411 X11Hwnd hwnd = (X11Hwnd)hwnds[0];
413 XEvent xevent = new XEvent ();
415 xevent.AnyEvent.type = XEventName.Expose;
417 if (hwnd.PendingExpose) {
418 xevent.ExposeEvent.window = hwnd.ClientWindow;
420 xevent.ExposeEvent.window = hwnd.WholeWindow;
421 xevent.ExposeEvent.x = hwnd.nc_invalid.X;
422 xevent.ExposeEvent.y = hwnd.nc_invalid.Y;
423 xevent.ExposeEvent.width = hwnd.nc_invalid.Width;
424 xevent.ExposeEvent.height = hwnd.nc_invalid.Height;
430 // don't override Dequeue like ConfigureQueue does.
433 /* a circular queue for holding X events for processing by GetMessage */
434 private class XEventQueue {
441 public XEventQueue (int initial_size)
443 if (initial_size % 2 != 0)
444 throw new Exception ("XEventQueue must be a power of 2 size");
446 xevents = new XEvent [initial_size];
453 public void Enqueue (XEvent xevent)
455 if (size == xevents.Length)
458 xevents [tail] = xevent;
459 tail = (tail + 1) & (xevents.Length - 1);
463 public XEvent Dequeue ()
466 throw new Exception ("Attempt to dequeue empty queue.");
468 XEvent res = xevents [head];
469 head = (head + 1) & (xevents.Length - 1);
477 throw new Exception ("Attempt to peek at empty queue.");
479 return xevents[head];
484 int newcap = (xevents.Length * 2);
485 XEvent [] na = new XEvent [newcap];
487 if (head + size > xevents.Length) {
488 Array.Copy (xevents, head, na, 0, xevents.Length - head);
489 Array.Copy (xevents, 0, na, xevents.Length - head, head + size - xevents.Length);
492 Array.Copy (xevents, head, na, 0, size);