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