Remove GIL management code since it is no longer needed
This commit is contained in:
parent
7f180ad3d9
commit
e8b5a72c96
@ -63,9 +63,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
|
|
||||||
self = (Face *)type->tp_alloc(type, 0);
|
self = (Face *)type->tp_alloc(type, 0);
|
||||||
if (self != NULL) {
|
if (self != NULL) {
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
error = FT_New_Face(library, path, index, &(self->face));
|
error = FT_New_Face(library, path, index, &(self->face));
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
if(error) { set_freetype_error("Failed to load face, with error:", error); Py_CLEAR(self); return NULL; }
|
if(error) { set_freetype_error("Failed to load face, with error:", error); Py_CLEAR(self); return NULL; }
|
||||||
#define CPY(n) self->n = self->face->n;
|
#define CPY(n) self->n = self->face->n;
|
||||||
CPY(units_per_EM); CPY(ascender); CPY(descender); CPY(height); CPY(max_advance_width); CPY(max_advance_height); CPY(underline_position); CPY(underline_thickness);
|
CPY(units_per_EM); CPY(ascender); CPY(descender); CPY(height); CPY(max_advance_width); CPY(max_advance_height); CPY(underline_position); CPY(underline_thickness);
|
||||||
@ -88,9 +86,7 @@ set_char_size(Face *self, PyObject *args) {
|
|||||||
unsigned int xdpi, ydpi;
|
unsigned int xdpi, ydpi;
|
||||||
int error;
|
int error;
|
||||||
if (!PyArg_ParseTuple(args, "llII", &char_width, &char_height, &xdpi, &ydpi)) return NULL;
|
if (!PyArg_ParseTuple(args, "llII", &char_width, &char_height, &xdpi, &ydpi)) return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
error = FT_Set_Char_Size(self->face, char_width, char_height, xdpi, ydpi);
|
error = FT_Set_Char_Size(self->face, char_width, char_height, xdpi, ydpi);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
if (error) { set_freetype_error("Failed to set char size, with error:", error); Py_CLEAR(self); return NULL; }
|
if (error) { set_freetype_error("Failed to set char size, with error:", error); Py_CLEAR(self); return NULL; }
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -107,9 +103,7 @@ load_char(Face *self, PyObject *args) {
|
|||||||
if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL;
|
if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL;
|
||||||
else if (0 < hintstyle && hintstyle < 3) flags |= FT_LOAD_TARGET_LIGHT;
|
else if (0 < hintstyle && hintstyle < 3) flags |= FT_LOAD_TARGET_LIGHT;
|
||||||
} else flags |= FT_LOAD_NO_HINTING;
|
} else flags |= FT_LOAD_NO_HINTING;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
error = FT_Load_Glyph(self->face, glyph_index, flags);
|
error = FT_Load_Glyph(self->face, glyph_index, flags);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
if (error) { set_freetype_error("Failed to load glyph, with error:", error); Py_CLEAR(self); return NULL; }
|
if (error) { set_freetype_error("Failed to load glyph, with error:", error); Py_CLEAR(self); return NULL; }
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -120,9 +114,7 @@ get_char_index(Face *self, PyObject *args) {
|
|||||||
int code;
|
int code;
|
||||||
unsigned int ans;
|
unsigned int ans;
|
||||||
if (!PyArg_ParseTuple(args, "C", &code)) return NULL;
|
if (!PyArg_ParseTuple(args, "C", &code)) return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
ans = FT_Get_Char_Index(self->face, code);
|
ans = FT_Get_Char_Index(self->face, code);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
|
|
||||||
return Py_BuildValue("I", ans);
|
return Py_BuildValue("I", ans);
|
||||||
}
|
}
|
||||||
|
|||||||
22
kitty/gl.h
22
kitty/gl.h
@ -194,9 +194,7 @@ GetString(PyObject UNUSED *self, PyObject *val) {
|
|||||||
static PyObject*
|
static PyObject*
|
||||||
Clear(PyObject UNUSED *self, PyObject *val) {
|
Clear(PyObject UNUSED *self, PyObject *val) {
|
||||||
unsigned long m = PyLong_AsUnsignedLong(val);
|
unsigned long m = PyLong_AsUnsignedLong(val);
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glClear((GLbitfield)m);
|
glClear((GLbitfield)m);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -206,9 +204,7 @@ DrawArrays(PyObject UNUSED *self, PyObject *args) {
|
|||||||
int mode, first;
|
int mode, first;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
if (!PyArg_ParseTuple(args, "iiI", &mode, &first, &count)) return NULL;
|
if (!PyArg_ParseTuple(args, "iiI", &mode, &first, &count)) return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glDrawArrays(mode, first, count);
|
glDrawArrays(mode, first, count);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -219,9 +215,7 @@ MultiDrawArrays(PyObject UNUSED *self, PyObject *args) {
|
|||||||
unsigned int draw_count;
|
unsigned int draw_count;
|
||||||
PyObject *a, *b;
|
PyObject *a, *b;
|
||||||
if (!PyArg_ParseTuple(args, "iO!O!I", &mode, &PyLong_Type, &a, &PyLong_Type, &b, &draw_count)) return NULL;
|
if (!PyArg_ParseTuple(args, "iO!O!I", &mode, &PyLong_Type, &a, &PyLong_Type, &b, &draw_count)) return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glMultiDrawArrays(mode, PyLong_AsVoidPtr(a), PyLong_AsVoidPtr(b), draw_count);
|
glMultiDrawArrays(mode, PyLong_AsVoidPtr(a), PyLong_AsVoidPtr(b), draw_count);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -231,9 +225,7 @@ DrawArraysInstanced(PyObject UNUSED *self, PyObject *args) {
|
|||||||
int mode, first;
|
int mode, first;
|
||||||
unsigned int count, primcount;
|
unsigned int count, primcount;
|
||||||
if (!PyArg_ParseTuple(args, "iiII", &mode, &first, &count, &primcount)) return NULL;
|
if (!PyArg_ParseTuple(args, "iiII", &mode, &first, &count, &primcount)) return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glDrawArraysInstanced(mode, first, count, primcount);
|
glDrawArraysInstanced(mode, first, count, primcount);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -522,9 +514,7 @@ TexStorage3D(PyObject UNUSED *self, PyObject *args) {
|
|||||||
int target, fmt;
|
int target, fmt;
|
||||||
unsigned int levels, width, height, depth;
|
unsigned int levels, width, height, depth;
|
||||||
if (!PyArg_ParseTuple(args, "iIiIII", &target, &levels, &fmt, &width, &height, &depth)) return NULL;
|
if (!PyArg_ParseTuple(args, "iIiIII", &target, &levels, &fmt, &width, &height, &depth)) return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glTexStorage3D(target, levels, fmt, width, height, depth);
|
glTexStorage3D(target, levels, fmt, width, height, depth);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -548,9 +538,7 @@ CopyImageSubData(PyObject UNUSED *self, PyObject *args) {
|
|||||||
&dest, &dest_target, &dest_level, &destX, &destY, &destZ,
|
&dest, &dest_target, &dest_level, &destX, &destY, &destZ,
|
||||||
&width, &height, &depth
|
&width, &height, &depth
|
||||||
)) return NULL;
|
)) return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glCopyImageSubData(src, src_target, src_level, srcX, srcY, srcZ, dest, dest_target, dest_level, destX, destY, destZ, width, height, depth);
|
glCopyImageSubData(src, src_target, src_level, srcX, srcY, srcZ, dest, dest_target, dest_level, destX, destY, destZ, width, height, depth);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -564,14 +552,12 @@ copy_image_sub_data(PyObject UNUSED *self, PyObject *args) {
|
|||||||
uint8_t *src = (uint8_t*)PyMem_Malloc(5 * width * height * num_levels);
|
uint8_t *src = (uint8_t*)PyMem_Malloc(5 * width * height * num_levels);
|
||||||
if (src == NULL) return PyErr_NoMemory();
|
if (src == NULL) return PyErr_NoMemory();
|
||||||
uint8_t *dest = src + (4 * width * height * num_levels);
|
uint8_t *dest = src + (4 * width * height * num_levels);
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, src_target);
|
glBindTexture(GL_TEXTURE_2D_ARRAY, src_target);
|
||||||
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, src);
|
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, src);
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, dest_target);
|
glBindTexture(GL_TEXTURE_2D_ARRAY, dest_target);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
for(unsigned int i = 0; i < width * height * num_levels; i++) dest[i] = src[4*i];
|
for(unsigned int i = 0; i < width * height * num_levels; i++) dest[i] = src[4*i];
|
||||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, num_levels, GL_RED, GL_UNSIGNED_BYTE, dest);
|
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, num_levels, GL_RED, GL_UNSIGNED_BYTE, dest);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
PyMem_Free(src);
|
PyMem_Free(src);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -584,9 +570,7 @@ TexSubImage3D(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (!PyArg_ParseTuple(args, "iiiiiIIIiiO!", &target, &level, &x, &y, &z, &width, &height, &depth, &fmt, &type, &PyLong_Type, &pixels)) return NULL;
|
if (!PyArg_ParseTuple(args, "iiiiiIIIiiO!", &target, &level, &x, &y, &z, &width, &height, &depth, &fmt, &type, &PyLong_Type, &pixels)) return NULL;
|
||||||
void *data = PyLong_AsVoidPtr(pixels);
|
void *data = PyLong_AsVoidPtr(pixels);
|
||||||
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glTexSubImage3D(target, level, x, y, z, width, height, depth, fmt, type, data);
|
glTexSubImage3D(target, level, x, y, z, width, height, depth, fmt, type, data);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -599,9 +583,7 @@ GetTexImage(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (!PyArg_ParseTuple(args, "iiiiO!", &target, &level, &fmt, &type, &PyLong_Type, &pixels)) return NULL;
|
if (!PyArg_ParseTuple(args, "iiiiO!", &target, &level, &fmt, &type, &PyLong_Type, &pixels)) return NULL;
|
||||||
void *data = PyLong_AsVoidPtr(pixels);
|
void *data = PyLong_AsVoidPtr(pixels);
|
||||||
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glGetTexImage(target, level, fmt, type, data);
|
glGetTexImage(target, level, fmt, type, data);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -615,9 +597,7 @@ GetBufferSubData(PyObject UNUSED *self, PyObject *args) {
|
|||||||
void *data = PyLong_AsVoidPtr(address);
|
void *data = PyLong_AsVoidPtr(address);
|
||||||
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
||||||
glBindBuffer(buftype, target);
|
glBindBuffer(buftype, target);
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glGetBufferSubData(buftype, offset, size, data);
|
glGetBufferSubData(buftype, offset, size, data);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
glBindBuffer(buftype, 0);
|
glBindBuffer(buftype, 0);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@ -632,10 +612,8 @@ replace_or_create_buffer(PyObject UNUSED *self, PyObject *args) {
|
|||||||
void *data = PyLong_AsVoidPtr(address);
|
void *data = PyLong_AsVoidPtr(address);
|
||||||
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
||||||
glBindBuffer(buftype, target);
|
glBindBuffer(buftype, target);
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
if (prev_sz == 0 || prev_sz != size) glBufferData(buftype, size, data, usage);
|
if (prev_sz == 0 || prev_sz != size) glBufferData(buftype, size, data, usage);
|
||||||
else glBufferSubData(buftype, 0, size, data);
|
else glBufferSubData(buftype, 0, size, data);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
glBindBuffer(buftype, 0);
|
glBindBuffer(buftype, 0);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
|||||||
@ -169,10 +169,8 @@ glfw_wait_events(PyObject UNUSED *self, PyObject *args) {
|
|||||||
time = PyFloat_AsDouble(PyTuple_GET_ITEM(args, 0));
|
time = PyFloat_AsDouble(PyTuple_GET_ITEM(args, 0));
|
||||||
if (PyErr_Occurred()) PyErr_Clear();
|
if (PyErr_Occurred()) PyErr_Clear();
|
||||||
}
|
}
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
if (time < 0) glfwWaitEvents();
|
if (time < 0) glfwWaitEvents();
|
||||||
else glfwWaitEventsTimeout(time);
|
else glfwWaitEventsTimeout(time);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,9 +232,7 @@ dealloc(Window* self) {
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
swap_buffers(Window *self) {
|
swap_buffers(Window *self) {
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
glfwSwapBuffers(self->window);
|
glfwSwapBuffers(self->window);
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -223,11 +223,9 @@ HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns) {
|
|||||||
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
|
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) {
|
||||||
// Fast path
|
// Fast path
|
||||||
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
memcpy(other->buf, self->buf, sizeof(Cell) * self->xnum * self->ynum);
|
memcpy(other->buf, self->buf, sizeof(Cell) * self->xnum * self->ynum);
|
||||||
memcpy(other->continued_map, self->continued_map, sizeof(bool) * self->ynum);
|
memcpy(other->continued_map, self->continued_map, sizeof(bool) * self->ynum);
|
||||||
other->count = self->count; other->start_of_data = self->start_of_data;
|
other->count = self->count; other->start_of_data = self->start_of_data;
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
other->count = 0; other->start_of_data = 0;
|
other->count = 0; other->start_of_data = 0;
|
||||||
|
|||||||
@ -435,16 +435,13 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *his
|
|||||||
|
|
||||||
// Fast path
|
// Fast path
|
||||||
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
if (other->xnum == self->xnum && other->ynum == self->ynum) {
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum);
|
memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum);
|
||||||
memcpy(other->continued_map, self->continued_map, sizeof(bool) * self->ynum);
|
memcpy(other->continued_map, self->continued_map, sizeof(bool) * self->ynum);
|
||||||
memcpy(other->buf, self->buf, self->xnum * self->ynum * sizeof(Cell));
|
memcpy(other->buf, self->buf, self->xnum * self->ynum * sizeof(Cell));
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the first line that contains some content
|
// Find the first line that contains some content
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
for (first = self->ynum - 1; true; first--) {
|
for (first = self->ynum - 1; true; first--) {
|
||||||
Cell *cells = lineptr(self, first);
|
Cell *cells = lineptr(self, first);
|
||||||
for(i = 0; i < self->xnum; i++) {
|
for(i = 0; i < self->xnum; i++) {
|
||||||
@ -452,7 +449,6 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *his
|
|||||||
}
|
}
|
||||||
if (!is_empty || !first) break;
|
if (!is_empty || !first) break;
|
||||||
}
|
}
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
|
|
||||||
if (first == 0) { *cursor_y_out = 0; return; } // All lines are empty
|
if (first == 0) { *cursor_y_out = 0; return; } // All lines are empty
|
||||||
|
|
||||||
|
|||||||
@ -51,7 +51,6 @@ static void
|
|||||||
rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf UNUSED *historybuf) {
|
rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf UNUSED *historybuf) {
|
||||||
bool src_line_is_continued = false;
|
bool src_line_is_continued = false;
|
||||||
index_type src_y = 0, src_x = 0, dest_x = 0, dest_y = 0, num = 0, src_x_limit = 0;
|
index_type src_y = 0, src_x = 0, dest_x = 0, dest_y = 0, num = 0, src_x_limit = 0;
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
|
||||||
|
|
||||||
first_dest_line;
|
first_dest_line;
|
||||||
do {
|
do {
|
||||||
@ -73,5 +72,4 @@ rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf
|
|||||||
if (!src_line_is_continued && src_y < src_limit) { next_dest_line(false); dest_x = 0; }
|
if (!src_line_is_continued && src_y < src_limit) { next_dest_line(false); dest_x = 0; }
|
||||||
} while (src_y < src_limit);
|
} while (src_y < src_limit);
|
||||||
dest->line->ynum = dest_y;
|
dest->line->ynum = dest_y;
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user