Compare commits

...

23 Commits

Author SHA1 Message Date
Bernd Herzog 4740df2e2c Usb serial shell workerthread #2 (#1738) 2024-01-07 22:25:43 +01:00
Bernd Herzog ec0f45a488 added sys locks to time critical lcd updates (#1737) 2024-01-07 19:50:26 +01:00
Totoo 9d7e06c255 Appstart and applist from serial (#1736)
* Start app, and list fixed ones.
* Add ext app support
2024-01-07 18:56:09 +01:00
sommermoregentraum 4b93e78dd9 make pacman's face (mouse) face to the dir which he hits the wall (#1735) 2024-01-07 16:55:45 +01:00
Bernd Herzog 5ced8c90d3 added radio saturation to performance counter (#1734) 2024-01-07 15:31:08 +01:00
Bernd Herzog 9d22711368 added usb event to not wait for the next frame (#1733) 2024-01-07 14:09:22 +01:00
Bernd Herzog 23e6295dd2 removed the use of the hackrf cpld eeprom (#1732)
* removed the use of the hackrf cpld eeprom

* readded comment
2024-01-07 12:29:53 +01:00
Bernd Herzog bbd75ab7ef fixed usb descriptor product string length (#1731) 2024-01-07 11:57:16 +01:00
jLynx 7508fee62c Fixed sdcard zip folder structure (#1730)
* Fixed sd card zip folder structure

* Update create_nightly_release.yml

Updated zip for work map

* Update create_stable_release.yml

Replicated on stable release
2024-01-07 19:51:27 +13:00
John Stockdale fe88760766 First version of rtcget and rtcset (#1727) 2024-01-06 09:15:59 +01:00
E.T 75ece38725 Dockerfile overhaul (#1726)
Create entrypoint to orchestrate the build steps
Supported commands: make, ninja
Passes additional arguments to the make / ninja command at the end (like -jNN)
There is a shortcut to make -jNN by just specifying -jNN
Anything else will be directly executed (like getting a shell into the container with bash -li is still possible)
2024-01-06 00:42:36 +01:00
E.T b53032a8a5 Ppfw docker build (#1725)
* Docker build ppfw by default

* Allow GH workflows to be triggered from any branch

* Fix asset path for github upload
2024-01-05 21:59:15 +01:00
Mark Thompson e7f8952ece Fix compile error in deprecated app (#1723) 2024-01-05 10:13:43 -06:00
Totoo 82a6ae0791 Geomap speed display (#1722)
* speed to geomap
* Add speed to map
* Fix hidden state
* UI fix on ADSB tx
* UI fixes
2024-01-05 13:44:30 +01:00
Totoo d303098e35 Moved SPainter and GPSSim to ext app (#1721)
* Moved GPSSim
* Moved spainter
2024-01-05 10:46:09 +01:00
Totoo 1a69ce2d97 Accessibility over serial (#1717)
* Initial accessibility support
* added it to some widgets to test
* More widget accessibility response
* More widgets, better output
* Mark selected widget on list
* typo
2024-01-05 07:43:30 +01:00
Erwin Ried eedebe1c52 Update your links (#1719) 2024-01-05 07:43:13 +01:00
E.T 9e61f80809 Create cmake target ppfw (#1716)
* Create cmake target ppfw and oci

It will build an OCI ppfw package to be flashed or shared on test-drive

Additionally some minor cmake cleanup
And updated gitignore to allow multiple build folders

* Make ppfw / oci build default
2024-01-04 22:59:25 +01:00
Mark Thompson ff591c68a0 Updated Flash message to say device will restart (#1715) 2024-01-04 11:47:45 -06:00
Totoo 8761b9d7e0 Send ASCII chars from USB serial to selected widget (#1708)
* Initial commit for keyboard emulation
* Added on_keyboard to some widgets
* TextEdit partly
* Multi key send at once
* Frequency control support
* Fix encoder emulation
* Add keyboard to geomap
* More widgets
2024-01-04 17:36:31 +01:00
Erwin Ried 1b5125b0a8 Adding the new TechMinds video (#1712)
I think it explains very well everything, even if he calls it "Portapak"
2024-01-04 11:28:33 +01:00
E.T 9ba885361d add _OCI to ppfw package name (#1711) 2024-01-04 20:52:42 +13:00
Mark Thompson afa34d83b2 Added Zoom-Out capability to Geomap (#1710) 2024-01-04 07:32:44 +01:00
67 changed files with 1601 additions and 222 deletions
+10 -8
View File
@@ -36,7 +36,12 @@ jobs:
uses: actions/checkout@master
with:
fetch-depth: 0
ref: next
#ref: next
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, uses the default branch.
# https://github.com/actions/checkout
# So scheduled runs will use the default branch (next) but its now possible to trigger a workflow from another branch
submodules: true
- name: Git Sumbodule Update
run: |
@@ -49,22 +54,19 @@ jobs:
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
- name: Create Small SD Card ZIP - No World Map
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard-no-map.zip sdcard
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
- name: Download world map
run: |
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
- name: Unzip world map
run: |
unzip world_map.zip -d sdcard/ADSB
- name: Create Firmware TAR
run: |
mkdir -p firmware_tar/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin firmware_tar/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p firmware_tar/APPS && cp build/firmware/application/*.ppma firmware_tar/APPS && cd firmware_tar && tar -cvaf ../portapack-mayhem.ppfw.tar *
- name: Create Firmware ZIP
run: |
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
- name: Create SD Card ZIP
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard.zip sdcard
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
- name: Create changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -98,8 +100,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./portapack-mayhem.ppfw.tar
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}.ppfw.tar
asset_path: build/firmware/portapack-mayhem.ppfw.tar
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar
asset_content_type: application/x-tar
- name: Upload Firmware Asset
id: upload-firmware-asset
+10 -8
View File
@@ -14,7 +14,12 @@ jobs:
uses: actions/checkout@master
with:
fetch-depth: 0
ref: next
#ref: next
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, uses the default branch.
# https://github.com/actions/checkout
# So scheduled runs will use the default branch (next) but its now possible to trigger a workflow from another branch
submodules: true
- name: Git Sumbodule Update
run: |
@@ -33,22 +38,19 @@ jobs:
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
- name: Create Small SD Card ZIP - No World Map
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard-no-map.zip sdcard
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
- name: Download world map
run: |
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
- name: Unzip world map
run: |
unzip world_map.zip -d sdcard/ADSB
- name: Create Firmware TAR
run: |
mkdir -p firmware_tar/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin firmware_tar/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p firmware_tar/APPS && cp build/firmware/application/*.ppma firmware_tar/APPS && cd firmware_tar && tar -cvaf ../portapack-mayhem.ppfw.tar *
- name: Create Firmware ZIP
run: |
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
- name: Create SD Card ZIP
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard.zip sdcard
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
- name: Create changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -92,8 +94,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./portapack-mayhem.ppfw.tar
asset_name: mayhem_${{ steps.version.outputs.version }}.ppfw.tar
asset_path: build/firmware/portapack-mayhem.ppfw.tar
asset_name: mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar
asset_content_type: application/x-tar
- name: Upload Firmware Asset
id: upload-firmware-asset
+1 -1
View File
@@ -49,7 +49,7 @@
*.map
*.lst
.dep/
build/
/build*
CMakeFiles/
# Debugging
+19 -2
View File
@@ -18,7 +18,7 @@
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.5)
cmake_policy(SET CMP0005 NEW)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
@@ -28,7 +28,7 @@ project(portapack-h1)
set(EXPECTED_GCC_VERSION "9.2.1")
set(VERSION "$ENV{VERSION_STRING}")
if ("$ENV{VERSION_STRING}" STREQUAL "")
if ("${VERSION}" STREQUAL "")
execute_process(
COMMAND git log -n 1 --format=%h
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
@@ -42,6 +42,11 @@ if ("$ENV{VERSION_STRING}" STREQUAL "")
else (GIT_VERSION_FOUND)
set(VERSION "${GIT_VERSION}")
endif (GIT_VERSION_FOUND)
set(VERSION_NOHASH "dev")
else()
set(VERSION_NOHASH "${VERSION}")
set(PPFW_FILENAME "portapack-mayhem.ppfw.tar")
endif()
execute_process(
@@ -50,6 +55,18 @@ execute_process(
)
set(VERSION_MD5 "0x${VERSION_MD5}")
if (NOT DEFINED PPFW_FILENAME)
# Funny business here: trying to generate unique names to prevent renaming of shared test-drive builds by the browser on download to .ppfw-42.tar as it wont be recognized by the flasher
execute_process(
COMMAND date "+%y%m%d-%H%M"
OUTPUT_VARIABLE PPFW_FILENAME
)
string(STRIP ${PPFW_FILENAME} PPFW_FILENAME)
set(PPFW_FILENAME "portapack-mayhem_${PPFW_FILENAME}_OCI.ppfw.tar")
endif()
message("Building version: ${VERSION} MD5: ${VERSION_MD5}")
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
+6 -1
View File
@@ -1,3 +1,6 @@
> [!IMPORTANT]
> *Shortly*, this repository **will be moved** from my personal GitHub [eried/portapack-mayhem](https://github.com/eried/portapack-mayhem) to an organization: [portapack-mayhem/portapack-mayhem](https://github.com/portapack-mayhem/portapack-mayhem). Please keep this in mind to **update your links** accordingly.
# PortaPack Mayhem
[![Build Status](https://travis-ci.com/eried/portapack-mayhem.svg?branch=master)](https://travis-ci.com/eried/portapack-mayhem) [![Nightly Release](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/total)](https://github.com/eried/portapack-mayhem/releases) [![GitHub Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/latest/total)](https://github.com/eried/portapack-mayhem/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://dcbadge.vercel.app/api/server/tuwVMv3?style=flat)](https://discord.gg/tuwVMv3)
@@ -12,7 +15,9 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
If you are new to *HackRF+PortaPack+Mayhem*, check this video:
[![What is?](https://img.youtube.com/vi/alrFbY5vxt4/0.jpg)](https://www.youtube.com/watch?v=alrFbY5vxt4)
[![Beginner's Guide To The HackRF & Portapak With Mayhem](https://img.youtube.com/vi/H-bqdWfbhpg/0.jpg)](https://www.youtube.com/watch?v=H-bqdWfbhpg)
For people familiar with the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware), this one might be interesting:<br>[What is the HackRF One Portapack H2+?](https://www.youtube.com/watch?v=alrFbY5vxt4)
# Frequently Asked Questions
+22 -14
View File
@@ -10,32 +10,40 @@ VOLUME /havoc
WORKDIR /havoc/firmware
# Fetch dependencies from APT
RUN apt-get update && \
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
apt-get -qy autoremove
RUN apt-get update \
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
&& apt-get -qy autoremove \
&& rm -rf /var/lib/apt/lists/*
#Install current pip from PyPa
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
python3 get-pip.py
#Fetch additional dependencies from Python 3.x pip
RUN pip install pyyaml
RUN ln -s /usr/bin/python3 /usr/bin/python && \
ln -s /usr/bin/pip3 /usr/bin/pip
RUN pip install pyyaml \
&& ln -s /usr/bin/python3 /usr/bin/python \
&& ln -s /usr/bin/pip3 /usr/bin/pip
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
# Grab the GNU ARM toolchain from arm.com
# Then extract contents to /opt/build/armbin/
RUN mkdir /opt/build && cd /opt/build && \
wget -O gcc-arm-none-eabi $ARMBINURL && \
mkdir armbin && \
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
RUN mkdir /opt/build \
&& cd /opt/build \
&& wget -O gcc-arm-none-eabi $ARMBINURL \
&& mkdir armbin \
&& tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
# Configure CCACHE
RUN mkdir ~/bin && cd ~/bin && \
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
RUN mkdir ~/bin \
&& cd ~/bin \
&& for tool in gcc g++ cpp c++; do \
ln -s $(which ccache) arm-none-eabi-$tool \
; done
CMD cd .. && cd build && \
cmake .. && make firmware
ADD firmware/tools/docker-entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# replace make with ninja temporarily while building your image if you prefer to use that by default
CMD ["make"]
+1 -1
View File
@@ -24,4 +24,4 @@ RUN mkdir ~/bin && cd ~/bin && \
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
CMD cd .. && cd build && \
cmake .. && make firmware
cmake .. && make ppfw
+22 -14
View File
@@ -10,32 +10,40 @@ VOLUME /havoc
WORKDIR /havoc/firmware
# Fetch dependencies from APT
RUN apt-get update && \
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
apt-get -qy autoremove
RUN apt-get update \
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
&& apt-get -qy autoremove \
&& rm -rf /var/lib/apt/lists/*
#Install current pip from PyPa
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
python3 get-pip.py
#Fetch additional dependencies from Python 3.x pip
RUN pip install pyyaml
RUN ln -s /usr/bin/python3 /usr/bin/python && \
ln -s /usr/bin/pip3 /usr/bin/pip
RUN pip install pyyaml \
&& ln -s /usr/bin/python3 /usr/bin/python \
&& ln -s /usr/bin/pip3 /usr/bin/pip
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
# Grab the GNU ARM toolchain from arm.com
# Then extract contents to /opt/build/armbin/
RUN mkdir /opt/build && cd /opt/build && \
wget -O gcc-arm-none-eabi $ARMBINURL && \
mkdir armbin && \
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
RUN mkdir /opt/build \
&& cd /opt/build \
&& wget -O gcc-arm-none-eabi $ARMBINURL \
&& mkdir armbin \
&& tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
# Configure CCACHE
RUN mkdir ~/bin && cd ~/bin && \
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
RUN mkdir ~/bin \
&& cd ~/bin \
&& for tool in gcc g++ cpp c++; do \
ln -s $(which ccache) arm-none-eabi-$tool \
; done
CMD cd .. && cd build && \
cmake .. && make firmware
ADD firmware/tools/docker-entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# replace make with ninja temporarily while building your image if you prefer to use that by default
CMD ["make"]
+29 -1
View File
@@ -18,6 +18,8 @@
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 3.5)
project(firmware)
set(BASEBAND ${PROJECT_SOURCE_DIR}/baseband)
@@ -59,7 +61,7 @@ add_custom_command(
)
add_custom_target(
firmware ALL
firmware
DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME}
)
@@ -88,6 +90,21 @@ add_custom_target(
DEPENDS program
)
add_custom_command(
OUTPUT ${PPFW_FILENAME}
COMMAND rm -rf firmware_tar
COMMAND mkdir -p firmware_tar/FIRMWARE
# Using VERSION_NOHASH to avoid dev builds piling up in the FIRMWARE folder of the sd card of testers in #test-drive
COMMAND cp ${FIRMWARE_FILENAME} firmware_tar/FIRMWARE/portapack-mayhem_${VERSION_NOHASH}.bin
COMMAND mkdir -p firmware_tar/APPS
COMMAND cp application/*.ppma firmware_tar/APPS
COMMAND cd firmware_tar && tar -cvaf ../${PPFW_FILENAME} *
DEPENDS firmware
# Dont use VERBATIM here as it prevents usage of globbing (*)
# There shouldnt be any funny business in the filenames above :)
)
# TODO: Bad hack to fix location of LICENSE file for tar.
add_custom_command(
OUTPUT ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
@@ -107,7 +124,18 @@ add_custom_command(
DEPENDS ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
)
add_custom_target(
ppfw ALL
DEPENDS ${PPFW_FILENAME}
)
add_custom_target(
oci
DEPENDS ${PPFW_FILENAME}
)
add_custom_target(
release
DEPENDS MD5SUMS SHA256SUMS
)
+4 -4
View File
@@ -265,7 +265,7 @@ set(CPPSRC
apps/ble_tx_app.cpp
apps/capture_app.cpp
apps/ert_app.cpp
apps/gps_sim_app.cpp
# apps/gps_sim_app.cpp
# apps/lge_app.cpp
apps/pocsag_app.cpp
# apps/replay_app.cpp
@@ -311,9 +311,9 @@ set(CPPSRC
apps/ui_settings.cpp
apps/ui_siggen.cpp
apps/ui_sonde.cpp
apps/ui_spectrum_painter_image.cpp
apps/ui_spectrum_painter_text.cpp
apps/ui_spectrum_painter.cpp
# apps/ui_spectrum_painter_image.cpp
# apps/ui_spectrum_painter_text.cpp
# apps/ui_spectrum_painter.cpp
apps/ui_ss_viewer.cpp
apps/ui_sstvtx.cpp
apps/ui_subghzd.cpp
+2 -1
View File
@@ -293,6 +293,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
ais::format::text(entry_.name),
0,
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::NONE,
ais::format::latlon_float(entry_.last_position.latitude.normalized()),
ais::format::latlon_float(entry_.last_position.longitude.normalized()),
entry_.last_position.true_heading,
@@ -315,7 +316,7 @@ AISRecentEntryDetailView& AISRecentEntryDetailView::operator=(const AISRecentEnt
void AISRecentEntryDetailView::update_position() {
if (send_updates)
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading, 0);
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading, 0, entry_.last_position.speed_over_ground);
}
void AISRecentEntryDetailView::focus() {
+2 -1
View File
@@ -266,6 +266,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
get_map_tag(entry_),
entry_.pos.altitude,
GeoPos::alt_unit::FEET,
GeoPos::spd_unit::MPH,
entry_.pos.latitude,
entry_.pos.longitude,
entry_.velo.heading);
@@ -290,7 +291,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
} else if (geomap_view_) {
// Map is showing, update the current item.
geomap_view_->update_tag(get_map_tag(entry_));
geomap_view_->update_position(entry.pos.latitude, entry.pos.longitude, entry.velo.heading, entry.pos.altitude);
geomap_view_->update_position(entry.pos.latitude, entry.pos.longitude, entry.velo.heading, entry.pos.altitude, entry.velo.speed);
} else {
// Details is showing, update details.
refresh_ui();
+3 -1
View File
@@ -85,10 +85,12 @@ ADSBPositionView::ADSBPositionView(
nav.push<GeoMapView>(
geopos.altitude(),
GeoPos::alt_unit::FEET,
GeoPos::spd_unit::HIDDEN,
geopos.lat(),
geopos.lon(),
[this](int32_t altitude, float lat, float lon) {
[this](int32_t altitude, float lat, float lon, int32_t speed) {
geopos.set_altitude(altitude);
geopos.set_speed(speed);
geopos.set_lat(lat);
geopos.set_lon(lon);
});
+2 -1
View File
@@ -58,7 +58,8 @@ class ADSBPositionView : public OptionTabView {
private:
GeoPos geopos{
{0, 2 * 16},
GeoPos::FEET};
GeoPos::FEET,
GeoPos::HIDDEN};
Button button_set_map{
{8 * 8, 6 * 16, 14 * 8, 2 * 16},
+2 -1
View File
@@ -317,7 +317,7 @@ void APRSDetailsView::update() {
}
if (send_updates)
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0);
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0, 0);
}
APRSDetailsView::~APRSDetailsView() {
@@ -339,6 +339,7 @@ APRSDetailsView::APRSDetailsView(
entry_copy.source_formatted,
0, // entry_copy.pos.altitude,
GeoPos::alt_unit::FEET,
GeoPos::spd_unit::HIDDEN,
entry_copy.pos.latitude,
entry_copy.pos.longitude,
0, /*entry_copy.velo.heading,*/
+12 -10
View File
@@ -48,7 +48,7 @@ void DfuMenu::paint(Painter& painter) {
text_info_line_3.set(to_string_dec_uint(utilisation, 6));
text_info_line_4.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6));
text_info_line_5.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6));
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_cpu_usage, 6));
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
text_info_line_7.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
text_info_line_8.set(to_string_dec_uint(chTimeNow() / 1000, 6));
@@ -94,23 +94,25 @@ DfuMenu2::DfuMenu2(NavigationView& nav)
&text_info_line_7,
&text_info_line_8,
&text_info_line_9,
&text_info_line_10});
&text_info_line_10,
&text_info_line_11});
}
void DfuMenu2::paint(Painter& painter) {
text_info_line_1.set(to_string_dec_uint(portapack::receiver_model.target_frequency(), 10));
text_info_line_2.set(to_string_dec_uint(portapack::receiver_model.baseband_bandwidth(), 10));
text_info_line_3.set(to_string_dec_uint(portapack::receiver_model.sampling_rate(), 10));
text_info_line_4.set(to_string_dec_uint((uint32_t)portapack::receiver_model.modulation(), 10));
text_info_line_5.set(to_string_dec_uint(portapack::receiver_model.am_configuration(), 10));
text_info_line_6.set(to_string_dec_uint(portapack::receiver_model.nbfm_configuration(), 10));
text_info_line_7.set(to_string_dec_uint(portapack::receiver_model.wfm_configuration(), 10));
text_info_line_8.set(to_string_dec_uint(portapack::transmitter_model.target_frequency(), 10));
text_info_line_9.set(to_string_dec_uint(portapack::transmitter_model.baseband_bandwidth(), 10));
text_info_line_10.set(to_string_dec_uint(portapack::transmitter_model.sampling_rate(), 10));
text_info_line_4.set(to_string_dec_uint(((uint32_t)shared_memory.m4_performance_counter) * 100 / 127, 10));
text_info_line_5.set(to_string_dec_uint((uint32_t)portapack::receiver_model.modulation(), 10));
text_info_line_6.set(to_string_dec_uint(portapack::receiver_model.am_configuration(), 10));
text_info_line_7.set(to_string_dec_uint(portapack::receiver_model.nbfm_configuration(), 10));
text_info_line_8.set(to_string_dec_uint(portapack::receiver_model.wfm_configuration(), 10));
text_info_line_9.set(to_string_dec_uint(portapack::transmitter_model.target_frequency(), 10));
text_info_line_10.set(to_string_dec_uint(portapack::transmitter_model.baseband_bandwidth(), 10));
text_info_line_11.set(to_string_dec_uint(portapack::transmitter_model.sampling_rate(), 10));
constexpr auto margin = 5;
constexpr auto lines = 10 + 2;
constexpr auto lines = 11 + 2;
painter.fill_rectangle(
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
+10 -7
View File
@@ -83,13 +83,15 @@ class DfuMenu2 : public View {
{{5 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "RX Freq:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "RX BW:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "RX SampR:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "Modulatn:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "AM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "NBFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "WFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "TX Freq:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "TX BW:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "TX SampR:", Color::dark_cyan()}};
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "RX Satu%:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "Modulatn:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "AM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "NBFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "WFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "TX Freq:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "TX BW:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "TX SampR:", Color::dark_cyan()},
};
Text text_info_line_1{{14 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_2{{14 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
@@ -101,6 +103,7 @@ class DfuMenu2 : public View {
Text text_info_line_8{{14 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_9{{14 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_10{{14 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_11{{14 * CHARACTER_WIDTH, 15 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
};
} /* namespace ui */
@@ -121,7 +121,7 @@ void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
painter.draw_string({12, 24}, this->nav_.style(), "This will take 15 seconds.");
painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LEDs RX");
painter.draw_string({12, 84}, this->nav_.style(), "and TX are flashing.");
painter.draw_string({12, 124}, this->nav_.style(), "Then restart the device.");
painter.draw_string({12, 124}, this->nav_.style(), "Device will then restart.");
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2);
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
+2 -3
View File
@@ -78,8 +78,6 @@ void NuoptixView::transmit(bool setup) {
}
transmitter_model.set_rf_amp(true);
transmitter_model.set_lna(40);
transmitter_model.set_vga(40);
transmitter_model.enable();
dtmf_message[0] = '*'; // "Pre-tone for restart" method #1
@@ -136,6 +134,7 @@ void NuoptixView::transmit(bool setup) {
shared_memory.bb_data.tones_data.silence = NUOPTIX_TONE_LENGTH; // 49ms tone, 49ms space
audio::set_rate(audio::Rate::Hz_24000);
baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, 6 * 2, true, true);
timecode++;
@@ -156,7 +155,7 @@ NuoptixView::NuoptixView(
tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.target_frequency(f);
transmitter_model.set_target_frequency(f);
};
};
+1
View File
@@ -94,6 +94,7 @@ SondeView::SondeView(NavigationView& nav)
sonde_id,
gps_info.alt,
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::HIDDEN,
gps_info.lat,
gps_info.lon,
999); // set a dummy heading out of range to draw a cross...probably not ideal?
+2 -1
View File
@@ -162,7 +162,8 @@ class SondeView : public View {
GeoPos geopos{
{0, 12 * 16},
GeoPos::alt_unit::METERS};
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::HIDDEN};
Button button_see_qr{
{2 * 8, 15 * 16, 12 * 8, 3 * 16},
+81 -2
View File
@@ -113,7 +113,6 @@ void EventDispatcher::run() {
while (is_running) {
const auto events = wait();
dispatch(events);
portapack::usb_serial.dispatch();
}
}
@@ -151,6 +150,8 @@ void EventDispatcher::dispatch(const eventmask_t events) {
*(uint32_t*)&shared_memory.bb_data.data[4]);
}
handle_shell();
if (events & EVT_MASK_APPLICATION) {
handle_application_queue();
}
@@ -163,6 +164,10 @@ void EventDispatcher::dispatch(const eventmask_t events) {
handle_rtc_tick();
}
if (events & EVT_MASK_USB) {
handle_usb();
}
if (events & EVT_MASK_SWITCHES) {
handle_switches();
}
@@ -216,6 +221,30 @@ void EventDispatcher::handle_rtc_tick() {
portapack::persistent_memory::cache::persist();
}
void EventDispatcher::handle_usb() {
portapack::usb_serial.dispatch();
}
void EventDispatcher::handle_shell() {
if (waiting_for_shellmode) {
waiting_for_shellmode = false;
shellmode_active = true;
while (shellmode_active) {
chThdSleepMilliseconds(5);
}
}
if (injected_touch_event != nullptr) {
on_touch_event(*injected_touch_event);
injected_touch_event = nullptr;
}
if (injected_keyboard_event != nullptr) {
on_keyboard_event(*injected_keyboard_event);
injected_keyboard_event = nullptr;
}
}
ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent event) {
if (!w->hidden()) {
// To achieve reverse depth ordering (last object drawn is
@@ -239,7 +268,25 @@ ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent ev
}
void EventDispatcher::emulateTouch(ui::TouchEvent event) {
on_touch_event(event);
injected_touch_event = &event;
while (injected_touch_event != nullptr) {
chThdSleepMilliseconds(5);
}
}
void EventDispatcher::emulateKeyboard(ui::KeyboardEvent event) {
injected_keyboard_event = &event;
while (injected_keyboard_event != nullptr) {
chThdSleepMilliseconds(5);
}
}
void EventDispatcher::on_keyboard_event(ui::KeyboardEvent event) {
// send the key to focused widget, or parent if not accepts it
auto target = context.focus_manager().focus_widget();
while ((target != nullptr) && !target->on_keyboard(event)) {
target = target->parent();
}
}
void EventDispatcher::on_touch_event(ui::TouchEvent event) {
@@ -262,7 +309,17 @@ void EventDispatcher::on_touch_event(ui::TouchEvent event) {
}
}
ui::Widget* EventDispatcher::getTopWidget() {
return top_widget;
}
ui::Widget* EventDispatcher::getFocusedWidget() {
return context.focus_manager().focus_widget();
}
void EventDispatcher::handle_lcd_frame_sync() {
bool waiting_for_frame = this->waiting_for_frame;
DisplayFrameSyncMessage message;
message_map.send(&message);
@@ -270,6 +327,28 @@ void EventDispatcher::handle_lcd_frame_sync() {
painter.paint_widget_tree(top_widget);
portapack::backlight()->on();
if (waiting_for_frame)
this->waiting_for_frame = false;
}
void EventDispatcher::wait_finish_frame() {
waiting_for_frame = true;
while (waiting_for_frame) {
chThdSleepMilliseconds(5);
}
}
void EventDispatcher::enter_shell_working_mode() {
waiting_for_shellmode = true;
while (waiting_for_shellmode) {
chThdSleepMilliseconds(5);
}
}
void EventDispatcher::exit_shell_working_mode() {
shellmode_active = false;
}
void EventDispatcher::handle_switches() {
+17
View File
@@ -45,6 +45,7 @@ constexpr auto EVT_MASK_ENCODER = EVENT_MASK(4);
constexpr auto EVT_MASK_TOUCH = EVENT_MASK(5);
constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6);
constexpr auto EVT_MASK_LOCAL = EVENT_MASK(7);
constexpr auto EVT_MASK_USB = EVENT_MASK(8);
class EventDispatcher {
public:
@@ -87,6 +88,14 @@ class EventDispatcher {
}
void emulateTouch(ui::TouchEvent event);
void emulateKeyboard(ui::KeyboardEvent event);
void wait_finish_frame();
void enter_shell_working_mode();
void exit_shell_working_mode();
ui::Widget* getTopWidget();
ui::Widget* getFocusedWidget();
private:
static Thread* thread_event_loop;
@@ -100,6 +109,11 @@ class EventDispatcher {
bool sd_card_present = false;
static bool display_sleep;
bool in_key_event = false;
volatile bool waiting_for_frame = false;
volatile bool waiting_for_shellmode = false;
volatile bool shellmode_active = false;
ui::TouchEvent* volatile injected_touch_event = nullptr;
ui::KeyboardEvent* volatile injected_keyboard_event = nullptr;
eventmask_t wait();
void dispatch(const eventmask_t events);
@@ -107,12 +121,15 @@ class EventDispatcher {
void handle_application_queue();
void handle_local_queue();
void handle_rtc_tick();
void handle_usb();
void handle_shell();
static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event);
ui::Widget* captured_widget{nullptr};
void on_touch_event(ui::TouchEvent event);
void on_keyboard_event(ui::KeyboardEvent event);
// void blink_timer();
void handle_lcd_frame_sync();
+12
View File
@@ -44,6 +44,16 @@ set(EXTCPPSRC
#lcr
external/jammer/main.cpp
external/jammer/ui_jammer.cpp
#gpssim
external/gpssim/main.cpp
external/gpssim/gps_sim_app.cpp
#spainter
external/spainter/main.cpp
external/spainter/ui_spectrum_painter.cpp
external/spainter/ui_spectrum_painter_text.cpp
external/spainter/ui_spectrum_painter_image.cpp
)
set(EXTAPPLIST
@@ -58,4 +68,6 @@ set(EXTAPPLIST
lge
lcr
jammer
gpssim
spainter
)
+16 -2
View File
@@ -28,6 +28,8 @@ MEMORY
ram_external_app_lge(rwx) : org = 0xEEF10000, len = 32k
ram_external_app_lcr(rwx) : org = 0xEEF20000, len = 32k
ram_external_app_jammer(rwx) : org = 0xEEF30000, len = 32k
ram_external_app_gpssim(rwx) : org = 0xEEF40000, len = 32k
ram_external_app_spainter(rwx) : org = 0xEEF50000, len = 32k
}
SECTIONS
@@ -88,18 +90,30 @@ SECTIONS
} > ram_external_app_lge
.external_app_lcr : ALIGN(4) SUBALIGN(4)
.external_app_lcr : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_lcr.application_information));
*(*ui*external_app*lcr*);
} > ram_external_app_lcr
.external_app_jammer : ALIGN(4) SUBALIGN(4)
.external_app_jammer : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_jammer.application_information));
*(*ui*external_app*jammer*);
} > ram_external_app_jammer
.external_app_gpssim : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_gpssim.application_information));
*(*ui*external_app*gpssim*);
} > ram_external_app_gpssim
.external_app_spainter : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_spainter.application_information));
*(*ui*external_app*spainter*);
} > ram_external_app_spainter
}
@@ -36,7 +36,7 @@
using namespace portapack;
namespace fs = std::filesystem;
namespace ui {
namespace ui::external_app::gpssim {
void GpsSimAppView::set_ready() {
ready_signal = true;
@@ -161,7 +161,8 @@ void GpsSimAppView::handle_replay_thread_done(const uint32_t return_code) {
GpsSimAppView::GpsSimAppView(
NavigationView& nav)
: nav_(nav) {
baseband::run_image(portapack::spi_flash::image_tag_gps);
// baseband::run_image(portapack::spi_flash::image_tag_gps);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({
&button_open,
@@ -211,4 +212,4 @@ void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) {
waterfall.set_parent_rect(waterfall_rect);
}
} /* namespace ui */
} /* namespace ui::external_app::gpssim */
@@ -25,6 +25,7 @@
#define __GPS_SIM_APP_HPP__
#include "app_settings.hpp"
#include "ui_language.hpp"
#include "radio_state.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
@@ -37,7 +38,7 @@
#include <string>
#include <memory>
namespace ui {
namespace ui::external_app::gpssim {
class GpsSimAppView : public View {
public:
@@ -108,7 +109,7 @@ class GpsSimAppView : public View {
Checkbox check_loop{
{21 * 8, 2 * 16},
4,
"Loop",
LanguageHelper::currentMessages[LANG_LOOP],
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
@@ -142,6 +143,6 @@ class GpsSimAppView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::gpssim */
#endif /*__GPS_SIM_APP_HPP__*/
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "gps_sim_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::gpssim {
void initialize_app(ui::NavigationView& nav) {
nav.push<GpsSimAppView>();
}
} // namespace ui::external_app::gpssim
extern "C" {
__attribute__((section(".external_app.app_gpssim.application_information"), used)) application_information_t _application_information_gpssim = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::gpssim::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "GPSSim",
/*.bitmap_data = */ {
0xC0,
0x07,
0xE0,
0x0F,
0x70,
0x1F,
0x78,
0x3E,
0x78,
0x3C,
0x78,
0x38,
0x78,
0x30,
0x78,
0x38,
0x78,
0x3C,
0x70,
0x1E,
0x70,
0x1F,
0xE0,
0x0F,
0xC0,
0x07,
0x80,
0x03,
0x20,
0x09,
0x50,
0x14,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_gpssim */ {'P', 'G', 'P', 'S'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+21 -7
View File
@@ -205,11 +205,13 @@ class Sprite {
byte who;
byte _speed;
byte dir;
byte lastDir;
byte phase;
// Sprite bits
byte palette2; // 4->16 color map index
byte bits; // index of sprite bits
signed char sy;
void Init(const byte* s) {
@@ -274,9 +276,17 @@ class Sprite {
f = pgm_read_byte(_pacLeftAnim + f);
else if (dir == MRight)
f = pgm_read_byte(_pacRightAnim + f);
else
else if (dir == MDown || dir == MUp)
f = pgm_read_byte(_pacVAnim + f);
if (dir == MUp)
else if (dir == MStopped) {
if (lastDir == MLeft)
f = pgm_read_byte(_pacLeftAnim + f);
else if (lastDir == MRight)
f = pgm_read_byte(_pacRightAnim + f);
else if (lastDir == MDown || lastDir == MUp)
f = pgm_read_byte(_pacVAnim + f);
}
if ((dir == MUp) || (dir == MStopped && lastDir == MUp))
sy = -1;
bits = f + PACMANSPRITE;
}
@@ -838,15 +848,19 @@ class Playfield {
else if (DEMO == 0 && s->who == PACMAN && choice[3] < 0x7FFF && but_RIGHT)
dir = MRight;
else if (DEMO == 0 && choice[0] < 0x7FFF && s->who == PACMAN && dir == MUp)
else if (DEMO == 0 && choice[0] < 0x7FFF && s->who == PACMAN && dir == MUp) {
dir = MUp;
else if (DEMO == 0 && choice[1] < 0x7FFF && s->who == PACMAN && dir == MLeft)
s->lastDir = MUp;
} else if (DEMO == 0 && choice[1] < 0x7FFF && s->who == PACMAN && dir == MLeft) {
dir = MLeft;
else if (DEMO == 0 && choice[2] < 0x7FFF && s->who == PACMAN && dir == MDown)
s->lastDir = MLeft;
} else if (DEMO == 0 && choice[2] < 0x7FFF && s->who == PACMAN && dir == MDown) {
dir = MDown;
else if (DEMO == 0 && choice[3] < 0x7FFF && s->who == PACMAN && dir == MRight)
s->lastDir = MDown;
} else if (DEMO == 0 && choice[3] < 0x7FFF && s->who == PACMAN && dir == MRight) {
dir = MRight;
else if ((DEMO == 0 && s->who != PACMAN) || DEMO == 1) {
s->lastDir = MRight;
} else if ((DEMO == 0 && s->who != PACMAN) || DEMO == 1) {
// Don't choose opposite of current direction?
int16_t dist = choice[4 - dir]; // favor current direction
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_spectrum_painter.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::spainter {
void initialize_app(ui::NavigationView& nav) {
nav.push<SpectrumPainterView>();
}
} // namespace ui::external_app::spainter
extern "C" {
__attribute__((section(".external_app.app_spainter.application_information"), used)) application_information_t _application_information_spainter = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::spainter::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "S.Painter",
/*.bitmap_data = */ {
0xFE,
0x3F,
0xFF,
0x3F,
0xFF,
0xFF,
0xFF,
0xBF,
0xFE,
0xBF,
0x00,
0x80,
0x80,
0xFF,
0x80,
0x00,
0x80,
0x00,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
},
/*.icon_color = */ ui::Color::orange().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_spainter */ {'P', 'S', 'P', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -31,12 +31,13 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::spainter {
SpectrumPainterView::SpectrumPainterView(
NavigationView& nav)
: nav_(nav) {
baseband::run_image(spi_flash::image_tag_spectrum_painter);
// baseband::run_image(spi_flash::image_tag_spectrum_painter);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({
&labels,
@@ -198,4 +199,4 @@ void SpectrumPainterView::paint(Painter& painter) {
}
}
} // namespace ui
} // namespace ui::external_app::spainter
@@ -22,6 +22,7 @@
#pragma once
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
@@ -37,7 +38,7 @@
#include "ui_spectrum_painter_image.hpp"
#include "ui_spectrum_painter_text.hpp"
namespace ui {
namespace ui::external_app::spainter {
class SpectrumPainterView : public View {
public:
@@ -115,7 +116,7 @@ class SpectrumPainterView : public View {
Checkbox check_loop{
{21 * 8, footer_location + 1 * 16},
4,
"Loop",
LanguageHelper::currentMessages[LANG_LOOP],
true};
ImageButton button_play{
@@ -163,4 +164,4 @@ class SpectrumPainterView : public View {
}};
};
} // namespace ui
} // namespace ui::external_app::spainter
@@ -29,7 +29,7 @@
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
namespace ui {
namespace ui::external_app::spainter {
SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
hidden(true);
@@ -254,4 +254,4 @@ void SpectrumInputImageView::paint(Painter& painter) {
}
}
} // namespace ui
} // namespace ui::external_app::spainter
@@ -32,7 +32,7 @@
#include "portapack.hpp"
#include "message.hpp"
namespace ui {
namespace ui::external_app::spainter {
class SpectrumInputImageView : public View {
public:
@@ -63,4 +63,4 @@ class SpectrumInputImageView : public View {
bool drawBMP_scaled(const ui::Rect r, const std::string file);
};
} // namespace ui
} // namespace ui::external_app::spainter
@@ -30,7 +30,7 @@
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
namespace ui {
namespace ui::external_app::spainter {
SpectrumInputTextView::SpectrumInputTextView(NavigationView& nav) {
hidden(true);
@@ -105,4 +105,4 @@ std::vector<uint8_t> SpectrumInputTextView::get_line(uint16_t y) {
return data;
}
} // namespace ui
} // namespace ui::external_app::spainter
@@ -32,7 +32,7 @@
#include "portapack.hpp"
#include "message.hpp"
namespace ui {
namespace ui::external_app::spainter {
class SpectrumInputTextView : public View {
public:
@@ -109,4 +109,4 @@ class SpectrumInputTextView : public View {
"Set message"};
};
} // namespace ui
} // namespace ui::external_app::spainter
+5 -4
View File
@@ -171,12 +171,13 @@ static bool encoder_update(const uint8_t raw) {
}
static bool encoder_read() {
const auto delta = encoder.update(
encoder_debounce[0].state() | (injected_encoder == 1),
encoder_debounce[1].state() | (injected_encoder == 2));
auto delta = encoder.update(encoder_debounce[0].state(), encoder_debounce[1].state());
if (injected_encoder > 0)
if (injected_encoder > 0) {
if (injected_encoder == 1) delta = -1;
if (injected_encoder == 2) delta = 1;
injected_encoder = 0;
}
if (delta != 0) {
encoder_position += delta;
+2 -10
View File
@@ -116,16 +116,8 @@ void set_direction(const rf::Direction new_direction) {
/* TODO: Refactor all the various "Direction" enumerations into one. */
/* TODO: Only make changes if direction changes, but beware of clock enabling. */
// Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction.
// Use CPLD's EEPROM config when transmitting
// Use the SRAM overlay when receiving
if (direction != new_direction) {
if (new_direction == rf::Direction::Transmit)
hackrf::cpld::init_from_eeprom();
else
// Prevents ghosting when switching back to RX from TX mode.
hackrf::cpld::load_sram_no_verify();
}
// Prevents ghosting when switching back to RX from TX mode.
hackrf::cpld::load_sram_no_verify();
direction = new_direction;
+1 -1
View File
@@ -36,7 +36,7 @@
* @brief Shell maximum arguments per command.
*/
#if !defined(SHELL_MAX_ARGUMENTS) || defined(__DOXYGEN__)
#define SHELL_MAX_ARGUMENTS 4
#define SHELL_MAX_ARGUMENTS 6
#endif
/**
+22
View File
@@ -92,6 +92,28 @@ void FreqManUIList::on_blur() {
set_dirty();
}
bool FreqManUIList::on_keyboard(const KeyboardEvent key) {
if (!db_ || db_->empty())
return false;
auto delta = 0;
if (key == '-' && get_index() > 0) delta = -1;
if (key == '+' && get_index() < db_->entry_count() - 1) delta = 1;
if (delta != 0) {
adjust_selected_index(delta);
set_dirty();
return true;
}
if (key == 10) {
if (on_select) {
on_select(get_index());
return true;
}
}
return false;
}
bool FreqManUIList::on_key(const KeyEvent key) {
if (!db_ || db_->empty())
return false;
+2
View File
@@ -50,6 +50,8 @@ class FreqManUIList : public Widget {
void on_blur() override;
bool on_key(const KeyEvent key) override;
bool on_encoder(EncoderEvent delta) override;
bool on_keyboard(const KeyboardEvent event) override;
void set_parent_rect(Rect new_parent_rect) override;
void set_index(size_t index);
+117 -43
View File
@@ -38,13 +38,17 @@ namespace ui {
GeoPos::GeoPos(
const Point pos,
const alt_unit altitude_unit)
: altitude_unit_(altitude_unit) {
const alt_unit altitude_unit,
const spd_unit speed_unit)
: altitude_unit_(altitude_unit), speed_unit_(speed_unit) {
set_parent_rect({pos, {30 * 8, 3 * 16}});
add_children({&labels_position,
&label_spd_position,
&field_altitude,
&field_speed,
&text_alt_unit,
&text_speed_unit,
&field_lat_degrees,
&field_lat_minutes,
&field_lat_seconds,
@@ -56,6 +60,7 @@ GeoPos::GeoPos(
// Defaults
set_altitude(0);
set_speed(0);
set_lat(0);
set_lon(0);
@@ -67,10 +72,11 @@ GeoPos::GeoPos(
text_lon_decimal.set(to_string_decimal(lon_value, 5));
if (on_change && report_change)
on_change(altitude(), lat_value, lon_value);
on_change(altitude(), lat_value, lon_value, speed());
};
field_altitude.on_change = changed_fn;
field_speed.on_change = changed_fn;
field_lat_degrees.on_change = changed_fn;
field_lat_minutes.on_change = changed_fn;
field_lat_seconds.on_change = changed_fn;
@@ -79,11 +85,19 @@ GeoPos::GeoPos(
field_lon_seconds.on_change = changed_fn;
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
if (speed_unit_ == KMPH) text_speed_unit.set("kmph");
if (speed_unit_ == MPH) text_speed_unit.set("mph");
if (speed_unit_ == HIDDEN) {
text_speed_unit.hidden(true);
label_spd_position.hidden(true);
field_speed.hidden(true);
}
}
void GeoPos::set_read_only(bool v) {
// only setting altitude to read-only (allow manual panning via lat/lon fields)
field_altitude.set_focusable(!v);
field_speed.set_focusable(!v);
}
// Stupid hack to avoid an event loop
@@ -98,14 +112,18 @@ void GeoPos::focus() {
field_lat_degrees.focus();
}
void GeoPos::hide_altitude() {
void GeoPos::hide_altandspeed() {
// Color altitude grey to indicate it's not updated in manual panning mode
field_altitude.set_style(&Styles::grey);
field_speed.set_style(&Styles::grey);
}
void GeoPos::set_altitude(int32_t altitude) {
field_altitude.set_value(altitude);
}
void GeoPos::set_speed(int32_t speed) {
field_speed.set_value(speed);
}
void GeoPos::set_lat(float lat) {
field_lat_degrees.set_value(lat);
@@ -139,21 +157,38 @@ int32_t GeoPos::altitude() {
return field_altitude.value();
};
int32_t GeoPos::speed() {
return field_speed.value();
};
GeoMap::GeoMap(
Rect parent_rect)
: Widget{parent_rect}, markerListLen(0) {
}
bool GeoMap::on_encoder(const EncoderEvent delta) {
if ((delta > 0) && (map_zoom < 5)) {
map_zoom++;
// Valid map_zoom values are -2 to -MAX_MAP_ZOOM_OUT, and +1 to +MAX_MAP_ZOOM_IN (values of 0 and -1 are not permitted)
if (delta > 0) {
if (map_zoom < MAX_MAP_ZOOM_IN) {
if (map_zoom == -2) {
map_zoom = 1;
} else {
map_zoom++;
// Ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
if (geomap_rect_width % map_zoom != 0) {
map_zoom--;
// When zooming in, ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
if ((map_zoom > 1) && (geomap_rect_width % map_zoom != 0)) {
map_zoom--;
}
}
}
} else if (delta < 0) {
if (map_zoom > -MAX_MAP_ZOOM_OUT) {
if (map_zoom == 1) {
map_zoom = -2;
} else {
map_zoom--;
}
}
} else if ((delta < 0) && (map_zoom > 1)) {
map_zoom--;
} else {
return false;
}
@@ -164,17 +199,31 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
return true;
}
void GeoMap::map_zoom_line(ui::Color* buffer) {
if (map_zoom <= 1) {
return;
}
void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
if (map_zoom == 1) {
map_file.read(buffer, pixels << 1);
} else if (map_zoom > 1) {
map_file.read(buffer, (pixels / map_zoom) << 1);
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
// For 240 width, than means no check is needed for map_zoom values up to 6
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
for (int j = 0; j < map_zoom; j++) {
buffer[(i * map_zoom) + j] = buffer[i];
// Zoom in: Expand each pixel to "map_zoom" number of pixels.
// Future TODO: Add dithering to smooth out the pixelation.
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
// For 240 width, than means no check is needed for map_zoom values up to 6
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
for (int j = 0; j < map_zoom; j++) {
buffer[(i * map_zoom) + j] = buffer[i];
}
}
} else {
ui::Color* zoom_out_buffer = new ui::Color[(pixels * (-map_zoom))];
map_file.read(zoom_out_buffer, (pixels * (-map_zoom)) << 1);
// Zoom out: Collapse each group of "-map_zoom" pixels into one pixel.
// Future TODO: Average each group of pixels (in both X & Y directions if possible).
for (int i = 0; i < geomap_rect_width; i++) {
buffer[i] = zoom_out_buffer[i * (-map_zoom)];
}
delete zoom_out_buffer;
}
}
@@ -187,9 +236,12 @@ void GeoMap::draw_markers(Painter& painter) {
int x = (map_width * (item.lon + 180) / 360) - x_pos;
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
if (map_zoom != 1) {
if (map_zoom > 1) {
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2);
y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2);
} else if (map_zoom < 0) {
x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2);
y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2);
}
if ((x >= 0) && (x < r.width()) &&
@@ -208,31 +260,39 @@ void GeoMap::draw_markers(Painter& painter) {
}
void GeoMap::paint(Painter& painter) {
uint16_t line, j;
uint32_t zoom_seek_x, zoom_seek_y;
std::array<ui::Color, 240> map_line_buffer;
const auto r = screen_rect();
std::array<ui::Color, 240> map_line_buffer;
// Ony redraw map if it moved by at least 1 pixel
// or the markers list was updated
int x_diff = abs(x_pos - prev_x_pos);
int y_diff = abs(y_pos - prev_y_pos);
if (markerListUpdated || (x_diff >= map_zoom * 3) || (y_diff >= map_zoom * 3)) {
if (map_zoom == 1) {
zoom_seek_x = zoom_seek_y = 0;
} else {
zoom_seek_x = (r.width() - (r.width() / map_zoom)) / 2;
zoom_seek_y = (r.height() - (r.height() / map_zoom)) / 2;
if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) {
int32_t zoom_seek_x = x_pos;
int32_t zoom_seek_y = y_pos;
// Adjust starting corner position of map per zoom setting
if (map_zoom > 1) {
zoom_seek_x += (r.width() - (r.width() / map_zoom)) / 2;
zoom_seek_y += (r.height() - (r.height() / map_zoom)) / 2;
} else if (map_zoom < 0) {
zoom_seek_x += (r.width() - (r.width() * (-map_zoom))) / 2;
zoom_seek_y += (r.height() - (r.height() * (-map_zoom))) / 2;
}
for (line = 0; line < (r.height() / map_zoom); line++) {
map_file.seek(4 + ((x_pos + zoom_seek_x + (map_width * (y_pos + line + zoom_seek_y))) << 1));
map_file.read(map_line_buffer.data(), (r.width() / map_zoom) << 1);
map_zoom_line(map_line_buffer.data());
for (j = 0; j < map_zoom; j++) {
display.draw_pixels({0, r.top() + (line * map_zoom) + j, r.width(), 1}, map_line_buffer);
// Read from map file and display to zoomed scale
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
map_read_line(map_line_buffer.data(), r.width());
for (uint16_t j = 0; j < duplicate_lines; j++) {
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
}
}
prev_x_pos = x_pos;
prev_y_pos = y_pos;
@@ -255,6 +315,13 @@ void GeoMap::paint(Painter& painter) {
}
}
bool GeoMap::on_keyboard(KeyboardEvent key) {
if (key == '+' || key == ' ') return on_encoder(1);
if (key == '-') return on_encoder(-1);
return false;
}
bool GeoMap::on_touch(const TouchEvent event) {
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
set_highlighted(true);
@@ -323,8 +390,8 @@ bool GeoMap::manual_panning() {
}
void GeoMap::draw_scale(Painter& painter) {
uint16_t km = 100;
uint16_t scale_width = km * pixels_per_km * map_zoom;
uint16_t km = 800;
uint16_t scale_width = (map_zoom > 0) ? km * pixels_per_km * map_zoom : km * pixels_per_km / (-map_zoom);
while (scale_width > screen_width / 2) {
scale_width /= 2;
@@ -413,7 +480,7 @@ void GeoMapView::focus() {
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT);
}
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude, int32_t speed) {
if (geomap.manual_panning()) {
geomap.set_dirty();
return;
@@ -422,12 +489,14 @@ void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t a
lat_ = lat;
lon_ = lon;
altitude_ = altitude;
speed_ = speed;
// Stupid hack to avoid an event loop
geopos.set_report_change(false);
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.set_altitude(altitude_);
geopos.set_speed(speed_);
geopos.set_report_change(true);
geomap.set_angle(angle);
@@ -446,11 +515,12 @@ void GeoMapView::setup() {
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
geopos.on_change = [this](int32_t altitude, float lat, float lon, int32_t speed) {
altitude_ = altitude;
lat_ = lat;
lon_ = lon;
geopos.hide_altitude();
speed_ = speed;
geopos.hide_altandspeed();
geomap.set_manual_panning(true);
geomap.move(lon_, lat_);
geomap.set_dirty();
@@ -482,6 +552,7 @@ GeoMapView::GeoMapView(
const std::string& tag,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
GeoPos::spd_unit speed_unit,
float lat,
float lon,
uint16_t angle,
@@ -489,6 +560,7 @@ GeoMapView::GeoMapView(
: nav_(nav),
altitude_(altitude),
altitude_unit_(altitude_unit),
speed_unit_(speed_unit),
lat_(lat),
lon_(lon),
angle_(angle),
@@ -516,12 +588,14 @@ GeoMapView::GeoMapView(
NavigationView& nav,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
GeoPos::spd_unit speed_unit,
float lat,
float lon,
const std::function<void(int32_t, float, float)> on_done)
const std::function<void(int32_t, float, float, int32_t)> on_done)
: nav_(nav),
altitude_(altitude),
altitude_unit_(altitude_unit),
speed_unit_(speed_unit),
lat_(lat),
lon_(lon) {
mode_ = PROMPT;
@@ -540,7 +614,7 @@ GeoMapView::GeoMapView(
button_ok.on_select = [this, on_done, &nav](Button&) {
if (on_done)
on_done(altitude_, lat_, lon_);
on_done(altitude_, lat_, lon_, speed_);
nav.pop();
};
}
+39 -9
View File
@@ -29,6 +29,9 @@
#include "portapack.hpp"
#define MAX_MAP_ZOOM_IN 5
#define MAX_MAP_ZOOM_OUT 10
namespace ui {
enum GeoMapMode {
@@ -59,19 +62,27 @@ class GeoPos : public View {
FEET = 0,
METERS
};
enum spd_unit {
NONE = 0,
MPH,
KMPH,
HIDDEN = 255
};
std::function<void(int32_t, float, float)> on_change{};
std::function<void(int32_t, float, float, int32_t)> on_change{};
GeoPos(const Point pos, const alt_unit altitude_unit);
GeoPos(const Point pos, const alt_unit altitude_unit, const spd_unit speed_unit);
void focus() override;
void set_read_only(bool v);
void set_altitude(int32_t altitude);
void set_speed(int32_t speed);
void set_lat(float lat);
void set_lon(float lon);
int32_t altitude();
void hide_altitude();
int32_t speed();
void hide_altandspeed();
float lat();
float lon();
@@ -81,22 +92,35 @@ class GeoPos : public View {
bool read_only{false};
bool report_change{true};
alt_unit altitude_unit_{};
spd_unit speed_unit_{};
Labels labels_position{
{{1 * 8, 0 * 16}, "Alt:", Color::light_grey()},
{{1 * 8, 1 * 16}, "Lat: \xB0 ' \"", Color::light_grey()}, // 0xB0 is degree ° symbol in our 8x16 font
{{1 * 8, 2 * 16}, "Lon: \xB0 ' \"", Color::light_grey()},
};
Labels label_spd_position{
{{15 * 8, 0 * 16}, "Spd:", Color::light_grey()},
};
NumberField field_altitude{
{6 * 8, 0 * 16},
5,
{-1000, 50000},
250,
' '};
NumberField field_speed{
{19 * 8, 0 * 16},
4,
{0, 5000},
1,
' '};
Text text_alt_unit{
{12 * 8, 0 * 16, 2 * 8, 16},
""};
Text text_speed_unit{
{25 * 8, 0 * 16, 4 * 8, 16},
""};
NumberField field_lat_degrees{
{5 * 8, 1 * 16},
@@ -159,6 +183,7 @@ class GeoMap : public Widget {
bool on_touch(const TouchEvent event) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_keyboard(const KeyboardEvent event) override;
bool init();
void set_mode(GeoMapMode mode);
@@ -187,7 +212,7 @@ class GeoMap : public Widget {
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black());
void draw_markers(Painter& painter);
void map_zoom_line(ui::Color* buffer);
void map_read_line(ui::Color* buffer, uint16_t pixels);
bool manual_panning_{false};
GeoMapMode mode_{};
@@ -220,6 +245,7 @@ class GeoMapView : public View {
const std::string& tag,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
GeoPos::spd_unit speed_unit,
float lat,
float lon,
uint16_t angle,
@@ -227,9 +253,10 @@ class GeoMapView : public View {
GeoMapView(NavigationView& nav,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
GeoPos::spd_unit speed_unit,
float lat,
float lon,
const std::function<void(int32_t, float, float)> on_done);
const std::function<void(int32_t, float, float, int32_t)> on_done);
~GeoMapView();
GeoMapView(const GeoMapView&) = delete;
@@ -239,7 +266,7 @@ class GeoMapView : public View {
void focus() override;
void update_position(float lat, float lon, uint16_t angle, int32_t altitude);
void update_position(float lat, float lon, uint16_t angle, int32_t altitude, int32_t speed = 0);
std::string title() const override { return "Map view"; };
@@ -253,10 +280,12 @@ class GeoMapView : public View {
void setup();
const std::function<void(int32_t, float, float)> on_done{};
const std::function<void(int32_t, float, float, int32_t)> on_done{};
GeoMapMode mode_{};
int32_t altitude_{};
int32_t speed_{};
GeoPos::alt_unit altitude_unit_{};
GeoPos::spd_unit speed_unit_{};
float lat_{};
float lon_{};
uint16_t angle_{};
@@ -266,7 +295,8 @@ class GeoMapView : public View {
GeoPos geopos{
{0, 0},
altitude_unit_};
altitude_unit_,
speed_unit_};
GeoMap geomap{
{0, GeoMap::banner_height, GeoMap::geomap_rect_width, GeoMap::geomap_rect_height}};
+13
View File
@@ -269,6 +269,19 @@ bool MenuView::on_key(const KeyEvent key) {
}
}
bool MenuView::on_keyboard(const KeyboardEvent key) {
if (key == '-') return set_highlighted(highlighted_item - 1);
if (key == '+') return set_highlighted(highlighted_item + 1);
if (key == 10) {
if (menu_items[highlighted_item].on_select) {
menu_items[highlighted_item].on_select(KeyEvent::Right);
}
return true;
}
return false;
}
bool MenuView::on_encoder(const EncoderEvent event) {
set_highlighted(highlighted_item + event);
return true;
+1
View File
@@ -97,6 +97,7 @@ class MenuView : public View {
void on_blur() override;
bool on_key(const KeyEvent event) override;
bool on_encoder(const EncoderEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
private:
void update_items();
+33 -1
View File
@@ -93,6 +93,13 @@ void FrequencyField::set_allow_digit_mode(bool allowed) {
}
}
void FrequencyField::getAccessibilityText(std::string& result) {
result = to_string_dec_int(value_);
}
void FrequencyField::getWidgetName(std::string& result) {
result = "FrequencyField";
}
void FrequencyField::paint(Painter& painter) {
const auto str_value = to_string_short_freq(value_);
const auto paint_style = has_focus() ? style().invert() : style();
@@ -165,12 +172,18 @@ bool FrequencyField::on_key(KeyEvent event) {
return false;
}
bool FrequencyField::on_keyboard(KeyboardEvent key) {
if (key == '+' || key == ' ') return on_encoder(1);
if (key == '-') return on_encoder(-1);
return false;
}
bool FrequencyField::on_encoder(const EncoderEvent delta) {
if (digit_mode_)
set_value(value_ + (delta * digit_step()));
else
set_value(value_ + (delta * step_));
return true;
}
@@ -321,6 +334,25 @@ void FrequencyKeypadView::on_button(Button& button) {
update_text();
}
bool FrequencyKeypadView::on_keyboard(const KeyboardEvent key) {
if (key == 8) {
digit_delete();
update_text();
return true;
}
if (key >= '0' && key <= '9') {
digit_add(key);
update_text();
return true;
}
if (key == '.') {
field_toggle();
update_text();
return true;
}
return false;
}
void FrequencyKeypadView::digit_add(const char c) {
if (state == State::DigitMHz) {
if (clear_field_if_digits_entered) {
+5
View File
@@ -61,10 +61,14 @@ class FrequencyField : public Widget {
bool on_key(KeyEvent event) override;
bool on_encoder(EncoderEvent delta) override;
bool on_keyboard(KeyboardEvent key) override;
bool on_touch(TouchEvent event) override;
void on_focus() override;
void on_blur() override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
const size_t length_;
range_t range_;
@@ -202,6 +206,7 @@ class FrequencyKeypadView : public View {
rf::Frequency value() const;
void set_value(const rf::Frequency new_value);
bool on_encoder(const EncoderEvent delta) override;
bool on_keyboard(const KeyboardEvent key) override;
private:
int16_t focused_button = 0;
@@ -6,6 +6,44 @@ namespace ui {
/* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps;
// iterates over all ppma-s, and if it is runnable on the current system, it'll call the callback, and pass info.
/* static */ void ExternalItemsMenuLoader::load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback) {
if (!callback) return;
if (sd_card::status() != sd_card::Status::Mounted)
return;
for (const auto& entry : std::filesystem::directory_iterator(u"APPS", u"*.ppma")) {
auto filePath = u"/APPS/" + entry.path();
File app;
auto openError = app.open(filePath);
if (openError)
continue;
application_information_t application_information = {};
auto readResult = app.read(&application_information, sizeof(application_information_t));
if (!readResult)
continue;
if (application_information.header_version != CURRENT_HEADER_VERSION)
continue;
bool versionMatches = VERSION_MD5 == application_information.app_version;
if (!versionMatches) continue;
// here the app is startable and good.
std::string appshortname = filePath.filename().string();
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppma") {
// Remove the ".ppma" suffix
appshortname = appshortname.substr(0, appshortname.size() - 5);
}
AppInfoConsole info{
.appCallName = appshortname.c_str(),
.appFriendlyName = reinterpret_cast<char*>(&application_information.app_name[0]),
.appLocation = application_information.menu_location};
callback(info);
}
}
/* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
bitmaps.clear();
@@ -47,7 +85,9 @@ namespace ui {
bitmaps.push_back(std::move(dyn_bmp));
gridItem.on_select = [&nav, app_location, filePath]() {
run_external_app(nav, filePath);
if (!run_external_app(nav, filePath)) {
nav.display_modal("Error", "The .ppma file in your APPS\nfolder can't be read. Please\nupdate your SD Card content.");
}
};
} else {
gridItem.color = Color::light_grey();
@@ -65,19 +105,19 @@ namespace ui {
return external_apps;
}
/* static */ void ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
/* static */ bool ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
File app;
auto openError = app.open(filePath);
if (openError)
chDbgPanic("file gone");
return false;
application_information_t application_information = {};
auto readResult = app.read(&application_information, sizeof(application_information_t));
if (!readResult)
chDbgPanic("no data");
return false;
app.seek(0);
@@ -93,7 +133,7 @@ namespace ui {
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
if (!readResult)
chDbgPanic("read error");
return false;
if (readResult.value() < std::filesystem::max_file_block_size)
break;
@@ -114,7 +154,7 @@ namespace ui {
readResult = app.read(target_memory, bytes_to_read);
if (!readResult)
chDbgPanic("read error #2");
return false;
if (readResult.value() != bytes_to_read)
break;
@@ -126,7 +166,7 @@ namespace ui {
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
if (!readResult)
chDbgPanic("read error #3");
return false;
if (readResult.value() < std::filesystem::max_file_block_size)
break;
@@ -134,6 +174,7 @@ namespace ui {
}
application_information.externalAppEntry(nav);
return true;
}
} // namespace ui
@@ -54,11 +54,11 @@ class ExternalItemsMenuLoader {
public:
static std::vector<GridItem> load_external_items(app_location_t, NavigationView&);
ExternalItemsMenuLoader() = delete;
static bool run_external_app(ui::NavigationView&, std::filesystem::path);
static void load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback);
private:
static std::vector<DynamicBitmap<16, 16>> bitmaps;
static void run_external_app(ui::NavigationView&, std::filesystem::path);
};
} // namespace ui
+173 -6
View File
@@ -70,7 +70,7 @@
#include "ui_settings.hpp"
#include "ui_siggen.hpp"
#include "ui_sonde.hpp"
#include "ui_spectrum_painter.hpp"
// #include "ui_spectrum_painter.hpp" //moved to ext app
#include "ui_ss_viewer.hpp"
#include "ui_sstvtx.hpp"
#include "ui_styles.hpp"
@@ -93,7 +93,7 @@
#include "ble_tx_app.hpp"
#include "capture_app.hpp"
#include "ert_app.hpp"
#include "gps_sim_app.hpp"
// #include "gps_sim_app.hpp" //moved to ext
// #include "lge_app.hpp" //moved to ext
#include "pocsag_app.hpp"
#include "replay_app.hpp"
@@ -111,6 +111,169 @@ namespace pmem = portapack::persistent_memory;
namespace ui {
// When adding or removing apps from the Menu, please update it here:
std::vector<AppInfoConsole> NavigationView::fixedAppListFC = {
{"adsbrx", "ADS-B", RX},
{"ais", "AIS Boats", RX},
{"aprsrx", "APRS", RX},
{"audio", "Audio", RX},
{"blerx", "BLE Rx", RX},
{"ert", "ERT Meter", RX},
{"level", "Level", RX},
{"pocsagrx", "POCSAG", RX},
{"radiosnde", "Radiosnde", RX},
{"recon", "Recon", RX},
{"search", "Search", RX},
{"tpms", "TPMS Cars", RX},
{"weather", "Weather", RX},
{"subghzd", "SubGhzD", RX},
{"adsbtx", "ADS-B", TX},
{"aprstx", "APRS TX", TX},
{"bht", "BHT Xy/EP", TX},
{"bletx", "BLE Tx", TX},
{"morsetx", "Morse", TX},
{"ooktx", "OOK", TX},
{"pocsatx", "POCSAG TX", TX},
{"rdstx", "RDS TX", TX},
{"soundbrd", "Soundbrd", TX},
{"sstvtx", "SSTV", TX},
{"touchtune", "TouchTune", TX},
{"capture", "Capture", RX},
{"replay", "Replay", TX},
{"remote", "Remote", TX},
{"scanner", "Scanner", RX},
{"microphone", "Microphone", TX},
{"lookingglass", "Looking Glass", RX}};
bool NavigationView::StartAppByName(const char* name) {
pop();
if (strcmp(name, "adsbrx") == 0) {
push<ADSBRxView>();
return true;
}
if (strcmp(name, "ais") == 0) {
push<AISAppView>();
return true;
}
if (strcmp(name, "aprsrx") == 0) {
push<APRSRXView>();
return true;
}
if (strcmp(name, "audio") == 0) {
push<AnalogAudioView>();
return true;
}
if (strcmp(name, "blerx") == 0) {
push<BLERxView>();
return true;
}
if (strcmp(name, "ert") == 0) {
push<ERTAppView>();
return true;
}
if (strcmp(name, "level") == 0) {
push<LevelView>();
return true;
}
if (strcmp(name, "pocsagrx") == 0) {
push<POCSAGAppView>();
return true;
}
if (strcmp(name, "radiosnode") == 0) {
push<SondeView>();
return true;
}
if (strcmp(name, "recon") == 0) {
push<ReconView>();
return true;
}
if (strcmp(name, "search") == 0) {
push<SearchView>();
return true;
}
if (strcmp(name, "tpms") == 0) {
push<TPMSAppView>();
return true;
}
if (strcmp(name, "weather") == 0) {
push<WeatherView>();
return true;
}
if (strcmp(name, "subghzd") == 0) {
push<SubGhzDView>();
return true;
}
if (strcmp(name, "adsbtx") == 0) {
push<ADSBTxView>();
return true;
}
if (strcmp(name, "aprstx") == 0) {
push<APRSTXView>();
return true;
}
if (strcmp(name, "bht") == 0) {
push<BHTView>();
return true;
}
if (strcmp(name, "bletx") == 0) {
push<BLETxView>();
return true;
}
if (strcmp(name, "morsetx") == 0) {
push<MorseView>();
return true;
}
if (strcmp(name, "ooktx") == 0) {
push<EncodersView>();
return true;
}
if (strcmp(name, "pocsatx") == 0) {
push<POCSAGTXView>();
return true;
}
if (strcmp(name, "rdstx") == 0) {
push<RDSView>();
return true;
}
if (strcmp(name, "soundbrd") == 0) {
push<SoundBoardView>();
return true;
}
if (strcmp(name, "sstvtx") == 0) {
push<SSTVTXView>();
return true;
}
if (strcmp(name, "touchtune") == 0) {
push<TouchTunesView>();
return true;
}
if (strcmp(name, "capture") == 0) {
push<CaptureAppView>();
return true;
}
if (strcmp(name, "replay") == 0) {
push<PlaylistView>();
return true;
}
if (strcmp(name, "remote") == 0) {
push<RemoteView>();
return true;
}
if (strcmp(name, "scanner") == 0) {
push<ScannerView>();
return true;
}
if (strcmp(name, "microphone") == 0) {
push<MicTXView>();
return true;
}
if (strcmp(name, "lookingglass") == 0) {
push<GlassView>();
return true;
}
return false;
}
/* StatusTray ************************************************************/
StatusTray::StatusTray(Point pos)
@@ -607,7 +770,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
{"BLE Tx", ui::Color::green(), &bitmap_icon_btle, [&nav]() { nav.push<BLETxView>(); }},
// {"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }}, //moved to ext
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
//{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }}, //moved to ext
//{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }}, //moved to ext
// { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); }},
// {"LGE", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push<LGEView>(); }}, //moved to ext
@@ -617,7 +780,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
{"POCSAG TX", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGTXView>(); }},
{"RDS", ui::Color::green(), &bitmap_icon_rds, [&nav]() { nav.push<RDSView>(); }},
{"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push<SoundBoardView>(); }},
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
//{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
{"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push<SSTVTXView>(); }},
// {"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }}, //moved to ext
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
@@ -751,6 +914,9 @@ SystemView::SystemView(
Context& SystemView::context() const {
return context_;
}
NavigationView* SystemView::get_navigation_view() {
return &navigation_view;
}
void SystemView::toggle_overlay() {
switch (++overlay_active) {
@@ -758,7 +924,7 @@ void SystemView::toggle_overlay() {
this->add_child(&this->overlay);
this->set_dirty();
shared_memory.request_m4_performance_counter = 1;
shared_memory.m4_cpu_usage = 0;
shared_memory.m4_performance_counter = 0;
shared_memory.m4_heap_usage = 0;
shared_memory.m4_stack_usage = 0;
break;
@@ -766,11 +932,12 @@ void SystemView::toggle_overlay() {
this->remove_child(&this->overlay);
this->add_child(&this->overlay2);
this->set_dirty();
shared_memory.request_m4_performance_counter = 0;
shared_memory.request_m4_performance_counter = 2;
break;
case 3:
this->remove_child(&this->overlay2);
this->set_dirty();
shared_memory.request_m4_performance_counter = 0;
overlay_active = 0;
break;
}
+11 -1
View File
@@ -40,7 +40,7 @@
#include "diskio.h"
#include "lfsr_random.hpp"
#include "sd_card.hpp"
#include "external_app.hpp"
#include <vector>
#include <utility>
@@ -57,6 +57,12 @@ enum modal_t {
ABORT
};
struct AppInfoConsole {
const char* appCallName;
const char* appFriendlyName;
const app_location_t appLocation;
};
class NavigationView : public View {
public:
std::function<void(const View&)> on_view_changed{};
@@ -99,6 +105,8 @@ class NavigationView : public View {
* Returns true if the handler was bound successfully. */
bool set_on_pop(std::function<void()> on_pop);
static std::vector<AppInfoConsole> fixedAppListFC; // fixed app list for console. vector, so can be incomplete and still iterateable
bool StartAppByName(const char* name); // Starts a View (app) by name stored in fixedAppListFC. This is to start apps from console
private:
struct ViewState {
std::unique_ptr<View> view;
@@ -321,6 +329,8 @@ class SystemView : public View {
void toggle_overlay();
void paint_overlay();
NavigationView* get_navigation_view();
private:
uint8_t overlay_active{0};
+11
View File
@@ -23,10 +23,20 @@
#include "usb_serial_endpoints.h"
#include "usb_serial_event.hpp"
uint32_t EVT_MASK_USB = EVENT_MASK(8);
extern void usb0_isr(void);
static Thread* thread_usb_event = NULL;
CH_IRQ_HANDLER(USB0_IRQHandler) {
CH_IRQ_PROLOGUE();
usb0_isr();
chSysLockFromIsr();
chEvtSignalI(thread_usb_event, EVT_MASK_USB);
chSysUnlockFromIsr();
CH_IRQ_EPILOGUE();
}
@@ -46,6 +56,7 @@ uint32_t __strex(uint32_t val, volatile uint32_t* addr) {
void nvic_enable_irq(uint8_t irqn) {
NVIC_ISER(irqn / 32) = (1 << (irqn % 32));
thread_usb_event = chThdSelf();
}
void usb_configuration_changed(usb_device_t* const device) {
+1 -1
View File
@@ -264,7 +264,7 @@ uint8_t usb_descriptor_string_manufacturer[] = {
};
uint8_t usb_descriptor_string_product[] = {
43, // bLength
34, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'P', 0x00,
'o', 0x00,
+259 -1
View File
@@ -40,7 +40,11 @@
#include "ff.h"
#include "chprintf.h"
#include "chqueues.h"
#include "ui_external_items_menu_loader.hpp"
#include "untar.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include <string>
#include <codecvt>
@@ -244,6 +248,9 @@ static void cmd_screenframe(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
auto evtd = getEventDispatcherInstance();
evtd->enter_shell_working_mode();
for (int i = 0; i < ui::screen_height; i++) {
std::array<ui::ColorRGB888, ui::screen_width> row;
portapack::display.read_pixels({0, i, ui::screen_width, 1}, row);
@@ -254,6 +261,9 @@ static void cmd_screenframe(BaseSequentialStream* chp, int argc, char* argv[]) {
}
chprintf(chp, "\r\n");
}
evtd->exit_shell_working_mode();
chprintf(chp, "ok\r\n");
}
@@ -271,6 +281,9 @@ static void cmd_screenframeshort(BaseSequentialStream* chp, int argc, char* argv
(void)argc;
(void)argv;
auto evtd = getEventDispatcherInstance();
evtd->enter_shell_working_mode();
for (int y = 0; y < ui::screen_height; y++) {
std::array<ui::ColorRGB888, ui::screen_width> row;
portapack::display.read_pixels({0, y, ui::screen_width, 1}, row);
@@ -284,8 +297,10 @@ static void cmd_screenframeshort(BaseSequentialStream* chp, int argc, char* argv
chprintf(chp, "\r\n");
}
evtd->exit_shell_working_mode();
chprintf(chp, "ok\r\n");
}
static void cmd_write_memory(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 2) {
chprintf(chp, "usage: write_memory <address> <value (1 or 4 bytes)>\r\n");
@@ -340,6 +355,11 @@ static void cmd_button(BaseSequentialStream* chp, int argc, char* argv[]) {
control::debug::inject_switch(button);
// Wait two frame syncs to ensure action has painted
auto evtd = getEventDispatcherInstance();
evtd->wait_finish_frame();
evtd->wait_finish_frame();
chprintf(chp, "ok\r\n");
}
@@ -362,6 +382,53 @@ static void cmd_touch(BaseSequentialStream* chp, int argc, char* argv[]) {
}
evtd->emulateTouch({{x, y}, ui::TouchEvent::Type::Start});
evtd->emulateTouch({{x, y}, ui::TouchEvent::Type::End});
// Wait two frame syncs to ensure action has painted
evtd->wait_finish_frame();
evtd->wait_finish_frame();
chprintf(chp, "ok\r\n");
}
// send ascii keys in 2 char hex representation. Can send multiple keys at once like: keyboard 414243 (this will be ABC)
static void cmd_keyboard(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: keyboard XX\r\n");
return;
}
auto evtd = getEventDispatcherInstance();
if (evtd == NULL) {
chprintf(chp, "error\r\n");
}
size_t data_string_len = strlen(argv[0]);
if (data_string_len % 2 != 0) {
chprintf(chp, "usage: keyboard XXXX\r\n");
return;
}
for (size_t i = 0; i < data_string_len; i++) {
char c = argv[0][i];
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
chprintf(chp, "usage: keyboard XX\r\n");
return;
}
}
char buffer[3] = {0, 0, 0};
for (size_t i = 0; i < data_string_len / 2; i++) {
buffer[0] = argv[0][i * 2];
buffer[1] = argv[0][i * 2 + 1];
uint8_t chr = (uint8_t)strtol(buffer, NULL, 16);
evtd->emulateKeyboard(chr);
}
// Wait two frame syncs to ensure action has painted
evtd->wait_finish_frame();
evtd->wait_finish_frame();
chprintf(chp, "ok\r\n");
}
@@ -545,6 +612,37 @@ static void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]) {
chprintf(chp, "ok\r\n");
}
static void cmd_rtcget(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)chp;
(void)argc;
(void)argv;
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
chprintf(chp, "Current time: %04d-%02d-%02d %02d:%02d:%02d\r\n", datetime.year(), datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), datetime.second());
}
static void cmd_rtcset(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage =
"usage: rtcset [year] [month] [day] [hour] [minute] [second]\r\n"
" all fields are required; milliseconds zero when set\r\n"
" (fractional seconds are not supported)\r\n";
if (argc != 6) {
chprintf(chp, usage);
return;
}
rtc::RTC new_datetime{
(uint16_t)strtol(argv[0], NULL, 10), (uint8_t)strtol(argv[1], NULL, 10),
(uint8_t)strtol(argv[2], NULL, 10), (uint32_t)strtol(argv[3], NULL, 10),
(uint32_t)strtol(argv[4], NULL, 10), (uint32_t)strtol(argv[5], NULL, 10)};
rtcSetTime(&RTCD1, &new_datetime);
chprintf(chp, "ok\r\n");
}
static void cpld_info(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage =
"usage: cpld_info <device>\r\n"
@@ -692,6 +790,159 @@ static void cpld_info(BaseSequentialStream* chp, int argc, char* argv[]) {
}
}
// walks throught the given widget's childs in recurse to get all support text and pass it to a callback function
static void widget_collect_accessibility(BaseSequentialStream* chp, ui::Widget* w, void (*callback)(BaseSequentialStream*, const std::string&, const std::string&), ui::Widget* focusedWidget) {
for (auto child : w->children()) {
if (!child->hidden()) {
std::string res = "";
child->getAccessibilityText(res);
std::string strtype = "";
child->getWidgetName(strtype);
if (child == focusedWidget) strtype += "*";
if (callback != NULL && !res.empty()) callback(chp, res, strtype);
widget_collect_accessibility(chp, child, callback, focusedWidget);
}
}
}
// callback when it found any response from a widget
static void accessibility_callback(BaseSequentialStream* chp, const std::string& strResult, const std::string& wgType) {
if (!wgType.empty()) {
chprintf(chp, "[");
chprintf(chp, wgType.c_str());
chprintf(chp, "] ");
}
chprintf(chp, "%s\r\n", strResult.c_str());
}
// gets all widget's accessibility helper text
static void cmd_accessibility_readall(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
auto evtd = getEventDispatcherInstance();
if (evtd == NULL) {
chprintf(chp, "error Can't get Event Dispatcherr\n");
return;
}
auto wg = evtd->getTopWidget();
if (wg == NULL) {
chprintf(chp, "error Can't get top Widget\r\n");
return;
}
auto focused = evtd->getFocusedWidget();
widget_collect_accessibility(chp, wg, accessibility_callback, focused);
chprintf(chp, "ok\r\n");
}
// gets focused widget's accessibility helper text
static void cmd_accessibility_readcurr(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
auto evtd = getEventDispatcherInstance();
if (evtd == NULL) {
chprintf(chp, "error Can't get Event Dispatcher\r\n");
return;
}
auto wg = evtd->getFocusedWidget();
if (wg == NULL) {
chprintf(chp, "error Can't get focused Widget\r\n");
return;
}
std::string res = "";
wg->getAccessibilityText(res);
if (res.empty()) {
// try with parent
wg = wg->parent();
if (wg == NULL) {
chprintf(chp, "error Widget not providing accessibility info\r\n");
return;
}
wg->getAccessibilityText(res);
if (res.empty()) {
chprintf(chp, "error Widget not providing accessibility info\r\n");
return;
}
}
std::string strtype = "";
wg->getWidgetName(strtype);
accessibility_callback(chp, res, strtype);
chprintf(chp, "\r\nok\r\n");
}
static void cmd_appstart(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
if (argc != 1) {
chprintf(chp, "Usage: appstart APPCALLNAME");
return;
}
auto evtd = getEventDispatcherInstance();
if (!evtd) return;
auto top_widget = evtd->getTopWidget();
if (!top_widget) return;
auto nav = static_cast<ui::SystemView*>(top_widget)->get_navigation_view();
if (!nav) return;
if (nav->StartAppByName(argv[0])) {
chprintf(chp, "ok\r\n");
return;
}
// since ext app loader changed, we can just pass the string to it, and it"ll return if started or not.
std::string appwithpath = "/APPS/";
appwithpath += argv[0];
appwithpath += ".ppma";
bool ret = ui::ExternalItemsMenuLoader::run_external_app(*nav, path_from_string8((char*)appwithpath.c_str()));
if (!ret) {
chprintf(chp, "error\r\n");
return;
}
chprintf(chp, "ok\r\n");
}
static void printAppInfo(BaseSequentialStream* chp, ui::AppInfoConsole& element) {
if (strlen(element.appCallName) == 0) return;
chprintf(chp, element.appCallName);
chprintf(chp, " ");
chprintf(chp, element.appFriendlyName);
chprintf(chp, " ");
switch (element.appLocation) {
case RX:
chprintf(chp, "[RX]\r\n");
break;
case TX:
chprintf(chp, "[TX]\r\n");
break;
case UTILITIES:
chprintf(chp, "[UTIL]\r\n");
break;
case DEBUG:
chprintf(chp, "[DEBUG]\r\n");
break;
default:
break;
}
}
// returns the installed apps, those can be called by appstart APPNAME
static void cmd_applist(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
auto evtd = getEventDispatcherInstance();
if (!evtd) return;
auto top_widget = evtd->getTopWidget();
if (!top_widget) return;
auto nav = static_cast<ui::SystemView*>(top_widget)->get_navigation_view();
if (!nav) return;
for (auto element : ui::NavigationView::fixedAppListFC) {
printAppInfo(chp, element);
}
ui::ExternalItemsMenuLoader::load_all_external_items_callback([chp](ui::AppInfoConsole& info) {
printAppInfo(chp, info);
});
chprintf(chp, "ok\r\n");
}
static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage =
"usage: cpld_read <device> <target>\r\n"
@@ -866,6 +1117,7 @@ static const ShellCommand commands[] = {
{"read_memory", cmd_read_memory},
{"button", cmd_button},
{"touch", cmd_touch},
{"keyboard", cmd_keyboard},
{"ls", cmd_sd_list_dir},
{"rm", cmd_sd_delete},
{"open", cmd_sd_open},
@@ -874,8 +1126,14 @@ static const ShellCommand commands[] = {
{"read", cmd_sd_read},
{"write", cmd_sd_write},
{"filesize", cmd_sd_filesize},
{"rtcget", cmd_rtcget},
{"rtcset", cmd_rtcset},
{"cpld_info", cpld_info},
{"cpld_read", cmd_cpld_read},
{"accessibility_readall", cmd_accessibility_readall},
{"accessibility_readcurr", cmd_accessibility_readcurr},
{"applist", cmd_applist},
{"appstart", cmd_appstart},
{NULL, NULL}};
static const ShellConfig shell_cfg1 = {
@@ -884,5 +1142,5 @@ static const ShellConfig shell_cfg1 = {
void create_shell(EventDispatcher* evtd) {
_eventDispatcherInstance = evtd;
shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO);
shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO + 10);
}
+14 -13
View File
@@ -476,13 +476,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PREP replay)
### GPS Simulator
set(MODE_CPPSRC
proc_gps_sim.cpp
)
DeclareTargets(PGPS gps_sim)
### Signal generator
set(MODE_CPPSRC
@@ -490,12 +483,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PSIG siggen)
### Spectrum painter
set(MODE_CPPSRC
proc_spectrum_painter.cpp
)
DeclareTargets(PSPT spectrum_painter)
### SSTV TX
@@ -637,6 +624,20 @@ set(MODE_CPPSRC
)
DeclareTargets(PAMT am_tv)
### GPS Simulator
set(MODE_CPPSRC
proc_gps_sim.cpp
)
DeclareTargets(PGPS gps_sim)
### Spectrum painter
set(MODE_CPPSRC
proc_spectrum_painter.cpp
)
DeclareTargets(PSPT spectrum_painter)
### Test
set(MODE_CPPSRC
+14
View File
@@ -95,6 +95,20 @@ void BasebandThread::run() {
buffer_c8_t buffer{
buffer_tmp.p, buffer_tmp.count, sampling_rate_};
if (shared_memory.request_m4_performance_counter == 0x02) {
uint8_t max = shared_memory.m4_performance_counter;
for (size_t i = 0; i < buffer_tmp.count; i++) {
int8_t a = buffer_tmp.p[i].real();
if (a < 0)
a = -a;
if (a > max)
max = a;
}
shared_memory.m4_performance_counter = max;
}
if (baseband_processor_) {
baseband_processor_->execute(buffer);
}
+10 -6
View File
@@ -135,13 +135,17 @@ void update_performance_counters() {
last_paint_state = !last_paint_state;
auto utilisation = get_cpu_utilisation_in_percent();
auto free_stack = (uint32_t)get_free_stack_space();
auto free_heap = chCoreStatus();
if (performance_counter_active == 0x01) {
auto utilisation = get_cpu_utilisation_in_percent();
auto free_stack = (uint32_t)get_free_stack_space();
auto free_heap = chCoreStatus();
shared_memory.m4_cpu_usage = utilisation;
shared_memory.m4_stack_usage = free_stack;
shared_memory.m4_heap_usage = free_heap;
shared_memory.m4_performance_counter = utilisation;
shared_memory.m4_stack_usage = free_stack;
shared_memory.m4_heap_usage = free_heap;
} else if (performance_counter_active == 0x02) {
shared_memory.m4_performance_counter = 0;
}
}
} /* extern "C" */
@@ -186,7 +186,7 @@ uint8_t usb_descriptor_string_manufacturer[] = {
uint8_t usb_descriptor_string_product[] = {
#ifdef HACKRF_ONE
43, // bLength
34, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'P', 0x00,
'o', 0x00,
+10
View File
@@ -191,6 +191,7 @@ class IO {
}
uint32_t io_read() {
chSysLock(); // Dont interrupt time critical tasks
io_stb_assert();
dir_read();
addr_0();
@@ -199,6 +200,7 @@ class IO {
__asm__("nop");
const auto switches_raw = data_read();
io_stb_deassert();
chSysUnlock();
return switches_raw;
}
@@ -297,6 +299,7 @@ class IO {
}
void lcd_command(const uint32_t value) {
chSysLock(); // Dont interrupt time critical tasks
data_write_high(0); /* Drive high byte (with zero -- don't care) */
dir_write(); /* Turn around data bus, MCU->CPLD */
addr(0); /* Indicate command */
@@ -312,9 +315,11 @@ class IO {
lcd_wr_deassert(); /* Complete write operation */
addr(1); /* Set up for data phase (most likely after a command) */
chSysUnlock();
}
void lcd_write_data(const uint32_t value) __attribute__((always_inline)) {
chSysLock(); // Dont interrupt time critical tasks
// NOTE: Assumes and DIR=0 and ADDR=1 from command phase.
data_write_high(value); /* Drive high byte */
__asm__("nop");
@@ -325,9 +330,11 @@ class IO {
__asm__("nop");
__asm__("nop");
lcd_wr_deassert(); /* Complete write operation */
chSysUnlock();
}
uint32_t lcd_read_data() {
chSysLock(); // Dont interrupt time critical tasks
// NOTE: Assumes ADDR=1 from command phase.
dir_read();
@@ -345,10 +352,12 @@ class IO {
halPolledDelay(18); // 90ns
const auto value_low = data_read();
chSysUnlock();
return (value_high << 8) | value_low;
}
void io_write(const bool address, const uint_fast16_t value) {
chSysLock(); // Dont interrupt time critical tasks
data_write_low(value);
dir_write();
addr(address);
@@ -360,6 +369,7 @@ class IO {
__asm__("nop");
__asm__("nop");
io_stb_deassert();
chSysUnlock();
}
/*
void lcd_data_write_command_and_data(
+1 -1
View File
@@ -71,7 +71,7 @@ struct SharedMemory {
void set_baseband_ready() { baseband_ready = true; }
uint8_t volatile request_m4_performance_counter{0};
uint8_t volatile m4_cpu_usage{0};
uint8_t volatile m4_performance_counter{0};
uint16_t volatile m4_stack_usage{0};
uint32_t volatile m4_heap_usage{0};
uint16_t volatile m4_buffer_missed{0};
+1
View File
@@ -371,6 +371,7 @@ enum class KeyEvent : uint8_t {
};
using EncoderEvent = int32_t;
using KeyboardEvent = uint8_t;
struct TouchEvent {
enum class Type : uint32_t {
+1 -1
View File
@@ -1,6 +1,6 @@
#include "ui_language.hpp"
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:"};
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop"};
const char** LanguageHelper::currentMessages = englishMessages;
+2 -1
View File
@@ -18,7 +18,8 @@ enum LangConsts {
LANG_SCAN,
LANG_CLEAR,
LANG_READY,
LANG_DATADP
LANG_DATADP,
LANG_LOOP
};
class LanguageHelper {
+216
View File
@@ -172,6 +172,10 @@ bool Widget::on_touch(const TouchEvent event) {
(void)event;
return false;
}
bool Widget::on_keyboard(const KeyboardEvent event) {
(void)event;
return false;
}
const std::vector<Widget*>& Widget::children() const {
return no_children;
@@ -233,6 +237,12 @@ void Widget::dirty_overlapping_children_in_rect(const Rect& child_rect) {
}
}
void Widget::getAccessibilityText(std::string& result) {
result = "";
}
void Widget::getWidgetName(std::string& result) {
result = "";
}
/* View ******************************************************************/
void View::paint(Painter& painter) {
@@ -363,6 +373,12 @@ void Text::set(std::string_view value) {
set_dirty();
}
void Text::getAccessibilityText(std::string& result) {
result = text;
}
void Text::getWidgetName(std::string& result) {
result = "Text";
}
void Text::paint(Painter& painter) {
const auto rect = screen_rect();
auto s = has_focus() ? style().invert() : style();
@@ -403,6 +419,17 @@ void Labels::paint(Painter& painter) {
}
}
void Labels::getAccessibilityText(std::string& result) {
result = "";
for (auto& label : labels_) {
result += label.text;
result += ", ";
}
}
void Labels::getWidgetName(std::string& result) {
result = "Labels";
}
/* LiveDateTime **********************************************************/
void LiveDateTime::on_tick_second() {
@@ -575,6 +602,13 @@ void ProgressBar::set_value(const uint32_t value) {
set_dirty();
}
void ProgressBar::getAccessibilityText(std::string& result) {
result = to_string_dec_uint(_value) + " / " + to_string_dec_uint(_max);
}
void ProgressBar::getWidgetName(std::string& result) {
result = "ProgressBar";
}
void ProgressBar::paint(Painter& painter) {
int v_scaled;
@@ -675,6 +709,13 @@ void Console::write(std::string message) {
}
}
void Console::getAccessibilityText(std::string& result) {
result = "{" + buffer + "}";
}
void Console::getWidgetName(std::string& result) {
result = "Console";
}
void Console::writeln(std::string message) {
write(message + "\n");
}
@@ -783,6 +824,13 @@ bool Checkbox::set_value(const bool value) {
return false;
}
void Checkbox::getAccessibilityText(std::string& result) {
result = text_ + ((value_) ? " checked" : " unchecked");
}
void Checkbox::getWidgetName(std::string& result) {
result = "Checkbox";
}
bool Checkbox::value() const {
return value_;
}
@@ -853,6 +901,11 @@ bool Checkbox::on_key(const KeyEvent key) {
return false;
}
bool Checkbox::on_keyboard(const KeyboardEvent event) {
if (event == 10 || event == 32) return set_value(not value_);
return false;
}
bool Checkbox::on_touch(const TouchEvent event) {
switch (event.type) {
case TouchEvent::Type::Start:
@@ -895,6 +948,13 @@ std::string Button::text() const {
return text_;
}
void Button::getAccessibilityText(std::string& result) {
result = text_;
}
void Button::getWidgetName(std::string& result) {
result = "Button";
}
void Button::paint(Painter& painter) {
Color bg, fg;
const auto r = screen_rect();
@@ -944,6 +1004,16 @@ bool Button::on_key(const KeyEvent key) {
return false;
}
bool Button::on_keyboard(const KeyboardEvent event) {
if (event == 10 || event == 32) {
if (on_select) {
on_select(*this);
return true;
}
}
return false;
}
bool Button::on_touch(const TouchEvent event) {
switch (event.type) {
case TouchEvent::Type::Start:
@@ -1031,6 +1101,13 @@ std::string ButtonWithEncoder::text() const {
return text_;
}
void ButtonWithEncoder::getAccessibilityText(std::string& result) {
result = text_;
}
void ButtonWithEncoder::getWidgetName(std::string& result) {
result = "ButtonWithEncoder";
}
void ButtonWithEncoder::paint(Painter& painter) {
Color bg, fg;
const auto r = screen_rect();
@@ -1080,6 +1157,16 @@ bool ButtonWithEncoder::on_key(const KeyEvent key) {
return false;
}
bool ButtonWithEncoder::on_keyboard(const KeyboardEvent key) {
if (key == 32 || key == 10) {
if (on_select) {
on_select(*this);
return true;
}
}
return false;
}
bool ButtonWithEncoder::on_touch(const TouchEvent event) {
switch (event.type) {
case TouchEvent::Type::Start:
@@ -1176,6 +1263,13 @@ void NewButton::set_text(const std::string value) {
set_dirty();
}
void NewButton::getAccessibilityText(std::string& result) {
result = text_;
}
void NewButton::getWidgetName(std::string& result) {
result = "NewButton";
}
std::string NewButton::text() const {
return text_;
}
@@ -1274,6 +1368,16 @@ bool NewButton::on_key(const KeyEvent key) {
return false;
}
bool NewButton::on_keyboard(const KeyboardEvent key) {
if (key == 32 || key == 10) {
if (on_select) {
on_select();
return true;
}
}
return false;
}
bool NewButton::on_touch(const TouchEvent event) {
switch (event.type) {
case TouchEvent::Type::Start:
@@ -1360,6 +1464,13 @@ ImageButton::ImageButton(
set_focusable(true);
}
void ImageButton::getAccessibilityText(std::string& result) {
result = "image";
}
void ImageButton::getWidgetName(std::string& result) {
result = "ImageButton";
}
bool ImageButton::on_key(const KeyEvent key) {
if (key == KeyEvent::Select) {
if (on_select) {
@@ -1371,6 +1482,16 @@ bool ImageButton::on_key(const KeyEvent key) {
return false;
}
bool ImageButton::on_keyboard(const KeyboardEvent key) {
if (key == 32 || key == 10) {
if (on_select) {
on_select(*this);
return true;
}
}
return false;
}
bool ImageButton::on_touch(const TouchEvent event) {
switch (event.type) {
case TouchEvent::Type::Start:
@@ -1440,6 +1561,13 @@ bool ImageToggle::value() const {
return value_;
}
void ImageToggle::getAccessibilityText(std::string& result) {
result = value_ ? "checked" : "unchecked";
}
void ImageToggle::getWidgetName(std::string& result) {
result = "ImageToggle";
}
void ImageToggle::set_value(bool b) {
if (b == value_)
return;
@@ -1475,6 +1603,13 @@ size_t ImageOptionsField::selected_index_value() const {
return options[selected_index_].second;
}
void ImageOptionsField::getAccessibilityText(std::string& result) {
result = "selected index: " + to_string_dec_uint(selected_index_);
}
void ImageOptionsField::getWidgetName(std::string& result) {
result = "ImageOptionsField";
}
void ImageOptionsField::set_selected_index(const size_t new_index) {
if (new_index < options.size()) {
if (new_index != selected_index()) {
@@ -1537,6 +1672,12 @@ bool ImageOptionsField::on_encoder(const EncoderEvent delta) {
return true;
}
bool ImageOptionsField::on_keyboard(const KeyboardEvent key) {
if (key == '+' || key == ' ' || key == 10) return on_encoder(1);
if (key == '-' || key == 8) return on_encoder(-1);
return false;
}
bool ImageOptionsField::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start) {
focus();
@@ -1568,6 +1709,20 @@ const OptionsField::value_t& OptionsField::selected_index_value() const {
return options_[selected_index_].second;
}
void OptionsField::getAccessibilityText(std::string& result) {
result = "options: ";
bool first = true;
for (const auto& option : options_) {
if (!first) result += " ,";
first = false;
result += option.first;
}
result += "; selected: " + selected_index_name();
}
void OptionsField::getWidgetName(std::string& result) {
result = "OptionsField";
}
void OptionsField::set_selected_index(const size_t new_index, bool trigger_change) {
if (new_index < options_.size()) {
if (new_index != selected_index() || trigger_change) {
@@ -1653,6 +1808,11 @@ bool OptionsField::on_encoder(const EncoderEvent delta) {
set_selected_index(new_value);
return true;
}
bool OptionsField::on_keyboard(const KeyboardEvent key) {
if (key == '+' || key == ' ' || key == 10) return on_encoder(1);
if (key == '-' || key == 8) return on_encoder(-1);
return false;
}
bool OptionsField::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start) {
@@ -1681,6 +1841,13 @@ const std::string& TextEdit::value() const {
return text_;
}
void TextEdit::getAccessibilityText(std::string& result) {
result = text_;
}
void TextEdit::getWidgetName(std::string& result) {
result = "TextEdit";
}
void TextEdit::set_cursor(uint32_t pos) {
cursor_pos_ = std::min<size_t>(pos, text_.length());
set_dirty();
@@ -1772,6 +1939,19 @@ bool TextEdit::on_key(const KeyEvent key) {
return true;
}
bool TextEdit::on_keyboard(const KeyboardEvent key) {
// if ascii printable
if (key >= 0x20 && key <= 0x7e) {
char_add(key);
return true;
}
if (key == 8) {
char_delete();
return true;
}
return false;
}
bool TextEdit::on_encoder(const EncoderEvent delta) {
int32_t new_pos = cursor_pos_ + delta;
@@ -1817,6 +1997,13 @@ const std::string& TextField::get_text() const {
return text;
}
void TextField::getAccessibilityText(std::string& result) {
result = text;
}
void TextField::getWidgetName(std::string& result) {
result = "TextField";
}
void TextField::set_text(std::string_view value) {
set(value);
if (on_change)
@@ -1872,6 +2059,13 @@ int32_t NumberField::value() const {
return value_;
}
void NumberField::getAccessibilityText(std::string& result) {
result = to_string_dec_int(value_);
}
void NumberField::getWidgetName(std::string& result) {
result = "NumberField";
}
void NumberField::set_value(int32_t new_value, bool trigger_change) {
if (can_loop) {
if (new_value >= range.first)
@@ -1928,6 +2122,22 @@ bool NumberField::on_encoder(const EncoderEvent delta) {
return true;
}
bool NumberField::on_keyboard(const KeyboardEvent key) {
if (key == 10) {
if (on_select) {
on_select(*this);
return true;
}
}
if (key == '+' || key == ' ') {
return on_encoder(1);
}
if (key == '-' || key == 8) {
return on_encoder(-1);
}
return false;
}
bool NumberField::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start) {
focus();
@@ -2066,6 +2276,12 @@ const std::string& SymField::to_string() const {
return value_;
}
void SymField::getAccessibilityText(std::string& result) {
result = value_;
}
void SymField::getWidgetName(std::string& result) {
result = "SymField";
}
void SymField::paint(Painter& painter) {
Point p = screen_pos();
+53
View File
@@ -101,10 +101,14 @@ class Widget {
virtual bool on_key(const KeyEvent event);
virtual bool on_encoder(const EncoderEvent event);
virtual bool on_touch(const TouchEvent event);
virtual bool on_keyboard(const KeyboardEvent event);
virtual const std::vector<Widget*>& children() const;
virtual Context& context() const;
virtual void getAccessibilityText(std::string& result);
virtual void getWidgetName(std::string& result);
void set_style(const Style* new_style);
const Style& style() const;
@@ -206,6 +210,8 @@ class Text : public Widget {
void set(std::string_view value);
void paint(Painter& painter) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
protected:
// NB: Don't truncate this string. The UI will only render
@@ -233,6 +239,8 @@ class Labels : public Widget {
void set_labels(std::initializer_list<Label> labels);
void paint(Painter& painter) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
std::vector<Label> labels_;
@@ -311,6 +319,8 @@ class ProgressBar : public Widget {
void set_value(const uint32_t value);
void paint(Painter& painter) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
uint32_t _value = 0;
@@ -344,6 +354,8 @@ class Console : public Widget {
void enable_scrolling(bool enable);
void on_show() override;
void on_hide() override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
Point pos{0, 0};
@@ -382,7 +394,10 @@ class Checkbox : public Widget {
void paint(Painter& painter) override;
bool on_key(const KeyEvent key) override;
bool on_keyboard(const KeyboardEvent key) override;
bool on_touch(const TouchEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
std::string text_;
@@ -418,6 +433,9 @@ class Button : public Widget {
void on_focus() override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
std::string text_;
@@ -456,6 +474,10 @@ class ButtonWithEncoder : public Widget {
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
std::string text_;
@@ -490,6 +512,10 @@ class NewButton : public Widget {
void on_focus() override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
void paint(Painter& painter) override;
@@ -544,6 +570,9 @@ class ImageButton : public Image {
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
};
/* A button that toggles between two images when set. */
@@ -577,6 +606,9 @@ class ImageToggle : public ImageButton {
bool value() const;
void set_value(bool b);
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
// Hide this field.
using ImageButton::on_select;
@@ -621,6 +653,9 @@ class ImageOptionsField : public Widget {
void on_focus() override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
options_t options;
@@ -658,6 +693,10 @@ class OptionsField : public Widget {
void on_focus() override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
const size_t length_;
@@ -702,6 +741,10 @@ class TextEdit : public Widget {
bool on_key(const KeyEvent key) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
void on_focus() override;
void on_blur() override;
@@ -729,6 +772,9 @@ class TextField : public Text {
bool on_encoder(EncoderEvent delta) override;
bool on_touch(TouchEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
using Text::set;
};
@@ -763,6 +809,10 @@ class NumberField : public Widget {
bool on_key(const KeyEvent key) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
range_t range;
@@ -839,6 +889,9 @@ class SymField : public Widget {
bool on_encoder(EncoderEvent delta) override;
bool on_touch(TouchEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
/* Ensure the specified symbol is in the symbol list. */
char ensure_valid(char symbol) const;
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
set -e # exit immediatelly on any failure
build_make() {
cd ..
mkdir -p build
cd build
cmake ..
make "$@"
exit 0 # Report success :)
}
build_ninja() {
cd ..
mkdir -p build
cd build
cmake -G Ninja ..
ninja "$@"
exit 0 # Report success :)
}
if [ "$1" = 'make' ]; then # build the default (or specified) target with make
shift # remove the first item from $@ as we consumed it, we can then pass the rest on to make
build_make "$@"
elif [[ $1 == -j* ]]; then # allow passing -j without typing make_default
# dont shift here as we wanna pass the -j
build_make "$@"
elif [ "$1" = 'ninja' ]; then # build the default (or specified) target with ninja
shift # remove the first item from $@ as we consumed it, we can then pass the rest on to make
build_ninja "$@"
fi
exec "$@"
+1 -1
View File
@@ -18,7 +18,7 @@
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 3.1.3)
cmake_minimum_required(VERSION 3.5)
set(CMAKE_TOOLCHAIN_FILE ../toolchain-arm-cortex-m.cmake)
project(hackrf_usb C)