Subversion Repositories MK-Marlin

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 ron 1
/**
2
 * Marlin 3D Printer Firmware
3
 * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
 *
5
 * Based on Sprinter and grbl.
6
 * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 *
21
 */
22
 
23
/**
24
 * ultralcd_impl_DOGM.h
25
 *
26
 * Graphics LCD implementation for 128x64 pixel LCDs by STB for ErikZalm/Marlin
27
 * Demonstrator: http://www.reprap.org/wiki/STB_Electronics
28
 * License: http://opensource.org/licenses/BSD-3-Clause
29
 *
30
 * With the use of:
31
 * u8glib by Oliver Kraus
32
 * https://github.com/olikraus/U8glib_Arduino
33
 * License: http://opensource.org/licenses/BSD-3-Clause
34
 */
35
 
36
/**
37
 * Implementation of the LCD display routines for a DOGM128 graphic display.
38
 * These are common LCD 128x64 pixel graphic displays.
39
 */
40
 
41
#ifndef ULTRALCD_IMPL_DOGM_H
42
#define ULTRALCD_IMPL_DOGM_H
43
 
44
#include "MarlinConfig.h"
45
 
46
#include <U8glib.h>
47
 
48
#include "ultralcd.h"
49
#include "dogm_bitmaps.h"
50
#include "utility.h"
51
#include "duration_t.h"
52
 
53
#if ENABLED(AUTO_BED_LEVELING_UBL)
54
  #include "ubl.h"
55
#endif
56
 
57
// Only Western languages support big / small fonts
58
#if DISABLED(DISPLAY_CHARSET_ISO10646_1)
59
  #undef USE_BIG_EDIT_FONT
60
  #undef USE_SMALL_INFOFONT
61
#endif
62
 
63
#if ENABLED(U8GLIB_ST7920)
64
  #include "ultralcd_st7920_u8glib_rrd.h"
65
#endif
66
 
67
#if ENABLED(U8GLIB_ST7565_64128N)
68
  #include "ultralcd_st7565_u8glib_VIKI.h"
69
#endif
70
 
71
#if ENABLED(USE_SMALL_INFOFONT)
72
  #include "dogm_font_data_6x9_marlin.h"
73
  #define FONT_STATUSMENU_NAME u8g_font_6x9
74
  #define INFO_FONT_HEIGHT 7
75
#else
76
  #define FONT_STATUSMENU_NAME FONT_MENU_NAME
77
  #define INFO_FONT_HEIGHT 8
78
#endif
79
 
80
#include "dogm_font_data_Marlin_symbols.h"   // The Marlin special symbols
81
#define FONT_SPECIAL_NAME Marlin_symbols
82
 
83
#if DISABLED(SIMULATE_ROMFONT)
84
  #if ENABLED(DISPLAY_CHARSET_ISO10646_1)
85
    #include "dogm_font_data_ISO10646_1.h"
86
    #define FONT_MENU_NAME ISO10646_1_5x7
87
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_PL)
88
    #include "dogm_font_data_ISO10646_1_PL.h"
89
    #define FONT_MENU_NAME ISO10646_1_PL_5x7
90
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_5)
91
    #include "dogm_font_data_ISO10646_5_Cyrillic.h"
92
    #define FONT_MENU_NAME ISO10646_5_Cyrillic_5x7
93
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_KANA)
94
    #include "dogm_font_data_ISO10646_Kana.h"
95
    #define FONT_MENU_NAME ISO10646_Kana_5x7
96
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_KO_KR)
97
    #include "dogm_font_data_ISO10646_ko_KR.h"
98
    #define FONT_MENU_NAME ISO10646_ko_KR
99
    #define TALL_FONT_CORRECTION 1
100
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_GREEK)
101
    #include "dogm_font_data_ISO10646_Greek.h"
102
    #define FONT_MENU_NAME ISO10646_Greek_5x7
103
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_CN)
104
    #include "dogm_font_data_ISO10646_CN.h"
105
    #define FONT_MENU_NAME ISO10646_CN
106
    #define TALL_FONT_CORRECTION 1
107
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_TR)
108
    #include "dogm_font_data_ISO10646_1_tr.h"
109
    #define FONT_MENU_NAME ISO10646_TR
110
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_CZ)
111
    #include "dogm_font_data_ISO10646_CZ.h"
112
    #define FONT_MENU_NAME ISO10646_CZ
113
  #elif ENABLED(DISPLAY_CHARSET_ISO10646_SK)
114
    #include "dogm_font_data_ISO10646_SK.h"
115
    #define FONT_MENU_NAME ISO10646_SK
116
  #else // fall-back
117
    #include "dogm_font_data_ISO10646_1.h"
118
    #define FONT_MENU_NAME ISO10646_1_5x7
119
  #endif
120
#else // SIMULATE_ROMFONT
121
  #if DISPLAY_CHARSET_HD44780 == JAPANESE
122
    #include "dogm_font_data_HD44780_J.h"
123
    #define FONT_MENU_NAME HD44780_J_5x7
124
  #elif DISPLAY_CHARSET_HD44780 == WESTERN
125
    #include "dogm_font_data_HD44780_W.h"
126
    #define FONT_MENU_NAME HD44780_W_5x7
127
  #elif DISPLAY_CHARSET_HD44780 == CYRILLIC
128
    #include "dogm_font_data_HD44780_C.h"
129
    #define FONT_MENU_NAME HD44780_C_5x7
130
  #else // fall-back
131
    #include "dogm_font_data_ISO10646_1.h"
132
    #define FONT_MENU_NAME ISO10646_1_5x7
133
  #endif
134
#endif // SIMULATE_ROMFONT
135
 
136
//#define FONT_STATUSMENU_NAME FONT_MENU_NAME
137
 
138
#define FONT_STATUSMENU 1
139
#define FONT_SPECIAL 2
140
#define FONT_MENU_EDIT 3
141
#define FONT_MENU 4
142
 
143
// DOGM parameters (size in pixels)
144
#define DOG_CHAR_WIDTH         6
145
#define DOG_CHAR_HEIGHT        12
146
#if ENABLED(USE_BIG_EDIT_FONT)
147
  #define FONT_MENU_EDIT_NAME u8g_font_9x18
148
  #define DOG_CHAR_WIDTH_EDIT  9
149
  #define DOG_CHAR_HEIGHT_EDIT 18
150
#else
151
  #define FONT_MENU_EDIT_NAME FONT_MENU_NAME
152
  #define DOG_CHAR_WIDTH_EDIT  DOG_CHAR_WIDTH
153
  #define DOG_CHAR_HEIGHT_EDIT DOG_CHAR_HEIGHT
154
#endif
155
 
156
#ifndef TALL_FONT_CORRECTION
157
  #define TALL_FONT_CORRECTION 0
158
#endif
159
 
160
#define START_COL              0
161
 
162
// LCD selection
163
#if ENABLED(REPRAPWORLD_GRAPHICAL_LCD)
164
  U8GLIB_ST7920_128X64_4X u8g(LCD_PINS_RS); // 2 stripes
165
  // U8GLIB_ST7920_128X64 u8g(LCD_PINS_RS); // 8 stripes
166
#elif ENABLED(U8GLIB_ST7920)
167
  //U8GLIB_ST7920_128X64_4X u8g(LCD_PINS_D4, LCD_PINS_ENABLE, LCD_PINS_RS); // Original u8glib device. 2 stripes
168
                                                                            // No 4 stripe device available from u8glib.
169
  //U8GLIB_ST7920_128X64_1X u8g(LCD_PINS_D4, LCD_PINS_ENABLE, LCD_PINS_RS);    // Original u8glib device. 8 stripes
170
  U8GLIB_ST7920_128X64_RRD u8g(0); // Number of stripes can be adjusted in ultralcd_st7920_u8glib_rrd.h with PAGE_HEIGHT
171
#elif ENABLED(CARTESIO_UI)
172
  // The CartesioUI display
173
  #if defined(DOGLCD_MOSI) && DOGLCD_MOSI > -1 && defined(DOGLCD_SCK) && DOGLCD_SCK > -1
174
    // using SW-SPI
175
    //U8GLIB_DOGM128 u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0);  // 8 stripes
176
    U8GLIB_DOGM128_2X u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0); // 4 stripes
177
  #else
178
    //U8GLIB_DOGM128 u8g(DOGLCD_CS, DOGLCD_A0);  // 8 stripes
179
    U8GLIB_DOGM128_2X u8g(DOGLCD_CS, DOGLCD_A0); // 4 stripes
180
  #endif
181
#elif ENABLED(U8GLIB_LM6059_AF)
182
  // Based on the Adafruit ST7565 (http://www.adafruit.com/products/250)
183
  //U8GLIB_LM6059 u8g(DOGLCD_CS, DOGLCD_A0);  // 8 stripes
184
  U8GLIB_LM6059_2X u8g(DOGLCD_CS, DOGLCD_A0); // 4 stripes
185
#elif ENABLED(U8GLIB_ST7565_64128N)
186
  // The MaKrPanel, Mini Viki, and Viki 2.0, ST7565 controller as well
187
  // U8GLIB_ST7565_64128n_2x_VIKI u8g(0);  // using SW-SPI DOGLCD_MOSI != -1 && DOGLCD_SCK
188
  U8GLIB_ST7565_64128n_2x_VIKI u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0);  // using SW-SPI
189
  //U8GLIB_NHD_C12864_2X u8g(DOGLCD_CS, DOGLCD_A0); // 4 stripes  HWSPI
190
#elif ENABLED(MKS_12864OLED_SSD1306)
191
  // MKS 128x64 (SSD1306) OLED I2C LCD
192
  U8GLIB_SSD1306_128X64 u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0);      // 8 stripes
193
  //U8GLIB_SSD1306_128X64_2X u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0); // 4 stripes
194
#elif ENABLED(U8GLIB_SSD1306)
195
  // Generic support for SSD1306 OLED I2C LCDs
196
  //U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_FAST);  // 8 stripes
197
  U8GLIB_SSD1306_128X64_2X u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_FAST); // 4 stripes
198
#elif ENABLED(MKS_12864OLED)
199
  // MKS 128x64 (SH1106) OLED I2C LCD
200
  U8GLIB_SH1106_128X64 u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0);      // 8 stripes
201
  //U8GLIB_SH1106_128X64_2X u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0); // 4 stripes
202
#elif ENABLED(U8GLIB_SH1106)
203
  // Generic support for SH1106 OLED I2C LCDs
204
  //U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_FAST);  // 8 stripes
205
  U8GLIB_SH1106_128X64_2X u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_FAST); // 4 stripes
206
#elif ENABLED(U8GLIB_SSD1309)
207
  // Generic support for SSD1309 OLED I2C LCDs
208
  U8GLIB_SSD1309_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_FAST);
209
#elif ENABLED(MINIPANEL)
210
  // The MINIPanel display
211
  #if defined(DOGLCD_MOSI) && DOGLCD_MOSI > -1 && defined(DOGLCD_SCK) && DOGLCD_SCK > -1
212
    // using SW-SPI
213
    //U8GLIB_MINI12864 u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0);  // 8 stripes
214
    U8GLIB_MINI12864_2X u8g(DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0); // 4 stripes
215
  #else
216
    //U8GLIB_MINI12864 u8g(DOGLCD_CS, DOGLCD_A0);  // 8 stripes
217
    U8GLIB_MINI12864_2X u8g(DOGLCD_CS, DOGLCD_A0); // 4 stripes
218
  #endif
219
#else
220
  // for regular DOGM128 display with HW-SPI
221
  //U8GLIB_DOGM128 u8g(DOGLCD_CS, DOGLCD_A0);  // HW-SPI Com: CS, A0  // 8 stripes
222
  U8GLIB_DOGM128_2X u8g(DOGLCD_CS, DOGLCD_A0);  // HW-SPI Com: CS, A0 // 4 stripes
223
#endif
224
 
225
#ifndef LCD_PIXEL_WIDTH
226
  #define LCD_PIXEL_WIDTH 128
227
#endif
228
#ifndef LCD_PIXEL_HEIGHT
229
  #define LCD_PIXEL_HEIGHT 64
230
#endif
231
 
232
#include "utf_mapper.h"
233
 
234
int16_t lcd_contrast; // Initialized by settings.load()
235
static char currentfont = 0;
236
 
237
// The current graphical page being rendered
238
u8g_page_t &page = ((u8g_pb_t *)((u8g.getU8g())->dev->dev_mem))->p;
239
 
240
// For selective rendering within a Y range
241
#define PAGE_UNDER(yb) (u8g.getU8g()->current_page.y0 <= (yb))
242
#define PAGE_CONTAINS(ya, yb) (PAGE_UNDER(yb) && u8g.getU8g()->current_page.y1 >= (ya))
243
 
244
static void lcd_setFont(const char font_nr) {
245
  switch (font_nr) {
246
    case FONT_STATUSMENU : {u8g.setFont(FONT_STATUSMENU_NAME); currentfont = FONT_STATUSMENU;}; break;
247
    case FONT_MENU       : {u8g.setFont(FONT_MENU_NAME); currentfont = FONT_MENU;}; break;
248
    case FONT_SPECIAL    : {u8g.setFont(FONT_SPECIAL_NAME); currentfont = FONT_SPECIAL;}; break;
249
    case FONT_MENU_EDIT  : {u8g.setFont(FONT_MENU_EDIT_NAME); currentfont = FONT_MENU_EDIT;}; break;
250
    break;
251
  }
252
}
253
 
254
void lcd_print(const char c) {
255
  if (WITHIN(c, 1, LCD_STR_SPECIAL_MAX)) {
256
    u8g.setFont(FONT_SPECIAL_NAME);
257
    u8g.print(c);
258
    lcd_setFont(currentfont);
259
  }
260
  else charset_mapper(c);
261
}
262
 
263
char lcd_print_and_count(const char c) {
264
  if (WITHIN(c, 1, LCD_STR_SPECIAL_MAX)) {
265
    u8g.setFont(FONT_SPECIAL_NAME);
266
    u8g.print(c);
267
    lcd_setFont(currentfont);
268
    return 1;
269
  }
270
  else return charset_mapper(c);
271
}
272
 
273
/**
274
 * Core LCD printing functions
275
 * On DOGM all strings go through a filter for utf
276
 * But only use lcd_print_utf and lcd_printPGM_utf for translated text
277
 */
278
void lcd_print(const char *str) { while (*str) lcd_print(*str++); }
279
void lcd_printPGM(const char *str) { while (const char c = pgm_read_byte(str)) lcd_print(c), ++str; }
280
 
281
void lcd_print_utf(const char *str, uint8_t n=LCD_WIDTH) {
282
  char c;
283
  while (n && (c = *str)) n -= charset_mapper(c), ++str;
284
}
285
 
286
void lcd_printPGM_utf(const char *str, uint8_t n=LCD_WIDTH) {
287
  char c;
288
  while (n && (c = pgm_read_byte(str))) n -= charset_mapper(c), ++str;
289
}
290
 
291
#if ENABLED(SHOW_BOOTSCREEN)
292
 
293
  #if ENABLED(SHOW_CUSTOM_BOOTSCREEN)
294
 
295
    void lcd_custom_bootscreen() {
296
      constexpr u8g_uint_t left = (LCD_PIXEL_WIDTH  - (CUSTOM_BOOTSCREEN_BMPWIDTH)) / 2,
297
                           top = (LCD_PIXEL_HEIGHT - (CUSTOM_BOOTSCREEN_BMPHEIGHT)) / 2;
298
      #if ENABLED(CUSTOM_BOOTSCREEN_INVERTED)
299
        constexpr u8g_uint_t right = left + CUSTOM_BOOTSCREEN_BMPWIDTH,
300
                             bottom = top + CUSTOM_BOOTSCREEN_BMPHEIGHT;
301
      #endif
302
      u8g.firstPage();
303
      do {
304
        u8g.drawBitmapP(
305
          left, top,
306
          CEILING(CUSTOM_BOOTSCREEN_BMPWIDTH, 8), CUSTOM_BOOTSCREEN_BMPHEIGHT, custom_start_bmp
307
        );
308
        #if ENABLED(CUSTOM_BOOTSCREEN_INVERTED)
309
          u8g.setColorIndex(1);
310
          if (top) u8g.drawBox(0, 0, LCD_PIXEL_WIDTH, top);
311
          if (left) u8g.drawBox(0, top, left, CUSTOM_BOOTSCREEN_BMPHEIGHT);
312
          if (right < LCD_PIXEL_WIDTH) u8g.drawBox(right, top, LCD_PIXEL_WIDTH - right, CUSTOM_BOOTSCREEN_BMPHEIGHT);
313
          if (bottom < LCD_PIXEL_HEIGHT) u8g.drawBox(0, bottom, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT - bottom);
314
        #endif
315
      } while (u8g.nextPage());
316
      safe_delay(CUSTOM_BOOTSCREEN_TIMEOUT);
317
    }
318
 
319
  #endif // SHOW_CUSTOM_BOOTSCREEN
320
 
321
  void lcd_bootscreen() {
322
    #if ENABLED(SHOW_CUSTOM_BOOTSCREEN)
323
      lcd_custom_bootscreen();
324
    #endif
325
 
326
    constexpr uint8_t offy =
327
      #if ENABLED(START_BMPHIGH)
328
        (LCD_PIXEL_HEIGHT - (START_BMPHEIGHT)) / 2
329
      #else
330
        DOG_CHAR_HEIGHT
331
      #endif
332
    ;
333
 
334
    const uint8_t width = u8g.getWidth(), height = u8g.getHeight(),
335
                  offx = (width - (START_BMPWIDTH)) / 2;
336
 
337
    u8g.firstPage();
338
    do {
339
      u8g.drawBitmapP(offx, offy, (START_BMPWIDTH + 7) / 8, START_BMPHEIGHT, start_bmp);
340
      lcd_setFont(FONT_MENU);
341
      #ifndef STRING_SPLASH_LINE2
342
        const uint8_t txt1X = width - (sizeof(STRING_SPLASH_LINE1) - 1) * (DOG_CHAR_WIDTH);
343
        u8g.drawStr(txt1X, (height + DOG_CHAR_HEIGHT) / 2, STRING_SPLASH_LINE1);
344
      #else
345
        const uint8_t txt1X = (width - (sizeof(STRING_SPLASH_LINE1) - 1) * (DOG_CHAR_WIDTH)) / 2,
346
                      txt2X = (width - (sizeof(STRING_SPLASH_LINE2) - 1) * (DOG_CHAR_WIDTH)) / 2;
347
        u8g.drawStr(txt1X, height - (DOG_CHAR_HEIGHT) * 3 / 2, STRING_SPLASH_LINE1);
348
        u8g.drawStr(txt2X, height - (DOG_CHAR_HEIGHT) * 1 / 2, STRING_SPLASH_LINE2);
349
      #endif
350
    } while (u8g.nextPage());
351
    safe_delay(BOOTSCREEN_TIMEOUT);
352
  }
353
 
354
#endif // SHOW_BOOTSCREEN
355
 
356
#if ENABLED(LIGHTWEIGHT_UI)
357
  #include "status_screen_lite_ST7920.h"
358
#else
359
  #include "status_screen_DOGM.h"
360
#endif
361
 
362
// Initialize or re-initialize the LCD
363
static void lcd_implementation_init() {
364
 
365
  #if PIN_EXISTS(LCD_BACKLIGHT) // Enable LCD backlight
366
    OUT_WRITE(LCD_BACKLIGHT_PIN, HIGH);
367
  #endif
368
 
369
  #if !defined(LCD_RESET_PIN) && (ENABLED(MKS_12864OLED) || ENABLED(MKS_12864OLED_SSD1306))
370
    #define LCD_RESET_PIN LCD_PINS_RS
371
  #endif
372
 
373
  #if PIN_EXISTS(LCD_RESET)
374
    OUT_WRITE(LCD_RESET_PIN, LOW); // perform a clean hardware reset
375
    _delay_ms(5);
376
    OUT_WRITE(LCD_RESET_PIN, HIGH);
377
    _delay_ms(5); // delay to allow the display to initalize
378
  #endif
379
 
380
  #if PIN_EXISTS(LCD_RESET)
381
    u8g.begin();
382
  #endif
383
 
384
  #if DISABLED(MINIPANEL) // setContrast not working for Mini Panel
385
    u8g.setContrast(lcd_contrast);
386
  #endif
387
 
388
  #if ENABLED(LCD_SCREEN_ROT_90)
389
    u8g.setRot90();   // Rotate screen by 90°
390
  #elif ENABLED(LCD_SCREEN_ROT_180)
391
    u8g.setRot180();  // Rotate screen by 180°
392
  #elif ENABLED(LCD_SCREEN_ROT_270)
393
    u8g.setRot270();  // Rotate screen by 270°
394
  #endif
395
}
396
 
397
// The kill screen is displayed for unrecoverable conditions
398
void lcd_kill_screen() {
399
  #if ENABLED(LIGHTWEIGHT_UI)
400
    ST7920_Lite_Status_Screen::clear_text_buffer();
401
  #endif
402
  const uint8_t h4 = u8g.getHeight() / 4;
403
  u8g.firstPage();
404
  do {
405
    lcd_setFont(FONT_MENU);
406
    u8g.setPrintPos(0, h4 * 1);
407
    lcd_print_utf(lcd_status_message);
408
    u8g.setPrintPos(0, h4 * 2);
409
    lcd_printPGM_utf(PSTR(MSG_HALTED));
410
    u8g.setPrintPos(0, h4 * 3);
411
    lcd_printPGM_utf(PSTR(MSG_PLEASE_RESET));
412
  } while (u8g.nextPage());
413
}
414
 
415
void lcd_implementation_clear() { } // Automatically cleared by Picture Loop
416
 
417
#if ENABLED(ULTIPANEL)
418
 
419
  uint8_t row_y1, row_y2;
420
  uint8_t constexpr row_height = DOG_CHAR_HEIGHT + 2 * (TALL_FONT_CORRECTION);
421
 
422
  #if ENABLED(ADVANCED_PAUSE_FEATURE)
423
 
424
    static void lcd_implementation_hotend_status(const uint8_t row, const uint8_t extruder=active_extruder) {
425
      row_y1 = row * row_height + 1;
426
      row_y2 = row_y1 + row_height - 1;
427
 
428
      if (!PAGE_CONTAINS(row_y1 + 1, row_y2 + 2)) return;
429
 
430
      u8g.setPrintPos(LCD_PIXEL_WIDTH - 11 * (DOG_CHAR_WIDTH), row_y2);
431
      lcd_print('E');
432
      lcd_print((char)('1' + extruder));
433
      lcd_print(' ');
434
      lcd_print(itostr3(thermalManager.degHotend(extruder)));
435
      lcd_print('/');
436
 
437
      if (lcd_blink() || !thermalManager.is_heater_idle(extruder))
438
        lcd_print(itostr3(thermalManager.degTargetHotend(extruder)));
439
    }
440
 
441
  #endif // ADVANCED_PAUSE_FEATURE
442
 
443
  // Set the colors for a menu item based on whether it is selected
444
  static void lcd_implementation_mark_as_selected(const uint8_t row, const bool isSelected) {
445
    row_y1 = row * row_height + 1;
446
    row_y2 = row_y1 + row_height - 1;
447
 
448
    if (!PAGE_CONTAINS(row_y1 + 1, row_y2 + 2)) return;
449
 
450
    if (isSelected) {
451
      #if ENABLED(MENU_HOLLOW_FRAME)
452
        u8g.drawHLine(0, row_y1 + 1, LCD_PIXEL_WIDTH);
453
        u8g.drawHLine(0, row_y2 + 2, LCD_PIXEL_WIDTH);
454
      #else
455
        u8g.setColorIndex(1); // black on white
456
        u8g.drawBox(0, row_y1 + 2, LCD_PIXEL_WIDTH, row_height - 1);
457
        u8g.setColorIndex(0); // white on black
458
      #endif
459
    }
460
    #if DISABLED(MENU_HOLLOW_FRAME)
461
      else {
462
        u8g.setColorIndex(1); // unmarked text is black on white
463
      }
464
    #endif
465
    u8g.setPrintPos((START_COL) * (DOG_CHAR_WIDTH), row_y2);
466
  }
467
 
468
  // Draw a static line of text in the same idiom as a menu item
469
  static void lcd_implementation_drawmenu_static(const uint8_t row, const char* pstr, const bool center=true, const bool invert=false, const char* valstr=NULL) {
470
 
471
    lcd_implementation_mark_as_selected(row, invert);
472
 
473
    if (!PAGE_CONTAINS(row_y1, row_y2)) return;
474
 
475
    char c;
476
    int8_t n = LCD_WIDTH - (START_COL);
477
 
478
    if (center && !valstr) {
479
      int8_t pad = (LCD_WIDTH - utf8_strlen_P(pstr)) / 2;
480
      while (--pad >= 0) { u8g.print(' '); n--; }
481
    }
482
    while (n > 0 && (c = pgm_read_byte(pstr))) {
483
      n -= lcd_print_and_count(c);
484
      pstr++;
485
    }
486
    if (valstr) while (n > 0 && (c = *valstr)) {
487
      n -= lcd_print_and_count(c);
488
      valstr++;
489
    }
490
    while (n-- > 0) u8g.print(' ');
491
  }
492
 
493
  // Draw a generic menu item
494
  static void lcd_implementation_drawmenu_generic(const bool isSelected, const uint8_t row, const char* pstr, const char pre_char, const char post_char) {
495
    UNUSED(pre_char);
496
 
497
    lcd_implementation_mark_as_selected(row, isSelected);
498
 
499
    if (!PAGE_CONTAINS(row_y1, row_y2)) return;
500
 
501
    uint8_t n = LCD_WIDTH - (START_COL) - 2;
502
    while (char c = pgm_read_byte(pstr)) {
503
      n -= lcd_print_and_count(c);
504
      pstr++;
505
    }
506
    while (n--) u8g.print(' ');
507
    u8g.setPrintPos(LCD_PIXEL_WIDTH - (DOG_CHAR_WIDTH), row_y2);
508
    lcd_print(post_char);
509
    u8g.print(' ');
510
  }
511
 
512
  // Macros for specific types of menu items
513
  #define lcd_implementation_drawmenu_back(sel, row, pstr, dummy) lcd_implementation_drawmenu_generic(sel, row, pstr, LCD_STR_UPLEVEL[0], LCD_STR_UPLEVEL[0])
514
  #define lcd_implementation_drawmenu_submenu(sel, row, pstr, data) lcd_implementation_drawmenu_generic(sel, row, pstr, '>', LCD_STR_ARROW_RIGHT[0])
515
  #define lcd_implementation_drawmenu_gcode(sel, row, pstr, gcode) lcd_implementation_drawmenu_generic(sel, row, pstr, '>', ' ')
516
  #define lcd_implementation_drawmenu_function(sel, row, pstr, data) lcd_implementation_drawmenu_generic(sel, row, pstr, '>', ' ')
517
 
518
  // Draw a menu item with an editable value
519
  static void _drawmenu_setting_edit_generic(const bool isSelected, const uint8_t row, const char* pstr, const char* const data, const bool pgm) {
520
 
521
    lcd_implementation_mark_as_selected(row, isSelected);
522
 
523
    if (!PAGE_CONTAINS(row_y1, row_y2)) return;
524
 
525
    const uint8_t vallen = (pgm ? utf8_strlen_P(data) : utf8_strlen((char*)data));
526
    uint8_t n = LCD_WIDTH - (START_COL) - 2 - vallen;
527
 
528
    while (char c = pgm_read_byte(pstr)) {
529
      n -= lcd_print_and_count(c);
530
      pstr++;
531
    }
532
    u8g.print(':');
533
    while (n--) u8g.print(' ');
534
    u8g.setPrintPos(LCD_PIXEL_WIDTH - (DOG_CHAR_WIDTH) * vallen, row_y2);
535
    if (pgm)  lcd_printPGM(data);  else  lcd_print((char*)data);
536
  }
537
 
538
  // Macros for edit items
539
  #define lcd_implementation_drawmenu_setting_edit_generic(sel, row, pstr, data) _drawmenu_setting_edit_generic(sel, row, pstr, data, false)
540
  #define lcd_implementation_drawmenu_setting_edit_generic_P(sel, row, pstr, data) _drawmenu_setting_edit_generic(sel, row, pstr, data, true)
541
 
542
  #define DRAWMENU_SETTING_EDIT_GENERIC(_src) lcd_implementation_drawmenu_setting_edit_generic(sel, row, pstr, _src)
543
  #define DRAW_BOOL_SETTING(sel, row, pstr, data) lcd_implementation_drawmenu_setting_edit_generic_P(sel, row, pstr, (*(data))?PSTR(MSG_ON):PSTR(MSG_OFF))
544
 
545
  void lcd_implementation_drawedit(const char* const pstr, const char* const value=NULL) {
546
    const uint8_t labellen = utf8_strlen_P(pstr),
547
                  vallen = utf8_strlen(value);
548
 
549
    uint8_t rows = (labellen > LCD_WIDTH - 2 - vallen) ? 2 : 1;
550
 
551
    #if ENABLED(USE_BIG_EDIT_FONT)
552
      constexpr uint8_t lcd_width_edit = (LCD_PIXEL_WIDTH) / (DOG_CHAR_WIDTH_EDIT);
553
 
554
      uint8_t lcd_width, char_width;
555
      if (labellen <= lcd_width_edit - 1) {
556
        if (labellen + vallen + 2 >= lcd_width_edit) rows = 2;
557
        lcd_width = lcd_width_edit + 1;
558
        char_width = DOG_CHAR_WIDTH_EDIT;
559
        lcd_setFont(FONT_MENU_EDIT);
560
      }
561
      else {
562
        lcd_width = LCD_WIDTH - (START_COL);
563
        char_width = DOG_CHAR_WIDTH;
564
        lcd_setFont(FONT_MENU);
565
      }
566
    #else
567
      constexpr uint8_t lcd_width = LCD_WIDTH - (START_COL),
568
                        char_width = DOG_CHAR_WIDTH;
569
    #endif
570
 
571
    // Center either one or two rows
572
    const uint8_t segmentHeight = u8g.getHeight() / (rows + 1); // 1 / (rows+1) = 1/2 or 1/3
573
    uint8_t baseline = segmentHeight + (DOG_CHAR_HEIGHT_EDIT + 1) / 2;
574
 
575
    bool onpage = PAGE_CONTAINS(baseline + 1 - (DOG_CHAR_HEIGHT_EDIT), baseline);
576
    if (onpage) {
577
      u8g.setPrintPos(0, baseline);
578
      lcd_printPGM_utf(pstr);
579
    }
580
 
581
    if (value != NULL) {
582
      u8g.print(':');
583
      if (rows == 2) {
584
        baseline += segmentHeight;
585
        onpage = PAGE_CONTAINS(baseline + 1 - (DOG_CHAR_HEIGHT_EDIT), baseline);
586
      }
587
      if (onpage) {
588
        u8g.setPrintPos(((lcd_width - 1) - (vallen + 1)) * char_width, baseline); // Right-justified, leaving padded by spaces
589
        u8g.print(' '); // overwrite char if value gets shorter
590
        lcd_print(value);
591
      }
592
    }
593
  }
594
 
595
  #if ENABLED(SDSUPPORT)
596
 
597
    static void _drawmenu_sd(const bool isSelected, const uint8_t row, const char* const pstr, CardReader& theCard, const bool isDir) {
598
      UNUSED(pstr);
599
 
600
      lcd_implementation_mark_as_selected(row, isSelected);
601
 
602
      if (!PAGE_CONTAINS(row_y1, row_y2)) return;
603
 
604
      constexpr uint8_t maxlen = LCD_WIDTH - (START_COL) - 1;
605
      const char *outstr = theCard.longest_filename();
606
      if (theCard.longFilename[0]) {
607
        #if ENABLED(SCROLL_LONG_FILENAMES)
608
          if (isSelected) {
609
            uint8_t name_hash = row;
610
            for (uint8_t l = FILENAME_LENGTH; l--;)
611
              name_hash = ((name_hash << 1) | (name_hash >> 7)) ^ theCard.filename[l];  // rotate, xor
612
            if (filename_scroll_hash != name_hash) {                            // If the hash changed...
613
              filename_scroll_hash = name_hash;                                 // Save the new hash
614
              filename_scroll_max = MAX(0, utf8_strlen(theCard.longFilename) - maxlen); // Update the scroll limit
615
              filename_scroll_pos = 0;                                          // Reset scroll to the start
616
              lcd_status_update_delay = 8;                                      // Don't scroll right away
617
            }
618
            outstr += filename_scroll_pos;
619
          }
620
        #else
621
          theCard.longFilename[maxlen] = '\0'; // cutoff at screen edge
622
        #endif
623
      }
624
 
625
      if (isDir) lcd_print(LCD_STR_FOLDER[0]);
626
 
627
      char c;
628
      uint8_t n = maxlen;
629
      while (n && (c = *outstr)) {
630
        n -= lcd_print_and_count(c);
631
        ++outstr;
632
      }
633
      while (n) { --n; u8g.print(' '); }
634
    }
635
 
636
    #define lcd_implementation_drawmenu_sdfile(sel, row, pstr, theCard) _drawmenu_sd(sel, row, pstr, theCard, false)
637
    #define lcd_implementation_drawmenu_sddirectory(sel, row, pstr, theCard) _drawmenu_sd(sel, row, pstr, theCard, true)
638
 
639
  #endif // SDSUPPORT
640
 
641
  #if ENABLED(AUTO_BED_LEVELING_UBL)
642
 
643
    /**
644
     * UBL LCD "radar" map data
645
     */
646
    #define MAP_UPPER_LEFT_CORNER_X 35  // These probably should be moved to the .h file  But for now,
647
    #define MAP_UPPER_LEFT_CORNER_Y  8  // it is easier to play with things having them here
648
    #define MAP_MAX_PIXELS_X        53
649
    #define MAP_MAX_PIXELS_Y        49
650
 
651
    void lcd_implementation_ubl_plot(const uint8_t x_plot, const uint8_t y_plot) {
652
      // Scale the box pixels appropriately
653
      uint8_t x_map_pixels = ((MAP_MAX_PIXELS_X - 4) / (GRID_MAX_POINTS_X)) * (GRID_MAX_POINTS_X),
654
              y_map_pixels = ((MAP_MAX_PIXELS_Y - 4) / (GRID_MAX_POINTS_Y)) * (GRID_MAX_POINTS_Y),
655
 
656
              pixels_per_x_mesh_pnt = x_map_pixels / (GRID_MAX_POINTS_X),
657
              pixels_per_y_mesh_pnt = y_map_pixels / (GRID_MAX_POINTS_Y),
658
 
659
              x_offset = MAP_UPPER_LEFT_CORNER_X + 1 + (MAP_MAX_PIXELS_X - x_map_pixels - 2) / 2,
660
              y_offset = MAP_UPPER_LEFT_CORNER_Y + 1 + (MAP_MAX_PIXELS_Y - y_map_pixels - 2) / 2;
661
 
662
      // Clear the Mesh Map
663
 
664
      if (PAGE_CONTAINS(y_offset - 2, y_offset + y_map_pixels + 4)) {
665
        u8g.setColorIndex(1);  // First draw the bigger box in White so we have a border around the mesh map box
666
        u8g.drawBox(x_offset - 2, y_offset - 2, x_map_pixels + 4, y_map_pixels + 4);
667
        if (PAGE_CONTAINS(y_offset, y_offset + y_map_pixels)) {
668
          u8g.setColorIndex(0);  // Now actually clear the mesh map box
669
          u8g.drawBox(x_offset, y_offset, x_map_pixels, y_map_pixels);
670
        }
671
      }
672
 
673
      // Display Mesh Point Locations
674
 
675
      u8g.setColorIndex(1);
676
      const uint8_t sx = x_offset + pixels_per_x_mesh_pnt / 2;
677
            uint8_t  y = y_offset + pixels_per_y_mesh_pnt / 2;
678
      for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++, y += pixels_per_y_mesh_pnt)
679
        if (PAGE_CONTAINS(y, y))
680
          for (uint8_t i = 0, x = sx; i < GRID_MAX_POINTS_X; i++, x += pixels_per_x_mesh_pnt)
681
            u8g.drawBox(x, y, 1, 1);
682
 
683
      // Fill in the Specified Mesh Point
684
 
685
      uint8_t inverted_y = GRID_MAX_POINTS_Y - y_plot - 1;  // The origin is typically in the lower right corner.  We need to
686
                                                            // invert the Y to get it to plot in the right location.
687
 
688
      const uint8_t by = y_offset + inverted_y * pixels_per_y_mesh_pnt;
689
      if (PAGE_CONTAINS(by, by + pixels_per_y_mesh_pnt))
690
        u8g.drawBox(
691
          x_offset + x_plot * pixels_per_x_mesh_pnt, by,
692
          pixels_per_x_mesh_pnt, pixels_per_y_mesh_pnt
693
        );
694
 
695
      // Put Relevant Text on Display
696
 
697
      // Show X and Y positions at top of screen
698
      u8g.setColorIndex(1);
699
      if (PAGE_UNDER(7)) {
700
        u8g.setPrintPos(5, 7);
701
        lcd_print("X:");
702
        lcd_print(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]))));
703
        u8g.setPrintPos(74, 7);
704
        lcd_print("Y:");
705
        lcd_print(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]))));
706
      }
707
 
708
      // Print plot position
709
      if (PAGE_CONTAINS(LCD_PIXEL_HEIGHT - (INFO_FONT_HEIGHT - 1), LCD_PIXEL_HEIGHT)) {
710
        u8g.setPrintPos(5, LCD_PIXEL_HEIGHT);
711
        lcd_print('(');
712
        u8g.print(x_plot);
713
        lcd_print(',');
714
        u8g.print(y_plot);
715
        lcd_print(')');
716
 
717
        // Show the location value
718
        u8g.setPrintPos(74, LCD_PIXEL_HEIGHT);
719
        lcd_print("Z:");
720
        if (!isnan(ubl.z_values[x_plot][y_plot]))
721
          lcd_print(ftostr43sign(ubl.z_values[x_plot][y_plot]));
722
        else
723
          lcd_printPGM(PSTR(" -----"));
724
      }
725
 
726
    }
727
 
728
  #endif // AUTO_BED_LEVELING_UBL
729
 
730
#endif // ULTIPANEL
731
 
732
#endif // __ULTRALCD_IMPL_DOGM_H