1 module cat.wheel.events; 2 3 import std.string; 4 import std.array; 5 import core.sync.mutex; 6 import bindbc.sdl; 7 8 import cat.wheel.except; 9 10 /** 11 * Initialize the SDL library. Call this once, and before anything else. 12 * systems: The subsystems to initialize 13 */ 14 void initSDL(uint systems) { 15 SDL_Init(systems).check; 16 } 17 18 /** 19 * Initialize one or more subsystems of SDL. 20 * systems: The subsystems to initialize 21 */ 22 void initSystem(uint systems) { 23 SDL_InitSubSystem(systems).check; 24 } 25 26 /** 27 * Shuts down the SDL library and all initialized subsystems 28 */ 29 void quitSDL() { 30 SDL_Quit(); 31 } 32 33 /** 34 * Shuts down specific subsystems of SDL 35 * systems: The subsystems to quit 36 */ 37 void quitSystem(uint systems) { 38 SDL_QuitSubSystem(systems); 39 } 40 41 /** 42 * Arguments to event handler delegates 43 */ 44 class EventArgs {} 45 46 /** 47 * The argument to the default PUMP event 48 */ 49 class PumpEventArgs : EventArgs { 50 package this(SDL_Event e) { event = e; } 51 52 /** 53 * The event 54 */ 55 public SDL_Event event; 56 } 57 58 /** 59 * Represents an SDL event handler, which controls the main thread and calls delegate functions specified by the program 60 */ 61 class Handler { 62 /** 63 * Adds a delegate function to be called by this handler when a specific event occurs 64 */ 65 void addDelegate(void delegate(EventArgs) del, int event) nothrow @safe { 66 _delegates[event] ~= del; 67 } 68 69 /** 70 * Forces the main loop to stop 71 */ 72 void stop() nothrow pure @safe { 73 _doContinue = false; 74 } 75 76 /** 77 * Starts the main loop for this Handler. The loop will run until the stop function has been called 78 */ 79 void handle() { 80 runDelegates(ED_START); 81 82 while (_doContinue) { 83 runDelegates(ED_PRE_PUMP); 84 85 _eventsSDL = _eventsSDL.init; 86 auto appender = appender(_eventsSDL); 87 88 SDL_Event e; 89 while (SDL_PollEvent(&e)) { 90 appender.put(e); 91 92 runDelegates(ED_PUMP, new PumpEventArgs(e)); 93 } 94 95 runDelegates(ED_POST_PUMP); 96 97 const int currentTime = SDL_GetTicks(); 98 _deltaTime = _lastTick - currentTime; 99 _lastTick = currentTime; 100 101 runDelegates(ED_PRE_TICK); 102 runDelegates(ED_TICK); 103 runDelegates(ED_POST_TICK); 104 } 105 106 runDelegates(ED_STOP); 107 } 108 109 /** 110 * Dispatches an event to be run by user-defined delegate functions 111 * event: Event UID 112 */ 113 void callEvent(uint event, EventArgs arg = new EventArgs()) { 114 runDelegates(event, arg); 115 } 116 117 /** 118 * The amount of time it took between the previous frame and this frame 119 * This function should only be called on the main thread. 120 */ 121 @property time() nothrow @safe { 122 return _deltaTime; 123 } 124 125 /** 126 * The SDL events that happened this frame. 127 * This function should only be called on the main thread. 128 */ 129 @property events() nothrow @safe { 130 return _eventsSDL; 131 } 132 133 private: 134 void delegate(EventArgs)[][int] _delegates; 135 136 void runDelegates(uint type, EventArgs args = new EventArgs()) { 137 if (type in _delegates) { 138 foreach (d; _delegates[type]) { 139 d(args); 140 } 141 } 142 } 143 144 shared(bool) _doContinue = true; 145 shared(SDL_Event[]) _eventsSDL; 146 147 int _deltaTime; 148 int _lastTick; 149 } 150 151 enum : uint { 152 /// Run once at the start of handling 153 ED_START = 0b000, 154 ED_PRE_PUMP = 0b001, 155 ED_PUMP = 0b010, 156 ED_POST_PUMP = 0b011, 157 ED_PRE_TICK = 0b100, 158 ED_TICK = 0b101, 159 ED_POST_TICK = 0b110, 160 ED_STOP = 0b111 161 }