mirror of
https://github.com/ocornut/imgui.git
synced 2025-12-22 06:15:37 +00:00
Backends: Fixed various warnings discovered when using MinGW GCC 15/Clang on latest backends.
dx12: 'ImGui_ImplDX12_Data* bd' shadowed local in spite of being in lambda.
This commit is contained in:
@@ -50,6 +50,12 @@
|
|||||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Clang/GCC warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
// DirectX10 data
|
// DirectX10 data
|
||||||
struct ImGui_ImplDX10_Texture
|
struct ImGui_ImplDX10_Texture
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,6 +52,12 @@
|
|||||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Clang/GCC warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
// DirectX11 data
|
// DirectX11 data
|
||||||
struct ImGui_ImplDX11_Texture
|
struct ImGui_ImplDX11_Texture
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,6 +62,12 @@
|
|||||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Clang/GCC warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
// MinGW workaround, see #4594
|
// MinGW workaround, see #4594
|
||||||
typedef decltype(D3D12SerializeRootSignature) *_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE;
|
typedef decltype(D3D12SerializeRootSignature) *_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE;
|
||||||
|
|
||||||
@@ -800,6 +806,28 @@ void ImGui_ImplDX12_InvalidateDeviceObjects()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
static void ImGui_ImplDX12_InitLegacySingleDescriptorMode(ImGui_ImplDX12_InitInfo* init_info)
|
||||||
|
{
|
||||||
|
// Wrap legacy behavior of passing space for a single descriptor
|
||||||
|
IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
|
||||||
|
init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
|
IM_ASSERT(bd->LegacySingleDescriptorUsed == false && "Only 1 simultaneous texture allowed with legacy ImGui_ImplDX12_Init() signature!");
|
||||||
|
*out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor;
|
||||||
|
*out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor;
|
||||||
|
bd->LegacySingleDescriptorUsed = true;
|
||||||
|
};
|
||||||
|
init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||||
|
IM_ASSERT(bd->LegacySingleDescriptorUsed == true);
|
||||||
|
bd->LegacySingleDescriptorUsed = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
|
bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
@@ -826,24 +854,7 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
|
|||||||
|
|
||||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
if (init_info->SrvDescriptorAllocFn == nullptr)
|
if (init_info->SrvDescriptorAllocFn == nullptr)
|
||||||
{
|
ImGui_ImplDX12_InitLegacySingleDescriptorMode(init_info);
|
||||||
// Wrap legacy behavior of passing space for a single descriptor
|
|
||||||
IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
|
|
||||||
init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle)
|
|
||||||
{
|
|
||||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
||||||
IM_ASSERT(bd->LegacySingleDescriptorUsed == false && "Only 1 simultaneous texture allowed with legacy ImGui_ImplDX12_Init() signature!");
|
|
||||||
*out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor;
|
|
||||||
*out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor;
|
|
||||||
bd->LegacySingleDescriptorUsed = true;
|
|
||||||
};
|
|
||||||
init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE)
|
|
||||||
{
|
|
||||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
|
||||||
IM_ASSERT(bd->LegacySingleDescriptorUsed == true);
|
|
||||||
bd->LegacySingleDescriptorUsed = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr);
|
IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr);
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,12 @@
|
|||||||
// DirectX
|
// DirectX
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
|
|
||||||
|
// Clang/GCC warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
// DirectX data
|
// DirectX data
|
||||||
struct ImGui_ImplDX9_Data
|
struct ImGui_ImplDX9_Data
|
||||||
{
|
{
|
||||||
@@ -368,8 +374,8 @@ static void ImGui_ImplDX9_CopyTextureRegion(bool tex_use_colors, const ImU32* sr
|
|||||||
#endif
|
#endif
|
||||||
for (int y = 0; y < h; y++)
|
for (int y = 0; y < h; y++)
|
||||||
{
|
{
|
||||||
ImU32* src_p = (ImU32*)((unsigned char*)src + src_pitch * y);
|
const ImU32* src_p = (const ImU32*)(void*)((const unsigned char*)src + src_pitch * y);
|
||||||
ImU32* dst_p = (ImU32*)((unsigned char*)dst + dst_pitch * y);
|
ImU32* dst_p = (ImU32*)(void*)((unsigned char*)dst + dst_pitch * y);
|
||||||
if (convert_rgba_to_bgra)
|
if (convert_rgba_to_bgra)
|
||||||
for (int x = w; x > 0; x--, src_p++, dst_p++) // Convert copy
|
for (int x = w; x > 0; x--, src_p++, dst_p++) // Convert copy
|
||||||
*dst_p = IMGUI_COL_TO_DX9_ARGB(*src_p);
|
*dst_p = IMGUI_COL_TO_DX9_ARGB(*src_p);
|
||||||
|
|||||||
@@ -545,6 +545,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara
|
|||||||
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP:
|
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP:
|
||||||
io.AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo());
|
io.AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo());
|
||||||
break;
|
break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
return ::CallWindowProcW(bd->PrevWndProc, hWnd, msg, wParam, lParam);
|
return ::CallWindowProcW(bd->PrevWndProc, hWnd, msg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
// Clang warnings with -Weverything
|
// Clang warnings with -Weverything
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
|
||||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -434,7 +435,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
|
|||||||
// (event->type == SDL_KEYDOWN) ? "DOWN" : "UP ", event->key.keysym.sym, SDL_GetKeyName(event->key.keysym.sym), event->key.keysym.scancode, SDL_GetScancodeName(event->key.keysym.scancode), event->key.keysym.mod);
|
// (event->type == SDL_KEYDOWN) ? "DOWN" : "UP ", event->key.keysym.sym, SDL_GetKeyName(event->key.keysym.sym), event->key.keysym.scancode, SDL_GetScancodeName(event->key.keysym.scancode), event->key.keysym.mod);
|
||||||
ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode);
|
ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode);
|
||||||
io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
|
io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
|
||||||
io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
io.SetKeyEventNativeData(key, (int)event->key.keysym.sym, (int)event->key.keysym.scancode, (int)event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
@@ -466,6 +467,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
|
|||||||
bd->WantUpdateGamepadsList = true;
|
bd->WantUpdateGamepadsList = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
// Clang warnings with -Weverything
|
// Clang warnings with -Weverything
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
|
||||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -409,7 +410,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
|
|||||||
// (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod);
|
// (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod);
|
||||||
ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode);
|
ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode);
|
||||||
io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
|
io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
|
||||||
io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
io.SetKeyEventNativeData(key, (int)event->key.key, (int)event->key.scancode, (int)event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
||||||
@@ -445,6 +446,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
|
|||||||
bd->WantUpdateGamepadsList = true;
|
bd->WantUpdateGamepadsList = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -471,7 +474,7 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void
|
|||||||
|
|
||||||
// Setup backend capabilities flags
|
// Setup backend capabilities flags
|
||||||
ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
|
ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
|
||||||
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%u.%u.%u; %u.%u.%u)",
|
snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%d.%d.%d; %d.%d.%d)",
|
||||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
|
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
|
||||||
io.BackendPlatformUserData = (void*)bd;
|
io.BackendPlatformUserData = (void*)bd;
|
||||||
io.BackendPlatformName = bd->BackendPlatformName;
|
io.BackendPlatformName = bd->BackendPlatformName;
|
||||||
|
|||||||
@@ -44,6 +44,8 @@
|
|||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// SDL
|
// SDL
|
||||||
|
|||||||
@@ -41,6 +41,8 @@
|
|||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// SDL
|
// SDL
|
||||||
|
|||||||
@@ -1250,7 +1250,7 @@ void ImGui_ImplVulkan_Shutdown()
|
|||||||
|
|
||||||
ImGui_ImplVulkan_DestroyDeviceObjects();
|
ImGui_ImplVulkan_DestroyDeviceObjects();
|
||||||
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
|
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
|
||||||
IM_FREE((void*)bd->VulkanInitInfo.PipelineRenderingCreateInfo.pColorAttachmentFormats);
|
IM_FREE((void*)const_cast<VkFormat*>(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pColorAttachmentFormats));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
io.BackendRendererName = nullptr;
|
io.BackendRendererName = nullptr;
|
||||||
|
|||||||
@@ -571,6 +571,7 @@ ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam)
|
|||||||
case 51: return ImGuiKey_Comma;
|
case 51: return ImGuiKey_Comma;
|
||||||
case 52: return ImGuiKey_Period;
|
case 52: return ImGuiKey_Period;
|
||||||
case 53: return ImGuiKey_Slash;
|
case 53: return ImGuiKey_Slash;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ImGuiKey_None;
|
return ImGuiKey_None;
|
||||||
|
|||||||
@@ -8146,7 +8146,7 @@ void ImGui::ShowAboutWindow(bool* p_open)
|
|||||||
if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) ImGui::Text(" RendererHasTextures");
|
if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) ImGui::Text(" RendererHasTextures");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height);
|
ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height);
|
||||||
ImGui::Text("io.Fonts->FontLoaderName: \"%s\"", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL");
|
ImGui::Text("io.Fonts->FontLoaderName: %s", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL");
|
||||||
ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
|
ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
|
||||||
ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
|
ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|||||||
Reference in New Issue
Block a user