terminal: add audio/visual feedback when no user program

Characters typed into the terminal are only sent to the hub when a user
program is running. This provides audio and visual feedback to indicate
to the user that keystrokes have no effect when the user program is not
running.
This commit is contained in:
David Lechner
2022-12-21 16:28:17 -06:00
committed by David Lechner
parent 808e6c0e85
commit 64b6251c1a
4 changed files with 66 additions and 1 deletions
+3
View File
@@ -4,6 +4,9 @@
## [Unreleased]
### Added
- Added audio and visual feedback when typing into terminal while user program is not running.
## [2.0.1] - 2022-12-21
### Fixed
+42 -1
View File
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import 'xterm/css/xterm.css';
import './terminal.scss';
import { Menu, MenuDivider, MenuItem, ResizeSensor } from '@blueprintjs/core';
import { ContextMenu2, ContextMenu2ContentProps } from '@blueprintjs/popover2';
import React, { useContext, useEffect, useMemo, useRef } from 'react';
@@ -13,7 +15,12 @@ import { TerminalContext } from './TerminalContext';
import { receiveData } from './actions';
import { useI18n } from './i18n';
import 'xterm/css/xterm.css';
// Source: https://freesound.org/people/altemark/sounds/45759/
// This sound is released under the Creative Commons Attribution 3.0 Unported
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
// made, apart from the conversion to base64.
const BELL_SOUND =
'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
function handleKeyEvent(event: KeyboardEvent): boolean {
if (
@@ -186,12 +193,46 @@ const Terminal: React.FC = (_props) => {
return () => removeEventListener('pb-terminal-focus', listener);
}, [xterm]);
// audio and visual notification of bell
const bellRef = useRef<HTMLAudioElement>(null);
const bellOverlayRef = useRef<HTMLDivElement>(null);
const bellTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
useEffect(() => {
const audioElement = bellRef.current;
const overlayElement = bellOverlayRef.current;
if (process.env.NODE_ENV === 'test' || !audioElement || !overlayElement) {
return;
}
const subscription = xterm.onBell(() => {
if (bellTimeoutRef.current) {
clearTimeout(bellTimeoutRef.current);
}
audioElement.play();
overlayElement.classList.add('pb-bell');
bellTimeoutRef.current = setTimeout(() => {
overlayElement.classList.remove('pb-bell');
}, 150);
});
return () => subscription.dispose();
}, [xterm, bellRef, bellOverlayRef, bellTimeoutRef]);
return (
<ContextMenu2
className="h-100"
content={contextMenu}
popoverProps={{ onClosed: () => xterm.focus() }}
>
<audio hidden preload="auto" ref={bellRef}>
<source src={BELL_SOUND} />
</audio>
<div className="pb-terminal-bell-overlay" ref={bellOverlayRef} />
<ResizeSensor onResize={(): void => fitAddon.fit()}>
<div ref={terminalRef} className="h-100" />
</ResizeSensor>
+2
View File
@@ -79,6 +79,8 @@ function* receiveTerminalData(): Generator {
// it should be fine as long a the logic for the state doesn't change.
if (!isUserProgramRunning) {
// if no user program is running, input goes to /dev/null
// print the BEL character (^G) to notify the user that the input was ignored
yield* put(sendData('\x07'));
continue;
}
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
@use '@blueprintjs/core/lib/scss/variables' as bp;
.pb-terminal-bell-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: bp.$pt-z-index-content - 1;
pointer-events: none;
&.pb-bell {
background-color: adjust-color(bp.$pt-intent-danger, $alpha: -0.5);
transition: linear 100ms;
}
}