aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/lcd/marlinui.h
diff options
context:
space:
mode:
Diffstat (limited to 'Marlin/src/lcd/marlinui.h')
-rw-r--r--Marlin/src/lcd/marlinui.h637
1 files changed, 637 insertions, 0 deletions
diff --git a/Marlin/src/lcd/marlinui.h b/Marlin/src/lcd/marlinui.h
new file mode 100644
index 0000000..2e55c9a
--- /dev/null
+++ b/Marlin/src/lcd/marlinui.h
@@ -0,0 +1,637 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "../inc/MarlinConfig.h"
+
+#include "../module/motion.h"
+
+#include "buttons.h"
+
+#if HAS_BUZZER
+ #include "../libs/buzzer.h"
+#endif
+
+#if ENABLED(SDSUPPORT)
+ #include "../sd/cardreader.h"
+#endif
+
+#if ENABLED(TOUCH_SCREEN_CALIBRATION)
+ #include "tft_io/touch_calibration.h"
+#endif
+
+#if EITHER(HAS_LCD_MENU, ULTIPANEL_FEEDMULTIPLY)
+ #define HAS_ENCODER_ACTION 1
+#endif
+
+#if E_MANUAL > 1
+ #define MULTI_MANUAL 1
+#endif
+
+#if HAS_DISPLAY
+ #include "../module/printcounter.h"
+#endif
+
+#if BOTH(HAS_LCD_MENU, ADVANCED_PAUSE_FEATURE)
+ #include "../feature/pause.h"
+ #include "../module/motion.h" // for active_extruder
+#endif
+
+#if HAS_WIRED_LCD
+
+ enum LCDViewAction : uint8_t {
+ LCDVIEW_NONE,
+ LCDVIEW_REDRAW_NOW,
+ LCDVIEW_CALL_REDRAW_NEXT,
+ LCDVIEW_CLEAR_CALL_REDRAW,
+ LCDVIEW_CALL_NO_REDRAW
+ };
+
+ #if HAS_ADC_BUTTONS
+ uint8_t get_ADC_keyValue();
+ #endif
+
+ #define LCD_UPDATE_INTERVAL TERN(HAS_TOUCH_BUTTONS, 50, 100)
+
+ #if HAS_LCD_MENU
+
+ #include "lcdprint.h"
+
+ #if !HAS_GRAPHICAL_TFT
+ void _wrap_string(uint8_t &col, uint8_t &row, const char * const string, read_byte_cb_t cb_read_byte, const bool wordwrap=false);
+ inline void wrap_string_P(uint8_t &col, uint8_t &row, PGM_P const pstr, const bool wordwrap=false) { _wrap_string(col, row, pstr, read_byte_rom, wordwrap); }
+ inline void wrap_string(uint8_t &col, uint8_t &row, const char * const string, const bool wordwrap=false) { _wrap_string(col, row, string, read_byte_ram, wordwrap); }
+ #endif
+
+ typedef void (*screenFunc_t)();
+ typedef void (*menuAction_t)();
+
+ #if ENABLED(AUTO_BED_LEVELING_UBL)
+ void lcd_mesh_edit_setup(const float &initial);
+ float lcd_mesh_edit();
+ #endif
+
+ #endif // HAS_LCD_MENU
+
+#endif // HAS_WIRED_LCD
+
+#if HAS_MARLINUI_U8GLIB
+ enum MarlinFont : uint8_t {
+ FONT_STATUSMENU = 1,
+ FONT_EDIT,
+ FONT_MENU
+ };
+#else
+ enum HD44780CharSet : uint8_t {
+ CHARSET_MENU,
+ CHARSET_INFO,
+ CHARSET_BOOT
+ };
+#endif
+
+#if PREHEAT_COUNT
+ typedef struct {
+ TERN_(HAS_HOTEND, uint16_t hotend_temp);
+ TERN_(HAS_HEATED_BED, uint16_t bed_temp );
+ TERN_(HAS_FAN, uint16_t fan_speed );
+ } preheat_t;
+#endif
+
+#if HAS_LCD_MENU
+
+ // Manual Movement class
+ class ManualMove {
+ private:
+ static AxisEnum axis;
+ #if MULTI_MANUAL
+ static int8_t e_index;
+ #else
+ static int8_t constexpr e_index = 0;
+ #endif
+ static millis_t start_time;
+ TERN_(IS_KINEMATIC, static xyze_pos_t all_axes_destination);
+ public:
+ static float menu_scale;
+ TERN_(IS_KINEMATIC, static float offset);
+ template <typename T>
+ void set_destination(const T& dest) {
+ #if IS_KINEMATIC
+ // Moves are segmented, so the entire move is not submitted at once.
+ // Using a separate variable prevents corrupting the in-progress move.
+ all_axes_destination = current_position;
+ all_axes_destination.set(dest);
+ #else
+ // Moves are submitted as single line to the planner using buffer_line.
+ current_position.set(dest);
+ #endif
+ }
+ #if IS_KINEMATIC
+ static bool processing;
+ #else
+ static bool constexpr processing = false;
+ #endif
+ static void task();
+ static void soon(AxisEnum axis
+ #if MULTI_MANUAL
+ , const int8_t eindex=-1
+ #endif
+ );
+ };
+
+#endif
+
+////////////////////////////////////////////
+//////////// MarlinUI Singleton ////////////
+////////////////////////////////////////////
+
+class MarlinUI {
+public:
+
+ MarlinUI() {
+ TERN_(HAS_LCD_MENU, currentScreen = status_screen);
+ }
+
+ #if HAS_MULTI_LANGUAGE
+ static uint8_t language;
+ static inline void set_language(const uint8_t lang) {
+ if (lang < NUM_LANGUAGES) {
+ language = lang;
+ return_to_status();
+ refresh();
+ }
+ }
+ #endif
+
+ #if ENABLED(SOUND_MENU_ITEM)
+ static bool buzzer_enabled; // Initialized by settings.load()
+ #else
+ static constexpr bool buzzer_enabled = true;
+ #endif
+
+ #if HAS_BUZZER
+ static void buzz(const long duration, const uint16_t freq);
+ #endif
+
+ FORCE_INLINE static void chirp() {
+ TERN_(HAS_CHIRP, buzz(LCD_FEEDBACK_FREQUENCY_DURATION_MS, LCD_FEEDBACK_FREQUENCY_HZ));
+ }
+
+ #if ENABLED(LCD_HAS_STATUS_INDICATORS)
+ static void update_indicators();
+ #endif
+
+ // LCD implementations
+ static void clear_lcd();
+
+ #if BOTH(HAS_LCD_MENU, TOUCH_SCREEN_CALIBRATION)
+ static void check_touch_calibration() {
+ if (touch_calibration.need_calibration()) currentScreen = touch_calibration_screen;
+ }
+ #endif
+
+ #if ENABLED(SDSUPPORT)
+ #define MEDIA_MENU_GATEWAY TERN(PASSWORD_ON_SD_PRINT_MENU, password.media_gatekeeper, menu_media)
+ static void media_changed(const uint8_t old_stat, const uint8_t stat);
+ #endif
+
+ #if ENABLED(DWIN_CREALITY_LCD)
+ static void refresh();
+ #else
+ FORCE_INLINE static void refresh() {
+ TERN_(HAS_WIRED_LCD, refresh(LCDVIEW_CLEAR_CALL_REDRAW));
+ }
+ #endif
+
+ #if HAS_WIRED_LCD
+ static bool detected();
+ static void init_lcd();
+ #else
+ static inline bool detected() { return true; }
+ static inline void init_lcd() {}
+ #endif
+
+ #if HAS_PRINT_PROGRESS
+ #if HAS_PRINT_PROGRESS_PERMYRIAD
+ typedef uint16_t progress_t;
+ #define PROGRESS_SCALE 100U
+ #define PROGRESS_MASK 0x7FFF
+ #else
+ typedef uint8_t progress_t;
+ #define PROGRESS_SCALE 1U
+ #define PROGRESS_MASK 0x7F
+ #endif
+ #if ENABLED(LCD_SET_PROGRESS_MANUALLY)
+ static progress_t progress_override;
+ static void set_progress(const progress_t p) { progress_override = _MIN(p, 100U * (PROGRESS_SCALE)); }
+ static void set_progress_done() { progress_override = (PROGRESS_MASK + 1U) + 100U * (PROGRESS_SCALE); }
+ static void progress_reset() { if (progress_override & (PROGRESS_MASK + 1U)) set_progress(0); }
+ #if ENABLED(SHOW_REMAINING_TIME)
+ static inline uint32_t _calculated_remaining_time() {
+ const duration_t elapsed = print_job_timer.duration();
+ const progress_t progress = _get_progress();
+ return elapsed.value * (100 * (PROGRESS_SCALE) - progress) / progress;
+ }
+ #if ENABLED(USE_M73_REMAINING_TIME)
+ static uint32_t remaining_time;
+ FORCE_INLINE static void set_remaining_time(const uint32_t r) { remaining_time = r; }
+ FORCE_INLINE static uint32_t get_remaining_time() { return remaining_time ?: _calculated_remaining_time(); }
+ FORCE_INLINE static void reset_remaining_time() { set_remaining_time(0); }
+ #else
+ FORCE_INLINE static uint32_t get_remaining_time() { return _calculated_remaining_time(); }
+ #endif
+ #endif
+ #endif
+ static progress_t _get_progress();
+ #if HAS_PRINT_PROGRESS_PERMYRIAD
+ FORCE_INLINE static uint16_t get_progress_permyriad() { return _get_progress(); }
+ #endif
+ static uint8_t get_progress_percent() { return uint8_t(_get_progress() / (PROGRESS_SCALE)); }
+ #else
+ static constexpr uint8_t get_progress_percent() { return 0; }
+ #endif
+
+ #if HAS_STATUS_MESSAGE
+ static char status_message[];
+ static uint8_t alert_level; // Higher levels block lower levels
+
+ #if ENABLED(STATUS_MESSAGE_SCROLLING)
+ static uint8_t status_scroll_offset;
+ static void advance_status_scroll();
+ static char* status_and_len(uint8_t &len);
+ #endif
+
+ static bool has_status();
+ static void reset_status(const bool no_welcome=false);
+ static void set_status(const char* const message, const bool persist=false);
+ static void set_status_P(PGM_P const message, const int8_t level=0);
+ static void status_printf_P(const uint8_t level, PGM_P const fmt, ...);
+ static void set_alert_status_P(PGM_P const message);
+ static inline void reset_alert_level() { alert_level = 0; }
+ #else
+ static constexpr bool has_status() { return false; }
+ static inline void reset_status(const bool=false) {}
+ static void set_status(const char* message, const bool=false);
+ static void set_status_P(PGM_P message, const int8_t=0);
+ static void status_printf_P(const uint8_t, PGM_P message, ...);
+ static inline void set_alert_status_P(PGM_P const) {}
+ static inline void reset_alert_level() {}
+ #endif
+
+ #if HAS_DISPLAY
+
+ static void init();
+ static void update();
+
+ static void abort_print();
+ static void pause_print();
+ static void resume_print();
+
+ #if HAS_WIRED_LCD
+
+ static millis_t next_button_update_ms;
+
+ static LCDViewAction lcdDrawUpdate;
+ FORCE_INLINE static bool should_draw() { return bool(lcdDrawUpdate); }
+ FORCE_INLINE static void refresh(const LCDViewAction type) { lcdDrawUpdate = type; }
+
+ #if ENABLED(SHOW_CUSTOM_BOOTSCREEN)
+ static void draw_custom_bootscreen(const uint8_t frame=0);
+ static void show_custom_bootscreen();
+ #endif
+
+ #if ENABLED(SHOW_BOOTSCREEN)
+ #ifndef BOOTSCREEN_TIMEOUT
+ #define BOOTSCREEN_TIMEOUT 2500
+ #endif
+ static void draw_marlin_bootscreen(const bool line2=false);
+ static void show_marlin_bootscreen();
+ static void show_bootscreen();
+ #endif
+
+ #if HAS_MARLINUI_U8GLIB
+
+ static void set_font(const MarlinFont font_nr);
+
+ #else
+
+ static void set_custom_characters(const HD44780CharSet screen_charset=CHARSET_INFO);
+
+ #if ENABLED(LCD_PROGRESS_BAR)
+ static millis_t progress_bar_ms; // Start time for the current progress bar cycle
+ static void draw_progress_bar(const uint8_t percent);
+ #if PROGRESS_MSG_EXPIRE > 0
+ static millis_t expire_status_ms; // = 0
+ FORCE_INLINE static void reset_progress_bar_timeout() { expire_status_ms = 0; }
+ #endif
+ #endif
+
+ #endif
+
+ static uint8_t lcd_status_update_delay;
+
+ #if HAS_LCD_CONTRAST
+ static int16_t contrast;
+ static void set_contrast(const int16_t value);
+ FORCE_INLINE static void refresh_contrast() { set_contrast(contrast); }
+ #endif
+
+ #if BOTH(FILAMENT_LCD_DISPLAY, SDSUPPORT)
+ static millis_t next_filament_display;
+ #endif
+
+ static void quick_feedback(const bool clear_buttons=true);
+ #if HAS_BUZZER
+ static void completion_feedback(const bool good=true);
+ #else
+ static inline void completion_feedback(const bool=true) {}
+ #endif
+
+ #if DISABLED(LIGHTWEIGHT_UI)
+ static void draw_status_message(const bool blink);
+ #endif
+
+ #if ENABLED(ADVANCED_PAUSE_FEATURE)
+ static void draw_hotend_status(const uint8_t row, const uint8_t extruder);
+ #endif
+
+ #if HAS_TOUCH_BUTTONS
+ static bool on_edit_screen;
+ static void screen_click(const uint8_t row, const uint8_t col, const uint8_t x, const uint8_t y);
+ #endif
+
+ static void status_screen();
+
+ #endif
+
+ #if HAS_MARLINUI_U8GLIB
+ static bool drawing_screen, first_page;
+ #else
+ static constexpr bool drawing_screen = false, first_page = true;
+ #endif
+
+ static bool get_blink();
+ static void kill_screen(PGM_P const lcd_error, PGM_P const lcd_component);
+ static void draw_kill_screen();
+
+ #else // No LCD
+
+ static inline void init() {}
+ static inline void update() {}
+ static inline void return_to_status() {}
+
+ #endif
+
+ #if ENABLED(SDSUPPORT)
+ #if BOTH(SCROLL_LONG_FILENAMES, HAS_LCD_MENU)
+ #define MARLINUI_SCROLL_NAME 1
+ #endif
+ #if MARLINUI_SCROLL_NAME
+ static uint8_t filename_scroll_pos, filename_scroll_max;
+ #endif
+ static const char * scrolled_filename(CardReader &theCard, const uint8_t maxlen, uint8_t hash, const bool doScroll);
+ #endif
+
+ #if PREHEAT_COUNT
+ static preheat_t material_preset[PREHEAT_COUNT];
+ static PGM_P get_preheat_label(const uint8_t m);
+ #endif
+
+ #if HAS_LCD_MENU
+ #if LCD_TIMEOUT_TO_STATUS
+ static millis_t return_to_status_ms;
+ #endif
+
+ #if HAS_TOUCH_BUTTONS
+ static uint8_t touch_buttons;
+ static uint8_t repeat_delay;
+ #else
+ static constexpr uint8_t touch_buttons = 0;
+ #endif
+
+ #if ENABLED(ENCODER_RATE_MULTIPLIER)
+ static bool encoderRateMultiplierEnabled;
+ static millis_t lastEncoderMovementMillis;
+ static void enable_encoder_multiplier(const bool onoff);
+ #define ENCODER_RATE_MULTIPLY(F) (ui.encoderRateMultiplierEnabled = F)
+ #else
+ #define ENCODER_RATE_MULTIPLY(F) NOOP
+ #endif
+
+ // Manual Movement
+ static ManualMove manual_move;
+
+ // Select Screen (modal NO/YES style dialog)
+ static bool selection;
+ static void set_selection(const bool sel) { selection = sel; }
+ static bool update_selection();
+
+ static bool lcd_clicked;
+ static bool use_click();
+
+ static void synchronize(PGM_P const msg=nullptr);
+
+ static screenFunc_t currentScreen;
+ static bool screen_changed;
+ static void goto_screen(const screenFunc_t screen, const uint16_t encoder=0, const uint8_t top=0, const uint8_t items=0);
+ static void save_previous_screen();
+
+ // goto_previous_screen and go_back may also be used as menu item callbacks
+ static void _goto_previous_screen(TERN_(TURBO_BACK_MENU_ITEM, const bool is_back));
+ static inline void goto_previous_screen() { _goto_previous_screen(TERN_(TURBO_BACK_MENU_ITEM, false)); }
+ static inline void go_back() { _goto_previous_screen(TERN_(TURBO_BACK_MENU_ITEM, true)); }
+
+ static void return_to_status();
+ static inline bool on_status_screen() { return currentScreen == status_screen; }
+ FORCE_INLINE static void run_current_screen() { (*currentScreen)(); }
+
+ #if ENABLED(LIGHTWEIGHT_UI)
+ static void lcd_in_status(const bool inStatus);
+ #endif
+
+ FORCE_INLINE static void defer_status_screen(const bool defer=true) {
+ #if LCD_TIMEOUT_TO_STATUS > 0
+ defer_return_to_status = defer;
+ #else
+ UNUSED(defer);
+ #endif
+ }
+
+ static inline void goto_previous_screen_no_defer() {
+ defer_status_screen(false);
+ goto_previous_screen();
+ }
+
+ #if ENABLED(SD_REPRINT_LAST_SELECTED_FILE)
+ static void reselect_last_file();
+ #endif
+
+ #if ENABLED(AUTO_BED_LEVELING_UBL)
+ static void ubl_plot(const uint8_t x_plot, const uint8_t y_plot);
+ #endif
+
+ static void draw_select_screen_prompt(PGM_P const pref, const char * const string=nullptr, PGM_P const suff=nullptr);
+
+ #elif HAS_WIRED_LCD
+
+ static constexpr bool lcd_clicked = false;
+ static constexpr bool on_status_screen() { return true; }
+ FORCE_INLINE static void run_current_screen() { status_screen(); }
+
+ #endif
+
+ #if BOTH(HAS_LCD_MENU, ADVANCED_PAUSE_FEATURE)
+ static void pause_show_message(const PauseMessage message, const PauseMode mode=PAUSE_MODE_SAME, const uint8_t extruder=active_extruder);
+ #else
+ static inline void _pause_show_message() {}
+ #define pause_show_message(...) _pause_show_message()
+ #endif
+
+ //
+ // EEPROM: Reset / Init / Load / Store
+ //
+ #if HAS_LCD_MENU
+ static void reset_settings();
+ #endif
+
+ #if ENABLED(EEPROM_SETTINGS)
+ #if HAS_LCD_MENU
+ static void init_eeprom();
+ static void load_settings();
+ static void store_settings();
+ #endif
+ #if DISABLED(EEPROM_AUTO_INIT)
+ static void eeprom_alert(const uint8_t msgid);
+ static inline void eeprom_alert_crc() { eeprom_alert(0); }
+ static inline void eeprom_alert_index() { eeprom_alert(1); }
+ static inline void eeprom_alert_version() { eeprom_alert(2); }
+ #endif
+ #endif
+
+ //
+ // Special handling if a move is underway
+ //
+ #if ANY(DELTA_CALIBRATION_MENU, DELTA_AUTO_CALIBRATION, PROBE_OFFSET_WIZARD) || (ENABLED(LCD_BED_LEVELING) && EITHER(PROBE_MANUALLY, MESH_BED_LEVELING))
+ #define LCD_HAS_WAIT_FOR_MOVE 1
+ static bool wait_for_move;
+ #else
+ static constexpr bool wait_for_move = false;
+ #endif
+
+ //
+ // Block interaction while under external control
+ //
+ #if HAS_LCD_MENU && EITHER(AUTO_BED_LEVELING_UBL, G26_MESH_VALIDATION)
+ static bool external_control;
+ FORCE_INLINE static void capture() { external_control = true; }
+ FORCE_INLINE static void release() { external_control = false; }
+ #if ENABLED(AUTO_BED_LEVELING_UBL)
+ static void external_encoder();
+ #endif
+ #else
+ static constexpr bool external_control = false;
+ #endif
+
+ #if HAS_ENCODER_ACTION
+
+ static volatile uint8_t buttons;
+ #if IS_RRW_KEYPAD
+ static volatile uint8_t keypad_buttons;
+ static bool handle_keypad();
+ #endif
+ #if HAS_SLOW_BUTTONS
+ static volatile uint8_t slow_buttons;
+ static uint8_t read_slow_buttons();
+ #endif
+
+ static void update_buttons();
+ static inline bool button_pressed() { return BUTTON_CLICK() || TERN(TOUCH_SCREEN, touch_pressed(), false); }
+ #if EITHER(AUTO_BED_LEVELING_UBL, G26_MESH_VALIDATION)
+ static void wait_for_release();
+ #endif
+
+ static uint32_t encoderPosition;
+
+ #define ENCODERBASE (TERN(REVERSE_ENCODER_DIRECTION, -1, +1))
+
+ #if EITHER(REVERSE_MENU_DIRECTION, REVERSE_SELECT_DIRECTION)
+ static int8_t encoderDirection;
+ #else
+ static constexpr int8_t encoderDirection = ENCODERBASE;
+ #endif
+
+ FORCE_INLINE static void encoder_direction_normal() {
+ #if EITHER(REVERSE_MENU_DIRECTION, REVERSE_SELECT_DIRECTION)
+ encoderDirection = ENCODERBASE;
+ #endif
+ }
+
+ FORCE_INLINE static void encoder_direction_menus() {
+ TERN_(REVERSE_MENU_DIRECTION, encoderDirection = -(ENCODERBASE));
+ }
+
+ FORCE_INLINE static void encoder_direction_select() {
+ TERN_(REVERSE_SELECT_DIRECTION, encoderDirection = -(ENCODERBASE));
+ }
+
+ #else
+
+ static inline void update_buttons() {}
+
+ #endif
+
+ #if ENABLED(TOUCH_SCREEN_CALIBRATION)
+ static void touch_calibration_screen();
+ #endif
+
+ #if HAS_GRAPHICAL_TFT
+ static void move_axis_screen();
+ #endif
+
+private:
+
+ #if HAS_STATUS_MESSAGE
+ static void finish_status(const bool persist);
+ #endif
+
+ #if HAS_WIRED_LCD
+ #if HAS_LCD_MENU && LCD_TIMEOUT_TO_STATUS > 0
+ static bool defer_return_to_status;
+ #else
+ static constexpr bool defer_return_to_status = false;
+ #endif
+ static void draw_status_screen();
+ #if HAS_GRAPHICAL_TFT
+ static void tft_idle();
+ #if ENABLED(TOUCH_SCREEN)
+ static bool touch_pressed();
+ #endif
+ #endif
+ #endif
+};
+
+extern MarlinUI ui;
+
+#define LCD_MESSAGEPGM_P(x) ui.set_status_P(x)
+#define LCD_ALERTMESSAGEPGM_P(x) ui.set_alert_status_P(x)
+
+#define LCD_MESSAGEPGM(x) LCD_MESSAGEPGM_P(GET_TEXT(x))
+#define LCD_ALERTMESSAGEPGM(x) LCD_ALERTMESSAGEPGM_P(GET_TEXT(x))