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 xqueue.Enqueue (xevent);
71 // wake up any thread blocking in DequeueUnlocked
72 Monitor.PulseAll (lockobj);
75 public void Enqueue (XEvent xevent)
78 EnqueueUnlocked (xevent);
82 public bool DequeueUnlocked (out XEvent xevent)
85 if (xqueue.Count > 0) {
86 xevent = xqueue.Dequeue ();
90 if (paint_queue.Count > 0 || configure_queue.Count > 0) {
91 xevent = new XEvent ();
95 // both queues are empty. go to sleep until NextTimeout
96 // (or until there's an event to handle).
98 if (dispatch_idle && need_dispatch_idle) {
99 OnIdle (EventArgs.Empty);
100 need_dispatch_idle = false;
103 if (Monitor.Wait (lockobj, NextTimeout (), true)) {
104 /* the lock was reaquired before timeout.
105 i.e. we have an event now */
109 xevent = new XEvent ();
114 public bool Dequeue (out XEvent xevent)
117 return DequeueUnlocked (out xevent);
121 public void RemovePaintUnlocked (Hwnd hwnd)
123 paint_queue.Remove (hwnd);
126 public void RemovePaint (Hwnd hwnd)
129 RemovePaintUnlocked (hwnd);
133 public void AddPaintUnlocked (Hwnd hwnd)
135 paint_queue.Enqueue (hwnd);
138 public void AddPaint (Hwnd hwnd)
141 AddPaintUnlocked (hwnd);
145 public void AddConfigureUnlocked (Hwnd hwnd)
147 configure_queue.Enqueue (hwnd);
150 public ConfigureQueue Configure {
151 get { return configure_queue; }
154 public PaintQueue Paint {
155 get { return paint_queue; }
160 Monitor.Enter (lockobj);
163 public void Unlock ()
165 Monitor.Exit (lockobj);
168 private int NextTimeout ()
170 int timeout = Int32.MaxValue;
171 DateTime now = DateTime.UtcNow;
173 foreach (Timer timer in timer_list) {
174 int next = (int) (timer.Expires - now).TotalMilliseconds;
176 return 0; // Have a timer that has already expired
182 if (timeout < Timer.Minimum) {
183 timeout = Timer.Minimum;
186 if (timeout == Int32.MaxValue)
187 timeout = Timeout.Infinite;
192 public void CheckTimers ()
195 DateTime now = DateTime.UtcNow;
197 count = timer_list.Count;
202 for (int i = 0; i < timer_list.Count; i++) {
205 timer = (Timer) timer_list [i];
207 if (timer.Enabled && timer.Expires <= now) {
214 public void SetTimer (Timer timer)
217 timer_list.Add (timer);
219 // we need to wake up any thread waiting in DequeueUnlocked,
220 // since it might need to wait for a different amount of time.
221 Monitor.PulseAll (lockobj);
226 public void KillTimer (Timer timer)
229 timer_list.Remove (timer);
231 // we need to wake up any thread waiting in DequeueUnlocked,
232 // since it might need to wait for a different amount of time.
233 Monitor.PulseAll (lockobj);
237 public event EventHandler Idle;
238 public void OnIdle (EventArgs e)
244 public bool NeedDispatchIdle {
245 get { return need_dispatch_idle; }
246 set { need_dispatch_idle = value; }
249 public bool DispatchIdle {
250 get { return dispatch_idle; }
251 set { dispatch_idle = value; }
254 public bool PostQuitState {
255 get { return quit_posted; }
256 set { quit_posted = value; }
259 public abstract class HwndEventQueue {
260 protected ArrayList hwnds;
262 public HwndEventQueue (int size)
264 hwnds = new ArrayList(size);
268 get { return hwnds.Count; }
271 public void Enqueue (Hwnd hwnd)
273 if (hwnds.Contains (hwnd)) {
274 Console.WriteLine ("hwnds can only appear in the queue once.");
275 Console.WriteLine (Environment.StackTrace);
281 public void Remove(Hwnd hwnd)
286 protected abstract XEvent Peek ();
288 public XEvent Dequeue ()
290 if (hwnds.Count == 0)
291 throw new Exception ("Attempt to dequeue empty queue.");
293 // populate the xevent
294 XEvent xevent = Peek ();
303 public class ConfigureQueue : HwndEventQueue
305 public ConfigureQueue (int size) : base (size)
309 protected override XEvent Peek ()
311 X11Hwnd hwnd = (X11Hwnd)hwnds[0];
313 XEvent xevent = new XEvent ();
314 xevent.AnyEvent.type = XEventName.ConfigureNotify;
316 xevent.ConfigureEvent.window = hwnd.ClientWindow;
317 xevent.ConfigureEvent.x = hwnd.X;
318 xevent.ConfigureEvent.y = hwnd.Y;
319 xevent.ConfigureEvent.width = hwnd.Width;
320 xevent.ConfigureEvent.height = hwnd.Height;
326 public class PaintQueue : HwndEventQueue
328 public PaintQueue (int size) : base (size)
332 protected override XEvent Peek ()
334 X11Hwnd hwnd = (X11Hwnd)hwnds[0];
336 XEvent xevent = new XEvent ();
338 xevent.AnyEvent.type = XEventName.Expose;
340 if (hwnd.PendingExpose) {
341 xevent.ExposeEvent.window = hwnd.ClientWindow;
343 xevent.ExposeEvent.window = hwnd.WholeWindow;
344 xevent.ExposeEvent.x = hwnd.nc_invalid.X;
345 xevent.ExposeEvent.y = hwnd.nc_invalid.Y;
346 xevent.ExposeEvent.width = hwnd.nc_invalid.Width;
347 xevent.ExposeEvent.height = hwnd.nc_invalid.Height;
354 /* a circular queue for holding X events for processing by GetMessage */
355 private class XEventQueue {
362 public XEventQueue (int initial_size)
364 if (initial_size % 2 != 0)
365 throw new Exception ("XEventQueue must be a power of 2 size");
367 xevents = new XEvent [initial_size];
374 public void Enqueue (XEvent xevent)
376 if (size == xevents.Length)
379 xevents [tail] = xevent;
380 tail = (tail + 1) & (xevents.Length - 1);
384 public XEvent Dequeue ()
387 throw new Exception ("Attempt to dequeue empty queue.");
389 XEvent res = xevents [head];
390 head = (head + 1) & (xevents.Length - 1);
398 throw new Exception ("Attempt to peek at empty queue.");
400 return xevents[head];
405 int newcap = (xevents.Length * 2);
406 XEvent [] na = new XEvent [newcap];
408 if (head + size > xevents.Length) {
409 Array.Copy (xevents, head, na, 0, xevents.Length - head);
410 Array.Copy (xevents, 0, na, xevents.Length - head, head + size - xevents.Length);
413 Array.Copy (xevents, head, na, 0, size);