├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── Readme.md ├── apps ├── CMakeLists.txt ├── rocket │ ├── App.cpp │ ├── App.h │ ├── AppWidgets.cpp │ ├── AppWidgets.h │ ├── CMakeLists.txt │ ├── Config.h │ ├── Launcher.cpp │ ├── Launcher.h │ ├── main.cpp │ └── rocket.service ├── tilem │ ├── CMakeLists.txt │ ├── Calculator.cpp │ ├── Calculator.h │ ├── Dialog.cpp │ ├── Dialog.h │ ├── KeyPad.cpp │ ├── KeyPad.h │ ├── Screen.cpp │ ├── Screen.h │ ├── assets │ │ ├── backup.xcf │ │ ├── skin.png │ │ ├── skin.xcf │ │ ├── ti84p.png │ │ └── ti84pcsekeymap.png │ ├── draft │ │ ├── tilem.draft │ │ └── tilem.png │ └── main.cpp └── yaft │ ├── CMakeLists.txt │ ├── YaftWidget.cpp │ ├── YaftWidget.h │ ├── config.cpp │ ├── config.h │ ├── draft │ ├── yaft.draft │ └── yaft.png │ ├── keyboard.cpp │ ├── keyboard.h │ ├── keymap.cpp │ ├── keymap.h │ ├── layout.cpp │ ├── layout.h │ ├── main.cpp │ ├── screen.cpp │ └── screen.h ├── cmake ├── BuildTypes.cmake ├── CPackOptions.cmake.in ├── Deploy.cmake ├── GenIPK.cmake └── rm-toolchain.cmake ├── doc ├── rocket.png ├── screenshot.sh ├── tilem.png └── yaft.png ├── libs ├── CMakeLists.txt ├── rMlib │ ├── CMakeLists.txt │ ├── Canvas.cpp │ ├── Device.cpp │ ├── EmulatedFramebuffer.cpp │ ├── EmulatedInput.cpp │ ├── EmulatedKeyCodes.h │ ├── FrameBuffer.cpp │ ├── Input.cpp │ └── include │ │ ├── Canvas.h │ │ ├── Device.h │ │ ├── Error.h │ │ ├── FrameBuffer.h │ │ ├── Graphics.h │ │ ├── Input.h │ │ ├── MathUtil.h │ │ ├── UI.h │ │ └── UI │ │ ├── AppContext.h │ │ ├── BuildContext.h │ │ ├── Button.h │ │ ├── DynamicWidget.h │ │ ├── Flex.h │ │ ├── Future.h │ │ ├── Gesture.h │ │ ├── Image.h │ │ ├── Layout.h │ │ ├── Navigator.h │ │ ├── RenderObject.h │ │ ├── Stack.h │ │ ├── StatefulWidget.h │ │ ├── StatelessWidget.h │ │ ├── Text.h │ │ ├── Timer.h │ │ ├── TypeID.h │ │ ├── Util.h │ │ ├── Widget.h │ │ └── Wrap.h ├── rm2fb │ ├── AddressHooking.cpp │ ├── AddressHooking.h │ ├── CMakeLists.txt │ ├── Client.cpp │ ├── Client.h │ ├── ControlSocket.cpp │ ├── ControlSocket.h │ ├── IOCTL.cpp │ ├── IOCTL.h │ ├── ImageHook.cpp │ ├── InputDevice.cpp │ ├── InputDevice.h │ ├── Message.h │ ├── Readme.md │ ├── Server.cpp │ ├── SharedBuffer.cpp │ ├── SharedBuffer.h │ ├── Versions │ │ ├── Version.cpp │ │ ├── Version.h │ │ ├── Version2.15.cpp │ │ ├── Version3.5.cpp │ │ └── Version3.6.cpp │ ├── opkg │ │ ├── postinst │ │ ├── prerm │ │ └── rm2fb-preload.env │ ├── rm2fb.service │ └── rm2fb.socket ├── swtcon │ ├── Addresses.h │ ├── CMakeLists.txt │ ├── Constants.h │ ├── Generator.h │ ├── SwtconState.cpp │ ├── SwtconState.h │ ├── Util.h │ ├── Vsync.cpp │ ├── Vsync.h │ ├── Waveforms.cpp │ ├── Waveforms.h │ ├── fb.cpp │ ├── fb.h │ ├── include │ │ └── swtcon.h │ └── swtcon.cpp └── unistdpp │ ├── CMakeLists.txt │ ├── file.cpp │ ├── include │ └── unistdpp │ │ ├── error.h │ │ ├── file.h │ │ ├── ioctl.h │ │ ├── mmap.h │ │ ├── pipe.h │ │ ├── poll.h │ │ ├── shared_mem.h │ │ ├── socket.h │ │ ├── traits.h │ │ └── unistdpp.h │ ├── mmap.cpp │ ├── pipe.cpp │ ├── poll.cpp │ ├── socket.cpp │ └── unistdpp.cpp ├── test ├── CMakeLists.txt ├── integration │ ├── assets │ │ ├── calculator.png │ │ ├── calculator_3.png │ │ ├── mines.png │ │ ├── startup.png │ │ ├── tilem.png │ │ ├── xochitl_2.15.png │ │ ├── xochitl_3.3.png │ │ ├── xochitl_3.5.png │ │ ├── xochitl_3.8.png │ │ └── yaft.png │ ├── test.sh │ └── xochitl.toml └── unit │ ├── .clang-tidy │ ├── CMakeLists.txt │ ├── TempFiles.h │ ├── TestRMLib.cpp │ ├── TestRocket.cpp │ ├── TestTilem.cpp │ ├── TestUnistdpp.cpp │ ├── TestYaft.cpp │ ├── assets │ ├── button-pressed.png │ ├── button-released.png │ ├── column-text.png │ ├── container-text.png │ ├── counter-1.png │ ├── counter-5.png │ ├── counter-init.png │ ├── counter-reset.png │ ├── empty.txt │ ├── expanded-flex-text.png │ ├── expanded-text.png │ ├── missing-icon-app.png │ ├── nav-init.png │ ├── nav-second.png │ ├── nav-third.png │ ├── rocket.png │ ├── rocket_a.png │ ├── row-text.png │ ├── running-current-0.png │ ├── running-current-1.png │ ├── stateless.png │ ├── test.txt │ ├── text-basic.png │ ├── text-sized.png │ ├── text-sized2.png │ ├── text-sized3.png │ ├── text-sized4.png │ ├── tilem-error.png │ ├── tilem-init.png │ ├── tilem-on.png │ ├── tilem-start.png │ ├── yaft-aA.png │ ├── yaft-hidden.png │ ├── yaft-init.png │ ├── yaft-keyboard-down.png │ ├── yaft-keyboard-down2.png │ ├── yaft-keyboard-held.png │ ├── yaft-keyboard-stuck.png │ ├── yaft-keyboard.png │ └── yaft-landscape.png │ └── rMLibTestHelper.h ├── tools ├── CMakeLists.txt ├── input-test │ ├── CMakeLists.txt │ └── main.cpp ├── ioctl-dump │ ├── CMakeLists.txt │ ├── dump.cpp │ ├── main.cpp │ ├── print.h │ └── rm2fb_ioctl_dump.conf ├── rm2fb-emu │ ├── CMakeLists.txt │ ├── Socket.h │ ├── rm2fb-emu.cpp │ └── rm2fb-test.cpp ├── swtcon-preload │ ├── CMakeLists.txt │ └── main.cpp ├── test │ ├── CMakeLists.txt │ ├── Readme.md │ └── main.cpp ├── ui-tests │ ├── CMakeLists.txt │ └── main.cpp └── update-dump │ ├── CMakeLists.txt │ └── main.cpp └── vendor ├── .clang-tidy ├── CMakeLists.txt ├── catch2 └── CMakeLists.txt ├── expected └── CMakeLists.txt ├── frida └── CMakeLists.txt ├── libTilEm ├── CMakeLists.txt ├── Makefile ├── Makefile.in ├── calcs.c ├── cert.c ├── flash.c ├── graycolor.c ├── grayimage.c ├── graylcd.c ├── graylcd.h ├── keypad.c ├── lcd.c ├── libtilemcore.a ├── link.c ├── md5.c ├── memory.c ├── monolcd.c ├── rom.c ├── scancodes.h ├── state.c ├── tilem.h ├── tilemint.h ├── timers.c ├── x1 │ ├── x1.h │ ├── x1_init.c │ ├── x1_io.c │ ├── x1_memory.c │ └── x1_subcore.c ├── x2 │ ├── x2.h │ ├── x2_init.c │ ├── x2_io.c │ ├── x2_memory.c │ └── x2_subcore.c ├── x3 │ ├── x3.h │ ├── x3_init.c │ ├── x3_io.c │ ├── x3_memory.c │ └── x3_subcore.c ├── x4 │ ├── x4.h │ ├── x4_init.c │ ├── x4_io.c │ ├── x4_memory.c │ └── x4_subcore.c ├── x5 │ ├── x5.h │ ├── x5_init.c │ ├── x5_io.c │ ├── x5_memory.c │ └── x5_subcore.c ├── x6 │ ├── x6.h │ ├── x6_init.c │ ├── x6_io.c │ ├── x6_memory.c │ └── x6_subcore.c ├── x7 │ ├── x7.h │ ├── x7_init.c │ ├── x7_io.c │ ├── x7_memory.c │ └── x7_subcore.c ├── xn │ ├── xn.h │ ├── xn_init.c │ ├── xn_io.c │ ├── xn_memory.c │ └── xn_subcore.c ├── xp │ ├── xp.h │ ├── xp_init.c │ ├── xp_io.c │ ├── xp_memory.c │ └── xp_subcore.c ├── xs │ ├── xs.h │ ├── xs_init.c │ ├── xs_io.c │ ├── xs_memory.c │ └── xs_subcore.c ├── xz │ ├── xz.h │ ├── xz_init.c │ ├── xz_io.c │ ├── xz_memory.c │ └── xz_subcore.c ├── z80.c ├── z80.h ├── z80cb.h ├── z80cmds.h ├── z80ddfd.h ├── z80ed.h └── z80main.h ├── libYaft ├── CMakeLists.txt ├── Readme.md ├── color.h ├── conf.h ├── ctrlseq │ ├── csi.h │ ├── dcs.h │ ├── esc.h │ └── osc.h ├── glyph.h ├── info │ └── yaft.src ├── parse.c ├── parse.h ├── terminal.cpp ├── terminal.h ├── util.h └── yaft.h ├── linux ├── CMakeLists.txt ├── mxcfb.h └── rm2.h ├── noto ├── CMakeLists.txt └── NotoSansMono-Regular.ttf ├── stb ├── CMakeLists.txt ├── stb.c ├── stb_image.h ├── stb_image_write.h └── stb_truetype.h ├── thick ├── CMakeLists.txt ├── thick.cpp └── thick.h ├── tomlplusplus ├── CMakeLists.txt └── toml++ │ └── toml.h └── utfcpp └── CMakeLists.txt /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Mozilla 2 | 3 | BreakBeforeBraces: Attach 4 | PointerBindsToType: true 5 | BreakConstructorInitializersBeforeComma: true 6 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 7 | FixNamespaceComments: true 8 | 9 | ColumnLimit: 80 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.png filter=lfs diff=lfs merge=lfs -text 2 | *.ttf filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build-*/ 3 | .clangd/ 4 | .cache/ 5 | compile_commands.json 6 | 7 | .DS_Store 8 | default.profraw 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | project(rm2-stuff VERSION 0.1.3 LANGUAGES CXX C) 4 | 5 | set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 6 | 7 | include(BuildTypes) 8 | include(Deploy) 9 | 10 | set(CMAKE_CXX_STANDARD "17") 11 | 12 | add_subdirectory(libs) 13 | add_subdirectory(tools) 14 | add_subdirectory(apps) 15 | 16 | add_subdirectory(vendor EXCLUDE_FROM_ALL) 17 | 18 | option(BUILD_TESTS "Build Tests" OFF) 19 | if (${BUILD_TESTS}) 20 | include(CTest) 21 | enable_testing() 22 | add_subdirectory(test) 23 | endif() 24 | 25 | set(CPACK_GENERATOR "DEB") 26 | set(CPACK_POST_BUILD_SCRIPTS "${PROJECT_SOURCE_DIR}/cmake/GenIPK.cmake") 27 | 28 | get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) 29 | list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") 30 | 31 | configure_file("${PROJECT_SOURCE_DIR}/cmake/CPackOptions.cmake.in" 32 | "${PROJECT_BINARY_DIR}/CPackOptions.cmake" 33 | @ONLY) 34 | 35 | set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/CPackOptions.cmake") 36 | 37 | include(CPack) 38 | 39 | add_opkg_targets() 40 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(tilem) 2 | add_subdirectory(rocket) 3 | add_subdirectory(yaft) 4 | 5 | add_custom_target(apps) 6 | add_dependencies(apps tilem yaft rocket) 7 | -------------------------------------------------------------------------------- /apps/rocket/App.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct AppRunInfo { 14 | pid_t pid = -1; 15 | bool paused = false; 16 | }; 17 | 18 | struct AppDescription { 19 | std::string path; // path of app draft file, is unique. 20 | 21 | std::string name; 22 | std::string description; 23 | 24 | std::string command; 25 | std::string icon; 26 | 27 | std::string iconPath; 28 | std::optional getIcon() const; 29 | 30 | static std::optional read(std::string_view path, 31 | std::string_view iconDir); 32 | }; 33 | 34 | std::vector 35 | readAppFiles(std::string_view directory); 36 | 37 | class App { 38 | public: 39 | App(AppDescription desc) 40 | : mDescription(std::move(desc)), iconImage(mDescription.getIcon()) {} 41 | 42 | void updateDescription(AppDescription desc); 43 | 44 | bool isRunning() const { return !runInfo.expired(); } 45 | bool isPaused() const { return isRunning() && runInfo.lock()->paused; } 46 | 47 | /// Starts a new instance of the app if it's not already running. 48 | /// \returns True if a new instance was started. 49 | bool launch(); 50 | 51 | void stop(); 52 | 53 | void pause(std::optional screen = std::nullopt); 54 | void resume(rmlib::fb::FrameBuffer* fb = nullptr); 55 | 56 | const AppDescription& description() const { return mDescription; } 57 | 58 | const std::optional& icon() const { return iconImage; } 59 | const std::optional& savedFB() const { return savedFb; } 60 | void resetSavedFB() { savedFb.reset(); } 61 | 62 | void setRemoveOnExit() { shouldRemove = true; } 63 | bool shouldRemoveOnExit() const { return shouldRemove; } 64 | 65 | private: 66 | AppDescription mDescription; 67 | 68 | std::weak_ptr runInfo; 69 | 70 | std::optional iconImage; 71 | std::optional savedFb; 72 | 73 | // Indicates that the app should be removed when it exists 74 | bool shouldRemove = false; 75 | }; 76 | 77 | class AppManager { 78 | public: 79 | static AppManager& getInstance(); 80 | 81 | bool update(); 82 | const unistdpp::FD& getWaitFD() const { return pipe.readPipe; } 83 | 84 | private: 85 | friend class App; 86 | unistdpp::Pipe pipe; 87 | 88 | std::vector> runInfos; 89 | 90 | static void onSigChild(int sig); 91 | 92 | AppManager(); 93 | ~AppManager(); 94 | }; 95 | -------------------------------------------------------------------------------- /apps/rocket/AppWidgets.cpp: -------------------------------------------------------------------------------- 1 | #include "AppWidgets.h" 2 | 3 | using namespace rmlib; 4 | 5 | const MemoryCanvas& 6 | getMissingImage() { 7 | static auto image = [] { 8 | auto mem = MemoryCanvas(128, 128, 2); 9 | mem.canvas.set(greyToRGB565(0xaa)); 10 | return mem; 11 | }(); 12 | return image; 13 | } 14 | -------------------------------------------------------------------------------- /apps/rocket/AppWidgets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "App.h" 6 | 7 | const rmlib::MemoryCanvas& 8 | getMissingImage(); 9 | 10 | class RunningAppWidget : public rmlib::StatelessWidget { 11 | public: 12 | RunningAppWidget(const App& app, 13 | rmlib::Callback onTap, 14 | rmlib::Callback onKill, 15 | bool isCurrent) 16 | : app(app) 17 | , onTap(std::move(onTap)) 18 | , onKill(std::move(onKill)) 19 | , isCurrent(isCurrent) {} 20 | 21 | auto build(rmlib::AppContext& /*unused*/, 22 | const rmlib::BuildContext& /*unused*/) const { 23 | using namespace rmlib; 24 | 25 | const Canvas& canvas = app.savedFB().has_value() ? app.savedFB()->canvas 26 | : getMissingImage().canvas; 27 | 28 | return container( 29 | Column(GestureDetector(Sized(Image(canvas), 234, 300), 30 | Gestures{}.onTap(onTap)), 31 | Row(Text(app.description().name), Button("X", onKill))), 32 | Insets::all(isCurrent ? 1 : 2), 33 | Insets::all(isCurrent ? 2 : 1), 34 | Insets::all(2)); 35 | } 36 | 37 | private: 38 | const App& app; 39 | rmlib::Callback onTap; 40 | rmlib::Callback onKill; 41 | bool isCurrent; 42 | }; 43 | 44 | class AppWidget : public rmlib::StatelessWidget { 45 | public: 46 | AppWidget(const App& app, rmlib::Callback onLaunch) 47 | : app(app), onLaunch(std::move(onLaunch)) {} 48 | 49 | auto build(rmlib::AppContext& /*unused*/, 50 | const rmlib::BuildContext& /*unused*/) const { 51 | using namespace rmlib; 52 | 53 | const Canvas& canvas = 54 | app.icon().has_value() ? app.icon()->canvas : getMissingImage().canvas; 55 | return container(GestureDetector(Column(Sized(Image(canvas), 128, 128), 56 | Text(app.description().name)), 57 | Gestures{}.onTap(onLaunch)), 58 | Insets::all(2), 59 | Insets::all(1), 60 | Insets::all(2)); 61 | } 62 | 63 | private: 64 | const App& app; 65 | rmlib::Callback onLaunch; 66 | }; 67 | -------------------------------------------------------------------------------- /apps/rocket/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(rocket) 2 | 3 | add_library(rocket_lib OBJECT App.cpp AppWidgets.cpp Launcher.cpp) 4 | add_library(rocket::lib ALIAS rocket_lib) 5 | 6 | target_link_libraries(rocket_lib PUBLIC rMlib) 7 | target_include_directories(rocket_lib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 8 | 9 | add_executable(${PROJECT_NAME} main.cpp) 10 | 11 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) 12 | 13 | target_link_libraries(${PROJECT_NAME} 14 | PRIVATE 15 | rocket_lib 16 | rMlib) 17 | 18 | install(TARGETS ${PROJECT_NAME} 19 | COMPONENT ${PROJECT_NAME} 20 | DESTINATION opt/bin/) 21 | install(FILES rocket.service 22 | COMPONENT ${PROJECT_NAME} 23 | DESTINATION lib/systemd/system) 24 | -------------------------------------------------------------------------------- /apps/rocket/Config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct ActionConfig { 9 | enum Type { Swipe, Pinch, Tap, Button }; 10 | 11 | Type type; 12 | std::variant 14 | direction; 15 | int fingers; 16 | 17 | int keycode; 18 | 19 | bool matches(const rmlib::input::SwipeGesture& g) const { 20 | return type == ActionConfig::Swipe && 21 | std::get(direction) == 22 | g.direction && 23 | fingers == g.fingers; 24 | } 25 | 26 | bool matches(const rmlib::input::PinchGesture& g) const { 27 | return type == ActionConfig::Pinch && 28 | std::get(direction) == 29 | g.direction && 30 | fingers == g.fingers; 31 | } 32 | 33 | bool matches(const rmlib::input::TapGesture& g) const { 34 | return type == ActionConfig::Tap && g.fingers == fingers; 35 | } 36 | 37 | bool matches(const rmlib::input::KeyEvent& ev) const { 38 | return type == ActionConfig::Button && ev.keyCode == keycode && 39 | ev.type == rmlib::input::KeyEvent::Release; 40 | } 41 | }; 42 | 43 | struct Action { 44 | ActionConfig config; 45 | std::function command; 46 | }; 47 | 48 | struct Config { 49 | std::vector actions; 50 | }; 51 | -------------------------------------------------------------------------------- /apps/rocket/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Launcher.h" 2 | 3 | #include 4 | 5 | using namespace rmlib; 6 | 7 | int 8 | main(int argc, char* argv[]) { 9 | unistdpp::fatalOnError(runApp(LauncherWidget())); 10 | 11 | auto fb = fb::FrameBuffer::open(); 12 | if (fb.has_value()) { 13 | fb->canvas.set(white); 14 | fb->doUpdate( 15 | fb->canvas.rect(), fb::Waveform::GC16Fast, fb::UpdateFlags::None); 16 | } 17 | 18 | return EXIT_SUCCESS; 19 | } 20 | -------------------------------------------------------------------------------- /apps/rocket/rocket.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Rocket Launcher 3 | After=home.mount 4 | StartLimitIntervalSec=600 5 | StartLimitBurst=4 6 | 7 | [Service] 8 | ExecStart=/opt/bin/rocket 9 | Restart=on-failure 10 | RestartSec=5 11 | Environment="HOME=/home/root" 12 | Environment=LD_PRELOAD=/opt/lib/librm2fb_client.so.1 13 | 14 | [Install] 15 | Alias=launcher.service 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /apps/tilem/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(tilem) 2 | 3 | add_library(tilem_lib OBJECT 4 | KeyPad.cpp Screen.cpp Dialog.cpp Calculator.cpp) 5 | add_library(tilem::lib ALIAS tilem_lib) 6 | 7 | target_link_libraries(tilem_lib PUBLIC rMlib tiemu) 8 | target_include_directories(tilem_lib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 9 | 10 | add_executable(${PROJECT_NAME} main.cpp) 11 | 12 | target_compile_definitions(${PROJECT_NAME} PRIVATE _LINUX) 13 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) 14 | 15 | target_link_libraries(${PROJECT_NAME} 16 | PRIVATE 17 | tilem::lib 18 | rMlib) 19 | 20 | install(TARGETS ${PROJECT_NAME} 21 | COMPONENT ${PROJECT_NAME} 22 | DESTINATION opt/bin) 23 | install(FILES draft/tilem.draft 24 | COMPONENT ${PROJECT_NAME} 25 | DESTINATION opt/etc/draft) 26 | install(FILES draft/tilem.png 27 | COMPONENT ${PROJECT_NAME} 28 | DESTINATION opt/etc/draft/icons) 29 | -------------------------------------------------------------------------------- /apps/tilem/Calculator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KeyPad.h" 4 | #include "Screen.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace tilem { 11 | class CalcState; 12 | 13 | class Calculator : public rmlib::StatefulWidget { 14 | public: 15 | Calculator(std::string romPath); 16 | 17 | static CalcState createState() ; 18 | 19 | private: 20 | friend class CalcState; 21 | 22 | std::string romPath; 23 | std::string savePath; 24 | }; 25 | 26 | class CalcState : public rmlib::StateBase { 27 | public: 28 | void init(rmlib::AppContext& context, const rmlib::BuildContext& buildCtx); 29 | 30 | static auto closeButton(rmlib::AppContext& context, int fontSize) { 31 | using namespace rmlib; 32 | 33 | return Sized(GestureDetector( 34 | Border(Text("X", fontSize), Insets{ 0, 0, /* left */ 2, 0 }), 35 | Gestures{}.onTap([&context] { context.stop(); })), 36 | fontSize, 37 | fontSize); 38 | } 39 | 40 | static auto header(rmlib::AppContext& context, int width) { 41 | using namespace rmlib; 42 | 43 | constexpr auto font_size = 48; 44 | // TODO: expand option 45 | return Border( 46 | Row(Sized(Text("TilEm", font_size), width - font_size - 2, std::nullopt), 47 | closeButton(context, font_size)), 48 | Insets::all(1)); 49 | } 50 | 51 | auto build(rmlib::AppContext& context, 52 | const rmlib::BuildContext& buildCtx) const { 53 | using namespace rmlib; 54 | 55 | constexpr auto scale = 6.5; 56 | constexpr auto width = scale * 96; 57 | constexpr auto height = scale * 64; 58 | 59 | return Cleared( 60 | Center(Border(Column(header(context, width), 61 | Sized(Screen(mCalc), width, height), 62 | Sized(Keypad(mCalc), width, std::nullopt)), 63 | Insets::all(1)))); 64 | } 65 | 66 | ~CalcState(); 67 | 68 | private: 69 | TilemCalc* loadCalc() const; 70 | 71 | void updateCalcState(); 72 | 73 | TilemCalc* mCalc = nullptr; 74 | 75 | rmlib::TimerHandle updateTimer; 76 | rmlib::TimerHandle popupTimer; 77 | 78 | std::chrono::steady_clock::time_point lastUpdateTime; 79 | }; 80 | } // namespace tilem 81 | -------------------------------------------------------------------------------- /apps/tilem/Dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "Dialog.h" 2 | 3 | using namespace rmlib; 4 | 5 | void 6 | DownloadDialog::State::init(AppContext& ctx, const BuildContext& buildCtx) { 7 | startTimer = ctx.addTimer(std::chrono::seconds(0), [&] { 8 | constexpr auto url = 9 | "http://ipfs.io/ipfs/QmSNmqjQ1Ao4jff9pXbzv98ebuCzKf2ZuyEpSGL31D8z44"; 10 | auto cmd = "wget -O '" + getWidget().romPath + "' " + url; 11 | 12 | std::cout << cmd << "\n"; 13 | system(cmd.c_str()); 14 | 15 | Navigator::of(buildCtx).pop(); 16 | }); 17 | } 18 | 19 | DownloadDialog::State 20 | DownloadDialog::createState() { 21 | return State{}; 22 | } 23 | -------------------------------------------------------------------------------- /apps/tilem/Dialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class DownloadDialog : public rmlib::StatefulWidget { 7 | public: 8 | class State : public rmlib::StateBase { 9 | public: 10 | void init(rmlib::AppContext& ctx, const rmlib::BuildContext& buildCtx); 11 | 12 | auto build(rmlib::AppContext& appCtx, 13 | const rmlib::BuildContext& ctx) const { 14 | using namespace rmlib; 15 | 16 | return Center(Border( 17 | Cleared(Text("Downloading ROM '" + getWidget().romPath + "' ...")), 18 | Insets::all(5))); 19 | } 20 | 21 | private: 22 | rmlib::TimerHandle startTimer; 23 | }; 24 | 25 | DownloadDialog(std::string_view romPath) : romPath(romPath) {} 26 | 27 | static State createState(); 28 | 29 | std::string romPath; 30 | }; 31 | 32 | class ErrorDialog : public rmlib::StatelessWidget { 33 | public: 34 | ErrorDialog(std::string_view romPath) : romPath(romPath) {} 35 | 36 | auto build(rmlib::AppContext& appCtx, const rmlib::BuildContext& ctx) const { 37 | using namespace rmlib; 38 | return Center((Border( 39 | Cleared(Column( 40 | Text("Loading ROM '" + romPath + "' failed"), 41 | Row(Padding(Button("Download", [&ctx] { Navigator::of(ctx).pop(); }), 42 | Insets::all(10)), 43 | Padding(Button("Exit", [&appCtx] { appCtx.stop(); }), 44 | Insets::all(10))))), 45 | Insets::all(5)))); 46 | } 47 | std::string romPath; 48 | }; 49 | -------------------------------------------------------------------------------- /apps/tilem/KeyPad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "tilem.h" 7 | 8 | namespace tilem { 9 | 10 | struct Key; 11 | class KeypadRenderObject; 12 | 13 | class Keypad : public rmlib::Widget { 14 | public: 15 | Keypad(TilemCalc* calc); 16 | 17 | std::unique_ptr createRenderObject() const; 18 | 19 | private: 20 | friend class KeypadRenderObject; 21 | TilemCalc* calc = nullptr; 22 | size_t maxRowSize; 23 | size_t numRows; 24 | }; 25 | 26 | class KeypadRenderObject : public rmlib::LeafRenderObject { 27 | public: 28 | using LeafRenderObject::LeafRenderObject; 29 | 30 | void update(const Keypad& newWidget) { widget = &newWidget; } 31 | 32 | protected: 33 | rmlib::Size doLayout(const rmlib::Constraints& constraints) final; 34 | 35 | void drawKey(rmlib::Canvas& canvas, 36 | rmlib::Point pos, 37 | const Key& key, 38 | int keyWidth) const; 39 | 40 | rmlib::UpdateRegion doDraw(rmlib::Rect rect, rmlib::Canvas& canvas) final; 41 | 42 | void handleInput(const rmlib::input::Event& ev) final; 43 | 44 | private: 45 | constexpr static auto key_aspect = 1.5; 46 | constexpr static auto front_label_factor = 0.6; 47 | 48 | std::vector> keyLocations; 49 | std::unordered_map keyPointers; 50 | int keyWidth{}; 51 | int keyHeight{}; 52 | int padding{}; 53 | }; 54 | } // namespace tilem 55 | -------------------------------------------------------------------------------- /apps/tilem/Screen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace tilem { 8 | 9 | class ScreenRenderObject; 10 | 11 | class Screen : public rmlib::Widget { 12 | public: 13 | Screen(TilemCalc* calc) : calc(calc) {} 14 | 15 | std::unique_ptr createRenderObject() const; 16 | 17 | private: 18 | friend class ScreenRenderObject; 19 | TilemCalc* calc = nullptr; 20 | }; 21 | 22 | class ScreenRenderObject : public rmlib::LeafRenderObject { 23 | public: 24 | ScreenRenderObject(const Screen& widget); 25 | ~ScreenRenderObject() override; 26 | 27 | static void stateFrameCallback(TilemCalc* calc, void* selfPtr); 28 | 29 | void addTimer(TilemCalc* calc); 30 | 31 | void update(const Screen& newWidget); 32 | 33 | protected: 34 | rmlib::Size doLayout(const rmlib::Constraints& constraints) final; 35 | 36 | rmlib::UpdateRegion doDraw(rmlib::Rect rect, rmlib::Canvas& canvas) final; 37 | 38 | private: 39 | TilemLCDBuffer* lcd = nullptr; 40 | TilemLCDBuffer* oldLcd = nullptr; 41 | int timerID = -1; 42 | }; 43 | 44 | } // namespace tilem 45 | -------------------------------------------------------------------------------- /apps/tilem/assets/backup.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timower/rM2-stuff/b26e9c2bcff9c4d81bffc767e54498e0a7edcbde/apps/tilem/assets/backup.xcf -------------------------------------------------------------------------------- /apps/tilem/assets/skin.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e5298b6b9366acb78e48c55c2dd353cee94b680947ee342d4152056198eb6698 3 | size 31518 4 | -------------------------------------------------------------------------------- /apps/tilem/assets/skin.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timower/rM2-stuff/b26e9c2bcff9c4d81bffc767e54498e0a7edcbde/apps/tilem/assets/skin.xcf -------------------------------------------------------------------------------- /apps/tilem/assets/ti84p.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b957a6b9e65893eb986594d568f751c9a18dde184cb328a53a8ad183c56c7a6a 3 | size 213389 4 | -------------------------------------------------------------------------------- /apps/tilem/assets/ti84pcsekeymap.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:921e4805f8753e0e3a29c09c685ed3ebd75d196131f5d126737c8b6be98690f1 3 | size 2724 4 | -------------------------------------------------------------------------------- /apps/tilem/draft/tilem.draft: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 The Toltec Contributors 2 | # SPDX-License-Identifier: MIT 3 | 4 | name=TilEm 5 | desc=TI-84+ emulator 6 | call=/opt/bin/tilem 7 | term=: 8 | imgFile=tilem 9 | -------------------------------------------------------------------------------- /apps/tilem/draft/tilem.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:50a93e16c37be7bc3481c4fff11dd1aa4509883f0d714d840cb764b2e78848f2 3 | size 11679 4 | -------------------------------------------------------------------------------- /apps/tilem/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Calculator.h" 2 | 3 | #include 4 | 5 | using namespace rmlib; 6 | using namespace rmlib::input; 7 | 8 | namespace { 9 | 10 | constexpr auto calc_default_rom = "/home/root/ti84plus.rom"; 11 | 12 | } // namespace 13 | 14 | int 15 | main(int argc, char* argv[]) { 16 | const auto* calcName = argc > 1 ? argv[1] : calc_default_rom; 17 | 18 | unistdpp::fatalOnError(runApp(Navigator(tilem::Calculator(calcName)))); 19 | 20 | return EXIT_SUCCESS; 21 | } 22 | -------------------------------------------------------------------------------- /apps/yaft/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(yaft) 2 | 3 | add_library(yaft_app_lib OBJECT 4 | YaftWidget.cpp keyboard.cpp screen.cpp layout.cpp keymap.cpp config.cpp) 5 | add_library(Yaft::app_lib ALIAS yaft_app_lib) 6 | 7 | target_include_directories(yaft_app_lib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 8 | 9 | target_link_libraries(yaft_app_lib PUBLIC 10 | rMlib 11 | libYaft 12 | util 13 | tomlplusplus::tomlplusplus) 14 | 15 | if (APPLE) 16 | target_link_libraries(yaft_app_lib PUBLIC linux::mxcfb) 17 | endif() 18 | 19 | add_executable(${PROJECT_NAME} main.cpp) 20 | 21 | target_link_libraries(${PROJECT_NAME} 22 | PRIVATE 23 | yaft_app_lib 24 | rMlib) 25 | 26 | install(TARGETS ${PROJECT_NAME} 27 | COMPONENT ${PROJECT_NAME} 28 | DESTINATION opt/bin) 29 | 30 | install(DIRECTORY ${CMAKE_BINARY_DIR}/terminfo 31 | COMPONENT ${PROJECT_NAME} 32 | DESTINATION opt/share) 33 | 34 | install(FILES draft/yaft.draft 35 | COMPONENT ${PROJECT_NAME} 36 | DESTINATION opt/etc/draft) 37 | install(FILES draft/yaft.png 38 | COMPONENT ${PROJECT_NAME} 39 | DESTINATION opt/etc/draft/icons) 40 | 41 | add_deploy(${PROJECT_NAME}) 42 | -------------------------------------------------------------------------------- /apps/yaft/YaftWidget.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "config.h" 4 | #include "keyboard.h" 5 | #include "layout.h" 6 | #include "screen.h" 7 | 8 | #include 9 | 10 | #include 11 | 12 | class YaftState; 13 | 14 | class Yaft : public rmlib::StatefulWidget { 15 | public: 16 | Yaft(const char* cmd, char* const argv[], YaftConfigAndError config) 17 | : config(std::move(config.config)) 18 | , configError(std::move(config.err)) 19 | , cmd(cmd) 20 | , argv(argv) {} 21 | 22 | static YaftState createState() ; 23 | 24 | private: 25 | friend class YaftState; 26 | 27 | YaftConfig config; 28 | std::optional configError; 29 | 30 | const char* cmd; 31 | char* const* argv; 32 | }; 33 | 34 | class YaftState : public rmlib::StateBase { 35 | public: 36 | ~YaftState(); 37 | 38 | /// Logs the given string to the terminal console. 39 | void logTerm(std::string_view str); 40 | 41 | void init(rmlib::AppContext& ctx, const rmlib::BuildContext& /*unused*/); 42 | 43 | void checkLandscape(rmlib::AppContext& ctx); 44 | 45 | auto build(rmlib::AppContext& ctx, 46 | const rmlib::BuildContext& buildCtx) const { 47 | using namespace rmlib; 48 | 49 | const auto& layout = [this]() -> const Layout& { 50 | if (isLandscape) { 51 | return empty_layout; 52 | } 53 | 54 | if (hidden) { 55 | return hidden_layout; 56 | } 57 | 58 | return *getWidget().config.layout; 59 | }(); 60 | 61 | return Column( 62 | Expanded(Screen(term.get(), isLandscape, getWidget().config.autoRefresh)), 63 | Keyboard( 64 | term.get(), { layout, *getWidget().config.keymap }, [this](int num) { 65 | setState([](auto& self) { self.hidden = !self.hidden; }); 66 | })); 67 | } 68 | 69 | private: 70 | std::unique_ptr term; 71 | rmlib::TimerHandle pogoTimer; 72 | 73 | bool hidden = false; 74 | bool isLandscape = false; 75 | }; 76 | -------------------------------------------------------------------------------- /apps/yaft/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Error.h" 4 | 5 | #include 6 | #include 7 | 8 | #include "keymap.h" 9 | #include "layout.h" 10 | 11 | struct YaftConfig { 12 | enum class Orientation { Auto, Protrait, Landscape }; 13 | 14 | // Layout for virtual keyboard. 15 | const Layout* layout = nullptr; 16 | 17 | // Keymap of physical keyboard. 18 | const KeyMap* keymap = nullptr; 19 | 20 | Orientation orientation = Orientation::Auto; 21 | 22 | // Auto refresh full screen after 1024 updates. 23 | // Set to 0 to disable. 24 | int autoRefresh = 0; 25 | 26 | static YaftConfig getDefault(); 27 | }; 28 | 29 | struct YaftConfigError { 30 | enum { Missing, Syntax } type; 31 | std::string msg; 32 | }; 33 | 34 | struct YaftConfigAndError { 35 | YaftConfig config; 36 | 37 | std::optional err; 38 | }; 39 | 40 | /// Load the config from the `~/.config/yaft/config.toml` location. 41 | ErrorOr 42 | loadConfig(); 43 | 44 | OptError<> 45 | saveDefaultConfig(); 46 | 47 | /// Always returns a config, either the default one or the one on the file 48 | /// system. Will also make a new config file if it didn't exist. 49 | /// 50 | /// If any error occured during the loading of the config, it's also returned. 51 | YaftConfigAndError 52 | loadConfigOrMakeDefault(); 53 | -------------------------------------------------------------------------------- /apps/yaft/draft/yaft.draft: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 The Toltec Contributors 2 | # SPDX-License-Identifier: MIT 3 | 4 | name=yaft 5 | desc=Framebuffer terminal emulator with an on-screen touch keyboard 6 | call=/opt/bin/yaft 7 | term=: 8 | imgFile=yaft 9 | -------------------------------------------------------------------------------- /apps/yaft/draft/yaft.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5e34a13870db6d6cffb1f3801678b93cfd91a93cfc64465c5dcc0b663601630e 3 | size 17040 4 | -------------------------------------------------------------------------------- /apps/yaft/keymap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct EvKeyInfo { 8 | int scancode; 9 | 10 | int shfitScancode = 0; // Code when pressed with shift. 11 | 12 | int mod1Scancode = 0; // Left-alt scancode 13 | int mod2Scancode = 0; // Right-alt scancode 14 | 15 | // TODO: implement 16 | int holdCode = 0; // Key code when held. 17 | }; 18 | 19 | using KeyMap = std::unordered_map; 20 | 21 | extern const KeyMap qwerty_keymap; 22 | extern const KeyMap rm_qwerty_keymap; 23 | extern const KeyMap timower_keymap; 24 | 25 | // Name mapping for config file 26 | const std::initializer_list> 27 | keymaps = { 28 | { "qwerty", &qwerty_keymap }, 29 | { "rm-qwerty", &rm_qwerty_keymap }, 30 | { "timower", &timower_keymap }, 31 | }; 32 | -------------------------------------------------------------------------------- /apps/yaft/layout.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct KeyInfo { 7 | std::string_view name; 8 | int code; 9 | 10 | std::string_view altName{}; 11 | int altCode = 0; 12 | 13 | int width = 1; 14 | 15 | int longPressCode = 0; 16 | }; 17 | 18 | struct Layout { 19 | std::vector> rows; 20 | 21 | std::size_t numRows() const { return rows.size(); } 22 | std::size_t numCols() const; 23 | }; 24 | 25 | enum SpecialKeys { 26 | Escape = 0x1000000, 27 | Tab, 28 | Backspace, 29 | Enter, 30 | Del, 31 | Home, 32 | End, 33 | 34 | Left, 35 | Up, 36 | Right, 37 | Down, 38 | 39 | PageUp, 40 | PageDown, 41 | 42 | Mod1, 43 | Mod2, 44 | 45 | Callback = 0x1100000, 46 | }; 47 | 48 | inline bool 49 | isCallback(int code) { 50 | return (code & Callback) == Callback; 51 | } 52 | 53 | inline int 54 | getCallback(int code) { 55 | return code & 0x00fffff; 56 | } 57 | 58 | inline int 59 | makeCallback(int num) { 60 | return num | Callback; 61 | } 62 | 63 | enum ModifierKeys { 64 | Shift = 0x2000000, 65 | Ctrl = 0x4000000, 66 | Alt = 0x8000000, 67 | }; 68 | 69 | extern const Layout qwerty_layout; 70 | extern const Layout hidden_layout; 71 | extern const Layout empty_layout; 72 | 73 | const std::initializer_list> 74 | layouts = { 75 | { "qwerty", &qwerty_layout }, 76 | }; 77 | -------------------------------------------------------------------------------- /apps/yaft/main.cpp: -------------------------------------------------------------------------------- 1 | // yaft(2) 2 | #include "YaftWidget.h" 3 | #include "config.h" 4 | 5 | // rmLib 6 | #include 7 | 8 | // stdlib 9 | #include 10 | 11 | using namespace rmlib; 12 | 13 | namespace { 14 | const char* shellCmd = "/bin/bash"; 15 | } 16 | 17 | int 18 | main(int argc, char* argv[]) { 19 | static const char* shellArgs[3] = { shellCmd, "-l", nullptr }; 20 | 21 | /* for wcwidth() */ 22 | char* locale = nullptr; 23 | if ((locale = setlocale(LC_ALL, "en_US.UTF-8")) == nullptr && 24 | (locale = setlocale(LC_ALL, "")) == nullptr) { 25 | std::cout << "setlocale failed\n"; 26 | } 27 | std::cout << "Locale is: " << locale << "\n"; 28 | 29 | const char* cmd = nullptr; 30 | char* const* args = nullptr; 31 | if (argc > 1) { 32 | cmd = argv[1]; 33 | args = argv + 1; 34 | } else { 35 | cmd = shellArgs[0]; 36 | args = const_cast(shellArgs); 37 | } 38 | 39 | auto cfg = loadConfigOrMakeDefault(); 40 | 41 | unistdpp::fatalOnError(runApp(Yaft(cmd, args, std::move(cfg)))); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /apps/yaft/screen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "terminal.h" 4 | #include "yaft.h" 5 | 6 | #include 7 | 8 | class ScreenRenderObject; 9 | 10 | class Screen : public rmlib::Widget { 11 | public: 12 | Screen(struct terminal_t* term, bool isLandscape, int autoRefresh) 13 | : term(term), isLandscape(isLandscape) {} 14 | 15 | std::unique_ptr createRenderObject() const; 16 | 17 | private: 18 | friend class ScreenRenderObject; 19 | struct terminal_t* term; 20 | 21 | bool isLandscape = false; 22 | int autoRefresh = 0; 23 | }; 24 | 25 | class ScreenRenderObject : public rmlib::LeafRenderObject { 26 | public: 27 | ScreenRenderObject(const Screen& screen) 28 | : rmlib::LeafRenderObject(screen) { 29 | markNeedsRebuild(); 30 | } 31 | 32 | void update(const Screen& newWidget); 33 | 34 | protected: 35 | rmlib::Size doLayout(const rmlib::Constraints& constraints) final; 36 | 37 | rmlib::UpdateRegion doDraw(rmlib::Rect rect, rmlib::Canvas& canvas) final; 38 | 39 | void doRebuild(rmlib::AppContext& ctx, const rmlib::BuildContext& /*buildContext*/) final; 40 | 41 | void handleInput(const rmlib::input::Event& ev) final; 42 | 43 | private: 44 | rmlib::Rect drawLine(rmlib::Canvas& canvas, 45 | rmlib::Rect rect, 46 | terminal_t& term, 47 | int line) const; 48 | template 49 | void handleTouchEvent(const Ev& ev); 50 | 51 | bool shouldRefresh() const; 52 | 53 | int numUpdates = 0; 54 | 55 | int mouseSlot = -1; 56 | rmlib::Point lastMousePos; 57 | 58 | const rmlib::fb::FrameBuffer* fb = nullptr; 59 | }; 60 | -------------------------------------------------------------------------------- /cmake/BuildTypes.cmake: -------------------------------------------------------------------------------- 1 | option(COVERAGE "Generate coverage data in debug builds" OFF) 2 | option(SANITIZE "Use asan in debug builds" OFF) 3 | 4 | # Set fPIC, even for shared libraries.. 5 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 6 | 7 | if (NOT CMAKE_BUILD_TYPE) 8 | set(CMAKE_BUILD_TYPE Debug) 9 | endif() 10 | 11 | set(BASE_OPTS 12 | -fdiagnostics-color=always 13 | -Wall 14 | -Wextra 15 | -Wno-unused-parameter 16 | -Wno-gnu-statement-expression 17 | -Wno-gnu-statement-expression-from-macro-expansion 18 | -Wno-psabi) 19 | 20 | set(COVER_OPTS 21 | -fprofile-instr-generate 22 | -fcoverage-mapping) 23 | 24 | if (COVERAGE) 25 | list(APPEND BASE_OPTS ${COVER_OPTS}) 26 | endif() 27 | 28 | set(ASAN_OPTS 29 | -fsanitize=address 30 | -fno-omit-frame-pointer 31 | -fsanitize=undefined) 32 | 33 | if (SANITIZE) 34 | list(APPEND BASE_OPTS ${ASAN_OPTS} -Werror) 35 | endif() 36 | 37 | list(JOIN BASE_OPTS " " BASE_OPTS) 38 | 39 | set(CMAKE_C_FLAGS_DEBUG 40 | "${BASE_OPTS} -g -Og" 41 | CACHE STRING "" 42 | FORCE) 43 | set(CMAKE_CXX_FLAGS_DEBUG 44 | "${BASE_OPTS} -Og -g -Wno-c++20-designator" 45 | CACHE STRING "" 46 | FORCE) 47 | -------------------------------------------------------------------------------- /cmake/CPackOptions.cmake.in: -------------------------------------------------------------------------------- 1 | set(CPACK_PACKAGING_INSTALL_PREFIX "/") 2 | 3 | set(CPACK_PACKAGE_CONTACT "Timothy Werquin") 4 | set(CPACK_STRIP_FILES ON) 5 | 6 | set(CPACK_COMPONENTS_ALL "@CPACK_COMPONENTS_ALL@") 7 | set(CPACK_DEB_COMPONENT_INSTALL ON) 8 | 9 | foreach(CPACK_COMP IN LISTS CPACK_COMPONENTS_ALL) 10 | string(TOUPPER "${CPACK_COMP}" COMP_NAME) 11 | set(CPACK_DEBIAN_${COMP_NAME}_FILE_NAME "${CPACK_COMP}.ipk") 12 | 13 | set(CPACK_DEBIAN_${COMP_NAME}_PACKAGE_NAME "${CPACK_COMP}") 14 | endforeach() 15 | 16 | set(CPACK_DEBIAN_ARCHIVE_TYPE "gnutar") 17 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "rmall") 18 | 19 | # rm2fb stuff 20 | set(CPACK_DEBIAN_RM2DISPLAY_PACKAGE_DEPENDS "xochitl") 21 | set(CPACK_DEBIAN_RM2DISPLAY_PACKAGE_PROVIDES "display, rm2fb-client") 22 | set(CPACK_DEBIAN_RM2DISPLAY_PACKAGE_CONFLICTS "display, rm2fb-client, rm2fb") 23 | set(CPACK_DEBIAN_RM2DISPLAY_PACKAGE_REPLACES "display, rm2fb-client, rm2fb") 24 | set(CPACK_DEBIAN_RM2DISPLAY_PACKAGE_CONTROL_EXTRA 25 | "@CMAKE_SOURCE_DIR@/libs/rm2fb/opkg/postinst;@CMAKE_SOURCE_DIR@/libs/rm2fb/opkg/prerm") 26 | -------------------------------------------------------------------------------- /cmake/Deploy.cmake: -------------------------------------------------------------------------------- 1 | set(SSH_HOST "RemEmu" CACHE STRING "SSH host to deploy to") 2 | 3 | set(RUN_PREFIX 4 | "LD_PRELOAD=/home/root/librm2fb_client.so" 5 | CACHE STRING "Prefix when running over SSH") 6 | 7 | find_program(SCP scp) 8 | find_program(SSH ssh) 9 | 10 | if ("${SCP}" STREQUAL "SCP-NOTFOUND") 11 | message(WARNING "scp not found, skipping deploy") 12 | endif() 13 | if ("${SSH}" STREQUAL "SSH-NOTFOUND") 14 | message(WARNING "scp not found, skipping deploy") 15 | endif() 16 | 17 | function(add_deploy target) 18 | if (NOT TARGET ${target}) 19 | message(FATAL_ERROR "add_deploy called on non-existent target: ${target}") 20 | endif() 21 | 22 | if ("${SCP}" STREQUAL "SCP-NOTFOUND") 23 | return() 24 | endif() 25 | 26 | add_custom_target(deploy_${target} 27 | COMMAND ${SCP} $ "${SSH_HOST}:" 28 | DEPENDS ${target}) 29 | 30 | if ("${SSH}" STREQUAL "SSH-NOTFOUND") 31 | return() 32 | endif() 33 | 34 | add_custom_target(run_${target} 35 | COMMAND 36 | ${SSH} -t -t "${SSH_HOST}" 37 | ${RUN_PREFIX} "./$" 38 | DEPENDS deploy_${target} 39 | USES_TERMINAL) 40 | endfunction() 41 | 42 | 43 | function(add_opkg_targets) 44 | if ("${SCP}" STREQUAL "SCP-NOTFOUND" OR "${SSH}" STREQUAL "SSH-NOTFOUND") 45 | return() 46 | endif() 47 | 48 | get_cmake_property(all_comps COMPONENTS) 49 | list(REMOVE_ITEM all_comps "Unspecified") 50 | 51 | foreach(comp IN LISTS all_comps) 52 | add_custom_target(opkg_${comp} 53 | COMMAND "${SCP}" "${CMAKE_BINARY_DIR}/${comp}.ipk" "${SSH_HOST}:" 54 | COMMAND "${SSH}" "${SSH_HOST}" bash -l -c "'opkg install --force-reinstall ${comp}.ipk'" 55 | DEPENDS package) 56 | endforeach() 57 | 58 | endfunction() 59 | -------------------------------------------------------------------------------- /cmake/GenIPK.cmake: -------------------------------------------------------------------------------- 1 | # Cmake generates ar archives instead of tar.gz. 2 | # So this post archive script re-packages the .deb using tar. 3 | 4 | foreach(PACKAGE_FILE IN LISTS CPACK_PACKAGE_FILES) 5 | get_filename_component(PARENT_DIR "${PACKAGE_FILE}" DIRECTORY) 6 | 7 | set(TEMP_DIR "${PARENT_DIR}/tmp") 8 | file(MAKE_DIRECTORY "${TEMP_DIR}") 9 | 10 | execute_process( 11 | COMMAND ar x "${PACKAGE_FILE}" 12 | WORKING_DIRECTORY "${TEMP_DIR}" 13 | COMMAND_ERROR_IS_FATAL ANY) 14 | 15 | execute_process( 16 | COMMAND tar -czf "${PACKAGE_FILE}" "." 17 | WORKING_DIRECTORY "${TEMP_DIR}" 18 | COMMAND_ERROR_IS_FATAL ANY) 19 | 20 | message(STATUS "Created ${PACKAGE_FILE} as IPK") 21 | endforeach() 22 | -------------------------------------------------------------------------------- /cmake/rm-toolchain.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR arm) 3 | 4 | #set(TOOLCHAIN_ROOT "/opt/codex/rm2/4.0.117-1") 5 | set(TOOLCHAIN_ROOT "/opt/codex/rm11x/3.1.2") 6 | 7 | set(CMAKE_SYSROOT "${TOOLCHAIN_ROOT}/sysroots/cortexa7hf-neon-remarkable-linux-gnueabi") 8 | 9 | set(ENV{PKG_CONFIG_DIR} "") 10 | set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig") 11 | set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) 12 | 13 | set(host_root "${TOOLCHAIN_ROOT}/sysroots/x86_64-codexsdk-linux") 14 | set(triple "arm-remarkable-linux-gnueabi") 15 | set(tools "${host_root}/usr/bin/${triple}") 16 | set(prefix "${tools}/${triple}-") 17 | 18 | set(CMAKE_C_COMPILER "${prefix}gcc") 19 | set(CMAKE_C_COMPILER_AR "${prefix}gcc-ar") 20 | set(CMAKE_C_COMPILER_RANLIB "${prefix}gcc-ranlib") 21 | set(CMAKE_C_COMPILER_ARG1 "-mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7") 22 | 23 | 24 | set(CMAKE_CXX_COMPILER "${prefix}g++") 25 | set(CMAKE_CXX_COMPILER_AR "${prefix}gcc-ar") 26 | set(CMAKE_CXX_COMPILER_RANLIB "${prefix}gcc-ranlib") 27 | set(CMAKE_CXX_COMPILER_ARG1 "-mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7") 28 | 29 | set(CMAKE_ADDR2LINE "${prefix}addr2line") 30 | set(CMAKE_AR "${prefix}ar") 31 | set(CMAKE_NM "${prefix}nm") 32 | set(CMAKE_OBJCOPY "${prefix}objcopy") 33 | set(CMAKE_OBJDUMP "${prefix}objdump") 34 | set(CMAKE_RANLIB "${prefix}ranlib") 35 | set(CMAKE_READELF "${prefix}readelf") 36 | set(CMAKE_STRIP "${prefix}strip") 37 | 38 | 39 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 40 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 41 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 42 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 43 | -------------------------------------------------------------------------------- /doc/rocket.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2bc37ab9b8ae9d817a0b96526a887b14e1c405edd8090c869229d52bb7aa0fcc 3 | size 59863 4 | -------------------------------------------------------------------------------- /doc/screenshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | set -eux 4 | 5 | FB_IN=$1 6 | SCREEN_OUT=$2 7 | 8 | RGB_OUT=$(mktemp) 9 | 10 | scp "$FB_IN" "$RGB_OUT" 11 | ffmpeg -vcodec rawvideo -f rawvideo -pix_fmt rgb565 -s 1404x1872 -i "$RGB_OUT" -f image2 -vcodec png "$SCREEN_OUT" 12 | rm "$RGB_OUT" 13 | -------------------------------------------------------------------------------- /doc/tilem.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b347fdb06ff738200dede5570964b066081ac4f3dce20bec14136d90af6abd0 3 | size 100728 4 | -------------------------------------------------------------------------------- /doc/yaft.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e0807c67b82cb156dff3eb3124b723e05992e71a01030a48d64e1705a09c5fda 3 | size 42966 4 | -------------------------------------------------------------------------------- /libs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(swtcon) 2 | add_subdirectory(rMlib) 3 | add_subdirectory(rm2fb) 4 | add_subdirectory(unistdpp) 5 | -------------------------------------------------------------------------------- /libs/rMlib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(rMlib) 2 | 3 | option(EMULATE "Emulate a rM tablet using SDL" OFF) 4 | option(EMULATE_UINPUT "Emulate input devices using uinput" OFF) 5 | 6 | set(RMLIB_SOURCES 7 | Device.cpp 8 | Canvas.cpp) 9 | 10 | if (EMULATE) 11 | list(APPEND RMLIB_SOURCES EmulatedFramebuffer.cpp) 12 | else() 13 | list(APPEND RMLIB_SOURCES FrameBuffer.cpp) 14 | endif() 15 | 16 | if(EMULATE AND NOT EMULATE_UINPUT) 17 | list(APPEND RMLIB_SOURCES EmulatedInput.cpp) 18 | else() 19 | list(APPEND RMLIB_SOURCES Input.cpp) 20 | endif() 21 | 22 | 23 | add_library(${PROJECT_NAME} STATIC ${RMLIB_SOURCES}) 24 | 25 | target_include_directories(${PROJECT_NAME} 26 | PUBLIC 27 | ${CMAKE_CURRENT_SOURCE_DIR}/include 28 | PRIVATE 29 | ${CMAKE_CURRENT_SOURCE_DIR}) 30 | 31 | target_link_libraries(${PROJECT_NAME} 32 | PRIVATE 33 | thick 34 | stb 35 | PUBLIC 36 | utf8cpp 37 | tl::expected 38 | unistdpp) 39 | 40 | # If not emulating, or emulating uinput, link in udev, evdev and linux headers. 41 | if (NOT EMULATE OR EMULATE_UINPUT) 42 | find_package(PkgConfig) 43 | pkg_check_modules(LIBEVDEV REQUIRED libevdev) 44 | 45 | target_link_libraries(${PROJECT_NAME} 46 | PRIVATE 47 | ${LIBEVDEV_LIBRARIES} 48 | udev 49 | linux::mxcfb) 50 | 51 | target_include_directories(${PROJECT_NAME} 52 | PRIVATE ${LIBEVDEV_INCLUDE_DIRS}) 53 | endif() 54 | 55 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) 56 | 57 | if (EMULATE) 58 | target_link_libraries(${PROJECT_NAME} PRIVATE NotoFont::font) 59 | 60 | find_package(SDL2 REQUIRED) 61 | if (TARGET SDL2::SDL2) 62 | target_link_libraries(${PROJECT_NAME} PUBLIC SDL2::SDL2) 63 | else() 64 | target_link_libraries(${PROJECT_NAME} PUBLIC ${SDL2_LIBRARIES}) 65 | target_include_directories(${PROJECT_NAME} PUBLIC ${SDL2_INCLUDE_DIRS}) 66 | endif() 67 | target_link_libraries(${PROJECT_NAME} PUBLIC pthread) 68 | 69 | target_compile_definitions(${PROJECT_NAME} PUBLIC EMULATE) 70 | target_compile_definitions(${PROJECT_NAME} PUBLIC EMULATE_SCALE=2) 71 | 72 | if (EMULATE_UINPUT) 73 | target_compile_definitions(${PROJECT_NAME} PRIVATE EMULATE_UINPUT) 74 | endif() 75 | endif() 76 | 77 | install(TARGETS ${PROJECT_NAME} 78 | COMPONENT ${PROJECT_NAME} 79 | DESTINATION opt/lib) 80 | install(DIRECTORY include/ 81 | COMPONENT ${PROJECT_NAME} 82 | DESTINATION opt/include/rmlib) 83 | -------------------------------------------------------------------------------- /libs/rMlib/include/Device.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Error.h" 4 | #include "MathUtil.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /// Contains any device specific information. 11 | namespace rmlib::device { 12 | 13 | // NOLINTNEXTLINE 14 | enum class DeviceType { reMarkable1, reMarkable2 }; 15 | 16 | /// \returns The device type on which we're currently running or nullopt if 17 | /// detection fails. 18 | ErrorOr 19 | getDeviceType(); 20 | 21 | enum class InputType { MultiTouch, Pen, Key }; 22 | struct BaseDevice { 23 | InputType type = InputType::MultiTouch; 24 | Transform transform = {}; 25 | }; 26 | 27 | std::optional 28 | getBaseDevice(std::string_view name); 29 | 30 | // TODO: battery paths 31 | 32 | std::vector 33 | listDirectory(std::string_view path, bool onlyFiles = true); 34 | 35 | bool 36 | isPogoConnected(); 37 | 38 | } // namespace rmlib::device 39 | -------------------------------------------------------------------------------- /libs/rMlib/include/Error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | struct Error { 11 | std::string msg; 12 | 13 | static tl::unexpected make(std::string msg) { 14 | return tl::unexpected(Error{ std::move(msg) }); 15 | } 16 | 17 | Error(std::errc err) : msg(std::make_error_code(err).message()) {} 18 | Error(std::string msg) : msg(std::move(msg)) {} 19 | }; 20 | 21 | inline std::string 22 | to_string(const Error& err) { // NOLINT 23 | return err.msg; 24 | } 25 | 26 | template 27 | using ErrorOr = tl::expected; 28 | 29 | template 30 | using OptError = tl::expected; 31 | -------------------------------------------------------------------------------- /libs/rMlib/include/FrameBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Canvas.h" 4 | #include "Error.h" 5 | 6 | #include 7 | 8 | namespace rmlib::fb { 9 | 10 | // Waveform ints that match rm2 'actual' updates 11 | enum class Waveform { DU = 0, GC16 = 1, GC16Fast = 2, A2 = 3 }; 12 | 13 | enum UpdateFlags { None = 0, FullRefresh = 1, Sync = 2, Priority = 4 }; 14 | 15 | struct FrameBuffer { 16 | enum Type { rM1, Shim, rM2Stuff }; // NOLINT 17 | 18 | /// Opens the framebuffer. 19 | static ErrorOr open(std::optional requestedSize = {}); 20 | 21 | FrameBuffer(FrameBuffer&& other) = default; 22 | 23 | FrameBuffer(const FrameBuffer&) = delete; 24 | FrameBuffer& operator=(const FrameBuffer&) = delete; 25 | 26 | FrameBuffer& operator=(FrameBuffer&& other) = default; 27 | 28 | /// Closes the framebuffer and unmaps the memory. 29 | ~FrameBuffer() { close(); } 30 | 31 | void doUpdate(Rect region, Waveform waveform, UpdateFlags flags) const; 32 | 33 | void drawText(std::string_view text, 34 | Point location, 35 | int size = default_text_size, 36 | Waveform waveform = Waveform::GC16Fast, 37 | UpdateFlags flags = UpdateFlags::None) { 38 | auto textSize = Canvas::getTextSize(text, size); 39 | canvas.drawText(text, location, size); 40 | doUpdate({ location, location + textSize }, waveform, flags); 41 | } 42 | 43 | void clear() { 44 | canvas.set(white); 45 | doUpdate(canvas.rect(), Waveform::GC16Fast, UpdateFlags::None); 46 | } 47 | 48 | // members 49 | Type type; 50 | unistdpp::FD fd; 51 | Canvas canvas; 52 | 53 | private: 54 | FrameBuffer(Type type, unistdpp::FD fd, Canvas canvas) 55 | : type(type), fd(std::move(fd)), canvas(std::move(canvas)) {} 56 | 57 | void close(); 58 | 59 | static ErrorOr detectType(); 60 | }; 61 | 62 | } // namespace rmlib::fb 63 | -------------------------------------------------------------------------------- /libs/rMlib/include/Graphics.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timower/rM2-stuff/b26e9c2bcff9c4d81bffc767e54498e0a7edcbde/libs/rMlib/include/Graphics.h -------------------------------------------------------------------------------- /libs/rMlib/include/UI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | /// Ideas: (most stolen from flutter) 26 | // * Widgets are cheap to create, so have no real state. 27 | // * StatefulWidget has state in seperate object, making it still cheap 28 | // * The state is actually associated with the underlying render object in the 29 | // scene tree. 30 | namespace rmlib { 31 | 32 | namespace details { 33 | static inline AppContext* currentContext = nullptr; // NOLINT 34 | 35 | inline void 36 | stop(int signal) { 37 | currentContext->stop(); 38 | } 39 | 40 | } // namespace details 41 | 42 | template 43 | OptError<> 44 | runApp(AppWidget widget, std::optional size = {}) { 45 | auto context = TRY(AppContext::makeContext(size)); 46 | details::currentContext = &context; 47 | 48 | // TODO: fix widget lifetime 49 | context.setRootRenderObject(widget.createRenderObject()); 50 | 51 | std::signal(SIGINT, details::stop); 52 | std::signal(SIGTERM, details::stop); 53 | 54 | while (!context.shouldStop()) { 55 | context.step(); 56 | } 57 | 58 | std::signal(SIGINT, SIG_DFL); 59 | std::signal(SIGTERM, SIG_DFL); 60 | details::currentContext = nullptr; 61 | 62 | return {}; 63 | } 64 | 65 | } // namespace rmlib 66 | -------------------------------------------------------------------------------- /libs/rMlib/include/UI/BuildContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace rmlib { 6 | class RenderObject; 7 | 8 | } // namespace rmlib 9 | -------------------------------------------------------------------------------- /libs/rMlib/include/UI/Button.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace rmlib { 7 | 8 | class Button : public StatefulWidget