Add button support
This commit is contained in:
parent
ee6664c29c
commit
f2d398909b
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,3 +29,4 @@ videos/**
|
|||||||
.pioenvs
|
.pioenvs
|
||||||
.piolibdeps
|
.piolibdeps
|
||||||
lib/
|
lib/
|
||||||
|
.vscode/
|
||||||
|
177
libs/core/buttons.cpp
Normal file
177
libs/core/buttons.cpp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
#include "pxt.h"
|
||||||
|
#include "ev3.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User interaction on buttons
|
||||||
|
*/
|
||||||
|
enum class ButtonEvent {
|
||||||
|
//% block="click"
|
||||||
|
Click = 1,
|
||||||
|
//% block="long click"
|
||||||
|
LongClick = 2,
|
||||||
|
//% block="up"
|
||||||
|
Up = 3,
|
||||||
|
//% block="down"
|
||||||
|
Down = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace pxt {
|
||||||
|
|
||||||
|
void *buttonPoll(void *);
|
||||||
|
|
||||||
|
class Button {
|
||||||
|
public:
|
||||||
|
int id;
|
||||||
|
bool wasPressed;
|
||||||
|
bool isPressed;
|
||||||
|
int timePressed;
|
||||||
|
Button(int ev3id) {
|
||||||
|
id = ev3id;
|
||||||
|
wasPressed = false;
|
||||||
|
isPressed = false;
|
||||||
|
timePressed = 0;
|
||||||
|
}
|
||||||
|
void raise(ButtonEvent ev) { raiseEvent(ID_BUTTON_BASE + id, (int)ev); }
|
||||||
|
void setPressed(bool pr) {
|
||||||
|
if (pr == isPressed)
|
||||||
|
return;
|
||||||
|
isPressed = pr;
|
||||||
|
if (isPressed) {
|
||||||
|
wasPressed = true;
|
||||||
|
timePressed = current_time_ms();
|
||||||
|
raise(ButtonEvent::Down);
|
||||||
|
} else {
|
||||||
|
raise(ButtonEvent::Up);
|
||||||
|
auto isLong = current_time_ms() - timePressed > 500;
|
||||||
|
raise(isLong ? ButtonEvent::LongClick : ButtonEvent::Click);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class WButtons {
|
||||||
|
public:
|
||||||
|
Button buttons[0];
|
||||||
|
//% indexedInstanceNS=input indexedInstanceShim=pxt::getButton
|
||||||
|
/**
|
||||||
|
* Left button.
|
||||||
|
*/
|
||||||
|
//% block="button left" weight=95
|
||||||
|
Button buttonLeft;
|
||||||
|
/**
|
||||||
|
* Right button.
|
||||||
|
*/
|
||||||
|
//% block="button right" weight=94
|
||||||
|
Button buttonRight;
|
||||||
|
/**
|
||||||
|
* Up button.
|
||||||
|
*/
|
||||||
|
//% block="button up" weight=95
|
||||||
|
Button buttonUp;
|
||||||
|
/**
|
||||||
|
* Down button.
|
||||||
|
*/
|
||||||
|
//% block="button down" weight=95
|
||||||
|
Button buttonDown;
|
||||||
|
/**
|
||||||
|
* Enter button.
|
||||||
|
*/
|
||||||
|
//% block="button enter" weight=95
|
||||||
|
Button buttonEnter;
|
||||||
|
/**
|
||||||
|
* Exit button.
|
||||||
|
*/
|
||||||
|
//% block="button exit" weight=95
|
||||||
|
Button buttonExit;
|
||||||
|
|
||||||
|
WButtons()
|
||||||
|
: buttonLeft(BUTTON_ID_LEFT), buttonRight(BUTTON_ID_RIGHT), buttonUp(BUTTON_ID_UP),
|
||||||
|
buttonDown(BUTTON_ID_DOWN), buttonEnter(BUTTON_ID_ENTER), buttonExit(BUTTON_ID_ESCAPE) {
|
||||||
|
pthread_t pid;
|
||||||
|
pthread_create(&pid, NULL, buttonPoll, this);
|
||||||
|
pthread_detach(pid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
SINGLETON(WButtons);
|
||||||
|
|
||||||
|
const int LastButtonID = (Button *)&((WButtons *)0)->buttonExit - ((WButtons *)0)->buttons;
|
||||||
|
|
||||||
|
extern "C" uint16_t readButtons();
|
||||||
|
|
||||||
|
void *buttonPoll(void *arg) {
|
||||||
|
auto wb = (WButtons *)arg;
|
||||||
|
auto prevState = 0;
|
||||||
|
while (true) {
|
||||||
|
sleep_core_us(50000);
|
||||||
|
auto state = readButtons();
|
||||||
|
if (state == prevState)
|
||||||
|
continue;
|
||||||
|
for (int i = 0; i < LastButtonID; ++i) {
|
||||||
|
auto btn = &wb->buttons[i];
|
||||||
|
btn->setPressed(!!(state & btn->id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//%
|
||||||
|
Button *getButton(int id) {
|
||||||
|
if (!(0 <= id && id <= LastButtonID))
|
||||||
|
target_panic(42);
|
||||||
|
return &getWButtons()->buttons[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//% noRefCounting fixedInstances
|
||||||
|
namespace ButtonMethods {
|
||||||
|
/**
|
||||||
|
* Do something when a button (`A`, `B` or both `A` + `B`) is clicked, double clicked, etc...
|
||||||
|
* @param button the button that needs to be clicked or used
|
||||||
|
* @param event the kind of button gesture that needs to be detected
|
||||||
|
* @param body code to run when the event is raised
|
||||||
|
*/
|
||||||
|
//% help=input/button/on-event weight=99 blockGap=8
|
||||||
|
//% blockId=buttonEvent block="on %button|%event"
|
||||||
|
//% parts="buttonpair"
|
||||||
|
//% blockNamespace=input
|
||||||
|
//% button.fieldEditor="gridpicker"
|
||||||
|
//% button.fieldOptions.width=220
|
||||||
|
//% button.fieldOptions.columns=3
|
||||||
|
void onEvent(Button_ button, ButtonEvent ev, Action body) {
|
||||||
|
registerWithDal(button->id + ID_BUTTON_BASE, (int)ev, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a button is pressed or not.
|
||||||
|
* @param button the button to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/button/is-pressed weight=79
|
||||||
|
//% block="%button|is pressed"
|
||||||
|
//% blockId=buttonIsPressed
|
||||||
|
//% blockGap=8
|
||||||
|
//% parts="buttonpair"
|
||||||
|
//% blockNamespace=input
|
||||||
|
//% button.fieldEditor="gridpicker"
|
||||||
|
//% button.fieldOptions.width=220
|
||||||
|
//% button.fieldOptions.columns=3
|
||||||
|
bool isPressed(Button_ button) {
|
||||||
|
return button->isPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if the button was pressed again since the last time you checked.
|
||||||
|
* @param button the button to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/button/was-pressed weight=78
|
||||||
|
//% block="%button|was pressed"
|
||||||
|
//% blockId=buttonWasPressed
|
||||||
|
//% parts="buttonpair" blockGap=8
|
||||||
|
//% blockNamespace=input advanced=true
|
||||||
|
//% button.fieldEditor="gridpicker"
|
||||||
|
//% button.fieldOptions.width=220
|
||||||
|
//% button.fieldOptions.columns=3
|
||||||
|
bool wasPressed(Button_ button) {
|
||||||
|
auto res = button->wasPressed;
|
||||||
|
button->wasPressed = false;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
19
libs/core/enums.d.ts
vendored
Normal file
19
libs/core/enums.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Auto-generated. Do not edit.
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User interaction on buttons
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare enum ButtonEvent {
|
||||||
|
//% block="click"
|
||||||
|
Click = 1,
|
||||||
|
//% block="long click"
|
||||||
|
LongClick = 2,
|
||||||
|
//% block="up"
|
||||||
|
Up = 3,
|
||||||
|
//% block="down"
|
||||||
|
Down = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-generated. Do not edit. Really.
|
@ -39,6 +39,7 @@ namespace pxt {
|
|||||||
|
|
||||||
static int startTime;
|
static int startTime;
|
||||||
static pthread_mutex_t execMutex;
|
static pthread_mutex_t execMutex;
|
||||||
|
static pthread_mutex_t eventMutex;
|
||||||
static pthread_cond_t newEventBroadcast;
|
static pthread_cond_t newEventBroadcast;
|
||||||
|
|
||||||
struct Thread {
|
struct Thread {
|
||||||
@ -186,12 +187,14 @@ void waitForEvent(int source, int value) {
|
|||||||
auto self = pthread_self();
|
auto self = pthread_self();
|
||||||
for (auto t = allThreads; t; t = t->next) {
|
for (auto t = allThreads; t; t = t->next) {
|
||||||
if (t->pid == self) {
|
if (t->pid == self) {
|
||||||
|
pthread_mutex_lock(&eventMutex);
|
||||||
t->waitSource = source;
|
t->waitSource = source;
|
||||||
t->waitValue = value;
|
t->waitValue = value;
|
||||||
// spourious wake ups may occur they say
|
// spourious wake ups may occur they say
|
||||||
while (t->waitSource) {
|
while (t->waitSource) {
|
||||||
pthread_cond_wait(&t->waitCond, &execMutex);
|
pthread_cond_wait(&t->waitCond, &eventMutex);
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&eventMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,9 +214,9 @@ static void dispatchEvent(Event &e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void *evtDispatcher(void *dummy) {
|
static void *evtDispatcher(void *dummy) {
|
||||||
startUser();
|
pthread_mutex_lock(&eventMutex);
|
||||||
while (true) {
|
while (true) {
|
||||||
pthread_cond_wait(&newEventBroadcast, &execMutex);
|
pthread_cond_wait(&newEventBroadcast, &eventMutex);
|
||||||
while (eventHead != NULL) {
|
while (eventHead != NULL) {
|
||||||
Event *ev = eventHead;
|
Event *ev = eventHead;
|
||||||
eventHead = ev->next;
|
eventHead = ev->next;
|
||||||
@ -236,6 +239,7 @@ static void *evtDispatcher(void *dummy) {
|
|||||||
|
|
||||||
void raiseEvent(int id, int event) {
|
void raiseEvent(int id, int event) {
|
||||||
auto e = mkEvent(id, event);
|
auto e = mkEvent(id, event);
|
||||||
|
pthread_mutex_lock(&eventMutex);
|
||||||
if (eventTail == NULL) {
|
if (eventTail == NULL) {
|
||||||
assert(eventHead == NULL);
|
assert(eventHead == NULL);
|
||||||
eventHead = eventTail = e;
|
eventHead = eventTail = e;
|
||||||
@ -244,6 +248,7 @@ void raiseEvent(int id, int event) {
|
|||||||
eventTail = e;
|
eventTail = e;
|
||||||
}
|
}
|
||||||
pthread_cond_broadcast(&newEventBroadcast);
|
pthread_cond_broadcast(&newEventBroadcast);
|
||||||
|
pthread_mutex_unlock(&eventMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerWithDal(int id, int event, Action a) {
|
void registerWithDal(int id, int event, Action a) {
|
||||||
@ -257,11 +262,14 @@ void dumpDmesg() {
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void InitEV3();
|
||||||
|
|
||||||
void initRuntime() {
|
void initRuntime() {
|
||||||
startTime = currTime();
|
startTime = currTime();
|
||||||
pthread_t disp;
|
pthread_t disp;
|
||||||
pthread_create(&disp, NULL, evtDispatcher, NULL);
|
pthread_create(&disp, NULL, evtDispatcher, NULL);
|
||||||
pthread_detach(disp);
|
pthread_detach(disp);
|
||||||
|
InitEV3();
|
||||||
startUser();
|
startUser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,15 @@
|
|||||||
#include "pxtbase.h"
|
#include "pxtbase.h"
|
||||||
#undef DMESG
|
#undef DMESG
|
||||||
|
|
||||||
|
#define ID_BUTTON_BASE 100
|
||||||
|
|
||||||
|
namespace pxt {
|
||||||
|
void raiseEvent(int id, int event);
|
||||||
|
void sleep_core_us(uint64_t us);
|
||||||
|
|
||||||
|
class Button;
|
||||||
|
typedef Button *Button_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
"pxt.h",
|
"pxt.h",
|
||||||
"pxtcore.h",
|
"pxtcore.h",
|
||||||
"linux.cpp",
|
"linux.cpp",
|
||||||
|
"buttons.cpp",
|
||||||
|
"shims.d.ts",
|
||||||
|
"enums.d.ts",
|
||||||
"ns.ts"
|
"ns.ts"
|
||||||
],
|
],
|
||||||
"testFiles": [
|
"testFiles": [
|
||||||
|
91
libs/core/shims.d.ts
vendored
Normal file
91
libs/core/shims.d.ts
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Auto-generated. Do not edit.
|
||||||
|
declare namespace input {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Left button.
|
||||||
|
*/
|
||||||
|
//% indexedInstanceNS=input indexedInstanceShim=pxt::getButton
|
||||||
|
//% block="button left" weight=95 fixedInstance shim=pxt::getButton(0)
|
||||||
|
const buttonLeft: Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right button.
|
||||||
|
*/
|
||||||
|
//% block="button right" weight=94 fixedInstance shim=pxt::getButton(1)
|
||||||
|
const buttonRight: Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Up button.
|
||||||
|
*/
|
||||||
|
//% block="button up" weight=95 fixedInstance shim=pxt::getButton(2)
|
||||||
|
const buttonUp: Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Down button.
|
||||||
|
*/
|
||||||
|
//% block="button down" weight=95 fixedInstance shim=pxt::getButton(3)
|
||||||
|
const buttonDown: Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter button.
|
||||||
|
*/
|
||||||
|
//% block="button enter" weight=95 fixedInstance shim=pxt::getButton(4)
|
||||||
|
const buttonEnter: Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exit button.
|
||||||
|
*/
|
||||||
|
//% block="button exit" weight=95 fixedInstance shim=pxt::getButton(5)
|
||||||
|
const buttonExit: Button;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//% noRefCounting fixedInstances
|
||||||
|
declare interface Button {
|
||||||
|
/**
|
||||||
|
* Do something when a button (`A`, `B` or both `A` + `B`) is clicked, double clicked, etc...
|
||||||
|
* @param button the button that needs to be clicked or used
|
||||||
|
* @param event the kind of button gesture that needs to be detected
|
||||||
|
* @param body code to run when the event is raised
|
||||||
|
*/
|
||||||
|
//% help=input/button/on-event weight=99 blockGap=8
|
||||||
|
//% blockId=buttonEvent block="on %button|%event"
|
||||||
|
//% parts="buttonpair"
|
||||||
|
//% blockNamespace=input
|
||||||
|
//% button.fieldEditor="gridpicker"
|
||||||
|
//% button.fieldOptions.width=220
|
||||||
|
//% button.fieldOptions.columns=3 shim=ButtonMethods::onEvent
|
||||||
|
onEvent(ev: ButtonEvent, body: () => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a button is pressed or not.
|
||||||
|
* @param button the button to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/button/is-pressed weight=79
|
||||||
|
//% block="%button|is pressed"
|
||||||
|
//% blockId=buttonIsPressed
|
||||||
|
//% blockGap=8
|
||||||
|
//% parts="buttonpair"
|
||||||
|
//% blockNamespace=input
|
||||||
|
//% button.fieldEditor="gridpicker"
|
||||||
|
//% button.fieldOptions.width=220
|
||||||
|
//% button.fieldOptions.columns=3 shim=ButtonMethods::isPressed
|
||||||
|
isPressed(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if the button was pressed again since the last time you checked.
|
||||||
|
* @param button the button to query the request
|
||||||
|
*/
|
||||||
|
//% help=input/button/was-pressed weight=78
|
||||||
|
//% block="%button|was pressed"
|
||||||
|
//% blockId=buttonWasPressed
|
||||||
|
//% parts="buttonpair" blockGap=8
|
||||||
|
//% blockNamespace=input advanced=true
|
||||||
|
//% button.fieldEditor="gridpicker"
|
||||||
|
//% button.fieldOptions.width=220
|
||||||
|
//% button.fieldOptions.columns=3 shim=ButtonMethods::wasPressed
|
||||||
|
wasPressed(): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-generated. Do not edit. Really.
|
Loading…
Reference in New Issue
Block a user