X11ThreadQueue.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  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 ([email protected])
  24. // Peter Dennis Bartok ([email protected])
  25. // Chris Toshok ([email protected])
  26. //
  27. using System;
  28. using System.Threading;
  29. using System.Collections;
  30. namespace System.Windows.Forms.X11Internal {
  31. internal class X11ThreadQueue {
  32. XEventQueue xqueue;
  33. PaintQueue paint_queue;
  34. ConfigureQueue configure_queue;
  35. ArrayList timer_list;
  36. Thread thread;
  37. bool quit_posted;
  38. bool dispatch_idle;
  39. bool need_dispatch_idle = true;
  40. object lockobj = new object ();
  41. static readonly int InitialXEventQueueSize = 128;
  42. static readonly int InitialHwndQueueSize = 50;
  43. public X11ThreadQueue (Thread thread)
  44. {
  45. xqueue = new XEventQueue (InitialXEventQueueSize);
  46. paint_queue = new PaintQueue (InitialHwndQueueSize);
  47. configure_queue = new ConfigureQueue (InitialHwndQueueSize);
  48. timer_list = new ArrayList ();
  49. this.thread = thread;
  50. this.quit_posted = false;
  51. this.dispatch_idle = true;
  52. }
  53. public int CountUnlocked {
  54. get { return xqueue.Count + paint_queue.Count; }
  55. }
  56. public Thread Thread {
  57. get { return thread; }
  58. }
  59. public void EnqueueUnlocked (XEvent xevent)
  60. {
  61. switch (xevent.type) {
  62. case XEventName.KeyPress:
  63. case XEventName.KeyRelease:
  64. case XEventName.ButtonPress:
  65. case XEventName.ButtonRelease:
  66. NeedDispatchIdle = true;
  67. break;
  68. case XEventName.MotionNotify:
  69. if (xqueue.Count > 0) {
  70. XEvent peek = xqueue.Peek ();
  71. if (peek.AnyEvent.type == XEventName.MotionNotify)
  72. return; // we've already got a pending motion notify.
  73. }
  74. // otherwise fall through and enqueue
  75. // the event.
  76. break;
  77. }
  78. xqueue.Enqueue (xevent);
  79. // wake up any thread blocking in DequeueUnlocked
  80. Monitor.PulseAll (lockobj);
  81. }
  82. public void Enqueue (XEvent xevent)
  83. {
  84. lock (lockobj) {
  85. EnqueueUnlocked (xevent);
  86. }
  87. }
  88. public bool Dequeue (out XEvent xevent)
  89. {
  90. StartOver:
  91. bool got_xevent = false;
  92. lock (lockobj) {
  93. if (xqueue.Count > 0) {
  94. got_xevent = true;
  95. xevent = xqueue.Dequeue ();
  96. }
  97. else
  98. xevent = new XEvent (); /* not strictly needed, but mcs complains */
  99. }
  100. if (got_xevent) {
  101. if (xevent.AnyEvent.type == XEventName.Expose) {
  102. #if spew
  103. Console.Write ("E");
  104. Console.Out.Flush ();
  105. #endif
  106. X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
  107. hwnd.AddExpose (xevent.AnyEvent.window == hwnd.ClientWindow,
  108. xevent.ExposeEvent.x, xevent.ExposeEvent.y,
  109. xevent.ExposeEvent.width, xevent.ExposeEvent.height);
  110. goto StartOver;
  111. }
  112. else if (xevent.AnyEvent.type == XEventName.ConfigureNotify) {
  113. #if spew
  114. Console.Write ("C");
  115. Console.Out.Flush ();
  116. #endif
  117. X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window);
  118. hwnd.AddConfigureNotify (xevent);
  119. goto StartOver;
  120. }
  121. else {
  122. #if spew
  123. Console.Write ("X");
  124. Console.Out.Flush ();
  125. #endif
  126. /* it was an event we can deal with directly, return it */
  127. return true;
  128. }
  129. }
  130. else {
  131. if (paint_queue.Count > 0) {
  132. xevent = paint_queue.Dequeue ();
  133. #if spew
  134. Console.Write ("e");
  135. Console.Out.Flush ();
  136. #endif
  137. return true;
  138. }
  139. else if (configure_queue.Count > 0) {
  140. xevent = configure_queue.Dequeue ();
  141. #if spew
  142. Console.Write ("c");
  143. Console.Out.Flush ();
  144. #endif
  145. return true;
  146. }
  147. }
  148. if (dispatch_idle && need_dispatch_idle) {
  149. OnIdle (EventArgs.Empty);
  150. need_dispatch_idle = false;
  151. }
  152. lock (lockobj) {
  153. if (CountUnlocked > 0)
  154. goto StartOver;
  155. if (Monitor.Wait (lockobj, NextTimeout (), true)) {
  156. // the lock was reaquired before the
  157. // timeout. meaning an event was
  158. // enqueued by X11Display.XEventThread.
  159. goto StartOver;
  160. }
  161. else {
  162. CheckTimers ();
  163. return false;
  164. }
  165. }
  166. }
  167. public void RemovePaint (Hwnd hwnd)
  168. {
  169. paint_queue.Remove (hwnd);
  170. }
  171. public void AddPaint (Hwnd hwnd)
  172. {
  173. paint_queue.Enqueue (hwnd);
  174. }
  175. public void AddConfigure (Hwnd hwnd)
  176. {
  177. configure_queue.Enqueue (hwnd);
  178. }
  179. public ConfigureQueue Configure {
  180. get { return configure_queue; }
  181. }
  182. public PaintQueue Paint {
  183. get { return paint_queue; }
  184. }
  185. public void Lock ()
  186. {
  187. Monitor.Enter (lockobj);
  188. }
  189. public void Unlock ()
  190. {
  191. Monitor.Exit (lockobj);
  192. }
  193. private int NextTimeout ()
  194. {
  195. int timeout = Int32.MaxValue;
  196. DateTime now = DateTime.UtcNow;
  197. foreach (Timer timer in timer_list) {
  198. int next = (int) (timer.Expires - now).TotalMilliseconds;
  199. if (next < 0)
  200. return 0; // Have a timer that has already expired
  201. if (next < timeout)
  202. timeout = next;
  203. }
  204. if (timeout < Timer.Minimum) {
  205. timeout = Timer.Minimum;
  206. }
  207. if (timeout == Int32.MaxValue)
  208. timeout = Timeout.Infinite;
  209. return timeout;
  210. }
  211. public void CheckTimers ()
  212. {
  213. int count;
  214. DateTime now = DateTime.UtcNow;
  215. count = timer_list.Count;
  216. if (count == 0)
  217. return;
  218. for (int i = 0; i < timer_list.Count; i++) {
  219. Timer timer;
  220. timer = (Timer) timer_list [i];
  221. if (timer.Enabled && timer.Expires <= now) {
  222. timer.Update (now);
  223. timer.FireTick ();
  224. }
  225. }
  226. }
  227. public void SetTimer (Timer timer)
  228. {
  229. lock (lockobj) {
  230. timer_list.Add (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);
  234. }
  235. }
  236. public void KillTimer (Timer timer)
  237. {
  238. lock (lockobj) {
  239. timer_list.Remove (timer);
  240. // we need to wake up any thread waiting in DequeueUnlocked,
  241. // since it might need to wait for a different amount of time.
  242. Monitor.PulseAll (lockobj);
  243. }
  244. }
  245. public event EventHandler Idle;
  246. public void OnIdle (EventArgs e)
  247. {
  248. if (Idle != null)
  249. Idle (thread, e);
  250. }
  251. public bool NeedDispatchIdle {
  252. get { return need_dispatch_idle; }
  253. set { need_dispatch_idle = value; }
  254. }
  255. public bool DispatchIdle {
  256. get { return dispatch_idle; }
  257. set { dispatch_idle = value; }
  258. }
  259. public bool PostQuitState {
  260. get { return quit_posted; }
  261. set { quit_posted = value; }
  262. }
  263. public abstract class HwndEventQueue {
  264. protected ArrayList hwnds;
  265. #if DebugHwndEventQueue
  266. protected ArrayList stacks;
  267. #endif
  268. public HwndEventQueue (int size)
  269. {
  270. hwnds = new ArrayList (size);
  271. #if DebugHwndEventQueue
  272. stacks = new ArrayList (size);
  273. #endif
  274. }
  275. public int Count {
  276. get { return hwnds.Count; }
  277. }
  278. public void Enqueue (Hwnd hwnd)
  279. {
  280. if (hwnds.Contains (hwnd)) {
  281. #if DebugHwndEventQueue
  282. Console.WriteLine ("hwnds can only appear in the queue once.");
  283. Console.WriteLine (Environment.StackTrace);
  284. Console.WriteLine ("originally added here:");
  285. Console.WriteLine (stacks[hwnds.IndexOf (hwnd)]);
  286. #endif
  287. return;
  288. }
  289. hwnds.Add(hwnd);
  290. #if DebugHwndEventQueue
  291. stacks.Add(Environment.StackTrace);
  292. #endif
  293. }
  294. public void Remove(Hwnd hwnd)
  295. {
  296. #if DebugHwndEventQueue
  297. int index = hwnds.IndexOf(hwnd);
  298. if (index != -1)
  299. stacks.RemoveAt(index);
  300. #endif
  301. hwnds.Remove(hwnd);
  302. }
  303. protected abstract XEvent Peek ();
  304. public virtual XEvent Dequeue ()
  305. {
  306. if (hwnds.Count == 0)
  307. throw new Exception ("Attempt to dequeue empty queue.");
  308. return Peek ();
  309. }
  310. }
  311. public class ConfigureQueue : HwndEventQueue
  312. {
  313. public ConfigureQueue (int size) : base (size)
  314. {
  315. }
  316. protected override XEvent Peek ()
  317. {
  318. X11Hwnd hwnd = (X11Hwnd)hwnds[0];
  319. XEvent xevent = new XEvent ();
  320. xevent.AnyEvent.type = XEventName.ConfigureNotify;
  321. xevent.ConfigureEvent.window = hwnd.ClientWindow;
  322. xevent.ConfigureEvent.x = hwnd.X;
  323. xevent.ConfigureEvent.y = hwnd.Y;
  324. xevent.ConfigureEvent.width = hwnd.Width;
  325. xevent.ConfigureEvent.height = hwnd.Height;
  326. return xevent;
  327. }
  328. public override XEvent Dequeue ()
  329. {
  330. XEvent xev = base.Dequeue ();
  331. hwnds.RemoveAt(0);
  332. #if DebugHwndEventQueue
  333. stacks.RemoveAt(0);
  334. #endif
  335. return xev;
  336. }
  337. }
  338. public class PaintQueue : HwndEventQueue
  339. {
  340. public PaintQueue (int size) : base (size)
  341. {
  342. }
  343. protected override XEvent Peek ()
  344. {
  345. X11Hwnd hwnd = (X11Hwnd)hwnds[0];
  346. XEvent xevent = new XEvent ();
  347. xevent.AnyEvent.type = XEventName.Expose;
  348. if (hwnd.PendingExpose) {
  349. xevent.ExposeEvent.window = hwnd.ClientWindow;
  350. } else {
  351. xevent.ExposeEvent.window = hwnd.WholeWindow;
  352. xevent.ExposeEvent.x = hwnd.nc_invalid.X;
  353. xevent.ExposeEvent.y = hwnd.nc_invalid.Y;
  354. xevent.ExposeEvent.width = hwnd.nc_invalid.Width;
  355. xevent.ExposeEvent.height = hwnd.nc_invalid.Height;
  356. }
  357. return xevent;
  358. }
  359. // don't override Dequeue like ConfigureQueue does.
  360. }
  361. /* a circular queue for holding X events for processing by GetMessage */
  362. private class XEventQueue {
  363. XEvent[] xevents;
  364. int head;
  365. int tail;
  366. int size;
  367. public XEventQueue (int initial_size)
  368. {
  369. if (initial_size % 2 != 0)
  370. throw new Exception ("XEventQueue must be a power of 2 size");
  371. xevents = new XEvent [initial_size];
  372. }
  373. public int Count {
  374. get { return size; }
  375. }
  376. public void Enqueue (XEvent xevent)
  377. {
  378. if (size == xevents.Length)
  379. Grow ();
  380. xevents [tail] = xevent;
  381. tail = (tail + 1) & (xevents.Length - 1);
  382. size++;
  383. }
  384. public XEvent Dequeue ()
  385. {
  386. if (size < 1)
  387. throw new Exception ("Attempt to dequeue empty queue.");
  388. XEvent res = xevents [head];
  389. head = (head + 1) & (xevents.Length - 1);
  390. size--;
  391. return res;
  392. }
  393. public XEvent Peek()
  394. {
  395. if (size < 1)
  396. throw new Exception ("Attempt to peek at empty queue.");
  397. return xevents[head];
  398. }
  399. private void Grow ()
  400. {
  401. int newcap = (xevents.Length * 2);
  402. XEvent [] na = new XEvent [newcap];
  403. if (head + size > xevents.Length) {
  404. Array.Copy (xevents, head, na, 0, xevents.Length - head);
  405. Array.Copy (xevents, 0, na, xevents.Length - head, head + size - xevents.Length);
  406. }
  407. else {
  408. Array.Copy (xevents, head, na, 0, size);
  409. }
  410. xevents = na;
  411. head = 0;
  412. tail = head + size;
  413. }
  414. }
  415. }
  416. }