Add button support

This commit is contained in:
Michal Moskal 2017-07-05 01:17:26 +01:00
parent ee6664c29c
commit f2d398909b
7 changed files with 313 additions and 3 deletions

1
.gitignore vendored
View File

@ -29,3 +29,4 @@ videos/**
.pioenvs
.piolibdeps
lib/
.vscode/

177
libs/core/buttons.cpp Normal file
View 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
View 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.

View File

@ -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();
}
}

View File

@ -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

View File

@ -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
View 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.