1 module libwasm.event;
2 
3 import libwasm.types;
4 import libwasm.bindings;
5 version (LDC)
6 import ldc.attributes;
7 
8 @trusted
9 extern(C)
10 export
11 @assumeUsed
12 nothrow
13 void domEvent(uint ctx, uint fun, Handle event) {
14   // NOTE: since all the Event structs (MouseEvent, KeyboardEvent)
15   // are all just empty structs with only static functions,
16   // the type signature of the delegate doesn't really matter much,
17   // as long as there is one param
18   // This allows us to sidestep the fact that we should have had
19   // one event handler per event type
20   // Type safety is ensured with static asserts in addEventListenerTyped
21   static struct Handler
22   {
23     union
24     {
25       void delegate(Event) nothrow handle;
26       struct
27       {
28         void* contextPtr;
29         void* funcPtr;
30       }
31     }
32   }
33   Handler c;
34   c.contextPtr = cast(void*) ctx;
35   c.funcPtr = cast(void*) fun;
36   c.handle(Event(event));
37 }
38 
39 @safe:
40 nothrow:
41 
42 private extern(C) {
43   nothrow:
44   void removeEventListener(Handle node, ListenerType type, uint ctx, uint fun, EventType type);
45   void addEventListener(Handle node, ListenerType type, uint ctx, uint fun, EventType type);
46 }
47 enum eventemitter;
48 
49 template ToEvent(EventType type) {
50   import memutils.ct : capitalize;
51   static if (type == EventType.event)
52     alias ToEvent = Event;
53   else {
54     // TODO: really want to use phobos functions to extract name but prevented by https://issues.dlang.org/show_bug.cgi?id=19268
55     static foreach (t; __traits(allMembers, EventType))
56     {
57       static if (__traits(getMember, EventType, t) == type)
58         mixin("alias ToEvent = "~capitalize!(t)~"Event;");
59     }
60   }
61 }
62 
63 auto toTuple(Delegate)(Delegate d) {
64   import memutils.ct : tuple;
65   auto ctx = cast(uint)d.ptr;
66   auto func = cast(uint)d.funcptr;
67   return tuple!("ctx","func")(ctx,func);
68 }
69 
70 EventType toEventType(Node)(ListenerType listener) {
71   with (ListenerType) {
72     final switch(listener) {
73     case click:
74       return EventType.mouse;
75     case input:
76       return EventType.input;
77     case change:
78       return EventType.event;
79     case keyup:
80     case keydown:
81     case keypress:
82       return EventType.keyboard;
83     case dblclick:
84       return EventType.mouse;
85     case focus:
86       return EventType.focus;
87     case blur:
88       return EventType.event;
89     case mouseup:
90     case mousedown:
91     case mousemove:
92       return EventType.mouse;
93       }
94   }
95 }
96 
97 template toListenerType(string t) {
98   mixin("enum toListenerType = ListenerType."~t~";");
99 }
100 
101 
102 // TODO: please combine with function below
103 auto removeEventListenerTyped(string name, T)(Handle node, auto ref T t) {
104   import std.traits : fullyQualifiedName, Parameters;
105   import memutils.ct : toLower;
106   // TODO: really want to use std.uni.toLower here but prevented by https://issues.dlang.org/show_bug.cgi?id=19268
107   enum type = toLower!(name[2..$]);
108   enum listenerType = toListenerType!type;
109   auto delPtr = &__traits(getMember, t, name);
110   enum eventType = listenerType.toEventType!T;
111   alias Event = ToEvent!eventType;
112   alias delParams = Parameters!(typeof(delPtr));
113   static if (delParams.length != 1)
114     static assert(false, "Expected 1 param of type "~Event.stringof~" in "~fullyQualifiedName!T~"."~name);
115   else static if (!is(delParams[0] == Event))
116     static assert(false,
117         "Expected param 1 of type " ~ Event.stringof ~ " instead of "
118         ~ delParams[0].stringof ~ " in ..." ~ name); // TODO: next line is not working so using ... instead. Due to bug https://issues.dlang.org/show_bug.cgi?id=19268
119     // static assert(false, "Expected param 1 of type "~Event.stringof~" instead of "~delParams[0].stringof~" in "~fullyQualifiedName!T~"."~name);
120   removeEventListener(node, listenerType, toTuple(delPtr).expand, eventType);
121 }
122 
123 auto addEventListenerTyped(string name, T)(Handle node, auto ref T t) {
124   import std.traits : fullyQualifiedName, Parameters;
125   import memutils.ct : toLower;
126   // TODO: really want to use std.uni.toLower here but prevented by https://issues.dlang.org/show_bug.cgi?id=19268
127   enum type = toLower!(name[2..$]);
128   enum listenerType = toListenerType!type;
129   auto delPtr = &__traits(getMember, t, name);
130   enum eventType = listenerType.toEventType!T;
131   alias Event = ToEvent!eventType;
132   alias delParams = Parameters!(typeof(delPtr));
133   static if (delParams.length != 1)
134     static assert(false, "Expected 1 param of type "~Event.stringof~" in "~fullyQualifiedName!T~"."~name);
135   else static if (!is(delParams[0] == Event))
136     static assert(false,
137         "Expected param 1 of type " ~ Event.stringof ~ " instead of "
138         ~ delParams[0].stringof ~ " in ..." ~ name); // TODO: next line is not working so using ... instead. Due to bug https://issues.dlang.org/show_bug.cgi?id=19268
139     // static assert(false, "Expected param 1 of type "~Event.stringof~" instead of "~delParams[0].stringof~" in "~fullyQualifiedName!T~"."~name);
140   addEventListener(node, listenerType, toTuple(delPtr).expand, eventType);
141 }
142 
143 struct EventEmitter {
144   nothrow:
145   //static if (is(T == void)) {
146   void delegate() plain = null;
147   void add(void delegate() nothrow @safe del) {
148     plain = del;
149   }
150   /*} // todo: add parameters to event emitters
151   else {
152     void delegate(T) plain = null;
153     void add(void delegate(T) nothrow @safe del) {
154       plain = del;
155     }
156   }*/
157   void delegate(size_t) addr = null;
158   void add(void delegate(size_t) nothrow @safe del) {
159     addr = del;
160   }
161 }
162 
163 mixin template Slot(string type) {
164   mixin("@eventemitter EventEmitter "~type~";");
165 }
166 
167 auto emit(T)(ref T t, EventEmitter emitter) {
168   if (emitter.plain != null)
169     emitter.plain();
170   if (emitter.addr != null) {
171     size_t addr = cast(size_t)(&t);
172     emitter.addr(addr);
173   }
174 }
175 
176 auto emit(EventEmitter emitter, size_t addr) {
177   if (emitter.plain != null)
178     emitter.plain();
179   if (emitter.addr != null) {
180     emitter.addr(addr);
181   }
182 }
183