Merge pull request #1361 from fredizzimo/ergodox_infinity_backlight
Add Ergodox Infinity backlight support
This commit is contained in:
commit
51a86b85f3
11 changed files with 139 additions and 35 deletions
|
@ -233,8 +233,10 @@ ifeq ($(strip $(LCD_ENABLE)), yes)
|
||||||
CIE1931_CURVE = yes
|
CIE1931_CURVE = yes
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(strip $(LED_ENABLE)), yes)
|
ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
|
||||||
CIE1931_CURVE = yes
|
ifeq ($(strip $(VISUALIZER_ENABLE)), yes)
|
||||||
|
CIE1931_CURVE = yes
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(strip $(CIE1931_CURVE)), yes)
|
ifeq ($(strip $(CIE1931_CURVE)), yes)
|
||||||
|
|
|
@ -25,43 +25,90 @@
|
||||||
#include "lcd_backlight_keyframes.h"
|
#include "lcd_backlight_keyframes.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LED_ENABLE
|
#ifdef BACKLIGHT_ENABLE
|
||||||
#include "led_keyframes.h"
|
#include "led_keyframes.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "visualizer_keyframes.h"
|
#include "visualizer_keyframes.h"
|
||||||
|
|
||||||
|
|
||||||
#if defined(LCD_ENABLE) && defined(LCD_BACKLIGHT_ENABLE)
|
#if defined(LCD_ENABLE) || defined(LCD_BACKLIGHT_ENABLE) || defined(BACKLIGHT_ENABLE)
|
||||||
|
|
||||||
|
static bool keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) {
|
||||||
|
#ifdef LCD_ENABLE
|
||||||
|
lcd_keyframe_enable(animation, state);
|
||||||
|
#endif
|
||||||
|
#ifdef LCD_BACKLIGHT_ENABLE
|
||||||
|
backlight_keyframe_enable(animation, state);
|
||||||
|
#endif
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
led_keyframe_enable(animation, state);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) {
|
||||||
|
#ifdef LCD_ENABLE
|
||||||
|
lcd_keyframe_disable(animation, state);
|
||||||
|
#endif
|
||||||
|
#ifdef LCD_BACKLIGHT_ENABLE
|
||||||
|
backlight_keyframe_disable(animation, state);
|
||||||
|
#endif
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
led_keyframe_disable(animation, state);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyframe_fade_in(keyframe_animation_t* animation, visualizer_state_t* state) {
|
||||||
|
bool ret = false;
|
||||||
|
#ifdef LCD_BACKLIGHT_ENABLE
|
||||||
|
ret |= backlight_keyframe_animate_color(animation, state);
|
||||||
|
#endif
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
ret |= led_keyframe_fade_in_all(animation, state);
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool keyframe_fade_out(keyframe_animation_t* animation, visualizer_state_t* state) {
|
||||||
|
bool ret = false;
|
||||||
|
#ifdef LCD_BACKLIGHT_ENABLE
|
||||||
|
ret |= backlight_keyframe_animate_color(animation, state);
|
||||||
|
#endif
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
ret |= led_keyframe_fade_out_all(animation, state);
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Don't worry, if the startup animation is long, you can use the keyboard like normal
|
// Don't worry, if the startup animation is long, you can use the keyboard like normal
|
||||||
// during that time
|
// during that time
|
||||||
keyframe_animation_t default_startup_animation = {
|
keyframe_animation_t default_startup_animation = {
|
||||||
.num_frames = 4,
|
.num_frames = 3,
|
||||||
.loop = false,
|
.loop = false,
|
||||||
.frame_lengths = {0, 0, 0, gfxMillisecondsToTicks(5000), 0},
|
.frame_lengths = {0, 0, gfxMillisecondsToTicks(5000)},
|
||||||
.frame_functions = {
|
.frame_functions = {
|
||||||
lcd_keyframe_enable,
|
keyframe_enable,
|
||||||
backlight_keyframe_enable,
|
|
||||||
lcd_keyframe_draw_logo,
|
lcd_keyframe_draw_logo,
|
||||||
backlight_keyframe_animate_color,
|
keyframe_fade_in,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
keyframe_animation_t default_suspend_animation = {
|
keyframe_animation_t default_suspend_animation = {
|
||||||
.num_frames = 4,
|
.num_frames = 3,
|
||||||
.loop = false,
|
.loop = false,
|
||||||
.frame_lengths = {0, gfxMillisecondsToTicks(1000), 0, 0},
|
.frame_lengths = {0, gfxMillisecondsToTicks(1000), 0},
|
||||||
.frame_functions = {
|
.frame_functions = {
|
||||||
lcd_keyframe_display_layer_text,
|
lcd_keyframe_display_layer_text,
|
||||||
backlight_keyframe_animate_color,
|
keyframe_fade_out,
|
||||||
lcd_keyframe_disable,
|
keyframe_disable,
|
||||||
backlight_keyframe_disable,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(LED_ENABLE)
|
#if defined(BACKLIGHT_ENABLE)
|
||||||
#define CROSSFADE_TIME 1000
|
#define CROSSFADE_TIME 1000
|
||||||
#define GRADIENT_TIME 3000
|
#define GRADIENT_TIME 3000
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ extern const uint8_t CIE1931_CURVE[];
|
||||||
#define GDISP_INITIAL_CONTRAST 0
|
#define GDISP_INITIAL_CONTRAST 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef GDISP_INITIAL_BACKLIGHT
|
#ifndef GDISP_INITIAL_BACKLIGHT
|
||||||
#define GDISP_INITIAL_BACKLIGHT 100
|
#define GDISP_INITIAL_BACKLIGHT 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GDISP_FLG_NEEDFLUSH (GDISP_FLG_DRIVER<<0)
|
#define GDISP_FLG_NEEDFLUSH (GDISP_FLG_DRIVER<<0)
|
||||||
|
@ -173,7 +173,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// software shutdown disable (i.e. turn stuff on)
|
// software shutdown disable (i.e. turn stuff on)
|
||||||
write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
|
write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF);
|
||||||
gfxSleepMilliseconds(10);
|
gfxSleepMilliseconds(10);
|
||||||
|
|
||||||
// Finish Init
|
// Finish Init
|
||||||
|
@ -183,7 +183,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
|
||||||
g->g.Width = GDISP_SCREEN_WIDTH;
|
g->g.Width = GDISP_SCREEN_WIDTH;
|
||||||
g->g.Height = GDISP_SCREEN_HEIGHT;
|
g->g.Height = GDISP_SCREEN_HEIGHT;
|
||||||
g->g.Orientation = GDISP_ROTATE_0;
|
g->g.Orientation = GDISP_ROTATE_0;
|
||||||
g->g.Powermode = powerOn;
|
g->g.Powermode = powerOff;
|
||||||
g->g.Backlight = GDISP_INITIAL_BACKLIGHT;
|
g->g.Backlight = GDISP_INITIAL_BACKLIGHT;
|
||||||
g->g.Contrast = GDISP_INITIAL_CONTRAST;
|
g->g.Contrast = GDISP_INITIAL_CONTRAST;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -204,7 +204,8 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
|
||||||
uint8_t* src = PRIV(g)->frame_buffer;
|
uint8_t* src = PRIV(g)->frame_buffer;
|
||||||
for (int y=0;y<GDISP_SCREEN_HEIGHT;y++) {
|
for (int y=0;y<GDISP_SCREEN_HEIGHT;y++) {
|
||||||
for (int x=0;x<GDISP_SCREEN_WIDTH;x++) {
|
for (int x=0;x<GDISP_SCREEN_WIDTH;x++) {
|
||||||
PRIV(g)->write_buffer[get_led_address(g, x, y)]=CIE1931_CURVE[*src];
|
uint8_t val = (uint16_t)*src * g->g.Backlight / 100;
|
||||||
|
PRIV(g)->write_buffer[get_led_address(g, x, y)]=CIE1931_CURVE[val];
|
||||||
++src;
|
++src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,8 +298,13 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
|
||||||
g->g.Orientation = (orientation_t)g->p.ptr;
|
g->g.Orientation = (orientation_t)g->p.ptr;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case GDISP_CONTROL_CONTRAST:
|
case GDISP_CONTROL_BACKLIGHT:
|
||||||
return;
|
if (g->g.Backlight == (unsigned)g->p.ptr)
|
||||||
|
return;
|
||||||
|
unsigned val = (unsigned)g->p.ptr;
|
||||||
|
g->g.Backlight = val > 100 ? 100 : val;
|
||||||
|
g->flags |= GDISP_FLG_NEEDFLUSH;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // GDISP_NEED_CONTROL
|
#endif // GDISP_NEED_CONTROL
|
||||||
|
|
|
@ -62,15 +62,10 @@ CUSTOM_MATRIX ?= yes # Custom matrix file
|
||||||
SERIAL_LINK_ENABLE = yes
|
SERIAL_LINK_ENABLE = yes
|
||||||
VISUALIZER_ENABLE ?= yes
|
VISUALIZER_ENABLE ?= yes
|
||||||
LCD_ENABLE ?= yes
|
LCD_ENABLE ?= yes
|
||||||
LED_ENABLE ?= no
|
BACKLIGHT_ENABLE ?= yes
|
||||||
LCD_BACKLIGHT_ENABLE ?= yes
|
LCD_BACKLIGHT_ENABLE ?= yes
|
||||||
MIDI_ENABLE = no
|
MIDI_ENABLE = no
|
||||||
RGBLIGHT_ENABLE = no
|
RGBLIGHT_ENABLE = no
|
||||||
|
|
||||||
ifdef LCD_ENABLE
|
|
||||||
include $(SUBPROJECT_PATH)/drivers/gdisp/st7565ergodox/driver.mk
|
include $(SUBPROJECT_PATH)/drivers/gdisp/st7565ergodox/driver.mk
|
||||||
endif
|
include $(SUBPROJECT_PATH)/drivers/gdisp/IS31FL3731C/driver.mk
|
||||||
|
|
||||||
ifdef LED_ENABLE
|
|
||||||
include $(SUBPROJECT_PATH)/drivers/gdisp/IS31FL3731C/driver.mk
|
|
||||||
endif
|
|
|
@ -1,7 +1,7 @@
|
||||||
SUBPROJECT_DEFAULT = infinity
|
SUBPROJECT_DEFAULT = infinity
|
||||||
LCD_BACKLIGHT_ENABLE = yes
|
LCD_BACKLIGHT_ENABLE = yes
|
||||||
LCD_ENABLE = yes
|
LCD_ENABLE = yes
|
||||||
LED_ENABLE = yes
|
BACKLIGHT_ENABLE = yes
|
||||||
BACKLIGHT_ENABLE = yes
|
BACKLIGHT_ENABLE = yes
|
||||||
NKRO_ENABLE = yes
|
NKRO_ENABLE = yes
|
||||||
TAP_DANCE_ENABLE = yes
|
TAP_DANCE_ENABLE = yes
|
||||||
|
|
|
@ -127,3 +127,17 @@ bool led_keyframe_normal_orientation(keyframe_animation_t* animation, visualizer
|
||||||
gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0);
|
gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool led_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) {
|
||||||
|
(void)state;
|
||||||
|
(void)animation;
|
||||||
|
gdispGSetPowerMode(LED_DISPLAY, powerOff);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool led_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) {
|
||||||
|
(void)state;
|
||||||
|
(void)animation;
|
||||||
|
gdispGSetPowerMode(LED_DISPLAY, powerOn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,9 @@ bool led_keyframe_crossfade(keyframe_animation_t* animation, visualizer_state_t*
|
||||||
bool led_keyframe_mirror_orientation(keyframe_animation_t* animation, visualizer_state_t* state);
|
bool led_keyframe_mirror_orientation(keyframe_animation_t* animation, visualizer_state_t* state);
|
||||||
bool led_keyframe_normal_orientation(keyframe_animation_t* animation, visualizer_state_t* state);
|
bool led_keyframe_normal_orientation(keyframe_animation_t* animation, visualizer_state_t* state);
|
||||||
|
|
||||||
|
bool led_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state);
|
||||||
|
bool led_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state);
|
||||||
|
|
||||||
extern keyframe_animation_t led_test_animation;
|
extern keyframe_animation_t led_test_animation;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,11 @@ SOFTWARE.
|
||||||
static visualizer_keyboard_status_t current_status = {
|
static visualizer_keyboard_status_t current_status = {
|
||||||
.layer = 0xFFFFFFFF,
|
.layer = 0xFFFFFFFF,
|
||||||
.default_layer = 0xFFFFFFFF,
|
.default_layer = 0xFFFFFFFF,
|
||||||
.mods = 0xFF,
|
|
||||||
.leds = 0xFFFFFFFF,
|
.leds = 0xFFFFFFFF,
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
.backlight_level = 0,
|
||||||
|
#endif
|
||||||
|
.mods = 0xFF,
|
||||||
.suspended = false,
|
.suspended = false,
|
||||||
#ifdef VISUALIZER_USER_DATA_SIZE
|
#ifdef VISUALIZER_USER_DATA_SIZE
|
||||||
.user_data = {0}
|
.user_data = {0}
|
||||||
|
@ -72,6 +75,9 @@ static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboa
|
||||||
status1->mods == status2->mods &&
|
status1->mods == status2->mods &&
|
||||||
status1->leds == status2->leds &&
|
status1->leds == status2->leds &&
|
||||||
status1->suspended == status2->suspended
|
status1->suspended == status2->suspended
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
&& status1->backlight_level == status2->backlight_level
|
||||||
|
#endif
|
||||||
#ifdef VISUALIZER_USER_DATA_SIZE
|
#ifdef VISUALIZER_USER_DATA_SIZE
|
||||||
&& memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0
|
&& memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0
|
||||||
#endif
|
#endif
|
||||||
|
@ -279,6 +285,18 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
|
||||||
bool enabled = visualizer_enabled;
|
bool enabled = visualizer_enabled;
|
||||||
if (force_update || !same_status(&state.status, ¤t_status)) {
|
if (force_update || !same_status(&state.status, ¤t_status)) {
|
||||||
force_update = false;
|
force_update = false;
|
||||||
|
#if BACKLIGHT_ENABLE
|
||||||
|
if(current_status.backlight_level != state.status.backlight_level) {
|
||||||
|
if (current_status.backlight_level != 0) {
|
||||||
|
gdispGSetPowerMode(LED_DISPLAY, powerOn);
|
||||||
|
uint16_t percent = (uint16_t)current_status.backlight_level * 100 / BACKLIGHT_LEVELS;
|
||||||
|
gdispGSetBacklight(LED_DISPLAY, percent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gdispGSetPowerMode(LED_DISPLAY, powerOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (visualizer_enabled) {
|
if (visualizer_enabled) {
|
||||||
if (current_status.suspended) {
|
if (current_status.suspended) {
|
||||||
stop_all_keyframe_animations();
|
stop_all_keyframe_animations();
|
||||||
|
@ -309,7 +327,7 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
|
||||||
update_keyframe_animation(animations[i], &state, delta, &sleep_time);
|
update_keyframe_animation(animations[i], &state, delta, &sleep_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef LED_ENABLE
|
#ifdef BACKLIGHT_ENABLE
|
||||||
gdispGFlush(LED_DISPLAY);
|
gdispGFlush(LED_DISPLAY);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -372,7 +390,7 @@ void visualizer_init(void) {
|
||||||
#ifdef LCD_ENABLE
|
#ifdef LCD_ENABLE
|
||||||
LCD_DISPLAY = get_lcd_display();
|
LCD_DISPLAY = get_lcd_display();
|
||||||
#endif
|
#endif
|
||||||
#ifdef LED_ENABLE
|
#ifdef BACKLIGHT_ENABLE
|
||||||
LED_DISPLAY = get_led_display();
|
LED_DISPLAY = get_led_display();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -445,6 +463,9 @@ void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uin
|
||||||
.default_layer = default_state,
|
.default_layer = default_state,
|
||||||
.mods = mods,
|
.mods = mods,
|
||||||
.leds = leds,
|
.leds = leds,
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
.backlight_level = current_status.backlight_level,
|
||||||
|
#endif
|
||||||
.suspended = current_status.suspended,
|
.suspended = current_status.suspended,
|
||||||
};
|
};
|
||||||
#ifdef VISUALIZER_USER_DATA_SIZE
|
#ifdef VISUALIZER_USER_DATA_SIZE
|
||||||
|
@ -467,3 +488,10 @@ void visualizer_resume(void) {
|
||||||
current_status.suspended = false;
|
current_status.suspended = false;
|
||||||
update_status(true);
|
update_status(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
void backlight_set(uint8_t level) {
|
||||||
|
current_status.backlight_level = level;
|
||||||
|
update_status(true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -34,6 +34,10 @@ SOFTWARE.
|
||||||
#include "lcd_backlight.h"
|
#include "lcd_backlight.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
#include "backlight.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
// use this function to merge both real_mods and oneshot_mods in a uint16_t
|
// use this function to merge both real_mods and oneshot_mods in a uint16_t
|
||||||
uint8_t visualizer_get_mods(void);
|
uint8_t visualizer_get_mods(void);
|
||||||
|
|
||||||
|
@ -65,9 +69,12 @@ struct keyframe_animation_t;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t layer;
|
uint32_t layer;
|
||||||
uint32_t default_layer;
|
uint32_t default_layer;
|
||||||
uint8_t mods;
|
|
||||||
uint32_t leds; // See led.h for available statuses
|
uint32_t leds; // See led.h for available statuses
|
||||||
|
uint8_t mods;
|
||||||
bool suspended;
|
bool suspended;
|
||||||
|
#ifdef BACKLIGHT_ENABLE
|
||||||
|
uint8_t backlight_level;
|
||||||
|
#endif
|
||||||
#ifdef VISUALIZER_USER_DATA_SIZE
|
#ifdef VISUALIZER_USER_DATA_SIZE
|
||||||
uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
|
uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -42,9 +42,8 @@ SRC += $(VISUALIZER_DIR)/resources/lcd_logo.c
|
||||||
OPT_DEFS += -DLCD_BACKLIGHT_ENABLE
|
OPT_DEFS += -DLCD_BACKLIGHT_ENABLE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(strip $(LED_ENABLE)), yes)
|
ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
|
||||||
SRC += $(VISUALIZER_DIR)/led_keyframes.c
|
SRC += $(VISUALIZER_DIR)/led_keyframes.c
|
||||||
OPT_DEFS += -DLED_ENABLE
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(GFXLIB)/gfx.mk
|
include $(GFXLIB)/gfx.mk
|
||||||
|
|
|
@ -28,6 +28,9 @@ void backlight_init(void)
|
||||||
eeconfig_init();
|
eeconfig_init();
|
||||||
}
|
}
|
||||||
backlight_config.raw = eeconfig_read_backlight();
|
backlight_config.raw = eeconfig_read_backlight();
|
||||||
|
if (backlight_config.level > BACKLIGHT_LEVELS) {
|
||||||
|
backlight_config.level = BACKLIGHT_LEVELS;
|
||||||
|
}
|
||||||
backlight_set(backlight_config.enable ? backlight_config.level : 0);
|
backlight_set(backlight_config.enable ? backlight_config.level : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue