From 768e8c60f5073cffb549347658c2a04ca6461278 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 10 Jul 2017 14:16:31 +0100 Subject: [PATCH] Move buttons to TS --- libs/core/buttons.cpp | 225 ------------------------------------------ libs/core/buttons.ts | 129 ++++++++++++++++++++++++ libs/core/core.ts | 8 ++ libs/core/enums.d.ts | 34 ------- libs/core/input.ts | 52 +++++++++- libs/core/ir.ts | 4 +- libs/core/mmap.cpp | 18 +++- libs/core/output.cpp | 12 ++- libs/core/pxt.json | 2 +- libs/core/shims.d.ts | 108 ++------------------ libs/core/touch.ts | 4 +- 11 files changed, 224 insertions(+), 372 deletions(-) delete mode 100644 libs/core/buttons.cpp create mode 100644 libs/core/buttons.ts diff --git a/libs/core/buttons.cpp b/libs/core/buttons.cpp deleted file mode 100644 index 28d0d600..00000000 --- a/libs/core/buttons.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "pxt.h" -#include "ev3.h" -#include - -/** -* 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; -} -} diff --git a/libs/core/buttons.ts b/libs/core/buttons.ts new file mode 100644 index 00000000..a876be6f --- /dev/null +++ b/libs/core/buttons.ts @@ -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) + } +} diff --git a/libs/core/core.ts b/libs/core/core.ts index b2f78820..9d30f875 100644 --- a/libs/core/core.ts +++ b/libs/core/core.ts @@ -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, } diff --git a/libs/core/enums.d.ts b/libs/core/enums.d.ts index 56e8a5f2..ea50d007 100644 --- a/libs/core/enums.d.ts +++ b/libs/core/enums.d.ts @@ -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 */ diff --git a/libs/core/input.ts b/libs/core/input.ts index f09a508e..7a129c93 100644 --- a/libs/core/input.ts +++ b/libs/core/input.ts @@ -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) } } } + diff --git a/libs/core/ir.ts b/libs/core/ir.ts index 5d6db175..f8d22773 100644 --- a/libs/core/ir.ts +++ b/libs/core/ir.ts @@ -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()) } } diff --git a/libs/core/mmap.cpp b/libs/core/mmap.cpp index 6b0f24be..1b0443a9 100644 --- a/libs/core/mmap.cpp +++ b/libs/core/mmap.cpp @@ -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); } } \ No newline at end of file diff --git a/libs/core/output.cpp b/libs/core/output.cpp index ee0aea40..820f3d30 100644 --- a/libs/core/output.cpp +++ b/libs/core/output.cpp @@ -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(); +} } -} \ No newline at end of file diff --git a/libs/core/pxt.json b/libs/core/pxt.json index a9b7ed57..bd404fe7 100644 --- a/libs/core/pxt.json +++ b/libs/core/pxt.json @@ -9,7 +9,7 @@ "linux.cpp", "mmap.cpp", "control.cpp", - "buttons.cpp", + "buttons.ts", "screen.cpp", "screen.ts", "output.cpp", diff --git a/libs/core/shims.d.ts b/libs/core/shims.d.ts index 00e0e887..406bf3f7 100644 --- a/libs/core/shims.d.ts +++ b/libs/core/shims.d.ts @@ -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. */ diff --git a/libs/core/touch.ts b/libs/core/touch.ts index 7967ca35..172bb455 100644 --- a/libs/core/touch.ts +++ b/libs/core/touch.ts @@ -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() {