1 /**
2  * Event queue module for dcell.
3  *
4  * Copyright: Copyright 2022 Garrett D'Amore
5  * Authors: Garrett D'Amore
6  * License:
7  *   Distributed under the Boost Software License, Version 1.0.
8  *   (See accompanying file LICENSE or https://www.boost.org/LICENSE_1_0.txt)
9  *   SPDX-License-Identifier: BSL-1.0
10  */
11 module dcell.evqueue;
12 
13 import core.sync.condition;
14 import core.sync.mutex;
15 import core.time;
16 import std.concurrency;
17 
18 import dcell.event;
19 
20 package class EventQueue
21 {
22     this()
23     {
24         mx = new Mutex();
25         cv = new Condition(mx);
26     }
27 
28     Event receive(this Q)(Duration dur)
29             if (is(Q == EventQueue) || is(Q == shared EventQueue))
30     {
31         Event ev;
32         synchronized (mx)
33         {
34             while ((q.length == 0) && !closed)
35             {
36                 if (!cv.wait(dur))
37                 {
38                     return (ev);
39                 }
40             }
41 
42             if (closed)
43             {
44                 return Event(EventType.closed);
45             }
46             ev = q[0];
47             q = q[1 .. $];
48         }
49 
50         return ev;
51     }
52 
53     Event receive(this Q)() if (is(Q == EventQueue) || is(Q == shared EventQueue))
54     {
55         Event ev;
56         synchronized (mx)
57         {
58             while ((q.length == 0) && !closed)
59             {
60                 cv.wait();
61             }
62             if (closed)
63             {
64                 return Event(EventType.closed);
65             }
66 
67             ev = q[0];
68             q = q[1 .. $];
69         }
70 
71         return ev;
72     }
73 
74     void close(this Q)() if (is(Q == EventQueue) || is(Q == shared EventQueue))
75     {
76         synchronized (mx)
77         {
78             closed = true;
79             cv.notifyAll();
80         }
81     }
82 
83     void send(this Q)(Event ev) if (is(Q == EventQueue) || is(Q == shared EventQueue))
84     {
85         synchronized (mx)
86         {
87             if (!closed) // cannot send any more events after close
88             {
89                 q ~= ev;
90                 cv.notify();
91             }
92         }
93     }
94 
95 private:
96 
97     Mutex mx;
98     Condition cv;
99     Event[] q;
100     bool closed;
101 }
102 
103 unittest
104 {
105     import core.thread;
106     auto eq = new EventQueue();
107 
108     eq.send(Event(EventType.key));
109     assert(eq.receive().type  == EventType.key);
110 
111     assert(eq.receive(msecs(10)).type == EventType.none);
112 
113     spawn(function(shared EventQueue eq){
114         Thread.sleep(msecs(50));
115         eq.send(Event(EventType.mouse));
116     }, cast(shared EventQueue)eq);
117     assert(eq.receive().type == EventType.mouse);
118     eq.close();
119     assert(eq.receive().type == EventType.closed);
120 }