Applied thread wakeup patch.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@13993 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/poll.h>
|
||||
#include "wx/gtk/win_gtk.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
@@ -50,25 +51,17 @@
|
||||
wxApp *wxTheApp = (wxApp *) NULL;
|
||||
wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
|
||||
|
||||
extern bool g_isIdle;
|
||||
|
||||
bool g_mainThreadLocked = FALSE;
|
||||
gint g_pendingTag = 0;
|
||||
|
||||
static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// local functions
|
||||
// idle system
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern "C"
|
||||
{
|
||||
gint wxapp_idle_callback( gpointer WXUNUSED(data) );
|
||||
gint wxapp_pending_callback( gpointer WXUNUSED(data) );
|
||||
}
|
||||
extern bool g_isIdle;
|
||||
|
||||
void wxapp_install_thread_wakeup();
|
||||
void wxapp_uninstall_thread_wakeup();
|
||||
void wxapp_install_idle_handler();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -165,29 +158,11 @@ void wxWakeUpIdle()
|
||||
// local functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void wxapp_install_idle_handler()
|
||||
{
|
||||
wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
|
||||
|
||||
g_isIdle = FALSE;
|
||||
|
||||
if (g_pendingTag == 0)
|
||||
g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
|
||||
|
||||
/* This routine gets called by all event handlers
|
||||
indicating that the idle is over. It may also
|
||||
get called from other thread for sending events
|
||||
to the main thread (and processing these in
|
||||
idle time). Very low priority. */
|
||||
|
||||
wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
|
||||
}
|
||||
|
||||
// the callback functions must be extern "C" to comply with GTK+ declarations
|
||||
extern "C"
|
||||
{
|
||||
|
||||
gint wxapp_pending_callback( gpointer WXUNUSED(data) )
|
||||
static gint wxapp_pending_callback( gpointer WXUNUSED(data) )
|
||||
{
|
||||
if (!wxTheApp) return TRUE;
|
||||
|
||||
@@ -214,7 +189,7 @@ gint wxapp_pending_callback( gpointer WXUNUSED(data) )
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
static gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
{
|
||||
if (!wxTheApp)
|
||||
return TRUE;
|
||||
@@ -239,7 +214,7 @@ gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
g_isIdle = TRUE;
|
||||
wxTheApp->m_idleTag = 0;
|
||||
|
||||
// Sent idle event to all who request them as long as
|
||||
// Send idle event to all who request them as long as
|
||||
// no events have popped up in the event queue.
|
||||
while (wxTheApp->ProcessIdle() && (gtk_events_pending() == 0))
|
||||
;
|
||||
@@ -254,69 +229,45 @@ gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
|
||||
gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
|
||||
static gint wxapp_poll_func( GPollFD *ufds, guint nfds, gint timeout )
|
||||
{
|
||||
// when getting called from GDK's time-out handler
|
||||
// we are no longer within GDK's grab on the GUI
|
||||
// thread so we must lock it here ourselves
|
||||
gint res;
|
||||
gdk_threads_enter();
|
||||
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
|
||||
// unblock other threads wishing to do some GUI things
|
||||
wxMutexGuiLeave();
|
||||
|
||||
g_mainThreadLocked = TRUE;
|
||||
|
||||
// wake up other threads
|
||||
wxUsleep( 1 );
|
||||
res = poll( (struct pollfd*) ufds, nfds, timeout );
|
||||
|
||||
// block other thread again
|
||||
wxMutexGuiEnter();
|
||||
|
||||
g_mainThreadLocked = FALSE;
|
||||
|
||||
wxapp_install_thread_wakeup();
|
||||
|
||||
// release lock again
|
||||
gdk_threads_leave();
|
||||
|
||||
return TRUE;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // wxUSE_THREADS
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#if wxUSE_THREADS
|
||||
|
||||
static int g_threadUninstallLevel = 0;
|
||||
|
||||
void wxapp_install_thread_wakeup()
|
||||
void wxapp_install_idle_handler()
|
||||
{
|
||||
g_threadUninstallLevel++;
|
||||
wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
|
||||
|
||||
if (g_threadUninstallLevel != 1) return;
|
||||
g_isIdle = FALSE;
|
||||
|
||||
if (wxTheApp->m_wakeUpTimerTag) return;
|
||||
if (g_pendingTag == 0)
|
||||
g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
|
||||
|
||||
wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 50, wxapp_wakeup_timerout_callback, (gpointer) NULL );
|
||||
// This routine gets called by all event handlers
|
||||
// indicating that the idle is over. It may also
|
||||
// get called from other thread for sending events
|
||||
// to the main thread (and processing these in
|
||||
// idle time). Very low priority.
|
||||
wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
|
||||
}
|
||||
|
||||
void wxapp_uninstall_thread_wakeup()
|
||||
{
|
||||
g_threadUninstallLevel--;
|
||||
|
||||
if (g_threadUninstallLevel != 0) return;
|
||||
|
||||
if (!wxTheApp->m_wakeUpTimerTag) return;
|
||||
|
||||
gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
|
||||
wxTheApp->m_wakeUpTimerTag = 0;
|
||||
}
|
||||
|
||||
#endif // wxUSE_THREADS
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Access to the root window global
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -352,8 +303,7 @@ wxApp::wxApp()
|
||||
wxapp_install_idle_handler();
|
||||
|
||||
#if wxUSE_THREADS
|
||||
m_wakeUpTimerTag = 0;
|
||||
wxapp_install_thread_wakeup();
|
||||
g_main_set_poll_func( wxapp_poll_func );
|
||||
#endif
|
||||
|
||||
m_colorCube = (unsigned char*) NULL;
|
||||
@@ -366,10 +316,6 @@ wxApp::~wxApp()
|
||||
{
|
||||
if (m_idleTag) gtk_idle_remove( m_idleTag );
|
||||
|
||||
#if wxUSE_THREADS
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
if (m_colorCube) free(m_colorCube);
|
||||
}
|
||||
|
||||
|
@@ -28,8 +28,6 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if wxUSE_THREADS
|
||||
extern void wxapp_install_thread_wakeup();
|
||||
extern void wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -341,7 +339,6 @@ void wxClipboard::Clear()
|
||||
{
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
/* As we have data we also own the clipboard. Once we no longer own
|
||||
@@ -374,7 +371,6 @@ void wxClipboard::Clear()
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -443,7 +439,6 @@ bool wxClipboard::AddData( wxDataObject *data )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
/* Tell the world we offer clipboard data */
|
||||
@@ -458,7 +453,6 @@ bool wxClipboard::AddData( wxDataObject *data )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
|
||||
return res;
|
||||
|
@@ -40,8 +40,6 @@ extern bool g_isIdle;
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if wxUSE_THREADS
|
||||
extern void wxapp_install_thread_wakeup();
|
||||
extern void wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -258,7 +256,6 @@ static gboolean target_drag_drop( GtkWidget *widget,
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
GdkAtom format = drop_target->GetMatchingPair();
|
||||
@@ -277,7 +274,6 @@ static gboolean target_drag_drop( GtkWidget *widget,
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -555,7 +551,6 @@ source_drag_data_get (GtkWidget *WXUNUSED(widget),
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
gtk_selection_data_set( selection_data,
|
||||
@@ -566,7 +561,6 @@ source_drag_data_get (GtkWidget *WXUNUSED(widget),
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
|
||||
delete[] d;
|
||||
@@ -801,7 +795,6 @@ wxDragResult wxDropSource::DoDragDrop( bool allowMove )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
/* don't start dragging if no button is down */
|
||||
@@ -832,7 +825,6 @@ wxDragResult wxDropSource::DoDragDrop( bool allowMove )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
|
||||
g_blockEventsOnDrag = FALSE;
|
||||
|
@@ -38,6 +38,7 @@
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/poll.h>
|
||||
#include "wx/gtk/win_gtk.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
@@ -50,25 +51,17 @@
|
||||
wxApp *wxTheApp = (wxApp *) NULL;
|
||||
wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
|
||||
|
||||
extern bool g_isIdle;
|
||||
|
||||
bool g_mainThreadLocked = FALSE;
|
||||
gint g_pendingTag = 0;
|
||||
|
||||
static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// local functions
|
||||
// idle system
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern "C"
|
||||
{
|
||||
gint wxapp_idle_callback( gpointer WXUNUSED(data) );
|
||||
gint wxapp_pending_callback( gpointer WXUNUSED(data) );
|
||||
}
|
||||
extern bool g_isIdle;
|
||||
|
||||
void wxapp_install_thread_wakeup();
|
||||
void wxapp_uninstall_thread_wakeup();
|
||||
void wxapp_install_idle_handler();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -165,29 +158,11 @@ void wxWakeUpIdle()
|
||||
// local functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void wxapp_install_idle_handler()
|
||||
{
|
||||
wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
|
||||
|
||||
g_isIdle = FALSE;
|
||||
|
||||
if (g_pendingTag == 0)
|
||||
g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
|
||||
|
||||
/* This routine gets called by all event handlers
|
||||
indicating that the idle is over. It may also
|
||||
get called from other thread for sending events
|
||||
to the main thread (and processing these in
|
||||
idle time). Very low priority. */
|
||||
|
||||
wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
|
||||
}
|
||||
|
||||
// the callback functions must be extern "C" to comply with GTK+ declarations
|
||||
extern "C"
|
||||
{
|
||||
|
||||
gint wxapp_pending_callback( gpointer WXUNUSED(data) )
|
||||
static gint wxapp_pending_callback( gpointer WXUNUSED(data) )
|
||||
{
|
||||
if (!wxTheApp) return TRUE;
|
||||
|
||||
@@ -214,7 +189,7 @@ gint wxapp_pending_callback( gpointer WXUNUSED(data) )
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
static gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
{
|
||||
if (!wxTheApp)
|
||||
return TRUE;
|
||||
@@ -239,7 +214,7 @@ gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
g_isIdle = TRUE;
|
||||
wxTheApp->m_idleTag = 0;
|
||||
|
||||
// Sent idle event to all who request them as long as
|
||||
// Send idle event to all who request them as long as
|
||||
// no events have popped up in the event queue.
|
||||
while (wxTheApp->ProcessIdle() && (gtk_events_pending() == 0))
|
||||
;
|
||||
@@ -254,69 +229,45 @@ gint wxapp_idle_callback( gpointer WXUNUSED(data) )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
|
||||
gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
|
||||
static gint wxapp_poll_func( GPollFD *ufds, guint nfds, gint timeout )
|
||||
{
|
||||
// when getting called from GDK's time-out handler
|
||||
// we are no longer within GDK's grab on the GUI
|
||||
// thread so we must lock it here ourselves
|
||||
gint res;
|
||||
gdk_threads_enter();
|
||||
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
|
||||
// unblock other threads wishing to do some GUI things
|
||||
wxMutexGuiLeave();
|
||||
|
||||
g_mainThreadLocked = TRUE;
|
||||
|
||||
// wake up other threads
|
||||
wxUsleep( 1 );
|
||||
res = poll( (struct pollfd*) ufds, nfds, timeout );
|
||||
|
||||
// block other thread again
|
||||
wxMutexGuiEnter();
|
||||
|
||||
g_mainThreadLocked = FALSE;
|
||||
|
||||
wxapp_install_thread_wakeup();
|
||||
|
||||
// release lock again
|
||||
gdk_threads_leave();
|
||||
|
||||
return TRUE;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // wxUSE_THREADS
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#if wxUSE_THREADS
|
||||
|
||||
static int g_threadUninstallLevel = 0;
|
||||
|
||||
void wxapp_install_thread_wakeup()
|
||||
void wxapp_install_idle_handler()
|
||||
{
|
||||
g_threadUninstallLevel++;
|
||||
wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
|
||||
|
||||
if (g_threadUninstallLevel != 1) return;
|
||||
g_isIdle = FALSE;
|
||||
|
||||
if (wxTheApp->m_wakeUpTimerTag) return;
|
||||
if (g_pendingTag == 0)
|
||||
g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
|
||||
|
||||
wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 50, wxapp_wakeup_timerout_callback, (gpointer) NULL );
|
||||
// This routine gets called by all event handlers
|
||||
// indicating that the idle is over. It may also
|
||||
// get called from other thread for sending events
|
||||
// to the main thread (and processing these in
|
||||
// idle time). Very low priority.
|
||||
wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
|
||||
}
|
||||
|
||||
void wxapp_uninstall_thread_wakeup()
|
||||
{
|
||||
g_threadUninstallLevel--;
|
||||
|
||||
if (g_threadUninstallLevel != 0) return;
|
||||
|
||||
if (!wxTheApp->m_wakeUpTimerTag) return;
|
||||
|
||||
gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
|
||||
wxTheApp->m_wakeUpTimerTag = 0;
|
||||
}
|
||||
|
||||
#endif // wxUSE_THREADS
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Access to the root window global
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -352,8 +303,7 @@ wxApp::wxApp()
|
||||
wxapp_install_idle_handler();
|
||||
|
||||
#if wxUSE_THREADS
|
||||
m_wakeUpTimerTag = 0;
|
||||
wxapp_install_thread_wakeup();
|
||||
g_main_set_poll_func( wxapp_poll_func );
|
||||
#endif
|
||||
|
||||
m_colorCube = (unsigned char*) NULL;
|
||||
@@ -366,10 +316,6 @@ wxApp::~wxApp()
|
||||
{
|
||||
if (m_idleTag) gtk_idle_remove( m_idleTag );
|
||||
|
||||
#if wxUSE_THREADS
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
if (m_colorCube) free(m_colorCube);
|
||||
}
|
||||
|
||||
|
@@ -28,8 +28,6 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if wxUSE_THREADS
|
||||
extern void wxapp_install_thread_wakeup();
|
||||
extern void wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -341,7 +339,6 @@ void wxClipboard::Clear()
|
||||
{
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
/* As we have data we also own the clipboard. Once we no longer own
|
||||
@@ -374,7 +371,6 @@ void wxClipboard::Clear()
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -443,7 +439,6 @@ bool wxClipboard::AddData( wxDataObject *data )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
/* Tell the world we offer clipboard data */
|
||||
@@ -458,7 +453,6 @@ bool wxClipboard::AddData( wxDataObject *data )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
|
||||
return res;
|
||||
|
@@ -40,8 +40,6 @@ extern bool g_isIdle;
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if wxUSE_THREADS
|
||||
extern void wxapp_install_thread_wakeup();
|
||||
extern void wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -258,7 +256,6 @@ static gboolean target_drag_drop( GtkWidget *widget,
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
GdkAtom format = drop_target->GetMatchingPair();
|
||||
@@ -277,7 +274,6 @@ static gboolean target_drag_drop( GtkWidget *widget,
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -555,7 +551,6 @@ source_drag_data_get (GtkWidget *WXUNUSED(widget),
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
gtk_selection_data_set( selection_data,
|
||||
@@ -566,7 +561,6 @@ source_drag_data_get (GtkWidget *WXUNUSED(widget),
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
|
||||
delete[] d;
|
||||
@@ -801,7 +795,6 @@ wxDragResult wxDropSource::DoDragDrop( bool allowMove )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* disable GUI threads */
|
||||
wxapp_uninstall_thread_wakeup();
|
||||
#endif
|
||||
|
||||
/* don't start dragging if no button is down */
|
||||
@@ -832,7 +825,6 @@ wxDragResult wxDropSource::DoDragDrop( bool allowMove )
|
||||
|
||||
#if wxUSE_THREADS
|
||||
/* re-enable GUI threads */
|
||||
wxapp_install_thread_wakeup();
|
||||
#endif
|
||||
|
||||
g_blockEventsOnDrag = FALSE;
|
||||
|
Reference in New Issue
Block a user