Files
Upsilon/ion/src/shared/events_keyboard.cpp
circuit10 b44a95a9b3 Casio fx-CG series port (#324)
* Initial test - working on Linux

* Try to make it work with liba

* Stop using liba and the filesystem

* IT WORKS

* Key input, full res, fix some of the crashes

* Fix the hang when doing calculations

* Add some more key mappings

* Fix the square root issue

* Icons

* Better key mappings, brightness control, better gamma correction, more effficient framebuffer

* Cleanup stage 1

* Cleanup stage 2

* Make the build system build a g3a

* Make it not exit when you press the menu button

* Add Casio port to README

* Use omega-master instead of omega-dev

* Fix mistake with cherry-picking in the README

* Fix internal storage crash

* Fix compile error on Numworks calculators

* Upsilon branding

* Sharper icon

* Make the CI work

* Add power off and improve menu

* Map Alpha + up/down to the brightness shortcut

* Add missing file

* Fix web CI build

* Revert "Fix web CI build"

This reverts commit f19657d9fc.

* Change "prizm" to "fxcg"

* Add FASTLOAD option for Add-in Push

* Add some charatcers to the catalog on Casio and improve key mappings

* Build with -Os -flto

* Disable LTO for now as it's causing crashes

* Put back the fonts I accidently changed

I'd like to add an option for this though as I prefer the ones from Epsilon
2023-05-10 18:28:18 +02:00

179 lines
5.0 KiB
C++

#include <ion/keyboard.h>
#include <ion/events.h>
#include <ion/timing.h>
#include <assert.h>
namespace Ion {
namespace Events {
static bool sleepWithTimeout(int duration, int * timeout) {
if (*timeout >= duration) {
Timing::msleep(duration);
*timeout -= duration;
return false;
} else {
Timing::msleep(*timeout);
*timeout = 0;
return true;
}
}
Event sLastEvent = Events::None;
Keyboard::State sLastKeyboardState;
bool sLastEventShift;
bool sLastEventAlpha;
bool sEventIsRepeating = 0;
int sEventRepetitionCount = 0;
constexpr int delayBeforeRepeat = 200;
constexpr int delayBetweenRepeat = 50;
static bool canRepeatEvent(Event e) {
return e == Events::Left
|| e == Events::Up
|| e == Events::Down
|| e == Events::Right
|| e == Events::Backspace
|| e == Events::ShiftLeft
|| e == Events::ShiftRight
|| e == Events::ShiftUp
|| e == Events::ShiftDown;
}
Event getPlatformEvent();
void ComputeAndSetRepetitionFactor(int eventRepetitionCount) {
// The Repetition factor is increased by 4 every 20 loops in getEvent(2 sec)
setLongRepetition((eventRepetitionCount / 20) * 4 + 1);
}
void resetLongRepetition() {
sEventRepetitionCount = 0;
ComputeAndSetRepetitionFactor(sEventRepetitionCount);
}
static Keyboard::Key keyFromState(Keyboard::State state) {
return static_cast<Keyboard::Key>(63 - __builtin_clzll(state));
}
static inline Event innerGetEvent(int * timeout) {
assert(*timeout > delayBeforeRepeat);
assert(*timeout > delayBetweenRepeat);
int time = 0;
uint64_t keysSeenUp = 0;
uint64_t keysSeenTransitioningFromUpToDown = 0;
while (true) {
Event platformEvent = getPlatformEvent();
if (platformEvent != None) {
return platformEvent;
}
Keyboard::State state = Keyboard::scan();
keysSeenUp |= ~state;
keysSeenTransitioningFromUpToDown = keysSeenUp & state;
bool lock = isLockActive();
if (keysSeenTransitioningFromUpToDown != 0) {
sEventIsRepeating = false;
resetLongRepetition();
didPressNewKey();
/* The key that triggered the event corresponds to the first non-zero bit
* in "match". This is a rather simple logic operation for the which many
* processors have an instruction (ARM thumb uses CLZ).
* Unfortunately there's no way to express this in standard C, so we have
* to resort to using a builtin function. */
Keyboard::Key key = (Keyboard::Key)(63-__builtin_clzll(keysSeenTransitioningFromUpToDown));
bool shift = isShiftActive() || state.keyDown(Keyboard::Key::Shift);
bool alpha = isAlphaActive() || state.keyDown(Keyboard::Key::Alpha);
// Allow the detected states to be overriden by the simulated states
// This is used for key mapping
if (state.simulatedShift() != Keyboard::ModSimState::None) {
shift = state.simulatedShift() == Keyboard::ModSimState::ForceOn;
}
if (state.simulatedAlpha() != Keyboard::ModSimState::None) {
alpha = state.simulatedAlpha() == Keyboard::ModSimState::ForceOn;
}
bool lock = isLockActive();
if ( key == Keyboard::Key::Left
|| key == Keyboard::Key::Right
|| key == Keyboard::Key::Up
|| key == Keyboard::Key::Backspace
|| key == Keyboard::Key::Down) {
if (lock) {
lock = false;
alpha = false;
// shift = false;
}
}
Event event(key, shift, alpha, lock);
sLastEventShift = shift;
sLastEventAlpha = alpha;
updateModifiersFromEvent(event);
sLastEvent = event;
sLastKeyboardState = state;
return event;
}
if (sleepWithTimeout(10, timeout)) {
// Timeout occurred
resetLongRepetition();
return Events::None;
}
time += 10;
// At this point, we know that keysSeenTransitioningFromUpToDown has *always* been zero
// In other words, no new key has been pressed
if (canRepeatEvent(sLastEvent)
&& state == sLastKeyboardState
&& sLastEventShift == state.keyDown(Keyboard::Key::Shift)
&& sLastEventAlpha == (state.keyDown(Keyboard::Key::Alpha) || lock))
{
int delay = (sEventIsRepeating ? delayBetweenRepeat : delayBeforeRepeat);
if (time >= delay) {
sEventIsRepeating = true;
sEventRepetitionCount++;
ComputeAndSetRepetitionFactor(sEventRepetitionCount);
return sLastEvent;
}
}
}
}
#if ION_EVENTS_JOURNAL
static Journal * sSourceJournal = nullptr;
static Journal * sDestinationJournal = nullptr;
void replayFrom(Journal * l) { sSourceJournal = l; }
void logTo(Journal * l) { sDestinationJournal = l; }
Event getEvent(int * timeout) {
if (sSourceJournal != nullptr) {
if (sSourceJournal->isEmpty()) {
sSourceJournal = nullptr;
} else {
return sSourceJournal->popEvent();
}
}
Event e = innerGetEvent(timeout);
if (sDestinationJournal != nullptr) {
sDestinationJournal->pushEvent(e);
}
return e;
}
#else
Event getEvent(int * timeout) {
return innerGetEvent(timeout);
}
#endif
}
}