mirror of
https://github.com/seekrs/MacroLibX.git
synced 2026-01-11 14:43:34 +00:00
working on xpm support
This commit is contained in:
4
Makefile
4
Makefile
@@ -6,7 +6,7 @@
|
|||||||
# By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ #
|
# By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ #
|
||||||
# +#+#+#+#+#+ +#+ #
|
# +#+#+#+#+#+ +#+ #
|
||||||
# Created: 2022/10/04 16:43:41 by maldavid #+# #+# #
|
# Created: 2022/10/04 16:43:41 by maldavid #+# #+# #
|
||||||
# Updated: 2023/04/04 13:09:06 by maldavid ### ########.fr #
|
# Updated: 2023/04/04 16:21:17 by maldavid ### ########.fr #
|
||||||
# #
|
# #
|
||||||
# **************************************************************************** #
|
# **************************************************************************** #
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ ifeq ($(TOOLCHAIN), gcc)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
CXXFLAGS = -std=c++17 -O3 -fPIC
|
CXXFLAGS = -std=c++17 -O3 -fPIC
|
||||||
INCLUDES = -I./includes -I./src -I./third_party -I/nfs/homes/maldavid/.xmake/packages/l/libsdl/2.26.4/8dfbcb8049e744a597cd5333e1b399cd/include
|
INCLUDES = -I./includes -I./src -I./third_party
|
||||||
|
|
||||||
ifeq ($(DEBUG), true)
|
ifeq ($(DEBUG), true)
|
||||||
CXXFLAGS += -g
|
CXXFLAGS += -g
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ For Arch based distros
|
|||||||
|
|
||||||
3. Compile your project
|
3. Compile your project
|
||||||
|
|
||||||
|
If you didn't disable Xpm support you'll have to link `libXpm`. You can remove it if you used `#define MLX_NO_XPM` before including `mlx.h`
|
||||||
```bash
|
```bash
|
||||||
clang myApp.c MacroLibX/libmlx.so -lSDL2
|
clang myApp.c MacroLibX/libmlx.so -lSDL2 -lXpm
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2022/10/04 16:56:35 by maldavid #+# #+# */
|
/* Created: 2022/10/04 16:56:35 by maldavid #+# #+# */
|
||||||
/* Updated: 2023/04/04 13:43:42 by maldavid ### ########.fr */
|
/* Updated: 2023/04/04 20:30:48 by maldavid ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -42,8 +42,11 @@ int mlx_destroy_image(void* mlx, void* img);
|
|||||||
void* mlx_png_file_to_image(void* mlx, char* filename, int* width, int* height);
|
void* mlx_png_file_to_image(void* mlx, char* filename, int* width, int* height);
|
||||||
void* mlx_jpg_file_to_image(void* mlx, char* filename, int* width, int* height);
|
void* mlx_jpg_file_to_image(void* mlx, char* filename, int* width, int* height);
|
||||||
void* mlx_bmp_file_to_image(void* mlx, char* filename, int* width, int* height);
|
void* mlx_bmp_file_to_image(void* mlx, char* filename, int* width, int* height);
|
||||||
void* mlx_xpm_file_to_image(void* mlx, char* filename, int* width, int* height);
|
|
||||||
void* mlx_xpm_to_image(void* mlx, char** xpm_data, int* width, int* height);
|
#ifndef MLX_NO_XPM
|
||||||
|
void* mlx_xpm_file_to_image(void* mlx, char* filename, int* width, int* height);
|
||||||
|
void* mlx_xpm_to_image(void* mlx, char** xpm_data, int* width, int* height);
|
||||||
|
#endif
|
||||||
|
|
||||||
int mlx_clear_window(void* mlx, void* win);
|
int mlx_clear_window(void* mlx, void* win);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2022/10/04 22:10:52 by maldavid #+# #+# */
|
/* Created: 2022/10/04 22:10:52 by maldavid #+# #+# */
|
||||||
/* Updated: 2023/04/04 14:54:12 by maldavid ### ########.fr */
|
/* Updated: 2023/04/05 01:11:36 by maldavid ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -14,7 +14,14 @@
|
|||||||
#include <renderer/images/texture.h>
|
#include <renderer/images/texture.h>
|
||||||
#include <renderer/core/render_core.h>
|
#include <renderer/core/render_core.h>
|
||||||
#include <X11/X.h> // for LSBFirst
|
#include <X11/X.h> // for LSBFirst
|
||||||
#include <cstdio>
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#ifndef MLX_NO_XPM
|
||||||
|
#include <X11/xpm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace mlx::core
|
namespace mlx::core
|
||||||
{
|
{
|
||||||
@@ -62,15 +69,85 @@ namespace mlx::core
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef MLX_NO_XPM
|
||||||
|
struct SafeXpmImage: public XpmImage // included in <X11/xpm.h>
|
||||||
|
{
|
||||||
|
~SafeXpmImage() { XpmFreeXpmImage(this); }
|
||||||
|
};
|
||||||
|
|
||||||
void* Application::newXpmTexture(char** data, int* w, int* h)
|
void* Application::newXpmTexture(char** data, int* w, int* h)
|
||||||
{
|
{
|
||||||
|
SafeXpmImage xpm_image;
|
||||||
|
if(XpmCreateXpmImageFromData(data, &xpm_image, nullptr) != XpmSuccess)
|
||||||
|
{
|
||||||
|
error::report(e_kind::error, "XPM reader : invalid xpm file");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
*w = xpm_image.width;
|
||||||
|
*h = xpm_image.height;
|
||||||
|
|
||||||
|
std::vector<uint8_t> formatted_data;
|
||||||
|
formatted_data.reserve((*w) * (*h) * 4);
|
||||||
|
uint32_t pixel_color;
|
||||||
|
for(int i = 0; i < *h; i++)
|
||||||
|
{
|
||||||
|
for(int j = 0, k = 0; k < *w; j += 4, k++)
|
||||||
|
{
|
||||||
|
pixel_color = -1;
|
||||||
|
for(int l = 0; l < xpm_image.ncolors; l++)
|
||||||
|
{
|
||||||
|
if(std::memcmp(xpm_image.colorTable[l].c_color, &xpm_image.data[(i * (*w)) + k], 1) == 0)
|
||||||
|
{
|
||||||
|
pixel_color = l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pixel_color < xpm_image.ncolors)
|
||||||
|
{
|
||||||
|
formatted_data[(i * (*w)) + j + 0] = xpm_image.colorTable[pixel_color].m_color[0];
|
||||||
|
formatted_data[(i * (*w)) + j + 1] = xpm_image.colorTable[pixel_color].m_color[1];
|
||||||
|
formatted_data[(i * (*w)) + j + 2] = xpm_image.colorTable[pixel_color].m_color[1];
|
||||||
|
formatted_data[(i * (*w)) + j + 3] = 0xFF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
formatted_data[(i * (*w)) + j + 0] = 0xFF;
|
||||||
|
formatted_data[(i * (*w)) + j + 1] = 0xFF;
|
||||||
|
formatted_data[(i * (*w)) + j + 2] = 0xFF;
|
||||||
|
formatted_data[(i * (*w)) + j + 3] = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Texture> texture = std::make_shared<Texture>();
|
std::shared_ptr<Texture> texture = std::make_shared<Texture>();
|
||||||
texture->create(pixels.get(), width, height, VK_FORMAT_R8G8B8A8_UNORM);
|
texture->create(formatted_data.data(), *w, *h, VK_FORMAT_R8G8B8A8_UNORM);
|
||||||
TextureID id = _texture_lib.addTextureToLibrary(texture);
|
TextureID id = _texture_lib.addTextureToLibrary(texture);
|
||||||
_texture_ids.push_back(id);
|
_texture_ids.push_back(id);
|
||||||
return &_texture_ids.back();
|
return &_texture_ids.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* Application::newXpmTexture(std::string filename, int* w, int* h)
|
||||||
|
{
|
||||||
|
SafeXpmImage xpm_image;
|
||||||
|
if(XpmReadFileToXpmImage(filename.c_str(), &xpm_image, nullptr) != XpmSuccess)
|
||||||
|
{
|
||||||
|
error::report(e_kind::error, "XPM reader : invalid xpm file");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
*w = xpm_image.width;
|
||||||
|
*h = xpm_image.height;
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> texture = std::make_shared<Texture>();
|
||||||
|
texture->create(reinterpret_cast<uint8_t*>(xpm_image.data), *w, *h, VK_FORMAT_R8G8B8A8_UNORM);
|
||||||
|
TextureID id = _texture_lib.addTextureToLibrary(texture);
|
||||||
|
_texture_ids.push_back(id);
|
||||||
|
return &_texture_ids.back();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Application::destroyTexture(void* ptr)
|
void Application::destroyTexture(void* ptr)
|
||||||
{
|
{
|
||||||
vkDeviceWaitIdle(Render_Core::get().getDevice().get());
|
vkDeviceWaitIdle(Render_Core::get().getDevice().get());
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2022/10/04 21:49:46 by maldavid #+# #+# */
|
/* Created: 2022/10/04 21:49:46 by maldavid #+# #+# */
|
||||||
/* Updated: 2023/04/04 13:43:04 by maldavid ### ########.fr */
|
/* Updated: 2023/04/04 20:53:21 by maldavid ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@@ -47,7 +49,10 @@ namespace mlx::core
|
|||||||
inline void pixelPut(void* win_ptr, int x, int y, int color) const noexcept;
|
inline void pixelPut(void* win_ptr, int x, int y, int color) const noexcept;
|
||||||
|
|
||||||
void* newTexture(int w, int h);
|
void* newTexture(int w, int h);
|
||||||
|
#ifndef MLX_NO_XPM
|
||||||
|
void* newXpmTexture(std::string filename, int* w, int* h);
|
||||||
void* newXpmTexture(char** data, int* w, int* h);
|
void* newXpmTexture(char** data, int* w, int* h);
|
||||||
|
#endif
|
||||||
void* newStbTexture(char* file, int* w, int* h); // stb textures are format managed by stb image (png, jpg, bpm, ...)
|
void* newStbTexture(char* file, int* w, int* h); // stb textures are format managed by stb image (png, jpg, bpm, ...)
|
||||||
char* mapTexture(void* img_ptr, int* bits_per_pixel, int* size_line, int* endian);
|
char* mapTexture(void* img_ptr, int* bits_per_pixel, int* size_line, int* endian);
|
||||||
inline void texturePut(void* win_ptr, void* img, int x, int y);
|
inline void texturePut(void* win_ptr, void* img, int x, int y);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2022/10/04 17:35:20 by maldavid #+# #+# */
|
/* Created: 2022/10/04 17:35:20 by maldavid #+# #+# */
|
||||||
/* Updated: 2023/04/04 13:44:12 by maldavid ### ########.fr */
|
/* Updated: 2023/04/04 21:02:29 by maldavid ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -16,6 +16,10 @@
|
|||||||
#include <renderer/core/render_core.h>
|
#include <renderer/core/render_core.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
#ifndef MLX_NO_XPM
|
||||||
|
#include <X11/xpm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
void* mlx_init()
|
void* mlx_init()
|
||||||
@@ -136,15 +140,17 @@ extern "C"
|
|||||||
return static_cast<mlx::core::Application*>(mlx)->newStbTexture(filename, width, height);
|
return static_cast<mlx::core::Application*>(mlx)->newStbTexture(filename, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef MLX_NO_XPM
|
||||||
void* mlx_xpm_file_to_image(void* mlx, char* filename, int* width, int* height)
|
void* mlx_xpm_file_to_image(void* mlx, char* filename, int* width, int* height)
|
||||||
{
|
{
|
||||||
//return static_cast<mlx::core::Application*>(mlx)->newXpmTexture(filename, width, height);
|
return static_cast<mlx::core::Application*>(mlx)->newXpmTexture(std::string(filename), width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* mlx_xpm_to_image(void* mlx, char** xpm_data, int* width, int* height)
|
void* mlx_xpm_to_image(void* mlx, char** xpm_data, int* width, int* height)
|
||||||
{
|
{
|
||||||
return static_cast<mlx::core::Application*>(mlx)->newXpmTexture(xpm_data, width, height);
|
return static_cast<mlx::core::Application*>(mlx)->newXpmTexture(xpm_data, width, height);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int mlx_pixel_put(void* mlx, void* win, int x, int y, int color)
|
int mlx_pixel_put(void* mlx, void* win, int x, int y, int color)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2023/03/31 15:14:50 by maldavid #+# #+# */
|
/* Created: 2023/03/31 15:14:50 by maldavid #+# #+# */
|
||||||
/* Updated: 2023/04/03 14:22:27 by maldavid ### ########.fr */
|
/* Updated: 2023/04/05 00:28:29 by maldavid ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* xpm_reader.h :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2023/04/04 14:55:09 by maldavid #+# #+# */
|
|
||||||
/* Updated: 2023/04/04 15:39:16 by maldavid ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/clckwrkbdgr/libchthon
|
|
||||||
* This xpm reader is a modified version of libchthon one
|
|
||||||
* because xmp readers are as rare as the sun in Scotland.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __MLX_XPM_READER__
|
|
||||||
#define __MLX_XPM_READER__
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace mlx
|
|
||||||
{
|
|
||||||
struct XPMData
|
|
||||||
{
|
|
||||||
XPMData();
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef uint32_t Color;
|
|
||||||
typedef uint8_t ColorComponent;
|
|
||||||
|
|
||||||
Color from_rgb(ColorComponent r, ColorComponent g, ColorComponent b);
|
|
||||||
uint8_t get_red(Color color);
|
|
||||||
uint8_t get_green(Color color);
|
|
||||||
uint8_t get_blue(Color color);
|
|
||||||
bool is_transparent(Color color);
|
|
||||||
Color rgb_to_argb(Color color);
|
|
||||||
|
|
||||||
class Pixmap
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Pixmap(uint32_t w = 1, uint32_t h = 1, uint32_t palette_size = 1);
|
|
||||||
|
|
||||||
void load(const std::vector<std::string>& xpm_lines);
|
|
||||||
|
|
||||||
Map<uint32_t> pixels;
|
|
||||||
std::vector<Color> palette;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t _color_count;
|
|
||||||
uint32_t _row_count;
|
|
||||||
std::vector<std::string> _interspaces;
|
|
||||||
std::vector<std::string> _colors;
|
|
||||||
std::vector<std::string> _values_interspaces;
|
|
||||||
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> _colors_interspaces;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -6,14 +6,14 @@
|
|||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2022/10/04 17:55:21 by maldavid #+# #+# */
|
/* Created: 2022/10/04 17:55:21 by maldavid #+# #+# */
|
||||||
/* Updated: 2023/04/04 14:54:04 by maldavid ### ########.fr */
|
/* Updated: 2023/04/04 21:41:32 by maldavid ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "../includes/mlx.h"
|
#include "../includes/mlx.h"
|
||||||
|
|
||||||
static char * exemple_xpm[] = {
|
static char* exemple_xpm[] = {
|
||||||
"24 20 3 1",
|
"24 20 3 1",
|
||||||
" c None",
|
" c None",
|
||||||
". c #3A32E4",
|
". c #3A32E4",
|
||||||
@@ -102,12 +102,12 @@ int main(void)
|
|||||||
mlx.mlx = mlx_init();
|
mlx.mlx = mlx_init();
|
||||||
mlx.win = mlx_new_window(mlx.mlx, 400, 400, "My window");
|
mlx.win = mlx_new_window(mlx.mlx, 400, 400, "My window");
|
||||||
mlx.logo = mlx_png_file_to_image(mlx.mlx, "42_logo.png", &w, &h);
|
mlx.logo = mlx_png_file_to_image(mlx.mlx, "42_logo.png", &w, &h);
|
||||||
pic = mlx_xpm_to_image(mlx.mlx, exemple_xpm, &w, &h);
|
|
||||||
mlx_put_image_to_window(mlx.mlx, mlx.win, pic, 20, 20);
|
|
||||||
mlx_pixel_put(mlx.mlx, mlx.win, 200, 10, 0xFFFF00FF);
|
mlx_pixel_put(mlx.mlx, mlx.win, 200, 10, 0xFFFF00FF);
|
||||||
mlx_put_image_to_window(mlx.mlx, mlx.win, mlx.logo, 200, 200);
|
mlx_put_image_to_window(mlx.mlx, mlx.win, mlx.logo, 200, 200);
|
||||||
img = create_image(&mlx);
|
img = create_image(&mlx);
|
||||||
mlx_put_image_to_window(mlx.mlx, mlx.win, img, 200, 20);
|
mlx_put_image_to_window(mlx.mlx, mlx.win, img, 200, 20);
|
||||||
|
pic = mlx_xpm_to_image(mlx.mlx, exemple_xpm, &w, &h);
|
||||||
|
mlx_put_image_to_window(mlx.mlx, mlx.win, pic, 100, 20);
|
||||||
mlx_loop_hook(mlx.mlx, update, &mlx);
|
mlx_loop_hook(mlx.mlx, update, &mlx);
|
||||||
mlx_loop(mlx.mlx);
|
mlx_loop(mlx.mlx);
|
||||||
mlx_destroy_image(mlx.mlx, img);
|
mlx_destroy_image(mlx.mlx, img);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
clang main.c ../libmlx.so `/nfs/homes/maldavid/.xmake/packages/l/libsdl/2.26.4/8dfbcb8049e744a597cd5333e1b399cd/bin/sdl2-config --cflags --libs` -g && ./a.out
|
clang main.c ../libmlx.so -lSDL2 -lXpm -g && ./a.out
|
||||||
|
|||||||
Reference in New Issue
Block a user