Add button support
This commit is contained in:
parent
ee6664c29c
commit
f2d398909b
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,3 +29,4 @@ videos/**
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
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 pthread_mutex_t execMutex;
|
||||
static pthread_mutex_t eventMutex;
|
||||
static pthread_cond_t newEventBroadcast;
|
||||
|
||||
struct Thread {
|
||||
@ -186,12 +187,14 @@ void waitForEvent(int source, int value) {
|
||||
auto self = pthread_self();
|
||||
for (auto t = allThreads; t; t = t->next) {
|
||||
if (t->pid == self) {
|
||||
pthread_mutex_lock(&eventMutex);
|
||||
t->waitSource = source;
|
||||
t->waitValue = value;
|
||||
// spourious wake ups may occur they say
|
||||
while (t->waitSource) {
|
||||
pthread_cond_wait(&t->waitCond, &execMutex);
|
||||
pthread_cond_wait(&t->waitCond, &eventMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&eventMutex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -211,9 +214,9 @@ static void dispatchEvent(Event &e) {
|
||||
}
|
||||
|
||||
static void *evtDispatcher(void *dummy) {
|
||||
startUser();
|
||||
pthread_mutex_lock(&eventMutex);
|
||||
while (true) {
|
||||
pthread_cond_wait(&newEventBroadcast, &execMutex);
|
||||
pthread_cond_wait(&newEventBroadcast, &eventMutex);
|
||||
while (eventHead != NULL) {
|
||||
Event *ev = eventHead;
|
||||
eventHead = ev->next;
|
||||
@ -236,6 +239,7 @@ static void *evtDispatcher(void *dummy) {
|
||||
|
||||
void raiseEvent(int id, int event) {
|
||||
auto e = mkEvent(id, event);
|
||||
pthread_mutex_lock(&eventMutex);
|
||||
if (eventTail == NULL) {
|
||||
assert(eventHead == NULL);
|
||||
eventHead = eventTail = e;
|
||||
@ -244,6 +248,7 @@ void raiseEvent(int id, int event) {
|
||||
eventTail = e;
|
||||
}
|
||||
pthread_cond_broadcast(&newEventBroadcast);
|
||||
pthread_mutex_unlock(&eventMutex);
|
||||
}
|
||||
|
||||
void registerWithDal(int id, int event, Action a) {
|
||||
@ -257,11 +262,14 @@ void dumpDmesg() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
extern "C" void InitEV3();
|
||||
|
||||
void initRuntime() {
|
||||
startTime = currTime();
|
||||
pthread_t disp;
|
||||
pthread_create(&disp, NULL, evtDispatcher, NULL);
|
||||
pthread_detach(disp);
|
||||
InitEV3();
|
||||
startUser();
|
||||
}
|
||||
}
|
||||
|
@ -4,4 +4,15 @@
|
||||
#include "pxtbase.h"
|
||||
#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
|
||||
|
@ -7,6 +7,9 @@
|
||||
"pxt.h",
|
||||
"pxtcore.h",
|
||||
"linux.cpp",
|
||||
"buttons.cpp",
|
||||
"shims.d.ts",
|
||||
"enums.d.ts",
|
||||
"ns.ts"
|
||||
],
|
||||
"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