1 /*
2  * ============================================================================
3  *  Title:    Event Handling Subsystem
4  *  Author:   J. Zbiciak
5  * ============================================================================
6  *  The Event subsystem receives input from the keyboard, joystick, and
7  *  (experimentally) mouse.
8  *
9  *  This is the common event core.  Frameworks such as SDL, SDL2, etc. should
10  *  adapt their notion of events to this.
11  *
12  *  EVENT notifies other subsystems of events by setting flags.  When
13  *  an event comes in, the event is looked up in a table and bits in a
14  *  uint32_t are set/cleared according to masks assigned to the event.
15  *  The masks are specified as four uint32_t's:  "AND" and "OR" masks
16  *  for a "down" event, and "AND" and "OR" masks for an "up" event.
17  *
18  *  Event->mask mappings are registered with EVENT via calls to "event_map".
19  *  Each event can only be mapped to ONE set of masks, and so the order
20  *  in which events are mapped determines the final set of mappings.  (This
21  *  allows a specialized config file to override a standard config.)  Mapping
22  *  an event to a NULL pointer or to an empty set/clear mask disables the
23  *  event.
24  * ============================================================================
25  *  The following event classes are handled by the EVENT subsystem:
26  *
27  *   -- Quit events       (eg. somebody hitting the little 'X' in the corner)
28  *   -- Keyboard events   (key down / key up)
29  *   -- Joystick events   (not yet implemented)
30  *   -- Mouse events      (not yet implemented, may never be implemented)
31  *   -- Activation events (hide / unhide window)
32  *
33  *  Event symbol names are assigned in 'event.h', and are stored as strings.
34  *  This should simplify dynamic configuration from CFG files.  For
35  *  simplicity's sake, I will convert joystick and quit events to
36  *  keyboard-like events.
37  * ============================================================================
38  *  EVENT_INIT       -- Initializes the Event Subsystem
39  *  EVENT_TICK       -- Processes currently pending events in the event queue
40  *  EVENT_MAP        -- Map an event to a set/clear mask and a word pointer.
41  * ============================================================================
42  */
43 
44 #include "config.h"
45 #include "periph/periph.h"
46 #include "cp1600/cp1600.h"
47 #include "cp1600/emu_link.h"
48 #include "event/event.h"
49 #include "event/event_tbl.h"
50 #include "event/event_plat.h"
51 
52 #define KBD_STACK_EMPTY (-1)
53 
54 /* ======================================================================== */
55 /*  EVENT_MASK_T     -- Structure containing AND/OR masks for an event.     */
56 /* ======================================================================== */
57 typedef struct event_mask_t
58 {
59     uint32_t    *word;           /* Word to mask, or NULL if none.   */
60     uint32_t    and_mask[2];    /* AND masks (up/down)              */
61     uint32_t    or_mask [2];    /* OR masks (up/down)               */
62 } event_mask_t;
63 
64 #define EV_Q_LEN (256)
65 #define COMBO_BMAP_SIZE ((EVENT_COMBO0 + 31) >> 5)
66 
67 /* ======================================================================== */
68 /*  COMBO_STATE_T    -- State associated with COMBOs.                       */
69 /* ======================================================================== */
70 typedef struct
71 {
72     uint64_t half_active;           /* combos that might be forming         */
73     uint64_t full_active;           /* combos actively asserted             */
74     uint64_t allocated;             /* combos that are currently defined    */
75 
76     uint64_t event_bits[64];        /* event bits associated w/ combos      */
77 
78     int         event_cnt;          /* total # of unique events in combos   */
79     event_num_t event_num[64];      /* event #s assoc'd w/ any combos       */
80     event_num_t pairs[64][2];       /* event #s assoc'd with each combo     */
81 
82     uint32_t in_combo[COMBO_BMAP_SIZE]; /* Bmap: event is in a combo.       */
83     uint32_t key_down[COMBO_BMAP_SIZE]; /* Bmap: key is pressed.            */
84 } combo_state_t;
85 
86 /* ======================================================================== */
87 /*  EVT_PVT_T        -- Private event machine state.                        */
88 /* ======================================================================== */
89 struct evt_pvt_t
90 {
91     void    *plat_pvt;              /* Private for platform-specific code.  */
92 
93     event_mask_t
94         mask_tbl[4][EVENT_COUNT];   /* Event mask tables.                   */
95 
96     int           current_map;      /* Currently active event mapping.      */
97     int           stacked_map;      /* "Pushed" event mapping, or none.     */
98 
99     /* Combo-related processing. */
100     double        soon;             /* When to next process events.         */
101     double        coalesce_time;    /* Time-window to recognize combos.     */
102     combo_state_t combo;            /* State associated with COMBOs.        */
103 
104     /* Event history queue, used for event processing, combos, and emu-link */
105     event_updn_t  ev_updn_q[EV_Q_LEN];
106     event_num_t   ev_num_q [EV_Q_LEN];
107     uint32_t      ev_q_wr, ev_q_el_rd, ev_q_ev_rd;
108 };
109 
110 /* ======================================================================== */
111 /*  Bitmap helper functions for combo event bitmap.                         */
112 /*  GET_EN_BIT   -- Gets the current value of the bit for event_num.        */
113 /*  SET_EN_BIT   -- Sets the bit to 1 for event_num.                        */
114 /*  CLR_EN_BIT   -- Clears the bit to 0 for event_num.                      */
115 /* ======================================================================== */
get_en_bit(const uint32_t * const bitmap,event_num_t event_num)116 LOCAL bool get_en_bit(const uint32_t *const bitmap, event_num_t event_num)
117 {
118     return (bitmap[event_num >> 5] >> (event_num & 31)) & 1;
119 }
120 
set_en_bit(uint32_t * const bitmap,event_num_t event_num)121 LOCAL void set_en_bit(uint32_t *const bitmap, event_num_t event_num)
122 {
123     bitmap[event_num >> 5] |= 1ul << (event_num & 31);
124 }
125 
clr_en_bit(uint32_t * const bitmap,event_num_t event_num)126 LOCAL void clr_en_bit(uint32_t *const bitmap, event_num_t event_num)
127 {
128     bitmap[event_num >> 5] &= ~(1ul << (event_num & 31));
129 }
130 
131 
132 LOCAL int event_emu_link(cp1600_t *, int *, void *);
133 LOCAL uint32_t event_tick(periph_t *const p, uint32_t len);
134 LOCAL void event_dtor(periph_t *const p);
135 
136 /* ======================================================================== */
137 /*  EVENT_INIT       -- Initializes the Event subsystem.                    */
138 /* ======================================================================== */
event_init(event_t * const event,const bool enable_mouse,const int initial_event_map)139 int event_init
140 (
141     event_t *const event,           // The event structure.
142     const bool enable_mouse,        // Are we enabling the mouse?
143     const int initial_event_map     // Initial event map at startup.
144 )
145 {
146     /* -------------------------------------------------------------------- */
147     /*  The event 'peripheral' is ticked every so often in order to         */
148     /*  drain input events from the event queue and post inputs to the      */
149     /*  emulator.                                                           */
150     /* -------------------------------------------------------------------- */
151     static const periph_t event_periph = {
152         PERIPH_NO_RDWR,
153         .min_tick = PERIPH_HZ(1000), .max_tick = PERIPH_HZ(200),
154         .tick = event_tick, .dtor = event_dtor
155     };
156     event->periph = event_periph;
157 
158     /* -------------------------------------------------------------------- */
159     /*  Set up our "private" structure.                                     */
160     /* -------------------------------------------------------------------- */
161     evt_pvt_t *pvt = CALLOC(evt_pvt_t, 1);
162     event->pvt = pvt;
163 
164     if (!pvt)
165     {
166         fprintf(stderr, "event_init: Unable to allocate private state.\n");
167         return -1;
168     }
169 
170     /* -------------------------------------------------------------------- */
171     /*  Initialize our active event mapping and event map stack.            */
172     /* -------------------------------------------------------------------- */
173     pvt->current_map  = initial_event_map;
174     pvt->stacked_map  = KBD_STACK_EMPTY;
175 
176     /* -------------------------------------------------------------------- */
177     /*  Set up our event-coalescing timer.                                  */
178     /* -------------------------------------------------------------------- */
179     pvt->soon          = 0.;
180     pvt->coalesce_time = 0.0001;   /* 1ms */
181 
182     /* -------------------------------------------------------------------- */
183     /*  Perform platform-specific initialization.                           */
184     /* -------------------------------------------------------------------- */
185     if (event_plat_init(enable_mouse, pvt, &pvt->plat_pvt))
186     {
187         fprintf(stderr,
188             "event_init: Could not initalize platform specific event code.\n");
189         free(pvt);
190         return -1;
191     }
192 
193     /* -------------------------------------------------------------------- */
194     /*  Register us on Emu-Link as major API #9.                            */
195     /* -------------------------------------------------------------------- */
196     emu_link_register(event_emu_link, 9, pvt);
197 
198     /* -------------------------------------------------------------------- */
199     /*  Done!                                                               */
200     /* -------------------------------------------------------------------- */
201     return 0;
202 }
203 
204 /* ======================================================================== */
205 /*  EVENT_DTOR   -- Tear down the event engine.                             */
206 /* ======================================================================== */
event_dtor(periph_t * const p)207 LOCAL void event_dtor(periph_t *const p)
208 {
209     event_t *const event = PERIPH_AS(event_t, p);
210     evt_pvt_t *const pvt = event->pvt;
211 
212     event_plat_dtor(pvt->plat_pvt);
213 
214     CONDFREE(event->pvt);
215 }
216 
217 /* ======================================================================== */
218 /*  EVENT_QUEUE_HAS_ROOM -- Returns true if there's room for N events.      */
219 /* ======================================================================== */
event_queue_has_room(evt_pvt_t * const pvt,const int count)220 int event_queue_has_room(evt_pvt_t *const pvt, const int count)
221 {
222     return pvt->ev_q_wr - pvt->ev_q_ev_rd + count < EV_Q_LEN;
223 }
224 
225 /* ======================================================================== */
226 /*  EVENT_ENQUEUE    -- Internal event queue containing expanded events.    */
227 /* ======================================================================== */
event_enqueue(evt_pvt_t * const pvt,const event_updn_t event_updn,const event_num_t event_num)228 void event_enqueue
229 (
230     evt_pvt_t *const   pvt,
231     const event_updn_t event_updn,
232     const event_num_t  event_num
233 )
234 {
235     /* -------------------------------------------------------------------- */
236     /*  Ignore events that are out of range or marked "ignore"              */
237     /* -------------------------------------------------------------------- */
238     if (!EVENT_VALID(event_num))
239         return;
240 
241     /* -------------------------------------------------------------------- */
242     /*  Drop oldest EMULINK event on overflow                               */
243     /* -------------------------------------------------------------------- */
244     if (pvt->ev_q_wr - pvt->ev_q_el_rd == EV_Q_LEN)
245         pvt->ev_q_el_rd++;
246 
247     /* -------------------------------------------------------------------- */
248     /*  We should never drop events internally.                             */
249     /* -------------------------------------------------------------------- */
250     assert(pvt->ev_q_wr - pvt->ev_q_ev_rd < EV_Q_LEN);
251 
252     /* -------------------------------------------------------------------- */
253     /*  Remember the current event.                                         */
254     /* -------------------------------------------------------------------- */
255     pvt->ev_num_q [pvt->ev_q_wr % EV_Q_LEN] = event_num;
256     pvt->ev_updn_q[pvt->ev_q_wr % EV_Q_LEN] = event_updn;
257 
258     pvt->ev_q_wr++;
259 }
260 
261 /* ======================================================================== */
262 /*  EVENT_DEQUEUE   Dequeue events queued by event_queue.  Does not         */
263 /*                  disturb the EMULINK queue pointer.                      */
264 /* ======================================================================== */
event_dequeue(evt_pvt_t * const pvt,event_updn_t * const event_updn,event_num_t * const event_num)265 LOCAL int event_dequeue
266 (
267     evt_pvt_t    *const pvt,
268     event_updn_t *const event_updn,
269     event_num_t  *const event_num
270 )
271 {
272     if (pvt->ev_q_ev_rd == pvt->ev_q_wr)
273         return 0;
274 
275     while (pvt->ev_q_el_rd >= EV_Q_LEN && pvt->ev_q_ev_rd >= EV_Q_LEN)
276     {
277         pvt->ev_q_el_rd -= EV_Q_LEN;
278         pvt->ev_q_ev_rd -= EV_Q_LEN;
279         pvt->ev_q_wr    -= EV_Q_LEN;
280     }
281 
282     const int q_rd = pvt->ev_q_ev_rd++ % EV_Q_LEN;
283 
284     *event_updn = pvt->ev_updn_q[q_rd];
285     *event_num  = pvt->ev_num_q [q_rd];
286 
287     return 1;
288 }
289 
290 /* ======================================================================== */
291 /*  EVENT_TICK   -- Processes currently pending events in the event queue   */
292 /* ======================================================================== */
event_tick(periph_t * const p,uint32_t len)293 LOCAL uint32_t event_tick(periph_t *const p, uint32_t len)
294 {
295     event_t   *const event = PERIPH_AS(event_t, p);
296     evt_pvt_t *const pvt   = event->pvt;
297 
298     /* -------------------------------------------------------------------- */
299     /*  First, pump the event loop and gather some events.                  */
300     /* -------------------------------------------------------------------- */
301     event_plat_pump(pvt, pvt->plat_pvt);
302 
303     /* -------------------------------------------------------------------- */
304     /*  If we've corked event queue processing, leave early if we're still  */
305     /*  before the deadline.                                                */
306     /* -------------------------------------------------------------------- */
307     const double now = get_time();
308     if (now < pvt->soon)
309         return len;
310 
311     pvt->soon = 0.;
312 
313     /* -------------------------------------------------------------------- */
314     /*  Let the platform fill our internal event queue.                     */
315     /* -------------------------------------------------------------------- */
316     if (event_plat_tick(pvt, pvt->plat_pvt))
317         return len;     /* Early exit; likely corking input. */
318 
319     /* -------------------------------------------------------------------- */
320     /*  Drain the internal event queue and trigger all the event.s          */
321     /* -------------------------------------------------------------------- */
322     event_num_t  event_num;
323     event_updn_t event_updn;
324     while (event_dequeue(pvt, &event_updn, &event_num))
325     {
326         /* ---------------------------------------------------------------- */
327         /*  Process the event.  If pvt->mask_tbl[event_num].word == NULL    */
328         /*  then we aren't interested in this event.                        */
329         /* ---------------------------------------------------------------- */
330         const event_mask_t *const event_mask =
331             &pvt->mask_tbl[pvt->current_map][event_num];
332 
333         if (event_mask->word == NULL)
334             continue;
335 
336         /* ---------------------------------------------------------------- */
337         /*  Apply the appropriate AND and OR masks to the word.             */
338         /* ---------------------------------------------------------------- */
339         *event_mask->word &= event_mask->and_mask[event_updn];
340         *event_mask->word |= event_mask->or_mask [event_updn];
341     }
342 
343     /* -------------------------------------------------------------------- */
344     /*  Allow the platform to perform any late event processing tasks.      */
345     /* -------------------------------------------------------------------- */
346     event_plat_tick_late(pvt, pvt->plat_pvt);
347 
348     /* -------------------------------------------------------------------- */
349     /*  Done!  We always "consume" our entire "tick."                       */
350     /* -------------------------------------------------------------------- */
351     return len;
352 }
353 
354 /* ======================================================================== */
355 /*  EVENT_EMU_LINK -- Allow games to get raw event feed from event queue.   */
356 /* ======================================================================== */
event_emu_link(cp1600_t * cpu,int * fail,void * opaque)357 LOCAL int event_emu_link(cp1600_t *cpu, int *fail, void *opaque)
358 {
359     evt_pvt_t *const pvt = (evt_pvt_t *)opaque;
360 
361     /* -------------------------------------------------------------------- */
362     /*  The event Emu-Link API is very simple:                              */
363     /*                                                                      */
364     /*  INPUTS:                                                             */
365     /*      R2 == 0x0000:  Just return event number and up/down             */
366     /*      R2 != 0x0000:  Try to write ASCII name of event @R1.            */
367     /*                     ASCII names are bounded to 18 chars + NUL.       */
368     /*                                                                      */
369     /*  OUTPUTS:                                                            */
370     /*      R0:   0xFFFF = No events, otherwise event #                     */
371     /*      R1:   0 = UP, 1 = DOWN                                          */
372     /*      R2:   Unmodified.                                               */
373     /* -------------------------------------------------------------------- */
374 
375     *fail = 0;
376 
377     if (pvt->ev_q_wr == pvt->ev_q_el_rd)
378     {
379         cpu->r[1] = 0;
380         return 0xFFFF;
381     }
382 
383     while (pvt->ev_q_el_rd >= EV_Q_LEN && pvt->ev_q_ev_rd >= EV_Q_LEN)
384     {
385         pvt->ev_q_el_rd -= EV_Q_LEN;
386         pvt->ev_q_ev_rd -= EV_Q_LEN;
387         pvt->ev_q_wr    -= EV_Q_LEN;
388     }
389 
390     const int q_rd = pvt->ev_q_el_rd++ % EV_Q_LEN;
391 
392     if (cpu->r[2] != 0)
393     {
394         const char *s = event_num_to_name(pvt->ev_num_q[q_rd]);
395         int i;
396         int addr = cpu->r[2];
397 
398         /* This should never fire; but I'd rather print this than crash. */
399         if (!s)
400             s = "INTERNAL ERROR";
401 
402         for (i = 0; i < 18 && *s; i++, s++)
403             CP1600_WR(cpu, addr++, *s);
404 
405         CP1600_WR(cpu, addr, 0);
406     }
407 
408     cpu->r[1] = pvt->ev_updn_q[q_rd];
409 
410     return pvt->ev_num_q[q_rd];
411 }
412 
413 /* ======================================================================== */
414 /*  EVENT_MAP        -- Maps an event to a particular AND/OR mask set       */
415 /* ======================================================================== */
event_map(event_t * event,const char * event_name,int map,uint32_t * word,uint32_t and_mask[2],uint32_t or_mask[2])416 int event_map
417 (
418     event_t    *event,          /* Event_t structure being set up.          */
419     const char *event_name,     /* Name of event to map.                    */
420     int         map,            /* Keyboard map number to map within.       */
421     uint32_t   *word,           /* Word modified by event, (NULL to ignore) */
422     uint32_t    and_mask[2],    /* AND masks for event up/down.             */
423     uint32_t    or_mask[2]      /* OR masks for event up/down.              */
424 )
425 {
426     evt_pvt_t *const pvt = event->pvt;
427     int num, j;
428 
429     /* -------------------------------------------------------------------- */
430     /*  Get the event number for this name.                                 */
431     /* -------------------------------------------------------------------- */
432     if ((num = event_name_to_num(event_name)) < 0)
433         return -1;
434 
435     /* -------------------------------------------------------------------- */
436     /*  Register ourselves with this event.                                 */
437     /* -------------------------------------------------------------------- */
438     pvt->mask_tbl[map][num].word = word;
439 
440     for (j = 0; j < 2; j++)
441     {
442         pvt->mask_tbl[map][num].and_mask[j] = and_mask[j];
443         pvt->mask_tbl[map][num].or_mask [j] = or_mask [j];
444     }
445 
446     /* -------------------------------------------------------------------- */
447     /*  Done:  Return success.                                              */
448     /* -------------------------------------------------------------------- */
449     return 0;
450 }
451 
452 /* ======================================================================== */
453 /*  EVENT_COMBO_EVENT_NUM    -- Return the compressed event number for a    */
454 /*                              combo, or combo->event_cnt if not found.    */
455 /* ======================================================================== */
event_combo_event_num(combo_state_t * const combo,const event_num_t event_num)456 LOCAL int event_combo_event_num
457 (
458     combo_state_t *const combo,
459     const event_num_t    event_num
460 )
461 {
462     for (int combo_event_num = 0; combo_event_num < combo->event_cnt;
463          combo_event_num++)
464         if (combo->event_num[combo_event_num] == event_num)
465             return combo_event_num;
466 
467     return combo->event_cnt;
468 }
469 
470 /* ======================================================================== */
471 /*  EVENT_ADD_TO_COMBO   -- Bind a name to half of a combo.                 */
472 /* ======================================================================== */
event_add_to_combo(combo_state_t * const combo,const char * const event_name,const int combo_num,const int which)473 LOCAL int event_add_to_combo
474 (
475     combo_state_t *const combo,
476     const char    *const event_name,
477     const int            combo_num,
478     const int            which   /* 0/1: first/second half of combo pair.   */
479 )
480 {
481     const uint64_t event_bit = 1ull << combo_num;
482 
483     const event_num_t event_num = event_name_to_num(event_name);
484     if (!EVENT_VALID(event_num))
485         return -1;
486 
487     if (event_num >= EVENT_COMBO0)
488     {
489         fprintf(stderr, "event:  Cannot use '%s' in a combo event\n",
490                 event_name);
491         return -1;
492     }
493 
494     const int combo_event_num = event_combo_event_num(combo, event_num);
495 
496     if (combo_event_num == combo->event_cnt)
497     {
498         combo->event_cnt++;
499         combo->event_num[combo_event_num] = event_num;
500         set_en_bit(combo->in_combo, event_num);
501     }
502 
503     if (combo->event_bits[combo_event_num] & event_bit)
504     {
505         fprintf(stderr, "event:  Must use two distinct events in a combo\n");
506         return -1;
507     }
508 
509     combo->event_bits[combo_event_num] |= event_bit;
510     combo->pairs[combo_num][which] = event_num;
511     return 0;
512 }
513 
514 /* ======================================================================== */
515 /*  EVENT_COMBINE    -- Register a combo event as COMBOxx                   */
516 /* ======================================================================== */
event_combine(event_t * const event,const char * const event_name1,const char * const event_name2,const int combo_num)517 int event_combine
518 (
519     event_t     *const event,
520     const char  *const event_name1,
521     const char  *const event_name2,
522     const int          combo_num
523 )
524 {
525     combo_state_t *const combo = &event->pvt->combo;
526     const uint32_t event_bit = 1u << combo_num;
527 
528     if (combo->allocated & event_bit)
529     {
530         fprintf(stderr, "event:  Error: COMBO%d already in use\n", combo_num);
531         return -1;
532     }
533 
534     combo->allocated |= event_bit;
535 
536     if (event_add_to_combo(combo, event_name1, combo_num, 0)) return -1;
537     if (event_add_to_combo(combo, event_name2, combo_num, 1)) return -1;
538 
539     return 0;
540 }
541 
542 /* ======================================================================== */
543 /*  GET_TRAILING_1      Helper function for event_check_combo.              */
544 /*  Source:  http://graphics.stanford.edu/~seander/bithacks.html            */
545 /*  Uses a deBruijn sequence to do the bit position calc.  Special thanks   */
546 /*  to Arnauld Chevallier for introducing me to deBruijn sequences!         */
547 /* ======================================================================== */
548 static const int mult_DeBruijn_bit_pos[32] =
549 {
550     0,   1, 28,  2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17,  4, 8,
551     31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18,  6, 11,  5, 10, 9
552 };
553 
get_trailing_1(uint64_t v)554 LOCAL uint32_t get_trailing_1(uint64_t v)
555 {
556     const uint32_t lo = (uint32_t)v;
557     const uint32_t hi = (uint32_t)(v >> 32);
558     const uint32_t lbit = lo & -lo;
559     const uint32_t hbit = hi & -hi;
560     return
561         lo ? mult_DeBruijn_bit_pos[((uint32_t)(lbit * 0x077CB531U)) >> 27]
562            : mult_DeBruijn_bit_pos[((uint32_t)(hbit * 0x077CB531U)) >> 27] + 32;
563 }
564 
565 /* ======================================================================== */
566 /*  EVENT_CHECK_COMBO_PRESS                                                 */
567 /*                                                                          */
568 /*  Look for compound keypresses, and convert them into special compound    */
569 /*  events.  Returns "true" if we need to cork further inputs.  Callers     */
570 /*  should stop draining their event queue at this time and wait until      */
571 /*  the next "tick."                                                        */
572 /* ======================================================================== */
event_check_combo_press(evt_pvt_t * const pvt,const event_num_t event_num)573 LOCAL bool event_check_combo_press
574 (
575     evt_pvt_t     *const pvt,
576     const event_num_t    event_num
577 )
578 {
579     combo_state_t *const combo = &pvt->combo;
580     bool cork = false;
581 
582     /* -------------------------------------------------------------------- */
583     /*  If this event isn't part of a combo, or is already down, pass thru  */
584     /* -------------------------------------------------------------------- */
585     if (event_num >= EVENT_COMBO0 || !get_en_bit(combo->in_combo, event_num) ||
586         get_en_bit(combo->key_down, event_num))
587     {
588         event_enqueue(pvt, EV_DOWN, event_num);
589         return cork;
590     }
591 
592     /* -------------------------------------------------------------------- */
593     /*  Mark this key as "pressed".                                         */
594     /* -------------------------------------------------------------------- */
595     set_en_bit(combo->key_down, event_num);
596 
597     /* -------------------------------------------------------------------- */
598     /*  New "full" combos will have a bit already set in "half_combo" that  */
599     /*  is also set in this event's combo list, and isn't already in the    */
600     /*  full-combos list.                                                   */
601     /* -------------------------------------------------------------------- */
602     const int combo_event_num = event_combo_event_num(combo, event_num);
603     assert(combo_event_num != combo->event_cnt);
604 
605     const uint64_t prev_half_active = combo->half_active;
606     const uint64_t prev_full_active = combo->full_active;
607     const uint64_t this_combos = combo->event_bits[combo_event_num];
608     const uint64_t now_active = this_combos & combo->half_active;
609 
610     combo->full_active |= now_active;
611     combo->half_active |= this_combos;
612 
613     /* -------------------------------------------------------------------- */
614     /*  If we made any new "half" combos, cork further event processing     */
615     /*  for "coalesce_time".                                                */
616     /* -------------------------------------------------------------------- */
617     if (prev_half_active != combo->half_active)
618     {
619         pvt->soon = get_time() + pvt->coalesce_time;
620         cork = true;
621     }
622 
623     /* -------------------------------------------------------------------- */
624     /*  If this didn't make any new combos, go ahead and send a EV_DOWN.    */
625     /* -------------------------------------------------------------------- */
626     uint64_t new_combos = combo->full_active & ~prev_full_active;
627     if (!new_combos)
628     {
629         event_enqueue(pvt, EV_DOWN, event_num);
630         return cork;
631     }
632 
633     /* -------------------------------------------------------------------- */
634     /*  If we made any new "full" combos, go send the appropriate up/dn     */
635     /* -------------------------------------------------------------------- */
636     while (new_combos)
637     {
638         const int combo_num = get_trailing_1(new_combos);
639         new_combos &= new_combos - 1;
640 
641         /* ---------------------------------------------------------------- */
642         /*  Send a key-up for the first guy in the combo.                   */
643         /* ---------------------------------------------------------------- */
644         const int other = combo->pairs[combo_num][0] == event_num ? 1 : 0;
645         const event_num_t other_event_num = combo->pairs[combo_num][other];
646 
647         if (event_num != other_event_num)
648             event_enqueue(pvt, EV_UP, other_event_num);
649 
650         /* ---------------------------------------------------------------- */
651         /*  Send a key-down for the newly created combo.                    */
652         /* ---------------------------------------------------------------- */
653         event_enqueue(pvt, EV_DOWN, (event_num_t)(EVENT_COMBO0 + combo_num));
654     }
655 
656     return cork;
657 }
658 
659 /* ======================================================================== */
660 /*  EVENT_CHECK_COMBO_RELEASE                                               */
661 /*                                                                          */
662 /*  Identifies when a combo has been released.  Returns "true" if we need   */
663 /*  cork further inputs.  Callers should stop draining their event queue    */
664 /*  at this time and wait until the next "tick."                            */
665 /* ======================================================================== */
event_check_combo_release(evt_pvt_t * const pvt,const event_num_t event_num)666 LOCAL bool event_check_combo_release
667 (
668     evt_pvt_t  *const pvt,
669     const event_num_t event_num
670 )
671 {
672     combo_state_t *const combo = &pvt->combo;
673     bool cork = false;
674 
675     /* -------------------------------------------------------------------- */
676     /*  If this event isn't part of a combo, or is already up, pass thru    */
677     /* -------------------------------------------------------------------- */
678     if (event_num >= EVENT_COMBO0 || !get_en_bit(combo->in_combo, event_num) ||
679         !get_en_bit(combo->key_down, event_num))
680     {
681         event_enqueue(pvt, EV_UP, event_num);
682         return cork;
683     }
684 
685     /* -------------------------------------------------------------------- */
686     /*  Mark this key as "unpressed".                                       */
687     /* -------------------------------------------------------------------- */
688     clr_en_bit(combo->key_down, event_num);
689 
690     /* -------------------------------------------------------------------- */
691     /*  Look for full combos that now got broken by this event, and half    */
692     /*  combos that are no longer even half-combos.                         */
693     /* -------------------------------------------------------------------- */
694     const int combo_event_num = event_combo_event_num(combo, event_num);;
695     assert(combo_event_num != combo->event_cnt);
696 
697     const uint64_t this_combos  = combo->event_bits[combo_event_num];
698     uint64_t dying_combos = this_combos & combo->full_active;
699 
700     combo->full_active &= ~dying_combos;
701     combo->half_active &= ~(this_combos & ~dying_combos);
702 
703     /* -------------------------------------------------------------------- */
704     /*  If we have any dying combos, cork further event processing for      */
705     /*  "coalesce_time".                                                    */
706     /* -------------------------------------------------------------------- */
707     if (dying_combos)
708     {
709         pvt->soon = get_time() + pvt->coalesce_time;
710         cork = 1;
711     }
712 
713     /* -------------------------------------------------------------------- */
714     /*  If this didn't break any combos, go ahead and send an EV_UP.        */
715     /* -------------------------------------------------------------------- */
716     if (!dying_combos)
717     {
718         event_enqueue(pvt, EV_UP, event_num);
719         return cork;
720     }
721 
722     /* -------------------------------------------------------------------- */
723     /*  If we broke any new "full" combos, go send the appropriate up/dn    */
724     /* -------------------------------------------------------------------- */
725     while (dying_combos)
726     {
727         const int combo_num = get_trailing_1(dying_combos);
728         dying_combos &= dying_combos - 1;
729 
730         /* ---------------------------------------------------------------- */
731         /*  Send a key-up for the dying combo.                              */
732         /* ---------------------------------------------------------------- */
733         event_enqueue(pvt, EV_UP, (event_num_t)(EVENT_COMBO0 + combo_num));
734 
735         /* ---------------------------------------------------------------- */
736         /*  Send a key-down for the remaining guy in the combo, *if* he's   */
737         /*  not active in any other combos.                                 */
738         /* ---------------------------------------------------------------- */
739         const int other = combo->pairs[combo_num][0] == event_num ? 1 : 0;
740         const event_num_t other_event_num = combo->pairs[combo_num][other];
741         const int other_combo_event_num =
742             event_combo_event_num(combo, other_event_num);
743 
744         if (other_combo_event_num != combo->event_cnt &&
745             !(combo->full_active & combo->event_bits[other_combo_event_num]))
746         {
747             event_enqueue(pvt, EV_DOWN, other_event_num);
748             break;
749         }
750     }
751 
752     return cork;
753 }
754 
755 /* ======================================================================== */
756 /*  EVENT_ENQUEUE_CHECK_COMBO                                               */
757 /*                                                                          */
758 /*  Enqueues an event, or combo-related events associated with this event.  */
759 /*  Returns "true" if we need to cork further inputs.  Callers should stop  */
760 /*  draining event queue at this time and wait until the next "tick."       */
761 /* ======================================================================== */
event_enqueue_check_combo(evt_pvt_t * const pvt,const event_updn_t event_updn,const event_num_t event_num)762 bool event_enqueue_check_combo
763 (
764     evt_pvt_t *const   pvt,
765     const event_updn_t event_updn,
766     const event_num_t  event_num
767 )
768 {
769     return event_updn == EV_UP ? event_check_combo_release(pvt, event_num)
770                                : event_check_combo_press(pvt, event_num);
771 }
772 
773 /* ======================================================================== */
774 /*  EVENT_SET_COMBO_COALESCE                                                */
775 /*  Adjust the coalesce timer for combo matching.                           */
776 /* ======================================================================== */
event_set_combo_coalesce(event_t * const event,const double coalesce_time)777 void event_set_combo_coalesce
778 (
779     event_t *const event,
780     const double   coalesce_time
781 )
782 {
783     evt_pvt_t *const pvt = event->pvt;
784     pvt->coalesce_time = coalesce_time;
785 }
786 
787 /* ======================================================================== */
788 /*  EVENT_CHANGE_ACTIVE_MAP  -- Change the current input mapping.           */
789 /* ======================================================================== */
event_change_active_map(event_t * const event,const ev_map_change_req map_change_req)790 void event_change_active_map(event_t *const event,
791                              const ev_map_change_req map_change_req)
792 {
793     evt_pvt_t *const pvt = event->pvt;
794     int previous_map = pvt->current_map;
795 
796     switch (map_change_req)
797     {
798         case EV_MAP_SET_0: case EV_MAP_SET_1:
799         case EV_MAP_SET_2: case EV_MAP_SET_3:
800             pvt->current_map = ((int)map_change_req - EV_MAP_SET_0) & 3;
801             break;
802 
803         case EV_MAP_NEXT:
804             pvt->current_map = (pvt->current_map + 1) & 3;
805             break;
806 
807         case EV_MAP_PREV:
808             pvt->current_map = (pvt->current_map - 1) & 3;
809             break;
810 
811         case EV_MAP_PSH_0: case EV_MAP_PSH_1:
812         case EV_MAP_PSH_2: case EV_MAP_PSH_3:
813             pvt->stacked_map = pvt->current_map | 4;
814             pvt->current_map = ((int)map_change_req - EV_MAP_PSH_0) & 3;
815             break;
816 
817         case EV_MAP_POP:
818             if (pvt->stacked_map)
819             {
820                 pvt->current_map = pvt->stacked_map & 3;
821                 pvt->stacked_map = 0;
822             }
823             break;
824 
825         case EV_MAP_NOP:
826         default:
827             break;
828     }
829 
830     if (previous_map != pvt->current_map)
831     {
832         jzp_clear_and_eol(
833             jzp_printf("Switching to input map %d", pvt->current_map));
834         jzp_flush();
835     }
836 }
837 
838 /* ======================================================================== */
839 /*  This program is free software; you can redistribute it and/or modify    */
840 /*  it under the terms of the GNU General Public License as published by    */
841 /*  the Free Software Foundation; either version 2 of the License, or       */
842 /*  (at your option) any later version.                                     */
843 /*                                                                          */
844 /*  This program is distributed in the hope that it will be useful,         */
845 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
846 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
847 /*  General Public License for more details.                                */
848 /*                                                                          */
849 /*  You should have received a copy of the GNU General Public License along */
850 /*  with this program; if not, write to the Free Software Foundation, Inc., */
851 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
852 /* ======================================================================== */
853 /*                 Copyright (c) 1998-2020, Joseph Zbiciak                  */
854 /* ======================================================================== */
855