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