~ruther/qmk_firmware

cd94ba031c832266cca609951abc97eb52c90567 — Nick Brassel 2 years ago dfec6ac
Quantum Painter QoL enhancements -- auto-poweroff, auto-flush, buffer sizing (#20013)

M docs/quantum_painter.md => docs/quantum_painter.md +12 -10
@@ 32,16 32,18 @@ Supported devices:

## Quantum Painter Configuration :id=quantum-painter-config

| Option                                   | Default | Purpose                                                                                                                                     |
|------------------------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------|
| `QUANTUM_PAINTER_NUM_IMAGES`             | `8`     | The maximum number of images/animations that can be loaded at any one time.                                                                 |
| `QUANTUM_PAINTER_NUM_FONTS`              | `4`     | The maximum number of fonts that can be loaded at any one time.                                                                             |
| `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS`  | `4`     | The maximum number of animations that can be executed at the same time.                                                                     |
| `QUANTUM_PAINTER_LOAD_FONTS_TO_RAM`      | `FALSE` | Whether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash.             |
| `QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE`    | `32`    | The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU. |
| `QUANTUM_PAINTER_SUPPORTS_256_PALETTE`   | `FALSE` | If 256-color palettes are supported. Requires significantly more RAM on the MCU.                                                            |
| `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS` | `FALSE` | If native color range is supported. Requires significantly more RAM on the MCU.                                                             |
| `QUANTUM_PAINTER_DEBUG`                  | _unset_ | Prints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging.     |
| Option                                   | Default | Purpose                                                                                                                                                                                      |
|------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `QUANTUM_PAINTER_DISPLAY_TIMEOUT`        | `30000` | This controls the amount of time (in milliseconds) that all displays will remain on after the last user input. If set to `0`, the display will remain on indefinitely.                       |
| `QUANTUM_PAINTER_TASK_THROTTLE`          | `1`     | This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between each execution. Affects animations, display timeout, and LVGL timing if enabled. |
| `QUANTUM_PAINTER_NUM_IMAGES`             | `8`     | The maximum number of images/animations that can be loaded at any one time.                                                                                                                  |
| `QUANTUM_PAINTER_NUM_FONTS`              | `4`     | The maximum number of fonts that can be loaded at any one time.                                                                                                                              |
| `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS`  | `4`     | The maximum number of animations that can be executed at the same time.                                                                                                                      |
| `QUANTUM_PAINTER_LOAD_FONTS_TO_RAM`      | `FALSE` | Whether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash.                                                              |
| `QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE`    | `32`    | The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU.                                                  |
| `QUANTUM_PAINTER_SUPPORTS_256_PALETTE`   | `FALSE` | If 256-color palettes are supported. Requires significantly more RAM on the MCU.                                                                                                             |
| `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS` | `FALSE` | If native color range is supported. Requires significantly more RAM on the MCU.                                                                                                              |
| `QUANTUM_PAINTER_DEBUG`                  | _unset_ | Prints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging.                                                      |

Drivers have their own set of configurable options, and are described in their respective sections.


M drivers/painter/gc9a01/qp_gc9a01.c => drivers/painter/gc9a01/qp_gc9a01.c +7 -0
@@ 1,4 1,5 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include <wait.h>


@@ 141,6 142,12 @@ painter_device_t qp_gc9a01_make_spi_device(uint16_t panel_width, uint16_t panel_
            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;
            driver->spi_dc_reset_config.dc_pin                     = dc_pin;
            driver->spi_dc_reset_config.reset_pin                  = reset_pin;

            if (!qp_internal_register_device((painter_device_t)driver)) {
                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
                return NULL;
            }

            return (painter_device_t)driver;
        }
    }

M drivers/painter/ili9xxx/qp_ili9163.c => drivers/painter/ili9xxx/qp_ili9163.c +7 -1
@@ 1,4 1,4 @@
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"


@@ 110,6 110,12 @@ painter_device_t qp_ili9163_make_spi_device(uint16_t panel_width, uint16_t panel
            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;
            driver->spi_dc_reset_config.dc_pin                     = dc_pin;
            driver->spi_dc_reset_config.reset_pin                  = reset_pin;

            if (!qp_internal_register_device((painter_device_t)driver)) {
                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
                return NULL;
            }

            return (painter_device_t)driver;
        }
    }

M drivers/painter/ili9xxx/qp_ili9341.c => drivers/painter/ili9xxx/qp_ili9341.c +7 -1
@@ 1,4 1,4 @@
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"


@@ 117,6 117,12 @@ painter_device_t qp_ili9341_make_spi_device(uint16_t panel_width, uint16_t panel
            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;
            driver->spi_dc_reset_config.dc_pin                     = dc_pin;
            driver->spi_dc_reset_config.reset_pin                  = reset_pin;

            if (!qp_internal_register_device((painter_device_t)driver)) {
                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
                return NULL;
            }

            return (painter_device_t)driver;
        }
    }

M drivers/painter/ili9xxx/qp_ili9488.c => drivers/painter/ili9xxx/qp_ili9488.c +7 -1
@@ 1,4 1,4 @@
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"


@@ 110,6 110,12 @@ painter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel
            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;
            driver->spi_dc_reset_config.dc_pin                     = dc_pin;
            driver->spi_dc_reset_config.reset_pin                  = reset_pin;

            if (!qp_internal_register_device((painter_device_t)driver)) {
                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
                return NULL;
            }

            return (painter_device_t)driver;
        }
    }

M drivers/painter/ssd1351/qp_ssd1351.c => drivers/painter/ssd1351/qp_ssd1351.c +7 -1
@@ 1,4 1,4 @@
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"


@@ 114,6 114,12 @@ painter_device_t qp_ssd1351_make_spi_device(uint16_t panel_width, uint16_t panel
            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;
            driver->spi_dc_reset_config.dc_pin                     = dc_pin;
            driver->spi_dc_reset_config.reset_pin                  = reset_pin;

            if (!qp_internal_register_device((painter_device_t)driver)) {
                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
                return NULL;
            }

            return (painter_device_t)driver;
        }
    }

M drivers/painter/st77xx/qp_st7735.c => drivers/painter/st77xx/qp_st7735.c +7 -1
@@ 1,5 1,5 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// Copyright 2022 David Hoelscher (@customMK)
// SPDX-License-Identifier: GPL-2.0-or-later



@@ 134,6 134,12 @@ painter_device_t qp_st7735_make_spi_device(uint16_t panel_width, uint16_t panel_
            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;
            driver->spi_dc_reset_config.dc_pin                     = dc_pin;
            driver->spi_dc_reset_config.reset_pin                  = reset_pin;

            if (!qp_internal_register_device((painter_device_t)driver)) {
                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
                return NULL;
            }

            return (painter_device_t)driver;
        }
    }

M drivers/painter/st77xx/qp_st7789.c => drivers/painter/st77xx/qp_st7789.c +7 -1
@@ 1,5 1,5 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"


@@ 133,6 133,12 @@ painter_device_t qp_st7789_make_spi_device(uint16_t panel_width, uint16_t panel_
            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;
            driver->spi_dc_reset_config.dc_pin                     = dc_pin;
            driver->spi_dc_reset_config.reset_pin                  = reset_pin;

            if (!qp_internal_register_device((painter_device_t)driver)) {
                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
                return NULL;
            }

            return (painter_device_t)driver;
        }
    }

M quantum/painter/qp.h => quantum/painter/qp.h +34 -2
@@ 1,4 1,4 @@
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once


@@ 11,6 11,22 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter global configurables (add to your keyboard's config.h)

#ifndef QUANTUM_PAINTER_DISPLAY_TIMEOUT
/**
 * @def This controls the amount of time (in milliseconds) that all displays will remain on after the last user input.
 *      If set to 0, the display will remain on indefinitely.
 */
#    define QUANTUM_PAINTER_DISPLAY_TIMEOUT 30000
#endif // QUANTUM_PAINTER_DISPLAY_TIMEOUT

#ifndef QUANTUM_PAINTER_TASK_THROTTLE
/**
 * @def This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between
 *      each execution.
 */
#    define QUANTUM_PAINTER_TASK_THROTTLE 1
#endif // QUANTUM_PAINTER_TASK_THROTTLE

#ifndef QUANTUM_PAINTER_NUM_IMAGES
/**
 * @def This controls the maximum number of images that Quantum Painter can load at any one time. Images can be loaded


@@ 53,7 69,7 @@
 * @def This controls the maximum size of the pixel data buffer used for single blocks of transmission. Larger buffers
 *      means more data is processed at one time, with less frequent transmissions, at the cost of RAM.
 */
#    define QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE 32
#    define QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE 1024
#endif

#ifndef QUANTUM_PAINTER_SUPPORTS_256_PALETTE


@@ 442,34 458,50 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai

#ifdef QUANTUM_PAINTER_RGB565_SURFACE_ENABLE
#    include "qp_rgb565_surface.h"
#else // QUANTUM_PAINTER_RGB565_SURFACE_ENABLE
#    define RGB565_SURFACE_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_RGB565_SURFACE_ENABLE

#ifdef QUANTUM_PAINTER_ILI9163_ENABLE
#    include "qp_ili9163.h"
#else // QUANTUM_PAINTER_ILI9163_ENABLE
#    define ILI9163_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ILI9163_ENABLE

#ifdef QUANTUM_PAINTER_ILI9341_ENABLE
#    include "qp_ili9341.h"
#else // QUANTUM_PAINTER_ILI9341_ENABLE
#    define ILI9341_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ILI9341_ENABLE

#ifdef QUANTUM_PAINTER_ILI9488_ENABLE
#    include "qp_ili9488.h"
#else // QUANTUM_PAINTER_ILI9488_ENABLE
#    define ILI9488_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ILI9488_ENABLE

#ifdef QUANTUM_PAINTER_ST7789_ENABLE
#    include "qp_st7789.h"
#else // QUANTUM_PAINTER_ST7789_ENABLE
#    define ST7789_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ST7789_ENABLE

#ifdef QUANTUM_PAINTER_ST7735_ENABLE
#    include "qp_st7735.h"
#else // QUANTUM_PAINTER_ST7735_ENABLE
#    define ST7735_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ST7735_ENABLE

#ifdef QUANTUM_PAINTER_GC9A01_ENABLE
#    include "qp_gc9a01.h"
#else // QUANTUM_PAINTER_GC9A01_ENABLE
#    define GC9A01_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_GC9A01_ENABLE

#ifdef QUANTUM_PAINTER_SSD1351_ENABLE
#    include "qp_ssd1351.h"
#else // QUANTUM_PAINTER_SSD1351_ENABLE
#    define SSD1351_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_SSD1351_ENABLE

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

M quantum/painter/qp_draw_image.c => quantum/painter/qp_draw_image.c +1 -13
@@ 1,4 1,4 @@
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"


@@ 414,15 414,3 @@ void qp_internal_animation_tick(void) {
    static uint32_t last_anim_exec = 0;
    deferred_exec_advanced_task(animation_executors, QUANTUM_PAINTER_CONCURRENT_ANIMATIONS, &last_anim_exec);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: qp_internal_task

void qp_internal_task(void) {
    qp_internal_animation_tick();
#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE
    // Run LVGL ticks
    void qp_lvgl_internal_tick(void);
    qp_lvgl_internal_tick();
#endif
}

A quantum/painter/qp_internal.c => quantum/painter/qp_internal.c +96 -0
@@ 0,0 1,96 @@
// Copyright 2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "qp_internal.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: device registration

enum {
    // Work out how many devices we're actually going to be instantiating
    // NOTE: We intentionally do not include surfaces here, despite them conforming to the same API.
    QP_NUM_DEVICES = (ILI9163_NUM_DEVICES)   // ILI9163
                     + (ILI9341_NUM_DEVICES) // ILI9341
                     + (ILI9488_NUM_DEVICES) // ILI9488
                     + (ST7789_NUM_DEVICES)  // ST7789
                     + (ST7735_NUM_DEVICES)  // ST7735
                     + (GC9A01_NUM_DEVICES)  // GC9A01
                     + (SSD1351_NUM_DEVICES) // SSD1351
};

static painter_device_t qp_devices[QP_NUM_DEVICES] = {NULL};

bool qp_internal_register_device(painter_device_t driver) {
    for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {
        if (qp_devices[i] == NULL) {
            qp_devices[i] = driver;
            return true;
        }
    }

    // We should never get here -- someone has screwed up their device counts during config
    qp_dprintf("qp_internal_register_device: no more space for devices!\n");
    return false;
}

#if (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0
static void qp_internal_display_timeout_task(void) {
    // Handle power on/off state
    static bool display_on                  = true;
    bool        should_change_display_state = false;
    bool        target_display_state        = false;
    if (last_input_activity_elapsed() < (QUANTUM_PAINTER_DISPLAY_TIMEOUT)) {
        should_change_display_state = display_on == false;
        target_display_state        = true;
    } else {
        should_change_display_state = display_on == true;
        target_display_state        = false;
    }

    if (should_change_display_state) {
        for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {
            if (qp_devices[i] != NULL) {
                qp_power(qp_devices[i], target_display_state);
            }
        }

        display_on = target_display_state;
    }
}
#endif // (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: qp_internal_task

_Static_assert((QUANTUM_PAINTER_TASK_THROTTLE) > 0 && (QUANTUM_PAINTER_TASK_THROTTLE) < 1000, "QUANTUM_PAINTER_TASK_THROTTLE must be between 1 and 999");

void qp_internal_task(void) {
    // Perform throttling of the internal processing of Quantum Painter
    static uint32_t last_tick = 0;
    uint32_t        now       = timer_read32();
    if (TIMER_DIFF_32(now, last_tick) < (QUANTUM_PAINTER_TASK_THROTTLE)) {
        return;
    }
    last_tick = now;

#if (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0
    qp_internal_display_timeout_task();
#endif // (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0

    // Handle animations
    void qp_internal_animation_tick(void);
    qp_internal_animation_tick();

#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE
    // Run LVGL ticks
    void qp_lvgl_internal_tick(void);
    qp_lvgl_internal_tick();
#endif

    // Flush (render) dirty regions to corresponding displays
    for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {
        if (qp_devices[i] != NULL) {
            qp_flush(qp_devices[i]);
        }
    }
}

M quantum/painter/qp_internal_driver.h => quantum/painter/qp_internal_driver.h +6 -1
@@ 1,4 1,4 @@
// Copyright 2021 Nick Brassel (@tzarc)
// Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once


@@ 82,3 82,8 @@ struct painter_driver_t {
    // Comms config pointer -- needs to point to an appropriate comms config if the comms driver requires it.
    void *comms_config;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Device internals

bool qp_internal_register_device(painter_device_t driver);

M quantum/painter/rules.mk => quantum/painter/rules.mk +1 -0
@@ 24,6 24,7 @@ SRC += \
    $(QUANTUM_DIR)/unicode/utf8.c \
    $(QUANTUM_DIR)/color.c \
    $(QUANTUM_DIR)/painter/qp.c \
    $(QUANTUM_DIR)/painter/qp_internal.c \
    $(QUANTUM_DIR)/painter/qp_stream.c \
    $(QUANTUM_DIR)/painter/qgf.c \
    $(QUANTUM_DIR)/painter/qff.c \