New test.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.X11Internal / X11ThreadQueue.cs
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:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
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.
19 //
20 // Copyright (c) 2004-2006 Novell, Inc.
21 //
22 // Authors:
23 //  Jackson Harper (jackson@ximian.com)
24 //  Peter Dennis Bartok (pbartok@novell.com)
25 //  Chris Toshok (toshok@ximian.com)
26 //
27
28 using System;
29 using System.Threading;
30 using System.Collections;
31
32 namespace System.Windows.Forms.X11Internal {
33
34         internal class X11ThreadQueue {
35
36                 XEventQueue xqueue;
37                 PaintQueue paint_queue;
38                 ConfigureQueue configure_queue;
39                 ArrayList timer_list;
40                 Thread thread;
41                 bool quit_posted;
42                 bool dispatch_idle;
43                 bool need_dispatch_idle = true;
44                 object lockobj = new object ();
45
46                 static readonly int InitialXEventQueueSize = 128;
47                 static readonly int InitialHwndQueueSize = 50;
48
49                 public X11ThreadQueue (Thread thread)
50                 {
51                         xqueue = new XEventQueue (InitialXEventQueueSize);
52                         paint_queue = new PaintQueue (InitialHwndQueueSize);
53                         configure_queue = new ConfigureQueue (InitialHwndQueueSize);
54                         timer_list = new ArrayList ();
55                         this.thread = thread;
56                         this.quit_posted = false;
57                         this.dispatch_idle = true;
58                 }
59
60                 public int CountUnlocked {
61                         get { return xqueue.Count + paint_queue.Count; }
62                 }
63
64                 public Thread Thread {
65                         get { return thread; }
66                 }
67
68                 public void EnqueueUnlocked (XEvent xevent)
69                 {
70                         xqueue.Enqueue (xevent);
71                         // wake up any thread blocking in DequeueUnlocked
72                         Monitor.PulseAll (lockobj);
73                 }
74
75                 public void Enqueue (XEvent xevent)
76                 {
77                         lock (lockobj) {
78                                 EnqueueUnlocked (xevent);
79                         }
80                 }
81
82                 public bool DequeueUnlocked (out XEvent xevent)
83                 {
84                 try_again:
85                         if (xqueue.Count > 0) {
86                                 xevent = xqueue.Dequeue ();
87                                 return true;
88                         }
89
90                         if (paint_queue.Count > 0 || configure_queue.Count > 0) {
91                                 xevent = new XEvent ();
92                                 return false;
93                         }
94
95                         // both queues are empty.  go to sleep until NextTimeout
96                         // (or until there's an event to handle).
97
98                         if (dispatch_idle && need_dispatch_idle) {
99                                 OnIdle (EventArgs.Empty);
100                                 need_dispatch_idle = false;
101                         }
102
103                         if (Monitor.Wait (lockobj, NextTimeout (), true)) {
104                                 /* the lock was reaquired before timeout.
105                                    i.e. we have an event now */
106                                 goto try_again;
107                         }
108                         else {
109                                 xevent = new XEvent ();
110                                 return false;
111                         }
112                 }
113
114                 public bool Dequeue (out XEvent xevent)
115                 {
116                         lock (lockobj) {
117                                 return DequeueUnlocked (out xevent);
118                         }
119                 }
120
121                 public void RemovePaintUnlocked (Hwnd hwnd)
122                 {
123                         paint_queue.Remove (hwnd);
124                 }
125
126                 public void RemovePaint (Hwnd hwnd)
127                 {
128                         lock (lockobj) {
129                                 RemovePaintUnlocked (hwnd);
130                         }
131                 }
132
133                 public void AddPaintUnlocked (Hwnd hwnd)
134                 {
135                         paint_queue.Enqueue (hwnd);
136                 }
137
138                 public void AddPaint (Hwnd hwnd)
139                 {
140                         lock (lockobj) {
141                                 AddPaintUnlocked (hwnd);
142                         }
143                 }
144
145                 public void AddConfigureUnlocked (Hwnd hwnd)
146                 {
147                         configure_queue.Enqueue (hwnd);
148                 }
149
150                 public ConfigureQueue Configure {
151                         get { return configure_queue; }
152                 }
153
154                 public PaintQueue Paint {
155                         get { return paint_queue; }
156                 }
157
158                 public void Lock ()
159                 {
160                         Monitor.Enter (lockobj);
161                 }
162
163                 public void Unlock ()
164                 {
165                         Monitor.Exit (lockobj);
166                 }
167
168                 private int NextTimeout ()
169                 {
170                         int timeout = Int32.MaxValue; 
171                         DateTime now = DateTime.UtcNow;
172
173                         foreach (Timer timer in timer_list) {
174                                 int next = (int) (timer.Expires - now).TotalMilliseconds;
175                                 if (next < 0)
176                                         return 0; // Have a timer that has already expired
177
178                                 if (next < timeout)
179                                         timeout = next;
180                         }
181
182                         if (timeout < Timer.Minimum) {
183                                 timeout = Timer.Minimum;
184                         }
185
186                         if (timeout == Int32.MaxValue)
187                                 timeout = Timeout.Infinite;
188
189                         return timeout;
190                 }
191
192                 public void CheckTimers ()
193                 {
194                         int count;
195                         DateTime now = DateTime.UtcNow;
196
197                         count = timer_list.Count;
198
199                         if (count == 0)
200                                 return;
201
202                         for (int i = 0; i < timer_list.Count; i++) {
203                                 Timer timer;
204
205                                 timer = (Timer) timer_list [i];
206
207                                 if (timer.Enabled && timer.Expires <= now) {
208                                         timer.Update (now);
209                                         timer.FireTick ();
210                                 }
211                         }
212                 }
213
214                 public void SetTimer (Timer timer)
215                 {
216                         lock (lockobj) {
217                                 timer_list.Add (timer);
218
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);
222                         }
223
224                 }
225
226                 public void KillTimer (Timer timer)
227                 {
228                         lock (lockobj) {
229                                 timer_list.Remove (timer);
230
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);
234                         }
235                 }
236
237                 public event EventHandler Idle;
238                 public void OnIdle (EventArgs e)
239                 {
240                         if (Idle != null)
241                                 Idle (thread, e);
242                 }
243
244                 public bool NeedDispatchIdle {
245                         get { return need_dispatch_idle; }
246                         set { need_dispatch_idle = value; }
247                 }
248
249                 public bool DispatchIdle {
250                         get { return dispatch_idle; }
251                         set { dispatch_idle = value; }
252                 }
253
254                 public bool PostQuitState {
255                         get { return quit_posted; }
256                         set { quit_posted = value; }
257                 }
258
259                 public abstract class HwndEventQueue {
260                         protected ArrayList hwnds;
261                         
262                         public HwndEventQueue (int size)
263                         {
264                                 hwnds = new ArrayList(size);
265                         }
266
267                         public int Count {
268                                 get { return hwnds.Count; }
269                         }
270
271                         public void Enqueue (Hwnd hwnd)
272                         {
273                                 if (hwnds.Contains (hwnd)) {
274                                         Console.WriteLine ("hwnds can only appear in the queue once.");
275                                         Console.WriteLine (Environment.StackTrace);
276                                         return;
277                                 }
278                                 hwnds.Add(hwnd);
279                         }
280
281                         public void Remove(Hwnd hwnd)
282                         {
283                                 hwnds.Remove(hwnd);
284                         }
285
286                         protected abstract XEvent Peek ();
287
288                         public XEvent Dequeue ()
289                         {
290                                 if (hwnds.Count == 0)
291                                         throw new Exception ("Attempt to dequeue empty queue.");
292
293                                 // populate the xevent
294                                 XEvent xevent = Peek ();
295
296                                 hwnds.RemoveAt(0);
297
298                                 return xevent;
299                         }
300                 }
301
302
303                 public class ConfigureQueue : HwndEventQueue
304                 {
305                         public ConfigureQueue (int size) : base (size)
306                         {
307                         }
308
309                         protected override XEvent Peek ()
310                         {
311                                 X11Hwnd hwnd = (X11Hwnd)hwnds[0];
312
313                                 XEvent xevent = new XEvent ();
314                                 xevent.AnyEvent.type = XEventName.ConfigureNotify;
315
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;
321                                 
322                                 return xevent;
323                         }
324                 }
325
326                 public class PaintQueue : HwndEventQueue
327                 {
328                         public PaintQueue (int size) : base (size)
329                         {
330                         }
331
332                         protected override XEvent Peek ()
333                         {
334                                 X11Hwnd hwnd = (X11Hwnd)hwnds[0];
335
336                                 XEvent xevent = new XEvent ();
337
338                                 xevent.AnyEvent.type = XEventName.Expose;
339
340                                 if (hwnd.PendingExpose) {
341                                         xevent.ExposeEvent.window = hwnd.ClientWindow;
342                                 } else {
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;
348                                 }
349
350                                 return xevent;
351                         }
352                 }
353
354                 /* a circular queue for holding X events for processing by GetMessage */
355                 private class XEventQueue {
356
357                         XEvent[] xevents;
358                         int head;
359                         int tail;
360                         int size;
361                         
362                         public XEventQueue (int initial_size)
363                         {
364                                 if (initial_size % 2 != 0)
365                                         throw new Exception ("XEventQueue must be a power of 2 size");
366
367                                 xevents = new XEvent [initial_size];
368                         }
369
370                         public int Count {
371                                 get { return size; }
372                         }
373
374                         public void Enqueue (XEvent xevent)
375                         {
376                                 if (size == xevents.Length)
377                                         Grow ();
378
379                                 xevents [tail] = xevent;
380                                 tail = (tail + 1) & (xevents.Length - 1);
381                                 size++;
382                         }
383
384                         public XEvent Dequeue ()
385                         {
386                                 if (size < 1)
387                                         throw new Exception ("Attempt to dequeue empty queue.");
388
389                                 XEvent res = xevents [head];
390                                 head = (head + 1) & (xevents.Length - 1);
391                                 size--;
392                                 return res;
393                         }
394
395                         public XEvent Peek()
396                         {
397                                 if (size < 1)
398                                         throw new Exception ("Attempt to peek at empty queue.");
399
400                                 return xevents[head];
401                         }
402
403                         private void Grow ()
404                         {
405                                 int newcap = (xevents.Length * 2);
406                                 XEvent [] na = new XEvent [newcap];
407
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);
411                                 }
412                                 else {
413                                         Array.Copy (xevents, head, na, 0, size);
414                                 }
415
416                                 xevents = na;
417                                 head = 0;
418                                 tail = head + size;
419                         }
420                 }
421         }
422 }
423