Fix bug #395
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / XEventQueue.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 // System.Windows.Forms.XEventQueue
23 //
24 // Authors:
25 //  Jackson Harper (jackson@ximian.com)
26 //  Peter Dennis Bartok (pbartok@novell.com)
27 //
28
29 using System;
30 using System.Threading;
31 using System.Collections;
32
33 namespace System.Windows.Forms {
34
35         internal class XEventQueue {
36
37                 private XQueue          xqueue;
38                 private XQueue          lqueue; // Events inserted from threads other then the main X thread
39                 private PaintQueue      paint;  // Paint-only queue
40                 internal ArrayList      timer_list;
41                 private Thread          thread;
42                 private bool            dispatch_idle;
43
44                 private static readonly int InitialXEventSize = 100;
45                 private static readonly int InitialLXEventSize = 10;
46                 private static readonly int InitialPaintSize = 50;
47
48                 public XEventQueue (Thread thread) {
49                         xqueue = new XQueue (InitialXEventSize);
50                         lqueue = new XQueue (InitialLXEventSize);
51                         paint = new PaintQueue(InitialPaintSize);
52                         timer_list = new ArrayList ();
53                         this.thread = thread;
54                         this.dispatch_idle = true;
55                 }
56
57                 public int Count {
58                         get {
59                                 lock (lqueue) {
60                                         return xqueue.Count + lqueue.Count;
61                                 }
62                         }
63                 }
64
65                 public PaintQueue Paint {
66                         get {
67                                 return paint;
68                         }
69                 }
70
71                 public Thread Thread {
72                         get {
73                                 return thread;
74                         }
75                 }
76
77                 public void Enqueue (XEvent xevent)
78                 {
79                         if (Thread.CurrentThread != thread) {
80                                 Console.WriteLine ("Hwnd.Queue.Enqueue called from a different thread without locking.");
81                                 Console.WriteLine (Environment.StackTrace);
82                         }
83
84                         xqueue.Enqueue (xevent);
85                 }
86
87                 public void EnqueueLocked (XEvent xevent)
88                 {
89                         lock (lqueue) {
90                                 lqueue.Enqueue (xevent);
91                         }
92                 }
93
94                 public XEvent Dequeue ()
95                 {
96                         if (Thread.CurrentThread != thread) {
97                                 Console.WriteLine ("Hwnd.Queue.Dequeue called from a different thread without locking.");
98                                 Console.WriteLine (Environment.StackTrace);
99                         }
100
101                         if (xqueue.Count == 0) {
102                                 lock (lqueue) {
103                                         return lqueue.Dequeue ();
104                                 }
105                         }
106                         return xqueue.Dequeue ();
107                 }
108
109                 public XEvent Peek()
110                 {
111                         if (Thread.CurrentThread != thread) {
112                                 Console.WriteLine ("Hwnd.Queue.Peek called from a different thread without locking.");
113                                 Console.WriteLine (Environment.StackTrace);
114                         }
115
116                         if (xqueue.Count == 0) {
117                                 lock (lqueue) {
118                                         return lqueue.Peek ();
119                                 }
120                         }                               
121                         return xqueue.Peek();
122                 }
123
124                 public bool DispatchIdle {
125                         get {
126                                 return dispatch_idle;
127                         }
128                         set {
129                                 dispatch_idle = value;
130                         }
131                 }
132
133                 public class PaintQueue {
134
135                         private ArrayList       hwnds;
136                         private XEvent          xevent;
137                         
138                         public PaintQueue (int size) {
139                                 hwnds = new ArrayList(size);
140                                 xevent = new XEvent();
141                                 xevent.AnyEvent.type = XEventName.Expose;
142                         }
143
144                         public int Count {
145                                 get { return hwnds.Count; }
146                         }
147
148                         public void Enqueue (Hwnd hwnd) {
149                                 hwnds.Add(hwnd);
150                         }
151
152                         public void Remove(Hwnd hwnd) {
153                                 if (!hwnd.expose_pending && !hwnd.nc_expose_pending) {
154                                         hwnds.Remove(hwnd);
155                                 }
156                         }
157
158                         public XEvent Dequeue () {
159                                 Hwnd            hwnd;
160                                 IEnumerator     next;
161
162                                 if (hwnds.Count == 0) {
163                                         xevent.ExposeEvent.window = IntPtr.Zero;
164                                         return xevent;
165                                 }
166
167                                 next = hwnds.GetEnumerator();
168                                 next.MoveNext();
169                                 hwnd = (Hwnd)next.Current;
170
171                                 // We only remove the event from the queue if we have one expose left since
172                                 // a single 'entry in our queue may be for both NC and Client exposed
173                                 if ( !(hwnd.nc_expose_pending && hwnd.expose_pending)) {
174                                         hwnds.Remove(hwnd);
175                                 }
176                                 if (hwnd.expose_pending) {
177                                         xevent.ExposeEvent.window = hwnd.client_window;
178 #if not
179                                         xevent.ExposeEvent.x = hwnd.invalid.X;
180                                         xevent.ExposeEvent.y = hwnd.invalid.Y;
181                                         xevent.ExposeEvent.width = hwnd.invalid.Width;
182                                         xevent.ExposeEvent.height = hwnd.invalid.Height;
183 #endif
184                                         return xevent;
185                                 } else {
186                                         xevent.ExposeEvent.window = hwnd.whole_window;
187                                         xevent.ExposeEvent.x = hwnd.nc_invalid.X;
188                                         xevent.ExposeEvent.y = hwnd.nc_invalid.Y;
189                                         xevent.ExposeEvent.width = hwnd.nc_invalid.Width;
190                                         xevent.ExposeEvent.height = hwnd.nc_invalid.Height;
191                                         return xevent;
192                                 }
193                         }
194                 }
195
196                 private class XQueue {
197
198                         private XEvent [] xevents;
199                         private int head;
200                         private int tail;
201                         private int size;
202                         
203                         public XQueue (int size)
204                         {
205                                 xevents = new XEvent [size];
206                         }
207
208                         public int Count {
209                                 get { return size; }
210                         }
211
212                         public void Enqueue (XEvent xevent)
213                         {
214                                 if (size == xevents.Length)
215                                         Grow ();
216                                 
217                                 xevents [tail] = xevent;
218                                 tail = (tail + 1) % xevents.Length;
219                                 size++;
220                         }
221
222                         public XEvent Dequeue ()
223                         {
224                                 if (size < 1)
225                                         throw new Exception ("Attempt to dequeue empty queue.");
226                                 XEvent res = xevents [head];
227                                 head = (head + 1) % xevents.Length;
228                                 size--;
229                                 return res;
230                         }
231
232                         public XEvent Peek() {
233                                 if (size < 1) {
234                                         throw new Exception ("Attempt to peek at empty queue");
235                                 }
236                                 return xevents[head];
237                         }
238
239                         private void Grow ()
240                         {
241                                 int newcap = (xevents.Length * 2);
242                                 XEvent [] na = new XEvent [newcap];
243                                 xevents.CopyTo (na, 0);
244                                 xevents = na;
245                                 head = 0;
246                                 tail = head + size;
247                         }
248                 }
249         }
250 }
251