diff --git a/apps/calculation/additional_outputs/expressions_list_controller.cpp b/apps/calculation/additional_outputs/expressions_list_controller.cpp index be7a5ed36..555de4433 100644 --- a/apps/calculation/additional_outputs/expressions_list_controller.cpp +++ b/apps/calculation/additional_outputs/expressions_list_controller.cpp @@ -25,10 +25,11 @@ int ExpressionsListController::reusableCellCount(int type) { } void ExpressionsListController::viewDidDisappear() { + ListController::viewDidDisappear(); // Reset cell memoization to avoid taking extra space in the pool for (int i = 0; i < k_maxNumberOfCells; i++) { - m_cells[i].setLayout(Layout()); - } + m_cells[i].setLayout(Layout()); + } } HighlightCell * ExpressionsListController::reusableCell(int index, int type) { diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.cpp b/apps/calculation/additional_outputs/illustrated_list_controller.cpp index 53bfeb41c..80ea4f52a 100644 --- a/apps/calculation/additional_outputs/illustrated_list_controller.cpp +++ b/apps/calculation/additional_outputs/illustrated_list_controller.cpp @@ -26,7 +26,7 @@ void IllustratedListController::didEnterResponderChain(Responder * previousFirst } void IllustratedListController::viewDidDisappear() { - StackViewController::viewDidDisappear(); + ListController::viewDidDisappear(); // Reset the context as it was before displaying the IllustratedListController Poincare::Context * context = App::app()->localContext(); if (m_savedExpression.isUninitialized()) { diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 4fc15ea09..c833b7633 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -32,7 +32,6 @@ PythonCount = "Count the occurrences of x" PythonDegrees = "Convert x from radians to degrees" PythonDivMod = "Quotient and remainder" PythonDrawString = "Display a text from pixel (x,y)" -PythonConstantE = "2.718281828459046" PythonErf = "Error function" PythonErfc = "Complementary error function" PythonEval = "Return the evaluated expression" @@ -124,7 +123,6 @@ PythonModf = "Fractional and integer parts of x" PythonMonotonic = "Value of a monotonic clock" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" -PythonConstantPi = "3.141592653589794" PythonPolar = "z in polar coordinates" PythonPop = "Remove and return the last item" PythonPower = "x raised to the power y" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 88a0c6954..67e6b1582 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -32,7 +32,6 @@ PythonCount = "Count the occurrences of x" PythonDegrees = "Convert x from radians to degrees" PythonDivMod = "Quotient and remainder" PythonDrawString = "Display a text from pixel (x,y)" -PythonConstantE = "2.718281828459046" PythonErf = "Error function" PythonErfc = "Complementary error function" PythonEval = "Return the evaluated expression" @@ -124,7 +123,6 @@ PythonModf = "Fractional and integer parts of x" PythonMonotonic = "Value of a monotonic clock" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" -PythonConstantPi = "3.141592653589794" PythonPolar = "z in polar coordinates" PythonPop = "Remove and return the last item" PythonPower = "x raised to the power y" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index ae9bb0b9f..074e16df1 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -32,7 +32,6 @@ PythonCount = "Count the occurrences of x" PythonDegrees = "Convert x from radians to degrees" PythonDivMod = "Quotient and remainder" PythonDrawString = "Display a text from pixel (x,y)" -PythonConstantE = "2.718281828459046" PythonErf = "Error function" PythonErfc = "Complementary error function" PythonEval = "Return the evaluated expression" @@ -124,7 +123,6 @@ PythonModf = "Fractional and integer parts of x" PythonMonotonic = "Value of a monotonic clock" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" -PythonConstantPi = "3.141592653589794" PythonPolar = "z in polar coordinates" PythonPop = "Remove and return the last item" PythonPower = "x raised to the power y" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index 4a98637cd..b326507bc 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -32,7 +32,6 @@ PythonCount = "Compte les occurrences de x" PythonDegrees = "Conversion de radians en degrés" PythonDivMod = "Quotient et reste" PythonDrawString = "Affiche un texte au pixel (x,y)" -PythonConstantE = "2.718281828459045" PythonErf = "Fonction d'erreur" PythonErfc = "Fonction d'erreur complémentaire" PythonEval = "Evalue l'expression en argument " @@ -124,7 +123,6 @@ PythonModf = "Parties fractionnaire et entière" PythonMonotonic = "Renvoie la valeur de l'horloge" PythonOct = "Conversion en octal" PythonPhase = "Argument de z" -PythonConstantPi = "3.141592653589793" PythonPolar = "Conversion en polaire" PythonPop = "Supprime le dernier élément" PythonPower = "x à la puissance y" diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n index 790a6e083..e19a51996 100644 --- a/apps/code/catalog.hu.i18n +++ b/apps/code/catalog.hu.i18n @@ -32,7 +32,6 @@ PythonCount = "Számolja az x elöfordulását" PythonDegrees = "x konvertálása x-röl radiánokra fokokra" PythonDivMod = "Hányados és maradék" PythonDrawString = "Szöveg megjelenítése pixelböl (x, y)" -PythonConstantE = "2.718281828459046" PythonErf = "Hiba funkció" PythonErfc = "Kiegészítö hibafunkció" PythonEval = "Visszaadja az értékelt kifejezést" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index bc0c946ce..ff20380ab 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -32,7 +32,6 @@ PythonCount = "Count the occurrences of x" PythonDegrees = "Convert x from radians to degrees" PythonDivMod = "Quotient and remainder" PythonDrawString = "Display a text from pixel (x,y)" -PythonConstantE = "2.718281828459046" PythonErf = "Error function" PythonErfc = "Complementary error function" PythonEval = "Return the evaluated expression" @@ -124,7 +123,6 @@ PythonModf = "Fractional and integer parts of x" PythonMonotonic = "Value of a monotonic clock" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" -PythonConstantPi = "3.141592653589794" PythonPolar = "z in polar coordinates" PythonPop = "Remove and return the last item" PythonPower = "x raised to the power y" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index d501675d5..111261574 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -185,6 +185,8 @@ PythonCommandTrunc = "trunc(x)" PythonCommandTurtleFunction = "turtle.function" PythonCommandTurtleFunctionWithoutArg = "turtle.\x11" PythonCommandUniform = "uniform(a,b)" +PythonConstantE = "2.718281828459045" +PythonConstantPi = "3.141592653589793" PythonTurtleCommandBackward = "backward(x)" PythonTurtleCommandBlack = "'black'" PythonTurtleCommandBlue = "'blue'" diff --git a/apps/exam_mode_configuration.h b/apps/exam_mode_configuration.h index 9377968b8..af8a8e960 100644 --- a/apps/exam_mode_configuration.h +++ b/apps/exam_mode_configuration.h @@ -2,13 +2,13 @@ #define APPS_EXAM_MODE_CONFIGURATION_H #include "global_preferences.h" -#include "settings/settings_message_tree.h" +#include "shared/settings_message_tree.h" #include namespace ExamModeConfiguration { // Settings menu -extern const Settings::SettingsMessageTree s_modelExamChildren[3]; +extern const Shared::SettingsMessageTree s_modelExamChildren[3]; int numberOfAvailableExamMode(); GlobalPreferences::ExamMode examModeAtIndex(int index); I18n::Message examModeActivationMessage(int index); diff --git a/apps/exam_mode_configuration_non_official.cpp b/apps/exam_mode_configuration_non_official.cpp index 8905e9a79..aceecdad6 100644 --- a/apps/exam_mode_configuration_non_official.cpp +++ b/apps/exam_mode_configuration_non_official.cpp @@ -2,9 +2,9 @@ using namespace Poincare; -constexpr Settings::SettingsMessageTree s_ledColorChildren[] = {Settings::SettingsMessageTree(I18n::Message::ColorRed), Settings::SettingsMessageTree(I18n::Message::ColorWhite), Settings::SettingsMessageTree(I18n::Message::ColorGreen), Settings::SettingsMessageTree(I18n::Message::ColorBlue), Settings::SettingsMessageTree(I18n::Message::ColorYellow), Settings::SettingsMessageTree(I18n::Message::ColorPurple), Settings::SettingsMessageTree(I18n::Message::ColorOrange)}; -constexpr Settings::SettingsMessageTree s_examModeMode[] = {Settings::SettingsMessageTree(I18n::Message::ExamModeModeStandard), Settings::SettingsMessageTree(I18n::Message::ExamModeModeNoSym), Settings::SettingsMessageTree(I18n::Message::ExamModeModeNoSymNoText)}; -constexpr Settings::SettingsMessageTree ExamModeConfiguration::s_modelExamChildren[] = {Settings::SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren), Settings::SettingsMessageTree(I18n::Message::ExamModeMode, s_examModeMode), Settings::SettingsMessageTree(I18n::Message::ActivateExamMode)}; +constexpr Shared::SettingsMessageTree s_ledColorChildren[] = {Shared::SettingsMessageTree(I18n::Message::ColorRed), Shared::SettingsMessageTree(I18n::Message::ColorWhite), Shared::SettingsMessageTree(I18n::Message::ColorGreen), Shared::SettingsMessageTree(I18n::Message::ColorBlue), Shared::SettingsMessageTree(I18n::Message::ColorYellow), Shared::SettingsMessageTree(I18n::Message::ColorPurple), Shared::SettingsMessageTree(I18n::Message::ColorOrange)}; +constexpr Shared::SettingsMessageTree s_examModeMode[] = {Shared::SettingsMessageTree(I18n::Message::ExamModeModeStandard), Shared::SettingsMessageTree(I18n::Message::ExamModeModeNoSym), Shared::SettingsMessageTree(I18n::Message::ExamModeModeNoSymNoText)}; +constexpr Shared::SettingsMessageTree ExamModeConfiguration::s_modelExamChildren[] = {Shared::SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren), Shared::SettingsMessageTree(I18n::Message::ExamModeMode, s_examModeMode), Shared::SettingsMessageTree(I18n::Message::ActivateExamMode)}; int ExamModeConfiguration::numberOfAvailableExamMode() { return 3; diff --git a/apps/exam_mode_configuration_official.cpp b/apps/exam_mode_configuration_official.cpp new file mode 100644 index 000000000..860b2f65c --- /dev/null +++ b/apps/exam_mode_configuration_official.cpp @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 +// Caution: Dutch exam mode is subject to a compliance certification by a government agency. Distribution of a non-certified Dutch exam mode is illegal. + +#include "exam_mode_configuration.h" + +constexpr Shared::SettingsMessageTree ExamModeConfiguration::s_modelExamChildren[2] = {Shared::SettingsMessageTree(I18n::Message::ActivateExamMode), Shared::SettingsMessageTree(I18n::Message::ActivateDutchExamMode)}; + +int ExamModeConfiguration::numberOfAvailableExamMode() { + if (GlobalPreferences::sharedGlobalPreferences()->language() != I18n::Language::EN || GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + return 1; + } + return 2; +} + +GlobalPreferences::ExamMode ExamModeConfiguration::examModeAtIndex(int index) { + return index == 0 ? GlobalPreferences::ExamMode::Standard : GlobalPreferences::ExamMode::Dutch; +} + +I18n::Message ExamModeConfiguration::examModeActivationMessage(int index) { + return index == 0 ? I18n::Message::ActivateExamMode : I18n::Message::ActivateDutchExamMode; +} + +I18n::Message ExamModeConfiguration::examModeActivationWarningMessage(GlobalPreferences::ExamMode mode, int line) { + if (mode == GlobalPreferences::ExamMode::Off) { + I18n::Message warnings[] = {I18n::Message::ExitExamMode1, I18n::Message::ExitExamMode2, I18n::Message::Default}; + return warnings[line]; + } else if (mode == GlobalPreferences::ExamMode::Standard) { + I18n::Message warnings[] = {I18n::Message::ActiveExamModeMessage1, I18n::Message::ActiveExamModeMessage2, I18n::Message::ActiveExamModeMessage3}; + return warnings[line]; + } + assert(mode == GlobalPreferences::ExamMode::Dutch); + I18n::Message warnings[] = {I18n::Message::ActiveDutchExamModeMessage1, I18n::Message::ActiveDutchExamModeMessage2, I18n::Message::ActiveDutchExamModeMessage3}; + return warnings[line]; +} + +KDColor ExamModeConfiguration::examModeColor(GlobalPreferences::ExamMode mode) { + /* The Dutch exam mode LED is supposed to be orange but we can only make + * blink "pure" colors: with RGB leds on or off (as the PWM is used for + * blinking). The closest "pure" color is Yellow. Moreover, Orange LED is + * already used when the battery is charging. Using yellow, we can assert + * that the yellow LED only means that Dutch exam mode is on and avoid + * confusing states when the battery is charging and states when the Dutch + * exam mode is on. */ + return mode == GlobalPreferences::ExamMode::Dutch ? KDColorYellow : KDColorRed; +} + +bool ExamModeConfiguration::appIsForbiddenInExamMode(I18n::Message appName, GlobalPreferences::ExamMode mode) { + return appName == I18n::Message::CodeApp && mode == GlobalPreferences::ExamMode::Dutch; +} + +bool ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::ExamMode mode) { + return mode == GlobalPreferences::ExamMode::Dutch; +} diff --git a/apps/settings/Makefile b/apps/settings/Makefile index 17229f77c..89731dd29 100644 --- a/apps/settings/Makefile +++ b/apps/settings/Makefile @@ -1,10 +1,6 @@ apps += Settings::App app_headers += apps/settings/app.h -app_settings_test_src = $(addprefix apps/settings/,\ - settings_message_tree.cpp \ -) - app_settings_src = $(addprefix apps/settings/,\ app.cpp \ cell_with_separator.cpp \ diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp index be25f7b35..bf86296d0 100644 --- a/apps/settings/main_controller.cpp +++ b/apps/settings/main_controller.cpp @@ -5,6 +5,7 @@ #include using namespace Poincare; +using namespace Shared; namespace Settings { diff --git a/apps/settings/main_controller.h b/apps/settings/main_controller.h index 76be4f1d9..e0e2066ef 100644 --- a/apps/settings/main_controller.h +++ b/apps/settings/main_controller.h @@ -2,7 +2,7 @@ #define SETTINGS_MAIN_CONTROLLER_H #include -#include "settings_message_tree.h" +#include #include "message_table_cell_with_gauge_with_separator.h" #include "sub_menu/about_controller.h" #include "sub_menu/accessibility_controller.h" @@ -13,22 +13,22 @@ namespace Settings { -extern const SettingsMessageTree s_modelAngleChildren[3]; -extern const SettingsMessageTree s_modelEditionModeChildren[2]; -extern const SettingsMessageTree s_modelFloatDisplayModeChildren[4]; -extern const SettingsMessageTree s_modelComplexFormatChildren[3]; -extern const SettingsMessageTree s_symbolChildren[4]; -extern const SettingsMessageTree s_modelMathOptionsChildren[5]; -extern const SettingsMessageTree s_modelResultDisplayChildren[2]; -extern const SettingsMessageTree s_modelFontChildren[2]; -extern const SettingsMessageTree s_accessibilityChildren[6]; -extern const SettingsMessageTree s_contributorsChildren[16]; +extern const Shared::SettingsMessageTree s_modelAngleChildren[3]; +extern const Shared::SettingsMessageTree s_modelEditionModeChildren[2]; +extern const Shared::SettingsMessageTree s_modelFloatDisplayModeChildren[4]; +extern const Shared::SettingsMessageTree s_modelComplexFormatChildren[3]; +extern const Shared::SettingsMessageTree s_symbolChildren[4]; +extern const Shared::SettingsMessageTree s_modelMathOptionsChildren[5]; +extern const Shared::SettingsMessageTree s_modelResultDisplayChildren[2]; +extern const Shared::SettingsMessageTree s_modelFontChildren[2]; +extern const Shared::SettingsMessageTree s_accessibilityChildren[6]; +extern const Shared::SettingsMessageTree s_contributorsChildren[16]; #ifdef USERNAME -extern const SettingsMessageTree s_modelAboutChildren[8]; +extern const Shared::SettingsMessageTree s_modelAboutChildren[8]; #else -extern const SettingsMessageTree s_modelAboutChildren[7]; +extern const Shared::SettingsMessageTree s_modelAboutChildren[7]; #endif -extern const SettingsMessageTree s_model; +extern const Shared::SettingsMessageTree s_model; class MainController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource { public: @@ -59,7 +59,7 @@ private: * k_indexOfAboutCell) */ constexpr static int k_indexOfPopUpCell = k_indexOfFontCell + 1; constexpr static int k_indexOfAboutCell = k_indexOfFontCell + 1; - static const SettingsMessageTree * model(); + static const Shared::SettingsMessageTree * model(); private: StackViewController * stackController() const; I18n::Message promptMessage() const; diff --git a/apps/settings/main_controller_prompt_beta.cpp b/apps/settings/main_controller_prompt_beta.cpp index f7546a2ce..c5eb3fef6 100644 --- a/apps/settings/main_controller_prompt_beta.cpp +++ b/apps/settings/main_controller_prompt_beta.cpp @@ -2,6 +2,8 @@ #include "../exam_mode_configuration.h" #include +using namespace Shared; + namespace Settings { constexpr SettingsMessageTree s_modelMenu[] = diff --git a/apps/settings/main_controller_prompt_none.cpp b/apps/settings/main_controller_prompt_none.cpp index d382a0b11..80c61b2c1 100644 --- a/apps/settings/main_controller_prompt_none.cpp +++ b/apps/settings/main_controller_prompt_none.cpp @@ -2,6 +2,8 @@ #include "../exam_mode_configuration.h" #include +using namespace Shared; + namespace Settings { constexpr SettingsMessageTree s_modelMenu[] = diff --git a/apps/settings/main_controller_prompt_update.cpp b/apps/settings/main_controller_prompt_update.cpp index b7586d190..77619e6f2 100644 --- a/apps/settings/main_controller_prompt_update.cpp +++ b/apps/settings/main_controller_prompt_update.cpp @@ -4,6 +4,8 @@ namespace Settings { +using namespace Shared; + constexpr SettingsMessageTree s_modelMenu[] = {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren), SettingsMessageTree(I18n::Message::Brightness), diff --git a/apps/settings/settings_message_tree.cpp b/apps/settings/settings_message_tree.cpp deleted file mode 100644 index f481eb543..000000000 --- a/apps/settings/settings_message_tree.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "settings_message_tree.h" - -namespace Settings { - -const MessageTree * SettingsMessageTree::children(int index) const { - return &m_children[index]; -} - -} diff --git a/apps/settings/sub_menu/generic_sub_controller.h b/apps/settings/sub_menu/generic_sub_controller.h index 545a79c80..c149a2508 100644 --- a/apps/settings/sub_menu/generic_sub_controller.h +++ b/apps/settings/sub_menu/generic_sub_controller.h @@ -2,7 +2,7 @@ #define SETTINGS_GENERIC_SUB_CONTROLLER_H #include -#include "../settings_message_tree.h" +#include namespace Settings { diff --git a/apps/settings/settings_message_tree.h b/apps/shared/settings_message_tree.h similarity index 71% rename from apps/settings/settings_message_tree.h rename to apps/shared/settings_message_tree.h index 10d1b5205..f3c3bf364 100644 --- a/apps/settings/settings_message_tree.h +++ b/apps/shared/settings_message_tree.h @@ -1,24 +1,26 @@ -#ifndef SETTINGS_MESSAGE_TREE_H -#define SETTINGS_MESSAGE_TREE_H +#ifndef SHARED_SETTINGS_MESSAGE_TREE_H +#define SHARED_SETTINGS_MESSAGE_TREE_H + #include #include -namespace Settings { +namespace Shared { class SettingsMessageTree : public MessageTree { public: constexpr SettingsMessageTree(I18n::Message label = I18n::Message::Default) : MessageTree(label, 0), m_children(nullptr) - { - }; + {} + template constexpr SettingsMessageTree(I18n::Message label, const SettingsMessageTree (&children)[N] = nullptr) : MessageTree(label, N), m_children(children) - { - }; - const MessageTree * children(int index) const override; + {} + + const MessageTree * children(int index) const override { return &m_children[index]; } + private: const SettingsMessageTree * m_children; }; diff --git a/apps/variable_box_controller.cpp b/apps/variable_box_controller.cpp index 36b9845c3..2d6d09e19 100644 --- a/apps/variable_box_controller.cpp +++ b/apps/variable_box_controller.cpp @@ -236,20 +236,20 @@ Layout VariableBoxController::expressionLayoutForRecord(Storage::Record record, assert(m_firstMemoizedLayoutIndex >= 0); } assert(index >= m_firstMemoizedLayoutIndex && index < m_firstMemoizedLayoutIndex + k_maxNumberOfDisplayedRows); - Layout result; if (m_layouts[index-m_firstMemoizedLayoutIndex].isUninitialized()) { /* Creating the layout of a very long variable might throw a pool exception. * We want to catch it and return a dummy layout instead, otherwise the user * won't be able to open the variable box again, until she deletes the * problematic variable -> and she has no help to remember its name, as she * can't open the variable box. */ + Layout result; Poincare::ExceptionCheckpoint ecp; if (ExceptionRun(ecp)) { result = GlobalContext::LayoutForRecord(record); } + m_layouts[index-m_firstMemoizedLayoutIndex] = result; } - m_layouts[index-m_firstMemoizedLayoutIndex] = result; - return result; + return m_layouts[index-m_firstMemoizedLayoutIndex]; } const char * VariableBoxController::extension() const { diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index 3b1e53e64..85f48cf13 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -86,13 +86,21 @@ mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) { } mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { - KDRect rect( - mp_obj_get_int(args[0]), - mp_obj_get_int(args[1]), - mp_obj_get_int(args[2]), - mp_obj_get_int(args[3]) - ); + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t width = mp_obj_get_int(args[2]); + mp_int_t height = mp_obj_get_int(args[3]); + if (width < 0) { + width = -width; + x = x - width; + } + if (height < 0) { + height = -height; + y = y - height; + } + KDRect rect(x, y, width, height); KDColor color = ColorForTuple(args[4]); + MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->fillRect(rect, color); // Cf comment on modkandinsky_draw_string