mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
* 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
179 lines
5.0 KiB
C++
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
|
|
|
|
|
|
}
|
|
}
|