diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 87e84e5fc..71799403c 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -299,6 +299,7 @@ void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded, b m_calculationDisplayOutput = calculation->displayOutput(context); // We must set which subviews are displayed before setLayouts to mark the right rectangle as dirty + m_scrollableOutputView.setDisplayableCenter(m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximate || m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximateToggle); m_scrollableOutputView.setDisplayCenter(m_calculationDisplayOutput == Calculation::DisplayOutput::ExactAndApproximate || m_calculationExpanded); m_scrollableOutputView.setLayouts(Poincare::Layout(), exactOutputLayout, approximateOutputLayout); I18n::Message equalMessage = calculation->exactAndApproximateDisplayedOutputsAreEqual(context) == Calculation::EqualSign::Equal ? I18n::Message::Equal : I18n::Message::AlmostEqual; diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index 2d01720da..c550d19f3 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -451,30 +451,6 @@ void ConsoleController::printText(const char * text, size_t length) { flushOutputAccumulationBufferToStore(); micropython_port_vm_hook_refresh_print(); } -// #if __EMSCRIPTEN__ - /* If we called micropython_port_interrupt_if_needed here, we would need to - * put in the WHITELIST all the methods that call - * ConsoleController::printText, which means all the MicroPython methods that - * call print... This is a lot of work + might reduce the performance as - * emterpreted code is slower. - * - * We thus do not allow print interruption on the web simulator. It would be - * better to allow it, but the biggest problem was on the device anyways - * -> It is much quicker to interrupt Python on the web simulator than on the - * device. - * - * TODO: Allow print interrpution on emscripten -> maybe by using WASM=1 ? */ - - /* - * This can be run in Omega, since it uses WebASM. - */ -// #else - /* micropython_port_vm_hook_loop is not enough to detect user interruptions, - * because it calls micropython_port_interrupt_if_needed every 20000 - * operations, and a print operation is quite long. We thus explicitely call - * micropython_port_interrupt_if_needed here. */ - micropython_port_interrupt_if_needed(); -// #endif } void ConsoleController::autoImportScript(Script script, bool force) { diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n index c2226b9d0..7775218d0 100644 --- a/apps/shared.universal.i18n +++ b/apps/shared.universal.i18n @@ -336,28 +336,28 @@ PHilaireLeRoux = "@0Babass2" SpeedOfLight = "2.99792458·10^8_m_s^-1" YearLight = "9.461·10^15_m" Boltzmann = "1.380649·10^-23_J_K^-1" -StefanBoltzmann = "5.670374·10^-8_W_m^-2_K^-4" -VacuumImpedance = "376.730313461_Ω" +StefanBoltzmann = "5.670374419·10^-8_W_m^-2_K^-4" +VacuumImpedance = "376.730313668_Ω" WaterTriplePoint = "273.16_K" AtmosphericPressure = "101325_Pa" -AtomicMassUnit = "1.660539040·10^-27_kg" -Wien = "2.8977729·10^-3_K_m" -BohrRadius = "5.2917721067·10^-11_m" -BohrMagneton = "9.274009994·10^-24_A_m^2" -NuclearMagneton = "5.050783699·10^-27_A_m^2" -MuonMass = "1.883531594·10^-28_kg" +AtomicMassUnit = "1.66053906660·10^-27_kg" +Wien = "2.897771955·10^-3_K_m" +BohrRadius = "5.29177210903·10^-11_m" +BohrMagneton = "9.2740100783·10^-24_A_m^2" +NuclearMagneton = "5.0507837461·10^-27_A_m^2" +MuonMass = "1.883531627·10^-28_kg" Avogadro = "6.02214076·10^23_mol^-1" -Gas = "8.31446261815324_J_mol^-1_K^-1" -Coulomb = "8.9875517887·10^9_N_m^2_C^-2" +Gas = "8.314462618_J_mol^-1_K^-1" +Coulomb = "8.9875517923·10^9_N_m^2_C^-2" Vacuum_permittivity = "8.8541878128·10^-12_F_m^-1" -Vacuum_permeability = "4π·10^-7_H_m^-1" +Vacuum_permeability = "1.25663706212·10^-6_H_m^-1" Planck = "6.62607015·10^-34_J_s" -ElectronMass = "9.109383·10^-31_kg" -ProtonMass = "1.672649·10^-27_kg" -NeutronMass = "1.67493·10^-27_kg" -ElementalCharge = "1.60217662·10^-19_C" +ElectronMass = "9.1093837015·10^-31_kg" +ProtonMass = "1.67262192369·10^-27_kg" +NeutronMass = "1.67492749804·10^-27_kg" +ElementalCharge = "1.602176634·10^-19_C" GAcceleration = "9.80665_m_s^-2" -GConstant = "6.674·10^-11_m^3_kg^-1_s^-2" +GConstant = "6.67430·10^-11_m^3_kg^-1_s^-2" SpeedOfSound0 = "343.4_m_s^-1" SpeedOfSoundWater = "1480_m_s^-1" SpeedOfSoundSteel = "5600_m_s^-1" @@ -370,7 +370,7 @@ EarthRadius = "6378140_m" MoonRadius = "3474600_m" EarthMoonDistance = "384400000_m" EarthSunDistance = "149597870000_m" -FaradayConstant = "96484_C_mol^-1" +FaradayConstant = "96485.33212_C_mol^-1" EscapeVelocityOfEarth = "11186_m_s^-1" EscapeVelocityOfMoon = "2380_m_s^-1" EscapeVelocityOfSun = "42100_m_s^-1" @@ -409,11 +409,11 @@ Pka14Value = "2.1" Pka15Value = "1.9" Pka16Value = "0" Pka = "pKa" -PlanckReduce = "1.504571800·10^-34_J_s" -PlanckMass = "2.176470·10^-8_kg" -PlanckLength = "1.616229·10^-35_m" -PlanckTime = "5.39116·10^-44_s" -PlanckTemperature = "1.416808·10^32_K" +PlanckReduce = "1.054571817·10^-34_J_s" +PlanckMass = "2.176434·10^-8_kg" +PlanckLength = "1.616255·10^-35_m" +PlanckTime = "5.391247·10^-44_s" +PlanckTemperature = "1.416784·10^32_K" PlanckCharge = "1.875·10^-18_C" PlanckForce = "1.210·10^44_N" PlanckEnergy = "1.956·10^9_J" @@ -425,12 +425,12 @@ PlanckTension = "1.0432·10^27_V" PlanckCurrent = "3.479·10^25_A" PlanckPressure = "4.635·10^113_Pa" PlanckImpedance = "29.986_Ω" -TauonMass = "3.16747·10^-27_kg" +TauonMass = "3.16754·10^-27_kg" WBosonMass = "1.4334·10^-25_kg" ZBosonMass = "1.62556·10^-25_kg" -FineStructure = "7.2973525664·10^-3" -RydbergConstant = "1.0973731568508·10^7_m^-1" -HartreeConstant = "4.359744650·10^-18_J" -MagneticFluxQuantum = "2.067833831·10^-15_Wb" -ConductanceQuantum = "7.7480917310·10^-5_S" -CirculationQuantum = "3.6369475486·10^-4_m^2_s^-1" \ No newline at end of file +FineStructure = "7.2973525693·10^-3" +RydbergConstant = "10973731.568160_m^-1" +HartreeConstant = "4.3597447222071·10^-18_J" +MagneticFluxQuantum = "2.067833848·10^-15_Wb" +ConductanceQuantum = "7.748091729·10^-5_S" +CirculationQuantum = "3.6369475516·10^-4_m^2_s^-1" \ No newline at end of file diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index 933f04021..5d0331f54 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -172,7 +172,7 @@ KDSize AbstractScrollableMultipleExpressionsView::ContentCell::privateMinimalSiz } KDSize centerSize = KDSizeZero; - if (displayCenter() || (forceFullDisplay && !m_centeredExpressionView.layout().isUninitialized())) { + if (displayCenter() || (forceFullDisplay && displayableCenter())) { centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); width += centerSize.width() + 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); } diff --git a/apps/shared/scrollable_multiple_expressions_view.h b/apps/shared/scrollable_multiple_expressions_view.h index 89dc76b16..54f1e67b0 100644 --- a/apps/shared/scrollable_multiple_expressions_view.h +++ b/apps/shared/scrollable_multiple_expressions_view.h @@ -33,6 +33,7 @@ public: } bool displayCenter() const { return constContentCell()->displayCenter(); } void setDisplayCenter(bool display); + void setDisplayableCenter(bool displayable) { contentCell()->setDisplayableCenter(displayable); } void reloadScroll(); bool handleEvent(Ion::Events::Event event) override; Poincare::Layout layout() const { return constContentCell()->layout(); } @@ -63,7 +64,9 @@ protected: } void setSelectedSubviewPosition(SubviewPosition subviewPosition); bool displayCenter() const { return m_displayCenter && !m_centeredExpressionView.layout().isUninitialized(); } + bool displayableCenter() const { return m_displayableCenter && !m_centeredExpressionView.layout().isUninitialized(); } void setDisplayCenter(bool display); + void setDisplayableCenter(bool displayable) {m_displayableCenter = displayable;} void layoutSubviews(bool force = false) override; int numberOfSubviews() const override; virtual Poincare::Layout layout() const override; @@ -79,6 +82,7 @@ protected: ExpressionView m_centeredExpressionView; SubviewPosition m_selectedSubviewPosition; bool m_displayCenter; + bool m_displayableCenter; }; virtual ContentCell * contentCell() = 0; virtual const ContentCell * constContentCell() const = 0; diff --git a/build/rules.mk b/build/rules.mk index 6698e3783..9089cebce 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -3,7 +3,7 @@ $(eval $(call rule_for, \ AS, %.o, %.s, \ $$(CC) $$(SFLAGS) -c $$< -o $$@, \ - global \ + global local \ )) $(eval $(call rule_for, \ @@ -56,7 +56,7 @@ $(eval $(call rule_for, \ $(eval $(call rule_for, \ WINDRES, %.o, %.rc, \ - $$(WINDRES) $$< -O coff -o $$@, \ + $$(WINDRES) $$(WRFLAGS) $$< -O coff -o $$@, \ global \ )) diff --git a/docs/build/index.md b/docs/build/index.md index b34fd48ca..18a34197b 100644 --- a/docs/build/index.md +++ b/docs/build/index.md @@ -11,7 +11,7 @@ breadcrumb: SDK We recommend using the [Msys2](https://www.msys2.org/) environment to install most of the required tools. We support Windows 7 and up. Once Msys2 has been installed, launch the Msys2 terminal application, and enter the following commands ``` -pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-freetype mingw-w64-x86_64-pkg-config mingw-w64-x86_64-fltk git make python +pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-freetype mingw-w64-x86_64-pkg-config mingw-w64-x86_64-libusb git make python echo "export PATH=/mingw64/bin:$PATH" >> .bashrc ``` diff --git a/ion/include/ion/events.h b/ion/include/ion/events.h index d4ded6253..b5340e2f2 100644 --- a/ion/include/ion/events.h +++ b/ion/include/ion/events.h @@ -60,6 +60,7 @@ bool isLockActive(); void setLongRepetition(bool longRepetition); bool isLongRepetition(); void updateModifiersFromEvent(Event e); +void didPressNewKey(); // Plain diff --git a/ion/include/ion/keyboard.h b/ion/include/ion/keyboard.h index f2b8042bd..ba056ffd2 100644 --- a/ion/include/ion/keyboard.h +++ b/ion/include/ion/keyboard.h @@ -43,7 +43,9 @@ public: } operator uint64_t() const { return m_bitField; } void setKey(Key k) { - m_bitField |= (uint64_t)1 << (uint8_t)k; + if (k != Key::None) { + m_bitField |= (uint64_t)1 << (uint8_t)k; + } } void clearKey(Key k) { m_bitField &= ~((uint64_t)1 << (uint8_t)k); diff --git a/ion/src/device/shared/Makefile b/ion/src/device/shared/Makefile index d13ec99b6..2db3b7db4 100644 --- a/ion/src/device/shared/Makefile +++ b/ion/src/device/shared/Makefile @@ -3,5 +3,6 @@ include ion/src/device/shared/usb/Makefile include ion/src/device/shared/drivers/Makefile ion_device_src += $(addprefix ion/src/device/shared/, \ + events.cpp \ stack.cpp \ ) diff --git a/ion/src/device/shared/events.cpp b/ion/src/device/shared/events.cpp new file mode 100644 index 000000000..6eab2b3fc --- /dev/null +++ b/ion/src/device/shared/events.cpp @@ -0,0 +1,10 @@ +#include + +namespace Ion { +namespace Events { + +void didPressNewKey() { +} + +} +} diff --git a/ion/src/device/shared/usb/calculator.h b/ion/src/device/shared/usb/calculator.h index 6dd9679f5..9b3a2f28f 100644 --- a/ion/src/device/shared/usb/calculator.h +++ b/ion/src/device/shared/usb/calculator.h @@ -66,7 +66,7 @@ public: 0, // bInterfaceNumber k_dfuInterfaceAlternateSetting, // bAlternateSetting 0, // bNumEndpoints: Other than endpoint 0 - 0xFE, // bInterfaceClass: DFU (http://www.usb.org/developers/defined_class) + 0xFE, // bInterfaceClass: DFU (https://www.usb.org/defined-class-codes) 1, // bInterfaceSubClass: DFU 2, // bInterfaceProtocol: DFU Mode (not DFU Runtime, which would be 1) 4, // iInterface: Index of the Interface string, see m_descriptor diff --git a/ion/src/shared/events_keyboard.cpp b/ion/src/shared/events_keyboard.cpp index 57ac683d0..d579e3719 100644 --- a/ion/src/shared/events_keyboard.cpp +++ b/ion/src/shared/events_keyboard.cpp @@ -65,6 +65,7 @@ Event getEvent(int * timeout) { if (keysSeenTransitionningFromUpToDown != 0) { sEventIsRepeating = false; resetLongRepetition(); + didPressNewKey(); /* The key that triggered the event corresponds to the first non-zero bit * in "match". This is a rather simple logic operation for the which many * processors have an instruction (ARM thumb uses CLZ). diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index 13591528e..c5b90d349 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -12,6 +12,7 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ rtc.cpp \ crc32.cpp \ display.cpp:-headless \ + events.cpp \ events_keyboard.cpp:-headless \ events_stdin.cpp:+headless \ framebuffer_base.cpp \ @@ -26,5 +27,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ timing.cpp \ ) +ion_simulator_assets = background.jpg horizontal_arrow.png vertical_arrow.png round.png small_squircle.png large_squircle.png +ion_simulator_assets_paths = $(add_prefix ion/src/simulator/assets/,$(ion_simulator_assets)) + include ion/src/simulator/$(TARGET)/Makefile include ion/src/simulator/external/Makefile diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index 8ec86055c..5134ae7cb 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -1,10 +1,12 @@ ion_src += $(addprefix ion/src/simulator/android/src/cpp/, \ images.cpp \ + haptics_enabled.cpp \ ) ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ dummy/language.cpp \ + haptics.cpp \ ) ion_src += ion/src/shared/collect_registers.cpp diff --git a/ion/src/simulator/android/src/cpp/haptics_enabled.cpp b/ion/src/simulator/android/src/cpp/haptics_enabled.cpp new file mode 100644 index 000000000..3f2e77487 --- /dev/null +++ b/ion/src/simulator/android/src/cpp/haptics_enabled.cpp @@ -0,0 +1,20 @@ +#include "../../../shared/haptics.h" +#include +#include + +namespace Ion { +namespace Simulator { +namespace Haptics { + +bool isEnabled() { + JNIEnv * env = static_cast(SDL_AndroidGetJNIEnv()); + jobject activity = static_cast(SDL_AndroidGetActivity()); + jclass j_class = env->FindClass("com/numworks/calculator/EpsilonActivity"); + jmethodID j_methodId = env->GetMethodID(j_class,"hapticFeedbackIsEnabled", "()Z"); + + return env->CallObjectMethod(activity, j_methodId); +} + +} +} +} diff --git a/ion/src/simulator/android/src/cpp/images.cpp b/ion/src/simulator/android/src/cpp/images.cpp index f4f2cec99..b96a9647c 100644 --- a/ion/src/simulator/android/src/cpp/images.cpp +++ b/ion/src/simulator/android/src/cpp/images.cpp @@ -25,6 +25,8 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi AndroidBitmap_lockPixels(env, j_bitmap, &bitmapPixels); // TODO: Handle the case where lockPixels fails + size_t bytesPerPixel = 4; + SDL_Texture * texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_ABGR8888, @@ -37,9 +39,11 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi texture, nullptr, bitmapPixels, - 4 * bitmapInfo.width + bytesPerPixel * bitmapInfo.width ); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + AndroidBitmap_unlockPixels(env, j_bitmap); return texture; diff --git a/ion/src/simulator/android/src/java/io/github/omega/simulator/OmegaActivity.java b/ion/src/simulator/android/src/java/io/github/omega/simulator/OmegaActivity.java index e0613a692..83b94a5c1 100644 --- a/ion/src/simulator/android/src/java/io/github/omega/simulator/OmegaActivity.java +++ b/ion/src/simulator/android/src/java/io/github/omega/simulator/OmegaActivity.java @@ -3,9 +3,12 @@ package io.github.omega.simulator; import java.util.Locale; import android.app.Activity; +import android.content.Context; +import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; +import android.provider.Settings; import android.util.Log; import com.google.android.gms.analytics.GoogleAnalytics; @@ -13,6 +16,7 @@ import com.google.android.gms.analytics.Tracker; import com.google.android.gms.analytics.HitBuilders; import org.libsdl.app.SDLActivity; +import org.libsdl.app.SDL; public class OmegaActivity extends SDLActivity { protected String[] getLibraries() { @@ -40,6 +44,12 @@ public class OmegaActivity extends SDLActivity { return bitmap; } + public boolean hapticFeedbackIsEnabled() { + ContentResolver contentResolver = SDL.getContext().getContentResolver(); + int val = Settings.System.getInt(contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 0); + return val != 0; + } + @Override protected void onCreate(Bundle savedInstanceState) { /* This is done to hide the status bar and the bottom navigation buttons. diff --git a/ion/src/simulator/assets/horizontal_arrow.png b/ion/src/simulator/assets/horizontal_arrow.png new file mode 100644 index 000000000..37c38df24 Binary files /dev/null and b/ion/src/simulator/assets/horizontal_arrow.png differ diff --git a/ion/src/simulator/assets/large_squircle.png b/ion/src/simulator/assets/large_squircle.png new file mode 100644 index 000000000..b83f4e223 Binary files /dev/null and b/ion/src/simulator/assets/large_squircle.png differ diff --git a/ion/src/simulator/assets/round.png b/ion/src/simulator/assets/round.png new file mode 100644 index 000000000..2085d125d Binary files /dev/null and b/ion/src/simulator/assets/round.png differ diff --git a/ion/src/simulator/assets/small_squircle.png b/ion/src/simulator/assets/small_squircle.png new file mode 100644 index 000000000..9e7583a4f Binary files /dev/null and b/ion/src/simulator/assets/small_squircle.png differ diff --git a/ion/src/simulator/assets/vertical_arrow.png b/ion/src/simulator/assets/vertical_arrow.png new file mode 100644 index 000000000..05672b9a5 Binary files /dev/null and b/ion/src/simulator/assets/vertical_arrow.png differ diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index 334e1d4d1..e3496ac80 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -5,6 +5,8 @@ ion_src += $(addprefix ion/src/simulator/ios/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ apple/language.m \ dummy/callback.cpp \ + dummy/haptics_enabled.cpp \ + haptics.cpp \ ) ion_src += ion/src/shared/collect_registers.cpp diff --git a/ion/src/simulator/ios/images.m b/ion/src/simulator/ios/images.m index c255aec61..6a68fe2c4 100644 --- a/ion/src/simulator/ios/images.m +++ b/ion/src/simulator/ios/images.m @@ -16,7 +16,9 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi size_t bytesPerRow = bytesPerPixel * width; size_t bitsPerComponent = 8; - void * bitmapData = malloc(height * width * bytesPerPixel); + size_t size = height * width * bytesPerPixel; + void * bitmapData = malloc(size); + memset(bitmapData, 0, size); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate( @@ -42,9 +44,11 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi texture, NULL, bitmapData, - 4 * width + bytesPerPixel * width ); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + free(bitmapData); return texture; diff --git a/ion/src/simulator/linux/Makefile b/ion/src/simulator/linux/Makefile index aa8500471..256685745 100644 --- a/ion/src/simulator/linux/Makefile +++ b/ion/src/simulator/linux/Makefile @@ -10,13 +10,15 @@ SFLAGS += -Iion/src/simulator/linux/include ion_src += $(addprefix ion/src/simulator/linux/, \ images.cpp \ language.cpp \ - background.s \ + assets.s \ ) ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ + dummy/haptics_enabled.cpp \ collect_registers_x86_64.s \ collect_registers.cpp \ + haptics.cpp \ ) ifeq ($(EPSILON_TELEMETRY),1) @@ -25,3 +27,16 @@ ion_src += ion/src/shared/telemetry_console.cpp endif LDFLAGS += -ljpeg + +$(eval $(call rule_for, \ + INCBIN, \ + ion/src/simulator/linux/assets.s ion/src/simulator/linux/images.h, \ + $(ion_simulator_assets_paths), \ + $$(PYTHON) ion/src/simulator/linux/incbin.py $(ion_simulator_assets) -o $$@, \ + global \ +)) + +$(call object_for,ion/src/simulator/linux/images.cpp): $(BUILD_DIR)/ion/src/simulator/linux/images.h + +# The header is refered to as so make sure it's findable this way +SFLAGS += -I$(BUILD_DIR) diff --git a/ion/src/simulator/linux/background.s b/ion/src/simulator/linux/background.s deleted file mode 100644 index 48382bb15..000000000 --- a/ion/src/simulator/linux/background.s +++ /dev/null @@ -1,5 +0,0 @@ -.global _ion_simulator_background_start -.global _ion_simulator_background_end -_ion_simulator_background_start: - .incbin "ion/src/simulator/assets/background.jpg" -_ion_simulator_background_end: diff --git a/ion/src/simulator/linux/images.cpp b/ion/src/simulator/linux/images.cpp index b9143a821..dbe94b123 100644 --- a/ion/src/simulator/linux/images.cpp +++ b/ion/src/simulator/linux/images.cpp @@ -2,49 +2,147 @@ #include #include +#include #include -extern unsigned char _ion_simulator_background_start; -extern unsigned char _ion_simulator_background_end; +#include -SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identifier) { +enum class AssetFormat { + JPG, + PNG +}; + +png_size_t readSize = 0; + +void ReadDataFromMemory(png_structp png, png_bytep outBytes, png_size_t byteSize) { + png_voidp io = png_get_io_ptr(png); + memcpy((char *)outBytes, (char *)io + readSize, byteSize); + readSize += byteSize; +} + +bool readPNG(unsigned char * start, size_t size, unsigned char ** bitmapData, unsigned int * width, unsigned int * height, unsigned int * bytesPerPixel, Uint32 * pixelFormat) { + readSize = 0; + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png) { + // Memory failure + return false; + } + png_infop info = png_create_info_struct(png); + if (!info) { + // Memory failure + png_destroy_read_struct(&png, NULL, NULL); + return false; + } + static constexpr size_t k_pngSignatureLength = 8; + if (png_sig_cmp((png_const_bytep)start, 0, k_pngSignatureLength) != 0) { + // Corrupted PNG + return false; + } + + png_set_read_fn(png, start, ReadDataFromMemory); + + png_read_info(png, info); + + int bitDepth = 0; + int colorType = -1; + png_get_IHDR(png, info, width, height, &bitDepth, &colorType, nullptr, nullptr, nullptr); + *bytesPerPixel = bitDepth*4/8; + + *pixelFormat = SDL_PIXELFORMAT_ARGB8888; + assert(colorType == PNG_COLOR_TYPE_RGB_ALPHA); + + const png_uint_32 bytesPerRow = png_get_rowbytes(png, info); + *bitmapData = new unsigned char[*height * bytesPerRow]; + unsigned char * rowData = *bitmapData; + for (unsigned int rowIndex = 0; rowIndex < *height; rowIndex++) { + png_read_row(png, static_cast(rowData), nullptr); + rowData += bytesPerRow; + } + + png_destroy_read_struct(&png, &info, nullptr); + return true; +} + +bool readJPG(const unsigned char * start, size_t size, unsigned char ** bitmapData, unsigned int * width, unsigned int * height, unsigned int * bytesPerPixel, Uint32 * pixelFormat) { + *pixelFormat = SDL_PIXELFORMAT_RGB24; struct jpeg_decompress_struct info; struct jpeg_error_mgr err; info.err = jpeg_std_error(&err); jpeg_create_decompress(&info); - unsigned char * jpegStart = &_ion_simulator_background_start; - unsigned long jpegSize = &_ion_simulator_background_end - &_ion_simulator_background_start; - jpeg_mem_src(&info, jpegStart, jpegSize); + jpeg_mem_src(&info, start, size); if (jpeg_read_header(&info, TRUE) != 1) { - return nullptr; + return false; } jpeg_start_decompress(&info); - int width = info.output_width; - int height = info.output_height; - int bytesPerPixel = info.output_components; - - unsigned char * bitmapData = new unsigned char[height * width * bytesPerPixel]; + *width = info.output_width; + *height = info.output_height; + *bytesPerPixel = info.output_components; + *bitmapData = new unsigned char[*height * *width * *bytesPerPixel]; while (info.output_scanline < info.output_height) { unsigned char * buffer_array[1]; - buffer_array[0] = bitmapData + info.output_scanline * width * bytesPerPixel; + buffer_array[0] = *bitmapData + info.output_scanline * *width * *bytesPerPixel; jpeg_read_scanlines(&info, buffer_array, 1); } jpeg_finish_decompress(&info); jpeg_destroy_decompress(&info); + return true; +} - Uint32 texturePixelFormat = SDL_PIXELFORMAT_RGB24; - assert(bytesPerPixel == SDL_BYTESPERPIXEL(texturePixelFormat)); +SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identifier) { + static constexpr const char * jpgExtension = ".jpg"; + static constexpr const char * pngExtension = ".png"; + + unsigned char * assetStart = nullptr; + unsigned long assertSize = 0; + AssetFormat format; + + // Find the asset corresponding to identifier + for (size_t i = 0; i < sizeof(resources_addresses)/sizeof(resources_addresses[0]); i++) { + if (strcmp(identifier, resources_addresses[i].identifier()) == 0) { + if (strcmp(jpgExtension, identifier + strlen(identifier) - strlen(jpgExtension)) == 0) { + format = AssetFormat::JPG; + } else { + assert(strcmp(pngExtension, identifier + strlen(identifier) - strlen(pngExtension)) == 0); + format = AssetFormat::PNG; + } + assetStart = resources_addresses[i].start(); + assertSize = resources_addresses[i].end() - resources_addresses[i].start(); + break; + } + } + assert(assetStart); + + unsigned int width; + unsigned int height; + unsigned int bytesPerPixel; + unsigned char * bitmapData; + Uint32 pixelFormat; + + switch (format) { + case AssetFormat::JPG: + if (!readJPG(assetStart, assertSize, &bitmapData, &width, &height, &bytesPerPixel, &pixelFormat)) { + return nullptr; + } + break; + default: + assert(format == AssetFormat::PNG); + if (!readPNG(assetStart, assertSize, &bitmapData, &width, &height, &bytesPerPixel, &pixelFormat)) { + return nullptr; + } + } + + assert(bytesPerPixel == SDL_BYTESPERPIXEL(pixelFormat)); SDL_Texture * texture = SDL_CreateTexture( renderer, - texturePixelFormat, + pixelFormat, SDL_TEXTUREACCESS_STATIC, width, height @@ -52,11 +150,13 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi SDL_UpdateTexture( texture, - NULL, + nullptr, bitmapData, width * bytesPerPixel ); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + delete[] bitmapData; return texture; diff --git a/ion/src/simulator/linux/incbin.py b/ion/src/simulator/linux/incbin.py new file mode 100644 index 000000000..f92eb03c8 --- /dev/null +++ b/ion/src/simulator/linux/incbin.py @@ -0,0 +1,70 @@ +# This script generates: +# - .s file representing assets in order to access them from C code +# - .h representing the mapping between symbols and assets + +import sys +import re +import argparse +import io +import os + +parser = argparse.ArgumentParser(description="Process some asset files.") +parser.add_argument('assets', metavar='asset', type=str, nargs='+', help='The list of assets to include') +parser.add_argument('-o', help='The file to generate') +args = parser.parse_args() + +def print_asset(f, asset): + asset_basename = os.path.splitext(asset)[0] + f.write(".global _ion_simulator_" + asset_basename + "_start\n") + f.write(".global _ion_simulator_" + asset_basename + "_end\n") + f.write("_ion_simulator_" + asset_basename + "_start:\n") + f.write(" .incbin \"ion/src/simulator/assets/" + asset + "\"\n") + f.write("_ion_simulator_" + asset_basename + "_end:\n\n") + +def print_assembly(files, path): + f = open(path, "w") + for asset in files: + print_asset(f, asset) + f.close() + +def print_declaration(f, asset): + asset_basename = os.path.splitext(asset)[0] + f.write("extern unsigned char _ion_simulator_" + asset_basename + "_start;\n") + f.write("extern unsigned char _ion_simulator_" + asset_basename + "_end;\n") + +def print_mapping(f, asset): + asset_basename = os.path.splitext(asset)[0] + f.write('ResourceMap("' + asset + '", &_ion_simulator_' + asset_basename +'_start, &_ion_simulator_' + asset_basename + '_end),\n') + +def print_header(files, path): + f = open(path, "w") + f.write("#ifndef ION_SIMULATOR_LINUX_IMAGES_H\n") + f.write("#define ION_SIMULATOR_LINUX_IMAGES_H\n\n") + f.write("// This file is auto-generated by incbin.py\n\n") + + for asset in files: + print_declaration(f, asset) + + f.write("\nclass ResourceMap {\n") + f.write("public:\n") + f.write(" constexpr ResourceMap(const char * identifier, unsigned char * start, unsigned char * end) : m_identifier(identifier), m_start(start), m_end(end) {}\n") + f.write(" const char * identifier() const { return m_identifier; }\n") + f.write(" unsigned char * start() const { return m_start; }\n") + f.write(" unsigned char * end() const { return m_end; }\n") + f.write("private:\n") + f.write(" const char * m_identifier;\n") + f.write(" unsigned char * m_start;\n") + f.write(" unsigned char * m_end;\n") + f.write("};\n\n") + f.write("static constexpr ResourceMap resources_addresses[] = {\n") + for asset in files: + print_mapping(f, asset) + + f.write("};\n\n") + f.write("#endif\n") + f.close() + +if (args.o.endswith(".s")): + print_assembly(args.assets, args.o) +if (args.o.endswith(".h")): + print_header(args.assets, args.o) diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index b00459f75..8171ffff2 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -5,8 +5,10 @@ ion_src += $(addprefix ion/src/simulator/macos/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ apple/language.m \ dummy/callback.cpp \ + dummy/haptics_enabled.cpp \ collect_registers_x86_64.s \ collect_registers.cpp \ + haptics.cpp \ ) ifeq ($(EPSILON_TELEMETRY),1) diff --git a/ion/src/simulator/macos/images.m b/ion/src/simulator/macos/images.m index 8e19b3b49..03c82ab51 100644 --- a/ion/src/simulator/macos/images.m +++ b/ion/src/simulator/macos/images.m @@ -14,12 +14,13 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi size_t width = CGImageGetWidth(cgImage); size_t height = CGImageGetHeight(cgImage); - size_t bytesPerPixel = 4; size_t bytesPerRow = bytesPerPixel * width; size_t bitsPerComponent = 8; - void * bitmapData = malloc(height * width * bytesPerPixel); + size_t size = height * width * bytesPerPixel; + void * bitmapData = malloc(size); + memset(bitmapData, 0, size); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate( @@ -45,9 +46,11 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi texture, NULL, bitmapData, - 4 * width + bytesPerPixel * width ); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + free(bitmapData); return texture; diff --git a/ion/src/simulator/shared/apple/helpers.mak b/ion/src/simulator/shared/apple/helpers.mak index dbbf8fc17..85e4b4f86 100644 --- a/ion/src/simulator/shared/apple/helpers.mak +++ b/ion/src/simulator/shared/apple/helpers.mak @@ -14,11 +14,16 @@ $(simulator_app_binary): $(foreach arch,$(ARCHS),$(BUILD_DIR)/$(arch)/%.bin) | $ $(call rule_label,LIPO) $(Q) $(LIPO) -create $^ -output $@ -# Background image +# Background & Keys images -$(call simulator_app_resource,background.jpg): ion/src/simulator/assets/background.jpg | $$(@D)/. +define rule_for_asset +simulator_app_deps += $(call simulator_app_resource,$(1)) +$(call simulator_app_resource,$(1)): ion/src/simulator/assets/$(1) | $$$$(@D)/. $(call rule_label,COPY) - $(Q) cp $^ $@ + $(Q) cp $$^ $$@ +endef + +$(foreach asset,$(ion_simulator_assets),$(eval $(call rule_for_asset,$(asset)))) # Process icons @@ -40,4 +45,3 @@ $(addprefix $(SIMULATOR_ICONSET)/,icon_%.png): ion/src/simulator/assets/logo.svg simulator_app_deps += $(simulator_app_binary) simulator_app_deps += $(call simulator_app_plist,Info.plist) -simulator_app_deps += $(call simulator_app_resource,background.jpg) diff --git a/ion/src/simulator/shared/display.cpp b/ion/src/simulator/shared/display.cpp index 28069012d..28994c8ab 100644 --- a/ion/src/simulator/shared/display.cpp +++ b/ion/src/simulator/shared/display.cpp @@ -32,7 +32,7 @@ void draw(SDL_Renderer * renderer, SDL_Rect * rect) { int pitch = 0; void * pixels = nullptr; SDL_LockTexture(sFramebufferTexture, nullptr, &pixels, &pitch); - assert(pitch == 2*Ion::Display::Width); + assert(pitch == sizeof(KDColor)*Ion::Display::Width); memcpy(pixels, Framebuffer::address(), sizeof(KDColor)*Ion::Display::Width*Ion::Display::Height); SDL_UnlockTexture(sFramebufferTexture); diff --git a/ion/src/simulator/shared/dummy/haptics_enabled.cpp b/ion/src/simulator/shared/dummy/haptics_enabled.cpp new file mode 100644 index 000000000..ee9e48eb8 --- /dev/null +++ b/ion/src/simulator/shared/dummy/haptics_enabled.cpp @@ -0,0 +1,15 @@ +#include "../haptics.h" + +namespace Ion { +namespace Simulator { +namespace Haptics { + +bool isEnabled() { + /* Dummy default to false as failsafe. + * Avoid to get haptics feedback that are not desactivable. */ + return false; +} + +} +} +} diff --git a/ion/src/simulator/shared/events.cpp b/ion/src/simulator/shared/events.cpp new file mode 100644 index 000000000..a522fe7dd --- /dev/null +++ b/ion/src/simulator/shared/events.cpp @@ -0,0 +1,13 @@ +#include +#include "haptics.h" +#include + +namespace Ion { +namespace Events { + +void didPressNewKey() { + Simulator::Haptics::rumble(); +} + +} +} diff --git a/ion/src/simulator/shared/events_keyboard.cpp b/ion/src/simulator/shared/events_keyboard.cpp index 806372e19..26d1ba208 100644 --- a/ion/src/simulator/shared/events_keyboard.cpp +++ b/ion/src/simulator/shared/events_keyboard.cpp @@ -1,5 +1,6 @@ #include "main.h" #include "platform.h" +#include "layout.h" #include #include @@ -193,6 +194,18 @@ Event getPlatformEvent() { result = eventFromSDLTextInputEvent(event.text); break; } +#if !EPSILON_SDL_SCREEN_ONLY + if (event.type == SDL_MOUSEMOTION) { + SDL_Point p; + SDL_GetMouseState(&p.x, &p.y); + Simulator::Layout::highlightKeyAt(&p); + } + /* On smarphones, don't forget to unhighlight the key when the finger is up. + * (finger up doesn't imply a mouse motion!) */ + if (event.type == SDL_FINGERUP) { + Simulator::Layout::unhighlightKey(); + } +#endif } if (result != None) { /* When events are not being processed - for instance when a Python script diff --git a/ion/src/simulator/shared/haptics.cpp b/ion/src/simulator/shared/haptics.cpp new file mode 100644 index 000000000..f0b61b2ea --- /dev/null +++ b/ion/src/simulator/shared/haptics.cpp @@ -0,0 +1,35 @@ +#include "haptics.h" +#include + +namespace Ion { +namespace Simulator { +namespace Haptics { + +static SDL_Haptic * sSDLHaptic = nullptr; + +void init() { + if (SDL_Init(SDL_INIT_HAPTIC) == 0) { + sSDLHaptic = SDL_HapticOpen(0); + if (sSDLHaptic) { + if (SDL_HapticRumbleInit(sSDLHaptic) != 0) { + sSDLHaptic = nullptr; + } + } + } +} + +void shutdown() { + if (sSDLHaptic) { + SDL_HapticClose(sSDLHaptic); + } +} + +void rumble() { + if (isEnabled() && sSDLHaptic) { + SDL_HapticRumblePlay(sSDLHaptic, 1.0, 40); + } +} + +} +} +} diff --git a/ion/src/simulator/shared/haptics.h b/ion/src/simulator/shared/haptics.h new file mode 100644 index 000000000..2adc20ede --- /dev/null +++ b/ion/src/simulator/shared/haptics.h @@ -0,0 +1,17 @@ +#ifndef ION_SIMULATOR_HAPTICS_H +#define ION_SIMULATOR_HAPTICS_H + +namespace Ion { +namespace Simulator { +namespace Haptics { + +void init(); +bool isEnabled(); +void rumble(); +void shutdown(); + +} +} +} + +#endif diff --git a/ion/src/simulator/shared/keyboard_sdl.cpp b/ion/src/simulator/shared/keyboard_sdl.cpp index 3b468914c..a75676aaa 100644 --- a/ion/src/simulator/shared/keyboard_sdl.cpp +++ b/ion/src/simulator/shared/keyboard_sdl.cpp @@ -69,15 +69,9 @@ State scan() { state = sKeyboardState; #else // Register a key for the mouse, if any - SDL_Point p; - Uint32 mouseState = SDL_GetMouseState(&p.x, &p.y); - if (!(previousState) && mouseState & SDL_BUTTON(SDL_BUTTON_LEFT)){ - Key k = Simulator::Layout::keyAt(&p); + Key k = Simulator::Layout::getHighlightedKey(); + if (SDL_GetMouseState(nullptr, nullptr) && SDL_BUTTON(SDL_BUTTON_LEFT)) { state.setKey(k); - previousState = true; - } - if (!(mouseState & SDL_BUTTON(SDL_BUTTON_LEFT))) { - previousState = false; } #endif diff --git a/ion/src/simulator/shared/layout.cpp b/ion/src/simulator/shared/layout.cpp index 088e5444c..ef07451f8 100644 --- a/ion/src/simulator/shared/layout.cpp +++ b/ion/src/simulator/shared/layout.cpp @@ -1,9 +1,16 @@ #include "layout.h" +#include "main.h" +#include "platform.h" +#include +#include +#include namespace Ion { namespace Simulator { namespace Layout { +#if !EPSILON_SDL_SCREEN_ONLY + static constexpr int backgroundWidth = 1160; static constexpr int backgroundHeight = 2220; @@ -15,16 +22,16 @@ static constexpr SDL_FRect screenRect = {X(192), Y(191), X(776), Y(582)}; static SDL_Rect sFrame; -static void makeAbsolute(const SDL_FRect * f, SDL_Rect * r) { - r->x = f->x * sFrame.w + sFrame.x; - r->y = f->y * sFrame.h + sFrame.y; - r->w = f->w * sFrame.w; - r->h = f->h * sFrame.h; +static void makeAbsolute(const SDL_FRect f, SDL_Rect * r) { + r->x = std::round(f.x * static_cast(sFrame.w) + static_cast(sFrame.x)); + r->y = std::round(f.y * static_cast(sFrame.h) + static_cast(sFrame.y)); + r->w = std::round(f.w * static_cast(sFrame.w)); + r->h = std::round(f.h * static_cast(sFrame.h)); } -static void makeAbsolute(const SDL_FPoint * f, SDL_Point * p) { - p->x = f->x * sFrame.w + sFrame.x; - p->y = f->y * sFrame.h + sFrame.y; +static void makeAbsolute(const SDL_FPoint f, SDL_Point * p) { + p->x = f.x * sFrame.w + sFrame.x; + p->y = f.y * sFrame.h + sFrame.y; } void recompute(int width, int height) { @@ -74,7 +81,7 @@ void recompute(int width, int height) { } void getScreenRect(SDL_Rect * rect) { - makeAbsolute(&screenRect, rect); + makeAbsolute(screenRect, rect); } void getBackgroundRect(SDL_Rect * rect) { @@ -84,86 +91,188 @@ void getBackgroundRect(SDL_Rect * rect) { rect->h = sFrame.h; } -static constexpr SDL_FPoint sKeyCenters[Keyboard::NumberOfValidKeys] = { - {X(185), Y(1029)}, // A1, Left - {X(273), Y(941)}, // A2, Up - {X(273), Y(1117)}, // A3, Down - {X(361), Y(1029)}, // A4, Right - {X(810), Y(1029)}, // A5, OK - {X(963), Y(1029)}, // A6, Back +class KeyLayout { +public: + enum class Shape : uint8_t { + HorizontalArrow, + VerticalArrow, + Round, + SmallSquircle, + LargeSquircle, + NumberOfShapes + }; + static constexpr size_t NumberOfShapes = (size_t)Shape::NumberOfShapes; + static constexpr const char * assetName[KeyLayout::NumberOfShapes] = { + "horizontal_arrow.png", + "vertical_arrow.png", + "round.png", + "small_squircle.png", + "large_squircle.png" + }; - {X(580), Y(958)}, // B1, Home - {X(580), Y(1094)}, // B2, Power + constexpr KeyLayout(float x, float y, Shape shape) : + m_center{X(x), Y(y)}, + m_shape(shape) {} + SDL_FPoint center() const { return m_center; } + Shape shape() const { return m_shape; } - {X(198), Y(1252)}, // C1, Shift - {X(352), Y(1252)}, // C2, Alpha - {X(506), Y(1252)}, // C3, xnt - {X(656), Y(1252)}, // C4, var - {X(810), Y(1252)}, // C5, toolbox - {X(963), Y(1252)}, // C6, Delete +private: + SDL_FPoint m_center; + Shape m_shape; +}; - {X(198), Y(1375)}, // D1, exp - {X(352), Y(1375)}, // D2, ln - {X(506), Y(1375)}, // D3, log - {X(656), Y(1375)}, // D4, i - {X(810), Y(1375)}, // D5, comma - {X(963), Y(1375)}, // D6, power +constexpr const char * const KeyLayout::assetName[KeyLayout::NumberOfShapes]; - {X(198), Y(1498)}, // E1, sin - {X(352), Y(1498)}, // E2, cos - {X(506), Y(1498)}, // E3, tan - {X(656), Y(1498)}, // E4, pi - {X(810), Y(1498)}, // E5, sqrt - {X(963), Y(1498)}, // E6, square +static constexpr KeyLayout sKeyLayouts[Keyboard::NumberOfValidKeys] = { + KeyLayout(195, 1029, KeyLayout::Shape::HorizontalArrow), // A1, Left + KeyLayout(273, 948, KeyLayout::Shape::VerticalArrow), // A2, Up + KeyLayout(273, 1108, KeyLayout::Shape::VerticalArrow), // A3, Down + KeyLayout(353, 1029, KeyLayout::Shape::HorizontalArrow), // A4, Right + KeyLayout(810, 1029, KeyLayout::Shape::Round), // A5, OK + KeyLayout(963, 1029, KeyLayout::Shape::Round), // A6, Back - {X(210), Y(1629)}, // F1, 7 - {X(395), Y(1629)}, // F2, 8 - {X(580), Y(1629)}, // F3, 9 - {X(765), Y(1629)}, // F4, ( - {X(950), Y(1629)}, // F5, ) + KeyLayout(580, 958, KeyLayout::Shape::LargeSquircle), // B1, Home + KeyLayout(580, 1094, KeyLayout::Shape::LargeSquircle), // B2, Power - {X(210), Y(1766)}, // G1, 4 - {X(395), Y(1766)}, // G2, 5 - {X(580), Y(1766)}, // G3, 6 - {X(765), Y(1766)}, // G4, * - {X(950), Y(1766)}, // G5, / + KeyLayout(198, 1253, KeyLayout::Shape::SmallSquircle), // C1, Shift + KeyLayout(352, 1253, KeyLayout::Shape::SmallSquircle), // C2, Alpha + KeyLayout(506, 1253, KeyLayout::Shape::SmallSquircle), // C3, xnt + KeyLayout(656, 1253, KeyLayout::Shape::SmallSquircle), // C4, var + KeyLayout(810, 1253, KeyLayout::Shape::SmallSquircle), // C5, toolbox + KeyLayout(963, 1253, KeyLayout::Shape::SmallSquircle), // C6, Delete - {X(210), Y(1902)}, // H1, 1 - {X(395), Y(1902)}, // H2, 2 - {X(580), Y(1902)}, // H3, 3 - {X(765), Y(1902)}, // H4, + - {X(950), Y(1902)}, // H5, - + KeyLayout(198, 1375, KeyLayout::Shape::SmallSquircle), // D1, exp + KeyLayout(352, 1375, KeyLayout::Shape::SmallSquircle), // D2, ln + KeyLayout(506, 1375, KeyLayout::Shape::SmallSquircle), // D3, log + KeyLayout(656, 1375, KeyLayout::Shape::SmallSquircle), // D4, i + KeyLayout(810, 1375, KeyLayout::Shape::SmallSquircle), // D5, comma + KeyLayout(963, 1375, KeyLayout::Shape::SmallSquircle), // D6, power - {X(210), Y(2040)}, // I1, 0 - {X(395), Y(2040)}, // I2, . - {X(580), Y(2040)}, // I3, x10 - {X(765), Y(2040)}, // I4, Ans - {X(950), Y(2040)}, // I5, EXE + KeyLayout(198, 1498, KeyLayout::Shape::SmallSquircle), // E1, sin + KeyLayout(352, 1498, KeyLayout::Shape::SmallSquircle), // E2, cos + KeyLayout(506, 1498, KeyLayout::Shape::SmallSquircle), // E3, tan + KeyLayout(656, 1498, KeyLayout::Shape::SmallSquircle), // E4, pi + KeyLayout(810, 1498, KeyLayout::Shape::SmallSquircle), // E5, sqrt + KeyLayout(963, 1498, KeyLayout::Shape::SmallSquircle), // E6, square + + KeyLayout(210, 1629, KeyLayout::Shape::LargeSquircle), // F1, 7 + KeyLayout(395, 1629, KeyLayout::Shape::LargeSquircle), // F2, 8 + KeyLayout(580, 1629, KeyLayout::Shape::LargeSquircle), // F3, 9 + KeyLayout(765, 1629, KeyLayout::Shape::LargeSquircle), // F4, ( + KeyLayout(950, 1629, KeyLayout::Shape::LargeSquircle), // F5, ) + + KeyLayout(210, 1766, KeyLayout::Shape::LargeSquircle), // G1, 4 + KeyLayout(395, 1766, KeyLayout::Shape::LargeSquircle), // G2, 5 + KeyLayout(580, 1766, KeyLayout::Shape::LargeSquircle), // G3, 6 + KeyLayout(765, 1766, KeyLayout::Shape::LargeSquircle), // G4, * + KeyLayout(950, 1766, KeyLayout::Shape::LargeSquircle), // G5, / + + KeyLayout(210, 1902, KeyLayout::Shape::LargeSquircle), // H1, 1 + KeyLayout(395, 1902, KeyLayout::Shape::LargeSquircle), // H2, 2 + KeyLayout(580, 1902, KeyLayout::Shape::LargeSquircle), // H3, 3 + KeyLayout(765, 1902, KeyLayout::Shape::LargeSquircle), // H4, + + KeyLayout(950, 1902, KeyLayout::Shape::LargeSquircle), // H5, - + + KeyLayout(210, 2040, KeyLayout::Shape::LargeSquircle), // I1, 0 + KeyLayout(395, 2040, KeyLayout::Shape::LargeSquircle), // I2, . + KeyLayout(580, 2040, KeyLayout::Shape::LargeSquircle), // I3, x10 + KeyLayout(765, 2040, KeyLayout::Shape::LargeSquircle), // I4, Ans + KeyLayout(950, 2040, KeyLayout::Shape::LargeSquircle), // I5, EXE }; static void getKeyCenter(int validKeyIndex, SDL_Point * point) { - assert(validKeyIndex >= 0); - assert(validKeyIndex < Keyboard::NumberOfValidKeys); - makeAbsolute(&sKeyCenters[validKeyIndex], point); + assert(validKeyIndex >= 0 && validKeyIndex < Keyboard::NumberOfValidKeys); + makeAbsolute(sKeyLayouts[validKeyIndex].center(), point); } -Keyboard::Key keyAt(SDL_Point * p) { - int minSquaredDistance = -1; - Keyboard::Key nearestKey = Keyboard::Key::None; +static void getKeyRectangle(int validKeyIndex, SDL_Texture * texture, SDL_Rect * rect) { + assert(validKeyIndex >= 0 && validKeyIndex < Keyboard::NumberOfValidKeys); + SDL_FPoint point = sKeyLayouts[validKeyIndex].center(); + int w, h; + SDL_QueryTexture(texture, NULL, NULL, &w, &h); + SDL_FRect fRect; + fRect.w = X(w); + fRect.h = Y(h); + fRect.x = point.x - fRect.w/2.0f; + fRect.y = point.y - fRect.h/2.0f; + makeAbsolute(fRect, rect); +} + +static SDL_Texture * sBackgroundTexture = nullptr; +static SDL_Texture * sKeyLayoutTextures[KeyLayout::NumberOfShapes]; + +void init(SDL_Renderer * renderer) { + sBackgroundTexture = IonSimulatorLoadImage(renderer, "background.jpg"); + for (size_t i = 0; i < KeyLayout::NumberOfShapes; i++) { + sKeyLayoutTextures[i] = IonSimulatorLoadImage(renderer, KeyLayout::assetName[i]); + } +} + +static int sHighlightedKeyIndex = -1; + +Keyboard::Key getHighlightedKey() { + Keyboard::Key k = Keyboard::Key::None; + if (sHighlightedKeyIndex >= 0) { + k = Keyboard::ValidKeys[sHighlightedKeyIndex]; + } + return k; +} + +void highlightKeyAt(SDL_Point * p) { + int newHighlightedKeyIndex = -1; + int minSquaredDistance = INT_MAX; + /* The closenessThreshold is apportioned to the size of the frame. As the + * width and the height have a constant ratio, we can compute the + * closenessThreshold from the frame width exclusively. */ + int closenessThreshold = sFrame.w/6; + int squaredClosenessThreshold = closenessThreshold*closenessThreshold; for (int i=0; ix; int dy = keyCenter.y - p->y; int squaredDistance = dx*dx + dy*dy; - if (squaredDistance < minSquaredDistance || minSquaredDistance < 0) { + if (squaredDistance < squaredClosenessThreshold && squaredDistance < minSquaredDistance) { minSquaredDistance = squaredDistance; - nearestKey = Keyboard::ValidKeys[i]; + newHighlightedKeyIndex = i; } } - return nearestKey; + if (newHighlightedKeyIndex != sHighlightedKeyIndex) { + sHighlightedKeyIndex = newHighlightedKeyIndex; + Main::setNeedsRefresh(); + } } +void unhighlightKey() { + sHighlightedKeyIndex = -1; + Main::setNeedsRefresh(); +} + +void drawHighlightedKey(SDL_Renderer * renderer) { + if (sHighlightedKeyIndex < 0) { + return; + } + int shape = static_cast(sKeyLayouts[sHighlightedKeyIndex].shape()); + SDL_Texture * keyTexture = sKeyLayoutTextures[shape]; + SDL_Rect rect; + getKeyRectangle(sHighlightedKeyIndex, keyTexture, &rect); + SDL_RenderCopy(renderer, keyTexture, nullptr, &rect); +} + +void draw(SDL_Renderer * renderer) { + SDL_Rect backgroundRect; + getBackgroundRect(&backgroundRect); + SDL_RenderCopy(renderer, sBackgroundTexture, nullptr, &backgroundRect); + drawHighlightedKey(renderer); +} + +void quit() { + SDL_DestroyTexture(sBackgroundTexture); + sBackgroundTexture = nullptr; +} + +#endif + } } } diff --git a/ion/src/simulator/shared/layout.h b/ion/src/simulator/shared/layout.h index 9e0f10ec6..91a990e1e 100644 --- a/ion/src/simulator/shared/layout.h +++ b/ion/src/simulator/shared/layout.h @@ -8,12 +8,22 @@ namespace Ion { namespace Simulator { namespace Layout { +#if !EPSILON_SDL_SCREEN_ONLY + void recompute(int width, int height); void getScreenRect(SDL_Rect * rect); void getBackgroundRect(SDL_Rect * rect); -Ion::Keyboard::Key keyAt(SDL_Point * p); +Ion::Keyboard::Key getHighlightedKey(); +void highlightKeyAt(SDL_Point * p); +void unhighlightKey(); + +void init(SDL_Renderer * renderer); +void draw(SDL_Renderer * renderer); +void quit(); + +#endif } } diff --git a/ion/src/simulator/shared/main_sdl.cpp b/ion/src/simulator/shared/main_sdl.cpp index 400cda9b0..7b85c02ee 100644 --- a/ion/src/simulator/shared/main_sdl.cpp +++ b/ion/src/simulator/shared/main_sdl.cpp @@ -1,7 +1,9 @@ #include "main.h" #include "display.h" -#include "platform.h" +#include "haptics.h" #include "layout.h" +#include "platform.h" +#include "telemetry.h" #include "random.h" #include @@ -93,6 +95,13 @@ int main(int argc, char * argv[]) { if (!argument_volatile) { savePython(); } + Ion::Simulator::Haptics::init(); + + ion_main(arguments.size(), &arguments[0]); + + // Shutdown + Ion::Simulator::Haptics::shutdown(); + Ion::Simulator::Main::quit(); #endif Ion::Simulator::Main::quit(); @@ -205,7 +214,6 @@ namespace Main { static SDL_Window * sWindow = nullptr; static SDL_Renderer * sRenderer = nullptr; -static SDL_Texture * sBackgroundTexture = nullptr; static bool sNeedsRefresh = false; static SDL_Rect sScreenRect; @@ -257,12 +265,9 @@ void init() { Display::init(sRenderer); - // No need to load background in web simulator. - #ifndef __EMSCRIPTEN__ - if (!argument_screen_only) { - sBackgroundTexture = IonSimulatorLoadImage(sRenderer, "background.jpg"); - } - #endif +#if !EPSILON_SDL_SCREEN_ONLY + Layout::init(sRenderer); +#endif relayout(); } @@ -273,6 +278,7 @@ void relayout() { SDL_GetWindowSize(sWindow, &windowWidth, &windowHeight); SDL_RenderSetLogicalSize(sRenderer, windowWidth, windowHeight); + #if !EPSILON_SDL_SCREEN_ONLY if (argument_screen_only) { // Keep original aspect ration in screen_only mode. float scale = (float)(Ion::Display::Width) / (float)(Ion::Display::Height); @@ -289,6 +295,12 @@ void relayout() { } else { Layout::recompute(windowWidth, windowHeight); } + #else + sScreenRect.x = 0; + sScreenRect.y = 0; + sScreenRect.w = windowWidth; + sScreenRect.h = windowHeight; + #endif setNeedsRefresh(); } @@ -302,27 +314,33 @@ void refresh() { return; } + #if EPSILON_SDL_SCREEN_ONLY + Display::draw(sRenderer, &sScreenRect); + #else if (argument_screen_only) { Display::draw(sRenderer, &sScreenRect); } else { SDL_Rect screenRect; Layout::getScreenRect(&screenRect); - SDL_Rect backgroundRect; - Layout::getBackgroundRect(&backgroundRect); SDL_SetRenderDrawColor(sRenderer, 194, 194, 194, 255); SDL_RenderClear(sRenderer); - SDL_RenderCopy(sRenderer, sBackgroundTexture, nullptr, &backgroundRect); + // Can change sNeedsRefresh state if a key is highlighted and needs to be reset + Layout::draw(sRenderer); Display::draw(sRenderer, &screenRect); } + #endif SDL_RenderPresent(sRenderer); - sNeedsRefresh = false; IonSimulatorCallbackDidRefresh(); } void quit() { +#if !EPSILON_SDL_SCREEN_ONLY + Layout::quit(); +#endif + Display::quit(); SDL_DestroyWindow(sWindow); SDL_Quit(); } diff --git a/ion/src/simulator/web/Makefile b/ion/src/simulator/web/Makefile index 61a7641dc..e8c729371 100644 --- a/ion/src/simulator/web/Makefile +++ b/ion/src/simulator/web/Makefile @@ -20,6 +20,8 @@ ion_src += $(addprefix ion/src/simulator/web/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/language.cpp \ + dummy/haptics_enabled.cpp \ + haptics.cpp \ ) ion_src += ion/src/shared/collect_registers.cpp diff --git a/ion/src/simulator/web/simulator.html.inc b/ion/src/simulator/web/simulator.html.inc index 577d4fb8c..ddb6452fb 100644 --- a/ion/src/simulator/web/simulator.html.inc +++ b/ion/src/simulator/web/simulator.html.inc @@ -109,17 +109,17 @@ a.action svg { KEYBOARD .nav span { position: absolute; } KEYBOARD .nav .left { - top: 36%; + top: 37%; left: 2%; width: 15%; height: 28%; } KEYBOARD .nav .right { - top: 36%; + top: 37%; left: 17%; width: 15%; height: 28%; } KEYBOARD .nav .top { - top: 7%; + top: 9%; left: 12%; width: 10%; height: 40%; } @@ -131,23 +131,25 @@ a.action svg { KEYBOARD .nav .home { top: 15%; left: 41%; - width: 16%; - height: 33%; } + width: 16.5%; + height: 31%; } KEYBOARD .nav .power { - top: 54%; - left: 42%; - width: 16%; - height: 33%; } + top: 55.4%; + left: 41%; + width: 16.5%; + height: 31%; } KEYBOARD .nav .ok { top: 31%; left: 67%; - width: 13%; - height: 38%; } + width: 13.5%; + height: 38%; + border-radius: 48%; } KEYBOARD .nav .back { top: 31%; - left: 84%; - width: 13%; - height: 38%; } + left: 83.3%; + width: 13.5%; + height: 38%; + border-radius: 48%; } KEYBOARD .functions { position: absolute; top: 26.75%; diff --git a/ion/src/simulator/windows/Makefile b/ion/src/simulator/windows/Makefile index 50c6396ac..a057924f5 100644 --- a/ion/src/simulator/windows/Makefile +++ b/ion/src/simulator/windows/Makefile @@ -6,6 +6,8 @@ ion_src += $(addprefix ion/src/simulator/windows/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ + dummy/haptics_enabled.cpp \ + haptics.cpp \ ) ion_src += ion/src/shared/collect_registers.cpp @@ -15,4 +17,22 @@ ion_src += ion/src/simulator/shared/dummy/telemetry_init.cpp ion_src += ion/src/shared/telemetry_console.cpp endif +# RC file dependencies +$(call object_for,ion/src/simulator/windows/resources.rc): WRFLAGS += -I $(BUILD_DIR) + +# The header of images is refered to as so make sure it's findable this way +SFLAGS += -I$(BUILD_DIR) + LDFLAGS += -lgdiplus + +$(eval $(call rule_for, \ + RESGEN, \ + ion/src/simulator/windows/resources_gen.rc ion/src/simulator/windows/images.h, \ + $(ion_simulator_assets_paths), \ + $$(PYTHON) ion/src/simulator/windows/resgen.py $(ion_simulator_assets) -o $$@, \ + global \ +)) + +$(call object_for,ion/src/simulator/windows/images.cpp): $(BUILD_DIR)/ion/src/simulator/windows/images.h +$(call object_for,ion/src/simulator/windows/resources.rc): $(BUILD_DIR)/ion/src/simulator/windows/resources_gen.rc + diff --git a/ion/src/simulator/windows/images.cpp b/ion/src/simulator/windows/images.cpp index 60a6c7cdb..1bae507fa 100644 --- a/ion/src/simulator/windows/images.cpp +++ b/ion/src/simulator/windows/images.cpp @@ -1,9 +1,11 @@ #include "../shared/platform.h" +#include #include #include #include #include +#include /* Loading images using GDI+ * On Windows, we decompress JPEG images using GDI+ which is widely available. @@ -40,7 +42,14 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); LPSTREAM stream; - const char * resname = MAKEINTRESOURCE(300); + int resourceID = -1; + for (size_t i = 0; i < sizeof(resourcesIdentifiers)/sizeof(resourcesIdentifiers[0]); i++) { + if (strcmp(identifier, resourcesIdentifiers[i].identifier()) == 0) { + resourceID = resourcesIdentifiers[i].id(); + } + } + assert(resourceID >= 0); + const char * resname = MAKEINTRESOURCE(resourceID); CreateStreamOnResource(resname, &stream); Gdiplus::Bitmap * image = Gdiplus::Bitmap::FromStream(stream); @@ -52,6 +61,8 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi Gdiplus::BitmapData * bitmapData = new Gdiplus::BitmapData; image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData); + size_t bytesPerPixel = 4; + SDL_Texture * texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_ARGB8888, @@ -64,8 +75,10 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi texture, NULL, bitmapData->Scan0, - 4 * width - ); + bytesPerPixel * width + ); + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); image->UnlockBits(bitmapData); delete bitmapData; diff --git a/ion/src/simulator/windows/resgen.py b/ion/src/simulator/windows/resgen.py new file mode 100644 index 000000000..2f15a6bba --- /dev/null +++ b/ion/src/simulator/windows/resgen.py @@ -0,0 +1,57 @@ +# This script generates two files: +# - A .rc: the list of the resources to be included in resources.rc +# - A C++ header: the mapping of the resource names and identifiers + +import sys +import re +import argparse +import io + +parser = argparse.ArgumentParser(description="Process some windows resources.") +parser.add_argument('assets', metavar='asset', type=str, nargs='+', help='The list of assets') +parser.add_argument('-o', required=True, help='The file to generate') +args = parser.parse_args() + +def print_declaration(f, asset, identifier): + f.write(str(identifier) + ' RCDATA ' + '"../assets/' + asset + '"\n') + +def print_mapping(f, asset, identifier): + f.write('ResourceID("' + asset + '", ' + str(identifier) + '),\n') + +def print_mapping_header(f): + f.write("#ifndef ION_SIMULATOR_WINDOWS_IMAGES_H\n") + f.write("#define ION_SIMULATOR_WINDOWS_IMAGES_H\n\n") + f.write("// This file is auto-generated by resgen.py\n\n") + f.write("\nclass ResourceID {\n") + f.write("public:\n") + f.write(" constexpr ResourceID(const char * identifier, int id) : m_identifier(identifier), m_id(id) {}\n") + f.write(" const char * identifier() const { return m_identifier; }\n") + f.write(" int id() const { return m_id; }\n") + f.write("private:\n") + f.write(" const char * m_identifier;\n") + f.write(" int m_id;\n") + f.write("};\n\n") + f.write("static constexpr ResourceID resourcesIdentifiers[] = {\n") + +def print_mapping_footer(f): + f.write("};\n\n") + f.write("#endif\n") + +def print(files, path, print_header, print_footer, process_asset): + f = open(path, "w") + print_header(f) + identifier = 1000 + for asset in files: + process_asset(f, asset, identifier) + identifier += 1 + print_footer(f) + f.close() + + +if (args.o.endswith(".rc")): + print(args.assets, args.o, lambda f: None, lambda f: None, print_declaration) + +if (args.o.endswith(".h")): + print(args.assets, args.o, print_mapping_header, print_mapping_footer, print_mapping) + + diff --git a/ion/src/simulator/windows/resources.rc b/ion/src/simulator/windows/resources.rc index 024eb2d13..9994a689a 100644 --- a/ion/src/simulator/windows/resources.rc +++ b/ion/src/simulator/windows/resources.rc @@ -1,4 +1,4 @@ -300 RCDATA "../assets/background.jpg" +#include 1 VERSIONINFO FILEVERSION 1,0,0,0 diff --git a/kandinsky/src/color.cpp b/kandinsky/src/color.cpp index b61c45061..1aa0225cb 100644 --- a/kandinsky/src/color.cpp +++ b/kandinsky/src/color.cpp @@ -4,10 +4,15 @@ KDColor KDColor::blend(KDColor first, KDColor second, uint8_t alpha) { /* This function is a hot path since it's being called for every single pixel * whenever we want to display a string. In this context, we're quite often * calling it with a value of either 0 or 0xFF, which can be very trivially - * dealt with. So let's make a special case for them. */ + * dealt with. Similarly, blending the same two colors yields a trivial + * result and can be bypassed. Let's make a special case for them. */ if (alpha == 0) { return second; - } else if (alpha == 0xFF) { + } + if (alpha == 0xFF) { + return first; + } + if (first == second) { return first; } @@ -15,14 +20,9 @@ KDColor KDColor::blend(KDColor first, KDColor second, uint8_t alpha) { // First is RRRRR GGGGGG BBBBB // Second is same - uint8_t oneMinusAlpha = 0xFF-alpha; + uint16_t oneMinusAlpha = 0x100-alpha; uint16_t red = first.red()*alpha + second.red()*oneMinusAlpha; uint16_t green = first.green()*alpha + second.green()*oneMinusAlpha; uint16_t blue = first.blue()*alpha + second.blue()*oneMinusAlpha; return RGB888(red>>8, green>>8, blue>>8); - - - // Blend White + black, ask for white - // white.red() = 0x1F << 3 = 0xF8 -// white.red() * 0xFF = 0xF708, we wanted 0xF800 } diff --git a/kandinsky/test/color.cpp b/kandinsky/test/color.cpp index e9bd1fa90..9cbf32dec 100644 --- a/kandinsky/test/color.cpp +++ b/kandinsky/test/color.cpp @@ -17,10 +17,25 @@ QUIZ_CASE(kandinsky_color_rgb) { quiz_assert(KDColor::RGB24(0x123456) == 0x11AA); } +void assert_colors_blend_to(KDColor c1, KDColor c2, uint8_t alpha, KDColor res) { + quiz_assert(KDColor::blend(c1, c2, alpha) == res ); +} + QUIZ_CASE(kandinsky_color_blend) { KDColor midGray = KDColor::RGB24(0x7F7F7F); - KDColor res = KDColor::blend(KDColorWhite, KDColorBlack, 0xFF); - quiz_assert(res == KDColorWhite); - quiz_assert(KDColor::blend(KDColorWhite, KDColorBlack, 0) == KDColorBlack); - quiz_assert(KDColor::blend(KDColorWhite, KDColorBlack, 0x7F) == midGray); + KDColor midTurquoise = KDColor::RGB24(0x007F7F); + + assert_colors_blend_to(KDColorWhite, KDColorBlack, 0x00, KDColorBlack); + assert_colors_blend_to(KDColorWhite, KDColorBlack, 0xFF, KDColorWhite); + assert_colors_blend_to(KDColorWhite, KDColorBlack, 0x7F, midGray); + + assert_colors_blend_to(KDColorGreen, KDColorBlue, 0x00, KDColorBlue); + assert_colors_blend_to(KDColorGreen, KDColorBlue, 0xFF, KDColorGreen); + assert_colors_blend_to(KDColorGreen, KDColorBlue, 0x7F, midTurquoise); + + // Assert that blending two identical colors does not produce strange colors. + for (uint16_t col = 0; col < 0xFFFF; col++) { + KDColor color = KDColor::RGB16(col); + assert_colors_blend_to(color, color, col>>8, color); + } } diff --git a/python/port/helpers.cpp b/python/port/helpers.cpp index eb905f91b..1a425556f 100644 --- a/python/port/helpers.cpp +++ b/python/port/helpers.cpp @@ -12,12 +12,15 @@ bool micropython_port_vm_hook_loop() { /* Doing too many things here slows down Python execution quite a lot. So we * only do things once in a while and return as soon as possible otherwise. */ - static int c = 0; - c = (c + 1) % 20000; - if (c != 0) { + static uint64_t t = Ion::Timing::millis(); + static constexpr uint64_t delay = 100; + + uint64_t t2 = Ion::Timing::millis(); + if (t2 - t < delay) { return false; } + t = t2; micropython_port_vm_hook_refresh_print(); // Check if the user asked for an interruption from the keyboard diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index beb261a73..41d163ce6 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -63,16 +63,6 @@ mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) { KDColor backgroundColor = (n_args >= 5) ? MicroPython::Color::Parse(args[4]) : KDColorWhite; MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->drawString(text, point, KDFont::LargeFont, textColor, backgroundColor); - /* Before and after execution of "modkandinsky_draw_string", - * "micropython_port_vm_hook_loop" is called by "mp_execute_bytecode" and will - * call "micropython_port_interrupt_if_needed" every 20000 calls. - * However, "drawString" function might take some time to execute leading to - * drastically decrease the frequency of calls to - * "micropython_port_vm_hook_loop" and thus to - * "micropython_port_interrupt_if_needed". So we add an extra - * check for user interruption here. This way the user can still interrupt an - * infinite loop calling 'drawString' for instance. */ - micropython_port_interrupt_if_needed(); return mp_const_none; } @@ -93,8 +83,6 @@ mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { KDColor color = MicroPython::Color::Parse(args[4]); MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->fillRect(rect, color); - // Cf comment on modkandinsky_draw_string - micropython_port_interrupt_if_needed(); return mp_const_none; } diff --git a/python/port/port.cpp b/python/port/port.cpp index 83fe8c910..63a9a3deb 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -244,7 +244,8 @@ KDColor MicroPython::Color::Parse(mp_obj_t input, Mode mode){ NamedColor("orange", Palette::Orange), NamedColor("purple", Palette::Purple), NamedColor("grey", Palette::GreyDark), - NamedColor("cyan", Palette::Cyan) + NamedColor("cyan", Palette::Cyan), + NamedColor("magenta", Palette::Magenta) }; for (NamedColor p : pairs) { if (strcmp(p.name(), color) == 0) {