mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			cache_comp
			...
			display_wr
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7504219a2d | ||
| 
						 | 
					6947df56d5 | ||
| 
						 | 
					29c97686fa | ||
| 
						 | 
					f3087abd55 | ||
| 
						 | 
					f55dfc7f5d | ||
| 
						 | 
					c5a9d30362 | ||
| 
						 | 
					fae06133d4 | 
@@ -176,7 +176,117 @@ class Display;
 | 
			
		||||
class DisplayPage;
 | 
			
		||||
class DisplayOnPageChangeTrigger;
 | 
			
		||||
 | 
			
		||||
using display_writer_t = std::function<void(Display &)>;
 | 
			
		||||
/** Optimized display writer that uses function pointers for stateless lambdas.
 | 
			
		||||
 *
 | 
			
		||||
 * Similar to TemplatableValue but specialized for display writer callbacks.
 | 
			
		||||
 * Saves ~8 bytes per stateless lambda on 32-bit platforms (16 bytes std::function → ~8 bytes discriminator+pointer).
 | 
			
		||||
 *
 | 
			
		||||
 * Supports both:
 | 
			
		||||
 * - Stateless lambdas (from YAML) → function pointer (4 bytes)
 | 
			
		||||
 * - Stateful lambdas/std::function (from C++ code) → std::function* (heap allocated)
 | 
			
		||||
 *
 | 
			
		||||
 * @tparam T The display type (e.g., Display, Nextion, GPIOLCDDisplay)
 | 
			
		||||
 */
 | 
			
		||||
template<typename T> class DisplayWriter {
 | 
			
		||||
 public:
 | 
			
		||||
  DisplayWriter() : type_(NONE) {}
 | 
			
		||||
 | 
			
		||||
  // For stateless lambdas (convertible to function pointer): use function pointer (4 bytes)
 | 
			
		||||
  template<typename F>
 | 
			
		||||
  DisplayWriter(F f) requires std::invocable<F, T &> && std::convertible_to<F, void (*)(T &)>
 | 
			
		||||
      : type_(STATELESS_LAMBDA) {
 | 
			
		||||
    this->stateless_f_ = f;  // Implicit conversion to function pointer
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // For stateful lambdas and std::function (not convertible to function pointer): use std::function* (heap allocated)
 | 
			
		||||
  // This handles backwards compatibility with external components
 | 
			
		||||
  template<typename F>
 | 
			
		||||
  DisplayWriter(F f) requires std::invocable<F, T &> &&(!std::convertible_to<F, void (*)(T &)>) : type_(LAMBDA) {
 | 
			
		||||
    this->f_ = new std::function<void(T &)>(std::move(f));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Copy constructor
 | 
			
		||||
  DisplayWriter(const DisplayWriter &other) : type_(other.type_) {
 | 
			
		||||
    if (type_ == LAMBDA) {
 | 
			
		||||
      this->f_ = new std::function<void(T &)>(*other.f_);
 | 
			
		||||
    } else if (type_ == STATELESS_LAMBDA) {
 | 
			
		||||
      this->stateless_f_ = other.stateless_f_;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Move constructor
 | 
			
		||||
  DisplayWriter(DisplayWriter &&other) noexcept : type_(other.type_) {
 | 
			
		||||
    if (type_ == LAMBDA) {
 | 
			
		||||
      this->f_ = other.f_;
 | 
			
		||||
      other.f_ = nullptr;
 | 
			
		||||
    } else if (type_ == STATELESS_LAMBDA) {
 | 
			
		||||
      this->stateless_f_ = other.stateless_f_;
 | 
			
		||||
    }
 | 
			
		||||
    other.type_ = NONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Assignment operators
 | 
			
		||||
  DisplayWriter &operator=(const DisplayWriter &other) {
 | 
			
		||||
    if (this != &other) {
 | 
			
		||||
      this->~DisplayWriter();
 | 
			
		||||
      new (this) DisplayWriter(other);
 | 
			
		||||
    }
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  DisplayWriter &operator=(DisplayWriter &&other) noexcept {
 | 
			
		||||
    if (this != &other) {
 | 
			
		||||
      this->~DisplayWriter();
 | 
			
		||||
      new (this) DisplayWriter(std::move(other));
 | 
			
		||||
    }
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~DisplayWriter() {
 | 
			
		||||
    if (type_ == LAMBDA) {
 | 
			
		||||
      delete this->f_;
 | 
			
		||||
    }
 | 
			
		||||
    // STATELESS_LAMBDA/NONE: no cleanup needed (function pointer or empty)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool has_value() const { return this->type_ != NONE; }
 | 
			
		||||
 | 
			
		||||
  void call(T &display) const {
 | 
			
		||||
    switch (this->type_) {
 | 
			
		||||
      case STATELESS_LAMBDA:
 | 
			
		||||
        this->stateless_f_(display);  // Direct function pointer call
 | 
			
		||||
        break;
 | 
			
		||||
      case LAMBDA:
 | 
			
		||||
        (*this->f_)(display);  // std::function call
 | 
			
		||||
        break;
 | 
			
		||||
      case NONE:
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Operator() for convenience
 | 
			
		||||
  void operator()(T &display) const { this->call(display); }
 | 
			
		||||
 | 
			
		||||
  // Operator* for backwards compatibility with (*writer_)(*this) pattern
 | 
			
		||||
  DisplayWriter &operator*() { return *this; }
 | 
			
		||||
  const DisplayWriter &operator*() const { return *this; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  enum : uint8_t {
 | 
			
		||||
    NONE,
 | 
			
		||||
    LAMBDA,
 | 
			
		||||
    STATELESS_LAMBDA,
 | 
			
		||||
  } type_;
 | 
			
		||||
 | 
			
		||||
  union {
 | 
			
		||||
    std::function<void(T &)> *f_;
 | 
			
		||||
    void (*stateless_f_)(T &);
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Type alias for Display writer - uses optimized DisplayWriter instead of std::function
 | 
			
		||||
using display_writer_t = DisplayWriter<Display>;
 | 
			
		||||
 | 
			
		||||
#define LOG_DISPLAY(prefix, type, obj) \
 | 
			
		||||
  if ((obj) != nullptr) { \
 | 
			
		||||
@@ -678,7 +788,7 @@ class Display : public PollingComponent {
 | 
			
		||||
  void sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3);
 | 
			
		||||
 | 
			
		||||
  DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
 | 
			
		||||
  optional<display_writer_t> writer_{};
 | 
			
		||||
  display_writer_t writer_{};
 | 
			
		||||
  DisplayPage *page_{nullptr};
 | 
			
		||||
  DisplayPage *previous_page_{nullptr};
 | 
			
		||||
  std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,18 @@
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/lcd_base/lcd_display.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace lcd_gpio {
 | 
			
		||||
 | 
			
		||||
class GPIOLCDDisplay;
 | 
			
		||||
 | 
			
		||||
using gpio_lcd_writer_t = display::DisplayWriter<GPIOLCDDisplay>;
 | 
			
		||||
 | 
			
		||||
class GPIOLCDDisplay : public lcd_base::LCDDisplay {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_writer(std::function<void(GPIOLCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
 | 
			
		||||
  void set_writer(gpio_lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void set_data_pins(GPIOPin *d0, GPIOPin *d1, GPIOPin *d2, GPIOPin *d3) {
 | 
			
		||||
    this->data_pins_[0] = d0;
 | 
			
		||||
@@ -43,7 +48,7 @@ class GPIOLCDDisplay : public lcd_base::LCDDisplay {
 | 
			
		||||
  GPIOPin *rw_pin_{nullptr};
 | 
			
		||||
  GPIOPin *enable_pin_{nullptr};
 | 
			
		||||
  GPIOPin *data_pins_[8]{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
 | 
			
		||||
  std::function<void(GPIOLCDDisplay &)> writer_;
 | 
			
		||||
  gpio_lcd_writer_t writer_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace lcd_gpio
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,18 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/lcd_base/lcd_display.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace lcd_pcf8574 {
 | 
			
		||||
 | 
			
		||||
class PCF8574LCDDisplay;
 | 
			
		||||
 | 
			
		||||
using pcf8574_lcd_writer_t = display::DisplayWriter<PCF8574LCDDisplay>;
 | 
			
		||||
 | 
			
		||||
class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_writer(std::function<void(PCF8574LCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
 | 
			
		||||
  void set_writer(pcf8574_lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void backlight();
 | 
			
		||||
@@ -24,7 +29,7 @@ class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
 | 
			
		||||
 | 
			
		||||
  // Stores the current state of the backlight.
 | 
			
		||||
  uint8_t backlight_value_;
 | 
			
		||||
  std::function<void(PCF8574LCDDisplay &)> writer_;
 | 
			
		||||
  pcf8574_lcd_writer_t writer_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace lcd_pcf8574
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,14 @@
 | 
			
		||||
#include "esphome/core/time.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/spi/spi.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace max7219 {
 | 
			
		||||
 | 
			
		||||
class MAX7219Component;
 | 
			
		||||
 | 
			
		||||
using max7219_writer_t = std::function<void(MAX7219Component &)>;
 | 
			
		||||
using max7219_writer_t = display::DisplayWriter<MAX7219Component>;
 | 
			
		||||
 | 
			
		||||
class MAX7219Component : public PollingComponent,
 | 
			
		||||
                         public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
 | 
			
		||||
@@ -57,7 +58,7 @@ class MAX7219Component : public PollingComponent,
 | 
			
		||||
  uint8_t num_chips_{1};
 | 
			
		||||
  uint8_t *buffer_;
 | 
			
		||||
  bool reverse_{false};
 | 
			
		||||
  optional<max7219_writer_t> writer_{};
 | 
			
		||||
  max7219_writer_t writer_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace max7219
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ enum ScrollMode {
 | 
			
		||||
 | 
			
		||||
class MAX7219Component;
 | 
			
		||||
 | 
			
		||||
using max7219_writer_t = std::function<void(MAX7219Component &)>;
 | 
			
		||||
using max7219_writer_t = display::DisplayWriter<MAX7219Component>;
 | 
			
		||||
 | 
			
		||||
class MAX7219Component : public display::DisplayBuffer,
 | 
			
		||||
                         public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
 | 
			
		||||
@@ -117,7 +117,7 @@ class MAX7219Component : public display::DisplayBuffer,
 | 
			
		||||
  uint32_t last_scroll_ = 0;
 | 
			
		||||
  uint16_t stepsleft_;
 | 
			
		||||
  size_t get_buffer_length_();
 | 
			
		||||
  optional<max7219_writer_t> writer_local_{};
 | 
			
		||||
  max7219_writer_t writer_local_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace max7219digit
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@
 | 
			
		||||
#include "esphome/components/uart/uart.h"
 | 
			
		||||
#include "nextion_base.h"
 | 
			
		||||
#include "nextion_component.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
#include "esphome/components/display/display_color_utils.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_NEXTION_TFT_UPLOAD
 | 
			
		||||
@@ -31,7 +32,7 @@ namespace nextion {
 | 
			
		||||
class Nextion;
 | 
			
		||||
class NextionComponentBase;
 | 
			
		||||
 | 
			
		||||
using nextion_writer_t = std::function<void(Nextion &)>;
 | 
			
		||||
using nextion_writer_t = display::DisplayWriter<Nextion>;
 | 
			
		||||
 | 
			
		||||
static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
 | 
			
		||||
 | 
			
		||||
@@ -1471,7 +1472,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
 | 
			
		||||
  CallbackManager<void(uint8_t, uint8_t, bool)> touch_callback_{};
 | 
			
		||||
  CallbackManager<void()> buffer_overflow_callback_{};
 | 
			
		||||
 | 
			
		||||
  optional<nextion_writer_t> writer_;
 | 
			
		||||
  nextion_writer_t writer_;
 | 
			
		||||
  optional<float> brightness_;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/components/ble_client/ble_client.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
@@ -29,7 +30,7 @@ enum UNIT {
 | 
			
		||||
  UNIT_DEG_E,     ///< show "°E"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using pvvx_writer_t = std::function<void(PVVXDisplay &)>;
 | 
			
		||||
using pvvx_writer_t = display::DisplayWriter<PVVXDisplay>;
 | 
			
		||||
 | 
			
		||||
class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
@@ -126,7 +127,7 @@ class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
 | 
			
		||||
  esp32_ble_tracker::ESPBTUUID char_uuid_ =
 | 
			
		||||
      esp32_ble_tracker::ESPBTUUID::from_raw("00001f1f-0000-1000-8000-00805f9b34fb");
 | 
			
		||||
 | 
			
		||||
  optional<pvvx_writer_t> writer_{};
 | 
			
		||||
  pvvx_writer_t writer_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace pvvx_mithermometer
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ namespace st7920 {
 | 
			
		||||
 | 
			
		||||
class ST7920;
 | 
			
		||||
 | 
			
		||||
using st7920_writer_t = std::function<void(ST7920 &)>;
 | 
			
		||||
using st7920_writer_t = display::DisplayWriter<ST7920>;
 | 
			
		||||
 | 
			
		||||
class ST7920 : public display::DisplayBuffer,
 | 
			
		||||
               public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
 | 
			
		||||
@@ -44,7 +44,7 @@ class ST7920 : public display::DisplayBuffer,
 | 
			
		||||
  void end_transaction_();
 | 
			
		||||
 | 
			
		||||
  int16_t width_ = 128, height_ = 64;
 | 
			
		||||
  optional<st7920_writer_t> writer_local_{};
 | 
			
		||||
  st7920_writer_t writer_local_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace st7920
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,14 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace tm1621 {
 | 
			
		||||
 | 
			
		||||
class TM1621Display;
 | 
			
		||||
 | 
			
		||||
using tm1621_writer_t = std::function<void(TM1621Display &)>;
 | 
			
		||||
using tm1621_writer_t = display::DisplayWriter<TM1621Display>;
 | 
			
		||||
 | 
			
		||||
class TM1621Display : public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
@@ -59,7 +60,7 @@ class TM1621Display : public PollingComponent {
 | 
			
		||||
  GPIOPin *cs_pin_;
 | 
			
		||||
  GPIOPin *read_pin_;
 | 
			
		||||
  GPIOPin *write_pin_;
 | 
			
		||||
  optional<tm1621_writer_t> writer_{};
 | 
			
		||||
  tm1621_writer_t writer_{};
 | 
			
		||||
  char row_[2][12];
 | 
			
		||||
  uint8_t state_;
 | 
			
		||||
  uint8_t device_;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/time.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +20,7 @@ class TM1637Display;
 | 
			
		||||
class TM1637Key;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using tm1637_writer_t = std::function<void(TM1637Display &)>;
 | 
			
		||||
using tm1637_writer_t = display::DisplayWriter<TM1637Display>;
 | 
			
		||||
 | 
			
		||||
class TM1637Display : public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
@@ -78,7 +79,7 @@ class TM1637Display : public PollingComponent {
 | 
			
		||||
  uint8_t length_;
 | 
			
		||||
  bool inverted_;
 | 
			
		||||
  bool on_{true};
 | 
			
		||||
  optional<tm1637_writer_t> writer_{};
 | 
			
		||||
  tm1637_writer_t writer_{};
 | 
			
		||||
  uint8_t buffer_[6] = {0};
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  std::vector<TM1637Key *> tm1637_keys_{};
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/time.h"
 | 
			
		||||
#include "esphome/components/display/display.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +19,7 @@ class KeyListener {
 | 
			
		||||
 | 
			
		||||
class TM1638Component;
 | 
			
		||||
 | 
			
		||||
using tm1638_writer_t = std::function<void(TM1638Component &)>;
 | 
			
		||||
using tm1638_writer_t = display::DisplayWriter<TM1638Component>;
 | 
			
		||||
 | 
			
		||||
class TM1638Component : public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
@@ -70,7 +71,7 @@ class TM1638Component : public PollingComponent {
 | 
			
		||||
  GPIOPin *stb_pin_;
 | 
			
		||||
  GPIOPin *dio_pin_;
 | 
			
		||||
  uint8_t *buffer_ = new uint8_t[8];
 | 
			
		||||
  optional<tm1638_writer_t> writer_{};
 | 
			
		||||
  tm1638_writer_t writer_{};
 | 
			
		||||
  std::vector<KeyListener *> listeners_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
display:
 | 
			
		||||
  - platform: sdl
 | 
			
		||||
    id: image_display
 | 
			
		||||
    auto_clear_enabled: false
 | 
			
		||||
    dimensions:
 | 
			
		||||
      width: 480
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,14 @@ display:
 | 
			
		||||
 | 
			
		||||
binary_sensor:
 | 
			
		||||
  - platform: sdl
 | 
			
		||||
    sdl_id: sdl_display
 | 
			
		||||
    id: key_up
 | 
			
		||||
    key: SDLK_a
 | 
			
		||||
  - platform: sdl
 | 
			
		||||
    sdl_id: sdl_display
 | 
			
		||||
    id: key_down
 | 
			
		||||
    key: SDLK_d
 | 
			
		||||
  - platform: sdl
 | 
			
		||||
    sdl_id: sdl_display
 | 
			
		||||
    id: key_enter
 | 
			
		||||
    key: SDLK_s
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user