Move buttons to TS

This commit is contained in:
Michal Moskal 2017-07-10 14:16:31 +01:00
parent 17488b5c6b
commit 768e8c60f5
11 changed files with 224 additions and 372 deletions

View File

@ -1,225 +0,0 @@
#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,
};
/**
* Patterns for lights under the buttons.
*/
enum class LightsPattern {
Off = LED_BLACK,
Green = LED_GREEN,
Red = LED_RED,
Orange = LED_ORANGE,
GreenFlash = LED_GREEN_FLASH,
RedFlash = LED_RED_FLASH,
OrangeFlash = LED_ORANGE_FLASH,
GreenPulse = LED_GREEN_PULSE,
RedPulse = LED_RED_PULSE,
OrangePulse = LED_ORANGE_PULSE,
};
namespace pxt {
void *buttonPoll(void *);
class Button {
public:
void *ptr;
int id;
bool wasPressed;
bool isPressed;
int timePressed;
Button(int ev3id) {
ptr = 0; // make sure we're not treated as ref-counted object
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;
WButtons()
: buttonLeft(BUTTON_ID_LEFT), buttonRight(BUTTON_ID_RIGHT), buttonUp(BUTTON_ID_UP),
buttonDown(BUTTON_ID_DOWN), buttonEnter(BUTTON_ID_ENTER) {
pthread_t pid;
pthread_create(&pid, NULL, buttonPoll, this);
pthread_detach(pid);
}
};
SINGLETON(WButtons);
const int LastButtonID = (Button *)&((WButtons *)0)->buttonEnter - ((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;
if ((prevState & BUTTON_ID_ESCAPE) && !(state & BUTTON_ID_ESCAPE))
target_reset();
prevState = state;
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];
}
void target_init() {
OutputInit();
ButtonLedInit();
SoundInit();
getWButtons(); // always on - handles ESCAPE key
DMESG("runtime started [%s]", HardwareVersionString());
}
}
namespace control {
/**
* Determine the version of system software currently running.
*/
//%
String deviceFirmwareVersion() {
return mkString(HardwareVersionString());
}
}
// TODO rename this? move it somewhere?
namespace output {
/**
* Set lights.
*/
//% blockId=setLights block="set lights %pattern"
void setLights(LightsPattern pattern) {
SetLedPattern((uint8_t)pattern);
}
}
//% 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;
}
}

129
libs/core/buttons.ts Normal file
View File

@ -0,0 +1,129 @@
/**
* Patterns for lights under the buttons.
*/
const enum LightsPattern {
Off = 0, // LED_BLACK
Green = 1, // LED_GREEN
Red = 2, // LED_RED
Orange = 3, // LED_ORANGE
GreenFlash = 4, // LED_GREEN_FLASH
RedFlash = 5, // LED_RED_FLASH
OrangeFlash = 6, // LED_ORANGE_FLASH
GreenPulse = 7, // LED_GREEN_PULSE
RedPulse = 8, // LED_RED_PULSE
OrangePulse = 9, // LED_ORANGE_PULSE
}
namespace input {
let btnsMM: MMap
let buttons: DevButton[]
export namespace internal {
export function getBtnsMM() {
initBtns()
return btnsMM;
}
}
function readButtons() {
let sl = btnsMM.slice(0, LMS.NUM_BUTTONS)
let ret = 0
for (let i = 0; i < sl.length; ++i) {
if (sl[i])
ret |= 1 << i
}
return ret
}
function initBtns() {
if (btnsMM) return
btnsMM = control.mmap("/dev/lms_ui", LMS.NUM_BUTTONS, 0)
if (!btnsMM) control.fail("no buttons?")
buttons = []
input.internal.unsafePollForChanges(50, readButtons, (prev, curr) => {
if (curr & LMS.BUTTON_ID_ESCAPE)
control.reset()
for (let b of buttons)
b.update(!!(curr & b.mask))
})
control.dmesg("runtime started, " + control.deviceFirmwareVersion())
}
class DevButton extends Button {
mask: number
constructor(mask: number) {
super()
this.mask = mask
initBtns()
buttons.push(this)
}
}
initBtns() // always ON as it handles ESCAPE button
/**
* Left button.
*/
//% whenUsed block="button left" weight=95 fixedInstance
export const buttonLeft: Button = new DevButton(LMS.BUTTON_ID_LEFT)
/**
* Right button.
*/
//% whenUsed block="button right" weight=94 fixedInstance
export const buttonRight: Button = new DevButton(LMS.BUTTON_ID_RIGHT)
/**
* Up button.
*/
//% whenUsed block="button up" weight=95 fixedInstance
export const buttonUp: Button = new DevButton(LMS.BUTTON_ID_UP)
/**
* Down button.
*/
//% whenUsed block="button down" weight=95 fixedInstance
export const buttonDown: Button = new DevButton(LMS.BUTTON_ID_DOWN)
/**
* Enter button.
*/
//% whenUsed block="button enter" weight=95 fixedInstance
export const buttonEnter: Button = new DevButton(LMS.BUTTON_ID_ENTER)
}
namespace control {
/**
* Determine the version of system software currently running.
*/
export function deviceFirmwareVersion(): string {
let buf = output.createBuffer(6)
input.internal.getBtnsMM().read(buf)
let r = ""
for (let i = 0; i < buf.length; ++i) {
let c = buf[i]
if (c == 0) break
r += String.fromCharCode(c)
}
return r
}
}
namespace output {
let currPattern: LightsPattern
/**
* Set lights.
*/
//% blockId=setLights block="set lights %pattern"
export function setLights(pattern: LightsPattern): void {
if (currPattern === pattern)
return
currPattern = pattern
let cmd = output.createBuffer(2)
cmd[0] = pattern + 48
input.internal.getBtnsMM().write(cmd)
}
}

View File

@ -18,6 +18,7 @@ const enum LMS {
NUM_INPUTS = 4,
LCD_WIDTH = 178,
LCD_HEIGHT = 128,
NUM_BUTTONS = 6,
DEVICE_TYPE_NXT_TOUCH = 1,
DEVICE_TYPE_NXT_LIGHT = 2,
@ -81,5 +82,12 @@ const enum LMS {
DEVICE_EVT_ANY = 0,
DEVICE_ID_NOTIFY = 10000,
DEVICE_ID_NOTIFY_ONE = 10001,
BUTTON_ID_UP = 0x01,
BUTTON_ID_ENTER = 0x02,
BUTTON_ID_DOWN = 0x04,
BUTTON_ID_RIGHT = 0x08,
BUTTON_ID_LEFT = 0x10,
BUTTON_ID_ESCAPE = 0x20,
}

34
libs/core/enums.d.ts vendored
View File

@ -1,40 +1,6 @@
// 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,
}
/**
* Patterns for lights under the buttons.
*/
declare enum LightsPattern {
Off = 0, // LED_BLACK
Green = 1, // LED_GREEN
Red = 2, // LED_RED
Orange = 3, // LED_ORANGE
GreenFlash = 4, // LED_GREEN_FLASH
RedFlash = 5, // LED_RED_FLASH
OrangeFlash = 6, // LED_ORANGE_FLASH
GreenPulse = 7, // LED_GREEN_PULSE
RedPulse = 8, // LED_RED_PULSE
OrangePulse = 9, // LED_ORANGE_PULSE
}
/**
* Drawing modes
*/

View File

@ -1,6 +1,6 @@
namespace input.internal {
//% shim=pxt::unsafePollForChanges
function unsafePollForChanges(
export function unsafePollForChanges(
periodMs: int32,
query: () => int32,
changeHandler: (prev: int32, curr: int32) => void
@ -423,8 +423,23 @@ namespace input.internal {
}
}
/**
* User interaction on buttons
*/
const enum ButtonEvent {
//% block="click"
Click = 1,
//% block="long click"
LongClick = 2,
//% block="up"
Up = 3,
//% block="down"
Down = 4,
}
namespace input {
export class ButtonTS extends control.Component {
//% fixedInstances
export class Button extends control.Component {
private downTime: number;
private _isPressed: boolean;
private _wasPressed: boolean;
@ -451,15 +466,34 @@ namespace input {
}
/**
* Check if button is currently pressed.
* Check if button is currently 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
isPressed() {
return this._isPressed
}
/**
* Check if button was pressed since last check.
* 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
wasPressed() {
const r = this._wasPressed
this._wasPressed = false
@ -467,13 +501,21 @@ namespace input {
}
/**
* Do something when a touch sensor is clicked, double clicked, etc...
* Do something when a button or sensor 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
onEvent(ev: ButtonEvent, body: () => void) {
control.onEvent(this._id, ev, body)
}
}
}

View File

@ -43,7 +43,7 @@ namespace input {
export class IrSensor extends internal.UartSensor {
private channel: IrRemoteChannel
private pollRunning: boolean
private buttons: ButtonTS[];
private buttons: Button[];
constructor() {
super()
@ -52,7 +52,7 @@ namespace input {
// otherwise button events won't work
this.mode = IrSensorMode.RemoteControl
for (let i = 0; i < 5; ++i) {
this.buttons.push(new ButtonTS())
this.buttons.push(new Button())
}
}

View File

@ -93,8 +93,22 @@ int length(MMap *s) {
/** Perform ioctl(2) on the underlaying file */
//%
void ioctl(MMap *mmap, uint32_t id, Buffer data) {
::ioctl(mmap->fd, id, data->data);
int ioctl(MMap *mmap, uint32_t id, Buffer data) {
return ::ioctl(mmap->fd, id, data->data);
}
/** Perform write(2) on the underlaying file */
//%
int write(MMap *mmap, Buffer data) {
return ::write(mmap->fd, data->data, data->length);
}
/** Perform read(2) on the underlaying file */
//%
int read(MMap *mmap, Buffer data) {
return ::read(mmap->fd, data->data, data->length);
}
}

View File

@ -16,6 +16,14 @@ extern "C" int WriteToPWMDevice(char *bytes, int num_bytes);
//%
void writePWM(Buffer buf) {
WriteToPWMDevice((char*)buf->data, buf->length);
WriteToPWMDevice((char *)buf->data, buf->length);
}
}
namespace pxt {
void target_init() {
OutputInit();
SoundInit();
}
}
}

View File

@ -9,7 +9,7 @@
"linux.cpp",
"mmap.cpp",
"control.cpp",
"buttons.cpp",
"buttons.ts",
"screen.cpp",
"screen.ts",
"output.cpp",

108
libs/core/shims.d.ts vendored
View File

@ -32,7 +32,15 @@ declare interface MMap {
/** Perform ioctl(2) on the underlaying file */
//% shim=MMapMethods::ioctl
ioctl(id: uint32, data: Buffer): void;
ioctl(id: uint32, data: Buffer): int32;
/** Perform write(2) on the underlaying file */
//% shim=MMapMethods::write
write(data: Buffer): int32;
/** Perform read(2) on the underlaying file */
//% shim=MMapMethods::read
read(data: Buffer): int32;
}
declare namespace control {
@ -62,104 +70,6 @@ declare namespace serial {
//% shim=serial::writeDmesg
function writeDmesg(): void;
}
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;
}
declare namespace control {
/**
* Determine the version of system software currently running.
*/
//% shim=control::deviceFirmwareVersion
function deviceFirmwareVersion(): string;
}
declare namespace output {
/**
* Set lights.
*/
//% blockId=setLights block="set lights %pattern" shim=output::setLights
function setLights(pattern: LightsPattern): void;
}
//% 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;
}
declare namespace screen {
/** Draw text. */

View File

@ -1,10 +1,10 @@
namespace input {
export class TouchSensor extends internal.AnalogSensor {
button: ButtonTS;
button: Button;
constructor() {
super()
this.button = new ButtonTS()
this.button = new Button()
}
_query() {