mirror of
https://github.com/libretro/RetroArch
synced 2025-02-20 06:40:18 +00:00
libnx: refactor all the code of the now deprecated gfx api over to the new nwindow / framebuffer api
This commit is contained in:
parent
74c8c13102
commit
462a4b24fd
@ -258,9 +258,6 @@ static void frontend_switch_deinit(void *data)
|
|||||||
if (psmInitialized)
|
if (psmInitialized)
|
||||||
psmExit();
|
psmExit();
|
||||||
|
|
||||||
#ifndef HAVE_OPENGL
|
|
||||||
gfxExit();
|
|
||||||
#endif
|
|
||||||
appletUnlockExit();
|
appletUnlockExit();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -402,18 +399,24 @@ void frontend_switch_showsplash(void)
|
|||||||
{
|
{
|
||||||
printf("[Splash] Showing splashScreen\n");
|
printf("[Splash] Showing splashScreen\n");
|
||||||
|
|
||||||
|
NWindow *win = nwindowGetDefault();
|
||||||
|
Framebuffer fb;
|
||||||
|
framebufferCreate(&fb, win, 1280, 720, PIXEL_FORMAT_RGBA_8888, 2);
|
||||||
|
framebufferMakeLinear(&fb);
|
||||||
|
|
||||||
if (splashData)
|
if (splashData)
|
||||||
{
|
{
|
||||||
uint32_t width = 0;
|
uint32_t width = 0;
|
||||||
uint32_t height = 0;
|
uint32_t height = 0;
|
||||||
uint32_t *frambuffer = (uint32_t *)gfxGetFramebuffer(&width, &height);
|
uint32_t stride;
|
||||||
|
uint32_t *frambuffer = (uint32_t *)framebufferBegin(&fb, &stride);
|
||||||
|
|
||||||
gfx_slow_swizzling_blit(frambuffer, splashData, width, height, 0, 0, false);
|
gfx_cpy_dsp_buf(frambuffer, splashData, width, height, stride, false);
|
||||||
|
|
||||||
gfxFlushBuffers();
|
framebufferEnd(&fb);
|
||||||
gfxSwapBuffers();
|
|
||||||
gfxWaitForVsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
framebufferClose(&fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* From rpng_test.c */
|
/* From rpng_test.c */
|
||||||
@ -670,16 +673,6 @@ static void frontend_switch_init(void *data)
|
|||||||
appletHook(&applet_hook_cookie, on_applet_hook, NULL);
|
appletHook(&applet_hook_cookie, on_applet_hook, NULL);
|
||||||
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
||||||
|
|
||||||
#ifndef HAVE_OPENGL
|
|
||||||
/* Init Resolution before initDefault */
|
|
||||||
gfxInitResolution(1280, 720);
|
|
||||||
|
|
||||||
gfxInitDefault();
|
|
||||||
gfxSetMode(GfxMode_TiledDouble);
|
|
||||||
|
|
||||||
gfxConfigureTransform(0);
|
|
||||||
#endif /* HAVE_OPENGL */
|
|
||||||
|
|
||||||
bool recording_supported = false;
|
bool recording_supported = false;
|
||||||
appletIsGamePlayRecordingSupported(&recording_supported);
|
appletIsGamePlayRecordingSupported(&recording_supported);
|
||||||
if(recording_supported)
|
if(recording_supported)
|
||||||
|
@ -56,6 +56,13 @@ typedef struct
|
|||||||
bool o_size;
|
bool o_size;
|
||||||
uint32_t o_height;
|
uint32_t o_height;
|
||||||
uint32_t o_width;
|
uint32_t o_width;
|
||||||
|
|
||||||
|
NWindow *win;
|
||||||
|
Framebuffer fb;
|
||||||
|
|
||||||
|
// needed for the switch font driver
|
||||||
|
uint32_t *out_buffer;
|
||||||
|
uint32_t stride;
|
||||||
} switch_video_t;
|
} switch_video_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -72,8 +79,10 @@ typedef struct
|
|||||||
bool resize;
|
bool resize;
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
float refresh_rate;
|
float refresh_rate;
|
||||||
|
NWindow *win;
|
||||||
} switch_ctx_data_t;
|
} switch_ctx_data_t;
|
||||||
|
|
||||||
void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend);
|
void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend);
|
||||||
|
void gfx_cpy_dsp_buf(uint32_t *buffer, uint32_t *image, int w, int h, uint32_t stride, bool blend);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -130,18 +130,47 @@ void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gfx_cpy_dsp_buf(uint32_t *buffer, uint32_t *image, int w, int h, uint32_t stride, bool blend)
|
||||||
|
{
|
||||||
|
uint32_t *dest = buffer;
|
||||||
|
uint32_t *src = image;
|
||||||
|
for (uint32_t y = 0; y < h; y ++)
|
||||||
|
{
|
||||||
|
for (uint32_t x = 0; x < w; x ++)
|
||||||
|
{
|
||||||
|
uint32_t pos = y * stride / sizeof(uint32_t) + x;
|
||||||
|
uint32_t pixel = *src;
|
||||||
|
|
||||||
|
if (blend) /* supercheap masking */
|
||||||
|
{
|
||||||
|
uint32_t dst = dest[pos];
|
||||||
|
uint8_t src_a = ((pixel & 0xFF000000) >> 24);
|
||||||
|
|
||||||
|
if (src_a > 0)
|
||||||
|
pixel &= 0x00FFFFFF;
|
||||||
|
else
|
||||||
|
pixel = dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[pos] = pixel;
|
||||||
|
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* needed to clear surface completely as hw scaling doesn't always scale to full resoution perflectly */
|
/* needed to clear surface completely as hw scaling doesn't always scale to full resoution perflectly */
|
||||||
static void clear_screen(switch_video_t *sw)
|
static void clear_screen(switch_video_t *sw)
|
||||||
{
|
{
|
||||||
uint32_t *out_buffer = NULL;
|
nwindowSetDimensions(sw->win, sw->vp.full_width, sw->vp.full_height);
|
||||||
gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height);
|
|
||||||
|
|
||||||
out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL);
|
uint32_t stride;
|
||||||
|
|
||||||
memset(out_buffer, 0, gfxGetFramebufferSize());
|
uint32_t *out_buffer = (uint32_t*)framebufferBegin(&sw->fb, &stride);
|
||||||
|
|
||||||
gfxFlushBuffers();
|
memset(out_buffer, 0, stride * 720);
|
||||||
gfxSwapBuffers();
|
|
||||||
|
framebufferEnd(&sw->fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *switch_init(const video_info_t *video,
|
static void *switch_init(const video_info_t *video,
|
||||||
@ -152,6 +181,11 @@ static void *switch_init(const video_info_t *video,
|
|||||||
if (!sw)
|
if (!sw)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
sw->win = nwindowGetDefault();
|
||||||
|
|
||||||
|
framebufferCreate(&sw->fb, sw->win, 1280, 720, PIXEL_FORMAT_RGBA_8888, 2);
|
||||||
|
framebufferMakeLinear(&sw->fb);
|
||||||
|
|
||||||
printf("loading switch gfx driver, width: %d, height: %d threaded: %d smooth %d\n", video->width, video->height, video->is_threaded, video->smooth);
|
printf("loading switch gfx driver, width: %d, height: %d threaded: %d smooth %d\n", video->width, video->height, video->is_threaded, video->smooth);
|
||||||
sw->vp.x = 0;
|
sw->vp.x = 0;
|
||||||
sw->vp.y = 0;
|
sw->vp.y = 0;
|
||||||
@ -186,18 +220,6 @@ static void *switch_init(const video_info_t *video,
|
|||||||
*input_data = switchinput;
|
*input_data = switchinput;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LIBNX
|
|
||||||
#ifdef HAVE_OPENGL
|
|
||||||
// Init Resolution before initDefault
|
|
||||||
gfxInitResolution(1280, 720);
|
|
||||||
|
|
||||||
gfxInitDefault();
|
|
||||||
gfxSetMode(GfxMode_TiledDouble);
|
|
||||||
|
|
||||||
gfxConfigureTransform(0);
|
|
||||||
#endif // HAVE_OPENGL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
font_driver_init_osd(sw, false,
|
font_driver_init_osd(sw, false,
|
||||||
video->is_threaded,
|
video->is_threaded,
|
||||||
FONT_DRIVER_RENDER_SWITCH);
|
FONT_DRIVER_RENDER_SWITCH);
|
||||||
@ -398,8 +420,8 @@ static bool switch_frame(void *data, const void *frame,
|
|||||||
sw->hw_scale.x_offset = ceil((sw->hw_scale.width - sw->scaler.out_width) / 2.0);
|
sw->hw_scale.x_offset = ceil((sw->hw_scale.width - sw->scaler.out_width) / 2.0);
|
||||||
if (!video_info->menu_is_alive)
|
if (!video_info->menu_is_alive)
|
||||||
{
|
{
|
||||||
clear_screen(sw);
|
clear_screen(sw);
|
||||||
gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height);
|
nwindowSetDimensions(sw->win, sw->hw_scale.width, sw->hw_scale.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sw->scaler.out_fmt = SCALER_FMT_ABGR8888;
|
sw->scaler.out_fmt = SCALER_FMT_ABGR8888;
|
||||||
@ -418,12 +440,16 @@ static bool switch_frame(void *data, const void *frame,
|
|||||||
sw->should_resize = false;
|
sw->should_resize = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL);
|
uint32_t stride;
|
||||||
|
|
||||||
|
out_buffer = (uint32_t *)framebufferBegin(&sw->fb, &stride);
|
||||||
|
sw->out_buffer = out_buffer;
|
||||||
|
sw->stride = stride;
|
||||||
|
|
||||||
if (sw->in_menu && !video_info->menu_is_alive && sw->smooth)
|
if (sw->in_menu && !video_info->menu_is_alive && sw->smooth)
|
||||||
{
|
{
|
||||||
memset(out_buffer, 0, sw->vp.full_width * sw->vp.full_height * 4);
|
memset(out_buffer, 0, stride * sw->vp.full_height);;
|
||||||
gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height);
|
nwindowSetDimensions(sw->win, sw->hw_scale.width, sw->hw_scale.height);
|
||||||
}
|
}
|
||||||
sw->in_menu = video_info->menu_is_alive;
|
sw->in_menu = video_info->menu_is_alive;
|
||||||
|
|
||||||
@ -434,12 +460,12 @@ static bool switch_frame(void *data, const void *frame,
|
|||||||
if (sw->menu_texture.pixels)
|
if (sw->menu_texture.pixels)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_NXRGUI
|
#ifdef HAVE_NXRGUI
|
||||||
gfx_slow_swizzling_blit(out_buffer, nx_backgroundImage, sw->vp.full_width, sw->vp.full_height, 0, 0, false);
|
gfx_cpy_dsp_buf(out_buffer, nx_backgroundImage, sw->vp.full_width, sw->vp.full_height, stride, false);
|
||||||
#else
|
#else
|
||||||
memset(out_buffer, 0, gfxGetFramebufferSize());
|
memset(out_buffer, 0, stride * sw->vp.full_height);;
|
||||||
#endif
|
#endif
|
||||||
scaler_ctx_scale(&sw->menu_texture.scaler, sw->tmp_image + ((sw->vp.full_height - sw->menu_texture.tgth) / 2) * sw->vp.full_width + ((sw->vp.full_width - sw->menu_texture.tgtw) / 2), sw->menu_texture.pixels);
|
scaler_ctx_scale(&sw->menu_texture.scaler, sw->tmp_image + ((sw->vp.full_height - sw->menu_texture.tgth) / 2) * sw->vp.full_width + ((sw->vp.full_width - sw->menu_texture.tgtw) / 2), sw->menu_texture.pixels);
|
||||||
gfx_slow_swizzling_blit(out_buffer, sw->tmp_image, sw->vp.full_width, sw->vp.full_height, 0, 0, true);
|
gfx_cpy_dsp_buf(out_buffer, sw->tmp_image, sw->vp.full_width, sw->vp.full_height, stride, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (sw->smooth) /* bilinear */
|
else if (sw->smooth) /* bilinear */
|
||||||
@ -453,16 +479,16 @@ static bool switch_frame(void *data, const void *frame,
|
|||||||
|
|
||||||
for (y = 0; y < h; y++)
|
for (y = 0; y < h; y++)
|
||||||
for (x = 0; x < w; x++)
|
for (x = 0; x < w; x++)
|
||||||
out_buffer[gfxGetFramebufferDisplayOffset(x + sw->hw_scale.x_offset, y)] = sw->image[y * w + x];
|
out_buffer[y * stride / sizeof(uint32_t) + (x + sw->hw_scale.x_offset)] = sw->image[y * w + x];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct scaler_ctx *ctx = &sw->scaler;
|
struct scaler_ctx *ctx = &sw->scaler;
|
||||||
scaler_ctx_scale(ctx, sw->image + (sw->vp.y * sw->vp.full_width) + sw->vp.x, frame);
|
scaler_ctx_scale(ctx, sw->image + (sw->vp.y * sw->vp.full_width) + sw->vp.x, frame);
|
||||||
gfx_slow_swizzling_blit(out_buffer, sw->image, sw->vp.full_width, sw->vp.full_height, 0, 0, false);
|
gfx_cpy_dsp_buf(out_buffer, sw->image, sw->vp.full_width, sw->vp.full_height, stride, false);
|
||||||
#ifdef HAVE_NXRGUI
|
#ifdef HAVE_NXRGUI
|
||||||
if (tmp_overlay)
|
if (tmp_overlay)
|
||||||
gfx_slow_swizzling_blit(out_buffer, tmp_overlay, sw->vp.full_width, sw->vp.full_height, 0, 0, true);
|
gfx_cpy_dsp_buf(out_buffer, tmp_overlay, sw->vp.full_width, sw->vp.full_height, stride, true);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,10 +504,7 @@ static bool switch_frame(void *data, const void *frame,
|
|||||||
if (msg)
|
if (msg)
|
||||||
font_driver_render_msg(video_info, NULL, msg, NULL);
|
font_driver_render_msg(video_info, NULL, msg, NULL);
|
||||||
|
|
||||||
gfxFlushBuffers();
|
framebufferEnd(&sw->fb);
|
||||||
gfxSwapBuffers();
|
|
||||||
if (sw->vsync)
|
|
||||||
gfxWaitForVsync();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -520,16 +543,13 @@ static bool switch_has_windowed(void *data)
|
|||||||
static void switch_free(void *data)
|
static void switch_free(void *data)
|
||||||
{
|
{
|
||||||
switch_video_t *sw = data;
|
switch_video_t *sw = data;
|
||||||
|
|
||||||
|
framebufferClose(&sw->fb);
|
||||||
|
|
||||||
if (sw->menu_texture.pixels)
|
if (sw->menu_texture.pixels)
|
||||||
free(sw->menu_texture.pixels);
|
free(sw->menu_texture.pixels);
|
||||||
|
|
||||||
free(sw);
|
free(sw);
|
||||||
|
|
||||||
#ifdef HAVE_LIBNX
|
|
||||||
#ifdef HAVE_OPENGL
|
|
||||||
gfxExit();
|
|
||||||
#endif // HAVE_OPENGL
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool switch_set_shader(void *data,
|
static bool switch_set_shader(void *data,
|
||||||
@ -633,15 +653,13 @@ static void switch_apply_state_changes(void *data)
|
|||||||
static void switch_set_texture_enable(void *data, bool enable, bool full_screen)
|
static void switch_set_texture_enable(void *data, bool enable, bool full_screen)
|
||||||
{
|
{
|
||||||
switch_video_t *sw = data;
|
switch_video_t *sw = data;
|
||||||
|
|
||||||
if (!sw->menu_texture.enable && enable)
|
if (!sw->menu_texture.enable && enable)
|
||||||
gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height);
|
nwindowSetDimensions(sw->win, sw->vp.full_width, sw->vp.full_height);
|
||||||
else if (!enable && sw->menu_texture.enable && sw->smooth)
|
else if (!enable && sw->menu_texture.enable && sw->smooth)
|
||||||
{
|
{
|
||||||
clear_screen(sw);
|
clear_screen(sw);
|
||||||
gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height);
|
nwindowSetDimensions(sw->win, sw->hw_scale.width, sw->hw_scale.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
sw->menu_texture.enable = enable;
|
sw->menu_texture.enable = enable;
|
||||||
sw->menu_texture.fullscreen = full_screen;
|
sw->menu_texture.fullscreen = full_screen;
|
||||||
}
|
}
|
||||||
|
@ -97,8 +97,8 @@ static void *switch_ctx_init(video_frame_info_t *video_info, void *video_driver)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Needs to be here
|
// Needs to be here
|
||||||
gfxInitResolutionDefault(); // 1080p
|
ctx_nx->win = nwindowGetDefault();
|
||||||
gfxConfigureResolution(1920, 1080);
|
nwindowSetDimensions(ctx_nx->win, 1920, 1080);
|
||||||
|
|
||||||
#ifdef HAVE_EGL
|
#ifdef HAVE_EGL
|
||||||
if (!egl_init_context(&ctx_nx->egl, EGL_NONE, EGL_DEFAULT_DISPLAY,
|
if (!egl_init_context(&ctx_nx->egl, EGL_NONE, EGL_DEFAULT_DISPLAY,
|
||||||
@ -139,7 +139,7 @@ static void switch_ctx_check_window(void *data, bool *quit,
|
|||||||
|
|
||||||
*resize = true;
|
*resize = true;
|
||||||
printf("[NXGL]: Resizing to %dx%d\n", *width, *height);
|
printf("[NXGL]: Resizing to %dx%d\n", *width, *height);
|
||||||
gfxConfigureCrop(0, 1080 - ctx_nx->height, ctx_nx->width, 1080);
|
nwindowSetCrop(ctx_nx->win, 0, 1080 - ctx_nx->height, ctx_nx->width, 1080);
|
||||||
}
|
}
|
||||||
|
|
||||||
*quit = (bool)false;
|
*quit = (bool)false;
|
||||||
@ -174,11 +174,11 @@ static bool switch_ctx_set_video_mode(void *data,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_EGL
|
#ifdef HAVE_EGL
|
||||||
if (!egl_create_surface(&ctx_nx->egl, &ctx_nx->native_window))
|
if (!egl_create_surface(&ctx_nx->egl, ctx_nx->win))
|
||||||
goto error;
|
goto error;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gfxConfigureCrop(0, 1080 - ctx_nx->height, ctx_nx->width, 1080);
|
nwindowSetCrop(ctx_nx->win, 0, 1080 - ctx_nx->height, ctx_nx->width, 1080);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -115,15 +115,19 @@ static void switch_font_render_line(
|
|||||||
float scale, const unsigned int color, float pos_x,
|
float scale, const unsigned int color, float pos_x,
|
||||||
float pos_y, unsigned text_align)
|
float pos_y, unsigned text_align)
|
||||||
{
|
{
|
||||||
|
switch_video_t* sw = (switch_video_t*)video_info->userdata;
|
||||||
|
|
||||||
|
if(!sw)
|
||||||
|
return;
|
||||||
|
|
||||||
int delta_x = 0;
|
int delta_x = 0;
|
||||||
int delta_y = 0;
|
int delta_y = 0;
|
||||||
|
|
||||||
unsigned fbWidth = 0;
|
unsigned fbWidth = sw->vp.full_width;
|
||||||
unsigned fbHeight = 0;
|
unsigned fbHeight = sw->vp.full_height;
|
||||||
|
|
||||||
|
if (sw->out_buffer) {
|
||||||
|
|
||||||
uint32_t *out_buffer = (uint32_t *)gfxGetFramebuffer(&fbWidth, &fbHeight);
|
|
||||||
if (out_buffer)
|
|
||||||
{
|
|
||||||
int x = roundf(pos_x * fbWidth);
|
int x = roundf(pos_x * fbWidth);
|
||||||
int y = roundf((1.0f - pos_y) * fbHeight);
|
int y = roundf((1.0f - pos_y) * fbHeight);
|
||||||
|
|
||||||
@ -148,7 +152,7 @@ static void switch_font_render_line(
|
|||||||
i += skip - 1;
|
i += skip - 1;
|
||||||
|
|
||||||
const struct font_glyph *glyph =
|
const struct font_glyph *glyph =
|
||||||
font->font_driver->get_glyph(font->font_data, code);
|
font->font_driver->get_glyph(font->font_data, code);
|
||||||
|
|
||||||
if (!glyph) /* Do something smarter here ... */
|
if (!glyph) /* Do something smarter here ... */
|
||||||
glyph = font->font_driver->get_glyph(font->font_data, '?');
|
glyph = font->font_driver->get_glyph(font->font_data, '?');
|
||||||
@ -169,12 +173,12 @@ static void switch_font_render_line(
|
|||||||
uint8_t *row = &font->atlas->buffer[y * font->atlas->width];
|
uint8_t *row = &font->atlas->buffer[y * font->atlas->width];
|
||||||
for (int x = tex_x; x < tex_x + width; x++)
|
for (int x = tex_x; x < tex_x + width; x++)
|
||||||
{
|
{
|
||||||
if (!row[x])
|
if (!row[x])
|
||||||
continue;
|
continue;
|
||||||
int x1 = off_x + (x - tex_x);
|
int x1 = off_x + (x - tex_x);
|
||||||
int y1 = off_y + (y - tex_y);
|
int y1 = off_y + (y - tex_y);
|
||||||
if (x1 < fbWidth && y1 < fbHeight)
|
if (x1 < fbWidth && y1 < fbHeight)
|
||||||
out_buffer[gfxGetFramebufferDisplayOffset(x1, y1)] = color;
|
sw->out_buffer[y1 * sw->stride / sizeof(uint32_t) + x1] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user