mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-25 21:17:01 +00:00
Make classes not depend on the implicit GImGui context (#5856, #6199): ImGuiWindow, ImGuiInputTextCallbackData, ImGuiListClipper, ImGuiStackSizes
This commit is a preparation toward adding ImGui apis with explicit context and making ImGui applications being able to use multiple context at the same time whatever their concurrency model. This commit modifies ImGuiInputTextCallback, ImGuiListClipper and ImGuiStackSize so those classes do not to depend on GImGui context anymore. About ImGuiInputTextCallback: - ImGuiInputTextCallback depends on ImGuiContext because it has a `InsertChars` method adding character to `g.InputTextState` - To make ImGuiInputTextCallback aware of which context to use, the appropriate context is given as argument of ImGuiInputTextCallback constructor. About ImGuiListClipper: - ImGuiListClipper apply to a context through its `Begin`, `End`, and `Step` method. - To make ImGuiListClipper aware of which context to use, the appropriate context is given as argument of ImGuiListClipper constructor. - Since the behavior is different than previously the class has been renamed ImGuiListClipperEx - In order to preserve backward compatibility, a subclass of ImGuiListClipperEx named ImGuiListClipper has been defined and forward the implicit context to ImGuiListClipperEx parent. About ImGuiTextFilter: - ImGuiTextFilter depends on the implicit context because the Draw(..) method call ImGui::InputText(...) - Instead from that commit the Draw(...) method takes an explicit context as first argument - Since the behavior is different than previously the class has been renamed ImGuiTextFilterEx - In order to preserve backward compatibility, a subclass of ImGuiTextFilterEx named ImGuiTextFilter has been defined. This subclass has a draw method override which and forward the implicit context to the parent class Draw(...) About ImGuiStackSizes: - ImGuiStackSizes was depending on ImGuiContext because of its `SetToCurrentState` and `CompareWithCurrentState` method - ImGuiStackSizes is an helper object use for comparing state of context. It does not necessarily need to compare the same context. For that reason, as opposed to previous classes it takes the context it wants to compare to as argument of its method. - For this occasion `SetToCurrentState` and `CompareWithCurrentState` have been renamed `SetToContextState` and `CompareWithContextState` to match the new method signature. ImGuiListClipper ImGuiInputTextCallbackData
This commit is contained in:
parent
10ace228bc
commit
c8ad25caa6
33
imgui.cpp
33
imgui.cpp
@ -2709,6 +2709,8 @@ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int it
|
|||||||
ImGuiListClipper::ImGuiListClipper()
|
ImGuiListClipper::ImGuiListClipper()
|
||||||
{
|
{
|
||||||
memset(this, 0, sizeof(*this));
|
memset(this, 0, sizeof(*this));
|
||||||
|
Ctx = ImGui::GetCurrentContext();
|
||||||
|
IM_ASSERT(Ctx != NULL);
|
||||||
ItemsCount = -1;
|
ItemsCount = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2719,7 +2721,7 @@ ImGuiListClipper::~ImGuiListClipper()
|
|||||||
|
|
||||||
void ImGuiListClipper::Begin(int items_count, float items_height)
|
void ImGuiListClipper::Begin(int items_count, float items_height)
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *Ctx;
|
||||||
ImGuiWindow* window = g.CurrentWindow;
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
|
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
|
||||||
|
|
||||||
@ -2744,7 +2746,7 @@ void ImGuiListClipper::Begin(int items_count, float items_height)
|
|||||||
|
|
||||||
void ImGuiListClipper::End()
|
void ImGuiListClipper::End()
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *Ctx;
|
||||||
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
|
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
|
||||||
{
|
{
|
||||||
// In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
|
// In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
|
||||||
@ -2776,7 +2778,7 @@ void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max)
|
|||||||
|
|
||||||
static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
|
static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *clipper->Ctx;
|
||||||
ImGuiWindow* window = g.CurrentWindow;
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
|
ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
|
||||||
IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
|
IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
|
||||||
@ -2899,7 +2901,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
|
|||||||
|
|
||||||
bool ImGuiListClipper::Step()
|
bool ImGuiListClipper::Step()
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *Ctx;
|
||||||
bool need_items_height = (ItemsHeight <= 0.0f);
|
bool need_items_height = (ItemsHeight <= 0.0f);
|
||||||
bool ret = ImGuiListClipper_StepInternal(this);
|
bool ret = ImGuiListClipper_StepInternal(this);
|
||||||
if (ret && (DisplayStart == DisplayEnd))
|
if (ret && (DisplayStart == DisplayEnd))
|
||||||
@ -3618,9 +3620,10 @@ void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
|
// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
|
||||||
ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
|
ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
|
||||||
{
|
{
|
||||||
memset(this, 0, sizeof(*this));
|
memset(this, 0, sizeof(*this));
|
||||||
|
Ctx = ctx;
|
||||||
Name = ImStrdup(name);
|
Name = ImStrdup(name);
|
||||||
NameBufLen = (int)strlen(name) + 1;
|
NameBufLen = (int)strlen(name) + 1;
|
||||||
ID = ImHashStr(name);
|
ID = ImHashStr(name);
|
||||||
@ -3637,7 +3640,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst
|
|||||||
FontWindowScale = 1.0f;
|
FontWindowScale = 1.0f;
|
||||||
SettingsOffset = -1;
|
SettingsOffset = -1;
|
||||||
DrawList = &DrawListInst;
|
DrawList = &DrawListInst;
|
||||||
DrawList->_Data = &context->DrawListSharedData;
|
DrawList->_Data = &Ctx->DrawListSharedData;
|
||||||
DrawList->_OwnerName = Name;
|
DrawList->_OwnerName = Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3652,7 +3655,7 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
|
|||||||
{
|
{
|
||||||
ImGuiID seed = IDStack.back();
|
ImGuiID seed = IDStack.back();
|
||||||
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
|
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *Ctx;
|
||||||
if (g.DebugHookIdInfo == id)
|
if (g.DebugHookIdInfo == id)
|
||||||
ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
|
ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
|
||||||
return id;
|
return id;
|
||||||
@ -3662,7 +3665,7 @@ ImGuiID ImGuiWindow::GetID(const void* ptr)
|
|||||||
{
|
{
|
||||||
ImGuiID seed = IDStack.back();
|
ImGuiID seed = IDStack.back();
|
||||||
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
|
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *Ctx;
|
||||||
if (g.DebugHookIdInfo == id)
|
if (g.DebugHookIdInfo == id)
|
||||||
ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
|
ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
|
||||||
return id;
|
return id;
|
||||||
@ -3672,7 +3675,7 @@ ImGuiID ImGuiWindow::GetID(int n)
|
|||||||
{
|
{
|
||||||
ImGuiID seed = IDStack.back();
|
ImGuiID seed = IDStack.back();
|
||||||
ImGuiID id = ImHashData(&n, sizeof(n), seed);
|
ImGuiID id = ImHashData(&n, sizeof(n), seed);
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *Ctx;
|
||||||
if (g.DebugHookIdInfo == id)
|
if (g.DebugHookIdInfo == id)
|
||||||
ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
|
ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
|
||||||
return id;
|
return id;
|
||||||
@ -6084,7 +6087,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|||||||
ImGuiWindowStackData window_stack_data;
|
ImGuiWindowStackData window_stack_data;
|
||||||
window_stack_data.Window = window;
|
window_stack_data.Window = window;
|
||||||
window_stack_data.ParentLastItemDataBackup = g.LastItemData;
|
window_stack_data.ParentLastItemDataBackup = g.LastItemData;
|
||||||
window_stack_data.StackSizesOnBegin.SetToCurrentState();
|
window_stack_data.StackSizesOnBegin.SetToContextState(&g);
|
||||||
g.CurrentWindowStack.push_back(window_stack_data);
|
g.CurrentWindowStack.push_back(window_stack_data);
|
||||||
if (flags & ImGuiWindowFlags_ChildMenu)
|
if (flags & ImGuiWindowFlags_ChildMenu)
|
||||||
g.BeginMenuCount++;
|
g.BeginMenuCount++;
|
||||||
@ -6744,7 +6747,7 @@ void ImGui::End()
|
|||||||
g.BeginMenuCount--;
|
g.BeginMenuCount--;
|
||||||
if (window->Flags & ImGuiWindowFlags_Popup)
|
if (window->Flags & ImGuiWindowFlags_Popup)
|
||||||
g.BeginPopupStack.pop_back();
|
g.BeginPopupStack.pop_back();
|
||||||
g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState();
|
g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithContextState(&g);
|
||||||
g.CurrentWindowStack.pop_back();
|
g.CurrentWindowStack.pop_back();
|
||||||
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
|
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
|
||||||
}
|
}
|
||||||
@ -9064,9 +9067,9 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save current stack sizes for later compare
|
// Save current stack sizes for later compare
|
||||||
void ImGuiStackSizes::SetToCurrentState()
|
void ImGuiStackSizes::SetToContextState(ImGuiContext* ctx)
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *ctx;
|
||||||
ImGuiWindow* window = g.CurrentWindow;
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
SizeOfIDStack = (short)window->IDStack.Size;
|
SizeOfIDStack = (short)window->IDStack.Size;
|
||||||
SizeOfColorStack = (short)g.ColorStack.Size;
|
SizeOfColorStack = (short)g.ColorStack.Size;
|
||||||
@ -9080,9 +9083,9 @@ void ImGuiStackSizes::SetToCurrentState()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare to detect usage errors
|
// Compare to detect usage errors
|
||||||
void ImGuiStackSizes::CompareWithCurrentState()
|
void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *ctx;
|
||||||
ImGuiWindow* window = g.CurrentWindow;
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
IM_UNUSED(window);
|
IM_UNUSED(window);
|
||||||
|
|
||||||
|
2
imgui.h
2
imgui.h
@ -2079,6 +2079,7 @@ struct ImGuiIO
|
|||||||
// - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow.
|
// - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow.
|
||||||
struct ImGuiInputTextCallbackData
|
struct ImGuiInputTextCallbackData
|
||||||
{
|
{
|
||||||
|
ImGuiContext* Ctx; // Parent UI context
|
||||||
ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only
|
ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only
|
||||||
ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only
|
ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only
|
||||||
void* UserData; // What user passed to InputText() // Read-only
|
void* UserData; // What user passed to InputText() // Read-only
|
||||||
@ -2303,6 +2304,7 @@ struct ImGuiStorage
|
|||||||
// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc.
|
// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc.
|
||||||
struct ImGuiListClipper
|
struct ImGuiListClipper
|
||||||
{
|
{
|
||||||
|
ImGuiContext* Ctx; // Parent UI context
|
||||||
int DisplayStart; // First item to display, updated by each call to Step()
|
int DisplayStart; // First item to display, updated by each call to Step()
|
||||||
int DisplayEnd; // End of items to display (exclusive)
|
int DisplayEnd; // End of items to display (exclusive)
|
||||||
int ItemsCount; // [Internal] Number of items
|
int ItemsCount; // [Internal] Number of items
|
||||||
|
@ -1182,8 +1182,8 @@ struct IMGUI_API ImGuiStackSizes
|
|||||||
short SizeOfDisabledStack;
|
short SizeOfDisabledStack;
|
||||||
|
|
||||||
ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
|
ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
|
||||||
void SetToCurrentState();
|
void SetToContextState(ImGuiContext* ctx);
|
||||||
void CompareWithCurrentState();
|
void CompareWithContextState(ImGuiContext* ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Data saved for each window pushed into the stack
|
// Data saved for each window pushed into the stack
|
||||||
@ -2237,6 +2237,7 @@ struct IMGUI_API ImGuiWindowTempData
|
|||||||
// Storage for one window
|
// Storage for one window
|
||||||
struct IMGUI_API ImGuiWindow
|
struct IMGUI_API ImGuiWindow
|
||||||
{
|
{
|
||||||
|
ImGuiContext* Ctx; // Parent UI context (needs to be set explicitly by parent).
|
||||||
char* Name; // Window name, owned by the window.
|
char* Name; // Window name, owned by the window.
|
||||||
ImGuiID ID; // == ImHashStr(Name)
|
ImGuiID ID; // == ImHashStr(Name)
|
||||||
ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_
|
ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_
|
||||||
@ -2347,10 +2348,10 @@ public:
|
|||||||
|
|
||||||
// We don't use g.FontSize because the window may be != g.CurrentWindow.
|
// We don't use g.FontSize because the window may be != g.CurrentWindow.
|
||||||
ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
|
ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
|
||||||
float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }
|
float CalcFontSize() const { ImGuiContext& g = *Ctx; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }
|
||||||
float TitleBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; }
|
float TitleBarHeight() const { ImGuiContext& g = *Ctx; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; }
|
||||||
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
|
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
|
||||||
float MenuBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; }
|
float MenuBarHeight() const { ImGuiContext& g = *Ctx; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; }
|
||||||
ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }
|
ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3843,7 +3843,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!)
|
// Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!)
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *Ctx;
|
||||||
ImGuiInputTextState* edit_state = &g.InputTextState;
|
ImGuiInputTextState* edit_state = &g.InputTextState;
|
||||||
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
|
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
|
||||||
IM_ASSERT(Buf == edit_state->TextA.Data);
|
IM_ASSERT(Buf == edit_state->TextA.Data);
|
||||||
@ -3947,8 +3947,9 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
|
|||||||
// Custom callback filter
|
// Custom callback filter
|
||||||
if (flags & ImGuiInputTextFlags_CallbackCharFilter)
|
if (flags & ImGuiInputTextFlags_CallbackCharFilter)
|
||||||
{
|
{
|
||||||
|
ImGuiContext& g = *GImGui;
|
||||||
ImGuiInputTextCallbackData callback_data;
|
ImGuiInputTextCallbackData callback_data;
|
||||||
memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
|
callback_data.Ctx = &g;
|
||||||
callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
|
callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
|
||||||
callback_data.EventChar = (ImWchar)c;
|
callback_data.EventChar = (ImWchar)c;
|
||||||
callback_data.Flags = flags;
|
callback_data.Flags = flags;
|
||||||
@ -4588,7 +4589,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|||||||
if (event_flag)
|
if (event_flag)
|
||||||
{
|
{
|
||||||
ImGuiInputTextCallbackData callback_data;
|
ImGuiInputTextCallbackData callback_data;
|
||||||
memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData));
|
callback_data.Ctx = &g;
|
||||||
callback_data.EventFlag = event_flag;
|
callback_data.EventFlag = event_flag;
|
||||||
callback_data.Flags = flags;
|
callback_data.Flags = flags;
|
||||||
callback_data.UserData = callback_user_data;
|
callback_data.UserData = callback_user_data;
|
||||||
@ -4651,6 +4652,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|||||||
if (is_resizable)
|
if (is_resizable)
|
||||||
{
|
{
|
||||||
ImGuiInputTextCallbackData callback_data;
|
ImGuiInputTextCallbackData callback_data;
|
||||||
|
callback_data.Ctx = &g;
|
||||||
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
|
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
|
||||||
callback_data.Flags = flags;
|
callback_data.Flags = flags;
|
||||||
callback_data.Buf = buf;
|
callback_data.Buf = buf;
|
||||||
|
Loading…
Reference in New Issue
Block a user