add splash screen and setup remote debugger
This commit is contained in:
BIN
PanoPainter.rc
BIN
PanoPainter.rc
Binary file not shown.
@@ -20,11 +20,13 @@ Global
|
|||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x64.ActiveCfg = Debug|x64
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x64.Build.0 = Debug|x64
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x64.Deploy.0 = Debug|x64
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x86.ActiveCfg = Debug|Win32
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x86.Build.0 = Debug|Win32
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Debug|x86.Build.0 = Debug|Win32
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|Any CPU.ActiveCfg = Release|Win32
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x64.ActiveCfg = Release|x64
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x64.ActiveCfg = Release|x64
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x64.Build.0 = Release|x64
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x64.Build.0 = Release|x64
|
||||||
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x64.Deploy.0 = Release|x64
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x86.ActiveCfg = Release|Win32
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x86.ActiveCfg = Release|Win32
|
||||||
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x86.Build.0 = Release|Win32
|
{6D5028CE-4D76-4B6A-A7C2-DE5A3268D433}.Release|x86.Build.0 = Release|Win32
|
||||||
{3A716FB6-DE62-439F-83B6-3C40915D6678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{3A716FB6-DE62-439F-83B6-3C40915D6678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
|||||||
@@ -428,6 +428,7 @@
|
|||||||
<ClInclude Include="libs\nanort\nanort.h" />
|
<ClInclude Include="libs\nanort\nanort.h" />
|
||||||
<ClInclude Include="libs\sqlite3\sqlite3.h" />
|
<ClInclude Include="libs\sqlite3\sqlite3.h" />
|
||||||
<ClInclude Include="libs\sqlite3\sqlite3ext.h" />
|
<ClInclude Include="libs\sqlite3\sqlite3ext.h" />
|
||||||
|
<ClInclude Include="resource.h" />
|
||||||
<ClInclude Include="src\abr.h" />
|
<ClInclude Include="src\abr.h" />
|
||||||
<ClInclude Include="src\action.h" />
|
<ClInclude Include="src\action.h" />
|
||||||
<ClInclude Include="src\app.h" />
|
<ClInclude Include="src\app.h" />
|
||||||
@@ -510,6 +511,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Xml Include="data\layout.xml">
|
<Xml Include="data\layout.xml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
|
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</DeploymentContent>
|
||||||
</Xml>
|
</Xml>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -543,6 +546,9 @@
|
|||||||
<None Include="data\shaders\uvs.glsl" />
|
<None Include="data\shaders\uvs.glsl" />
|
||||||
<None Include="data\shaders\vertex-color.glsl" />
|
<None Include="data\shaders\vertex-color.glsl" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="icon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
|
|||||||
@@ -578,6 +578,9 @@
|
|||||||
<ClInclude Include="src\settings.h">
|
<ClInclude Include="src\settings.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="resource.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="PanoPainter.rc">
|
<ResourceCompile Include="PanoPainter.rc">
|
||||||
@@ -683,4 +686,9 @@
|
|||||||
<Filter>shaders</Filter>
|
<Filter>shaders</Filter>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="icon.ico">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</Image>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
BIN
data/splash.bmp
Normal file
BIN
data/splash.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 400 KiB |
BIN
resource.h
BIN
resource.h
Binary file not shown.
@@ -96,6 +96,10 @@ void LogRemote::log(const char* format, ...)
|
|||||||
m_logfile.write(line.data(), line.size());
|
m_logfile.write(line.data(), line.size());
|
||||||
m_logfile.flush();
|
m_logfile.flush();
|
||||||
}
|
}
|
||||||
|
#if _WIN32
|
||||||
|
auto line = "DBG: " + std::string(buffer, n) + "\n";
|
||||||
|
OutputDebugStringA(line.c_str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
void LogRemote::log(const wchar_t* format, ...)
|
void LogRemote::log(const wchar_t* format, ...)
|
||||||
{
|
{
|
||||||
@@ -127,6 +131,10 @@ void LogRemote::log(const wchar_t* format, ...)
|
|||||||
m_logfile.write(line.data(), line.size());
|
m_logfile.write(line.data(), line.size());
|
||||||
m_logfile.flush();
|
m_logfile.flush();
|
||||||
}
|
}
|
||||||
|
#if _WIN32
|
||||||
|
auto line = L"DBG: " + std::wstring(buffer, n) + L"\n";
|
||||||
|
OutputDebugStringW(line.c_str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
LogRemote::~LogRemote()
|
LogRemote::~LogRemote()
|
||||||
{
|
{
|
||||||
|
|||||||
149
src/main.cpp
149
src/main.cpp
@@ -578,6 +578,77 @@ void win32_save_window_state()
|
|||||||
p.rcNormalPosition.top, p.rcNormalPosition.right, p.rcNormalPosition.bottom }));
|
p.rcNormalPosition.top, p.rcNormalPosition.right, p.rcNormalPosition.bottom }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch (Msg)
|
||||||
|
{
|
||||||
|
case WM_INITDIALOG:
|
||||||
|
{
|
||||||
|
static char base_path[MAX_PATH];
|
||||||
|
GetCurrentDirectoryA(MAX_PATH, base_path);
|
||||||
|
std::string path = std::string(base_path) + "\\data\\splash.bmp";
|
||||||
|
auto hbitmap = (HBITMAP)LoadImageA(NULL, path.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
|
||||||
|
SendMessage(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap);
|
||||||
|
SetDlgItemText(hWndDlg, IDC_STATIC_VERSION, g_version_number_w);
|
||||||
|
|
||||||
|
auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
|
||||||
|
auto x = unsigned{};
|
||||||
|
auto y = unsigned{};
|
||||||
|
GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y);
|
||||||
|
float z = (float)x / 96.f;
|
||||||
|
|
||||||
|
RECT r;
|
||||||
|
GetClientRect(hWndDlg, &r);
|
||||||
|
MoveWindow(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), 0, 0, 512 * z, 200 * z, TRUE);
|
||||||
|
SetWindowPos(hWndDlg, HWND_TOP, 0, 0, 512 * z, 200 * z, SWP_NOMOVE);
|
||||||
|
|
||||||
|
HWND hWndVersion = GetDlgItem(hWndDlg, IDC_STATIC_VERSION);
|
||||||
|
RECT rv;
|
||||||
|
GetClientRect(hWndVersion, &rv);
|
||||||
|
MoveWindow(hWndVersion, 0, 200 * z - (rv.bottom - rv.top), r.right - r.left, rv.bottom - rv.top, TRUE);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
case WM_USER + 1:
|
||||||
|
PostQuitMessage(0);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hWndDlg, Msg, wParam, lParam);;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND splash_dialog;
|
||||||
|
void splash_thread_loop()
|
||||||
|
{
|
||||||
|
splash_dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast<DLGPROC>(splash_proc));
|
||||||
|
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage(&msg, 0, 0, 0) > 0)
|
||||||
|
{
|
||||||
|
if (IsDialogMessage(splash_dialog, &msg))
|
||||||
|
{
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyWindow(splash_dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle)
|
||||||
|
{
|
||||||
|
RECT rc;
|
||||||
|
SetRectEmpty(&rc);
|
||||||
|
BOOL fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);
|
||||||
|
if (fRc) {
|
||||||
|
prc->left -= rc.left;
|
||||||
|
prc->top -= rc.top;
|
||||||
|
prc->right -= rc.right;
|
||||||
|
prc->bottom -= rc.bottom;
|
||||||
|
}
|
||||||
|
return fRc;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
WNDCLASS wc;
|
WNDCLASS wc;
|
||||||
@@ -587,17 +658,6 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
App::I.initLog();
|
App::I.initLog();
|
||||||
|
|
||||||
init_vk_map();
|
|
||||||
/*
|
|
||||||
if (!App::I.check_license())
|
|
||||||
{
|
|
||||||
MessageBoxA(NULL, "Unable to verify this demo license, please make sure you are connected to Internet.",
|
|
||||||
"PanoPainter - License Error", MB_ICONERROR | MB_OK);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
FILE* fp_check = fopen("data\\layout.xml", "rb");
|
FILE* fp_check = fopen("data\\layout.xml", "rb");
|
||||||
if (!fp_check)
|
if (!fp_check)
|
||||||
{
|
{
|
||||||
@@ -615,6 +675,9 @@ int main(int argc, char** argv)
|
|||||||
LOG("data files ok");
|
LOG("data files ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::thread dialog_thread(splash_thread_loop);
|
||||||
|
|
||||||
|
init_vk_map();
|
||||||
|
|
||||||
SetupExceptionHandler();
|
SetupExceptionHandler();
|
||||||
BT_SetTerminate();
|
BT_SetTerminate();
|
||||||
@@ -647,6 +710,12 @@ int main(int argc, char** argv)
|
|||||||
GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y);
|
GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y);
|
||||||
App::I.zoom *= (float)x / 96.f;
|
App::I.zoom *= (float)x / 96.f;
|
||||||
|
|
||||||
|
int show_cmd = SW_NORMAL;
|
||||||
|
Settings::value<Serializer::Integer>("window-show-cmd", show_cmd);
|
||||||
|
DWORD wnd_style = WS_OVERLAPPEDWINDOW;
|
||||||
|
//if (show_cmd == SW_MAXIMIZE)
|
||||||
|
// wnd_style != WS_MAXIMIZE;
|
||||||
|
|
||||||
RECT clientRect = { 0, 0, (int)App::I.width * App::I.zoom, (int)App::I.height * App::I.zoom };
|
RECT clientRect = { 0, 0, (int)App::I.width * App::I.zoom, (int)App::I.height * App::I.zoom };
|
||||||
POINT clientPos = { CW_USEDEFAULT, CW_USEDEFAULT };
|
POINT clientPos = { CW_USEDEFAULT, CW_USEDEFAULT };
|
||||||
if (Settings::has("window-rect"))
|
if (Settings::has("window-rect"))
|
||||||
@@ -659,9 +728,9 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AdjustWindowRect(&clientRect, WS_OVERLAPPEDWINDOW, false);
|
AdjustWindowRect(&clientRect, wnd_style, false);
|
||||||
}
|
}
|
||||||
hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", WS_OVERLAPPEDWINDOW, clientPos.x, clientPos.y,
|
hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", wnd_style, clientPos.x, clientPos.y,
|
||||||
(float)(clientRect.right - clientRect.left),
|
(float)(clientRect.right - clientRect.left),
|
||||||
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0);
|
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0);
|
||||||
|
|
||||||
@@ -723,7 +792,7 @@ int main(int argc, char** argv)
|
|||||||
wglDeleteContext(hRC);
|
wglDeleteContext(hRC);
|
||||||
DestroyWindow(hWnd);
|
DestroyWindow(hWnd);
|
||||||
|
|
||||||
hWnd = CreateWindow(wc.lpszClassName, window_title, WS_OVERLAPPEDWINDOW, clientPos.x, clientPos.y,
|
hWnd = CreateWindow(wc.lpszClassName, window_title, wnd_style, clientPos.x, clientPos.y,
|
||||||
(float)(clientRect.right - clientRect.left),
|
(float)(clientRect.right - clientRect.left),
|
||||||
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0);
|
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0);
|
||||||
|
|
||||||
@@ -767,12 +836,6 @@ int main(int argc, char** argv)
|
|||||||
LOG("init app");
|
LOG("init app");
|
||||||
App::I.init();
|
App::I.init();
|
||||||
|
|
||||||
int show_cmd = SW_NORMAL;
|
|
||||||
Settings::value<Serializer::Integer>("window-show-cmd", show_cmd);
|
|
||||||
|
|
||||||
LOG("show main window");
|
|
||||||
ShowWindow(hWnd, show_cmd);
|
|
||||||
|
|
||||||
if (!sandboxed)
|
if (!sandboxed)
|
||||||
{
|
{
|
||||||
LOG("init WinTab");
|
LOG("init WinTab");
|
||||||
@@ -798,6 +861,7 @@ int main(int argc, char** argv)
|
|||||||
const float target_tick_rate = 60;
|
const float target_tick_rate = 60;
|
||||||
unsigned long t0 = GetTickCount();
|
unsigned long t0 = GetTickCount();
|
||||||
unsigned long t1;
|
unsigned long t1;
|
||||||
|
bool first_frame = true;
|
||||||
int frames = 0;
|
int frames = 0;
|
||||||
float one_sec = 0;
|
float one_sec = 0;
|
||||||
float render_timer = 0;
|
float render_timer = 0;
|
||||||
@@ -850,6 +914,18 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (first_frame)
|
||||||
|
{
|
||||||
|
first_frame = false;
|
||||||
|
WINDOWPLACEMENT wp;
|
||||||
|
GetWindowPlacement(hWnd, &wp);
|
||||||
|
wp.showCmd = show_cmd;
|
||||||
|
SetWindowPlacement(hWnd, &wp);
|
||||||
|
// GetClientRect(hWnd, &clientRect);
|
||||||
|
// App::I.width = clientRect.right - clientRect.left;
|
||||||
|
// App::I.height = clientRect.bottom - clientRect.top;
|
||||||
|
}
|
||||||
|
|
||||||
App::I.tick(dt);
|
App::I.tick(dt);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(render_mutex);
|
std::unique_lock<std::mutex> lock(render_mutex);
|
||||||
@@ -901,6 +977,14 @@ int main(int argc, char** argv)
|
|||||||
if (start_in_vr)
|
if (start_in_vr)
|
||||||
App::I.vr_start();
|
App::I.vr_start();
|
||||||
|
|
||||||
|
LOG("show main window");
|
||||||
|
SetForegroundWindow(hWnd);
|
||||||
|
//ShowWindow(hWnd, show_cmd);
|
||||||
|
|
||||||
|
SendMessage(splash_dialog, WM_USER + 1, 0, 0);
|
||||||
|
if (dialog_thread.joinable())
|
||||||
|
dialog_thread.join();
|
||||||
|
|
||||||
MSG msg;
|
MSG msg;
|
||||||
LOG("start main loop");
|
LOG("start main loop");
|
||||||
while (running == 1)
|
while (running == 1)
|
||||||
@@ -941,7 +1025,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
// Clean up
|
// Clean up
|
||||||
WacomTablet::I.terminate();
|
WacomTablet::I.terminate();
|
||||||
DestroyWindow(hWnd);
|
|
||||||
UnregisterClass(className, hInst);
|
UnregisterClass(className, hInst);
|
||||||
LogRemote::I.stop();
|
LogRemote::I.stop();
|
||||||
}
|
}
|
||||||
@@ -958,9 +1042,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
|
|
||||||
switch (msg)
|
switch (msg)
|
||||||
{
|
{
|
||||||
case WM_DESTROY:
|
case WM_USER_CLOSE:
|
||||||
if (running != -1)
|
|
||||||
{
|
|
||||||
running = 0;
|
running = 0;
|
||||||
render_cv.notify_all();
|
render_cv.notify_all();
|
||||||
if (renderer.joinable())
|
if (renderer.joinable())
|
||||||
@@ -968,11 +1050,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
if (hmd_renderer.joinable())
|
if (hmd_renderer.joinable())
|
||||||
hmd_renderer.join();
|
hmd_renderer.join();
|
||||||
App::I.terminate();
|
App::I.terminate();
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WM_USER_CLOSE:
|
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
break;
|
return 0;
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
App::I.redraw = true;
|
App::I.redraw = true;
|
||||||
break;
|
break;
|
||||||
@@ -981,15 +1060,27 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
break;
|
break;
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
if (App::I.request_close())
|
if (App::I.request_close())
|
||||||
|
{
|
||||||
|
running = 0;
|
||||||
|
render_cv.notify_all();
|
||||||
|
if (renderer.joinable())
|
||||||
|
renderer.join();
|
||||||
|
if (hmd_renderer.joinable())
|
||||||
|
hmd_renderer.join();
|
||||||
|
App::I.terminate();
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WM_SIZE:
|
case WM_SIZE:
|
||||||
{
|
{
|
||||||
auto w = (float)LOWORD(lp);
|
auto w = (float)LOWORD(lp);
|
||||||
auto h = (float)HIWORD(lp);
|
auto h = (float)HIWORD(lp);
|
||||||
if (h != 0)
|
if (h != 0 && running == 1)
|
||||||
{
|
{
|
||||||
async_locker lock;
|
async_locker lock;
|
||||||
App::I.resize(w, h);
|
App::I.resize(w, h);
|
||||||
|
|||||||
Reference in New Issue
Block a user