This commit is contained in:
elushaX 2024-03-20 00:47:46 -07:00 committed by Ilya Shurupov
parent 6d02137dca
commit a46a2167ba
13 changed files with 182 additions and 90 deletions

View file

@ -37,6 +37,8 @@ DebugGUI::DebugGUI(Window* window) {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("Font.ttf", 20.f); io.Fonts->AddFontFromFileTTF("Font.ttf", 20.f);
io.ConfigInputTrickleEventQueue = false;
} }
DebugGUI::~DebugGUI() { DebugGUI::~DebugGUI() {

View file

@ -4,4 +4,87 @@
using namespace tp; using namespace tp;
EventHandler::EventHandler() = default; EventHandler::EventHandler() = default;
EventHandler::~EventHandler() = default; EventHandler::~EventHandler() = default;
void EventHandler::postEvent(InputID inputID, InputEvent inputEvent) {
mMutex.lock();
mEventQueue.pushBack({ inputID, inputEvent });
mMutex.unlock();
}
bool EventHandler::isEvents() {
mMutex.lock();
auto res = mEventQueue.length();
mMutex.unlock();
return res;
}
InputState::State transitions[4][4] = {
{ InputState::State::NONE, InputState::State::PRESSED, InputState::State::PRESSED, InputState::State::NONE },
{ InputState::State::PRESSED, InputState::State::PRESSED, InputState::State::HOLD, InputState::State::PRESSED },
{ InputState::State::HOLD, InputState::State::RELEASED, InputState::State::RELEASED, InputState::State::HOLD },
{ InputState::State::RELEASED, InputState::State::NONE, InputState::State::RELEASED, InputState::State::RELEASED },
};
bool transitionsReduce[4][4] = {
{ true, true, false, true },
{ true, true, false, true },
{ true, false, true, true },
{ true, false, true, true },
};
void EventHandler::processEvent() {
mMutex.lock();
auto lastEvent = mEventQueue.last();
const auto& eventData = lastEvent->data.second;
const auto& inputId = lastEvent->data.first;
switch (eventData.type) {
case InputEvent::Type::MOUSE_POS:
{
mPointer = eventData.mouseEvent;
mEventQueue.popFront();
break;
}
case InputEvent::Type::BUTTON_ACTION:
{
auto currentState = (int) mInputStates[(int) inputId].mCurrentState;
auto reportedEvent = (int) eventData.buttonAction;
mInputStates[(int) inputId].mCurrentState = transitions[currentState][reportedEvent];
if (transitionsReduce[currentState][reportedEvent]) {
mEventQueue.popFront();
}
break;
}
default:
{
mEventQueue.popFront();
}
}
mMutex.unlock();
}
const Vec2F& EventHandler::getPointer() const { return mPointer; }
bool EventHandler::isPressed(InputID id) const {
return mInputStates[(int) id].mCurrentState == InputState::State::PRESSED;
}
bool EventHandler::isReleased(InputID id) const {
return mInputStates[(int) id].mCurrentState == InputState::State::RELEASED;
}
bool EventHandler::isDown(InputID id) const {
return mInputStates[(int) id].mCurrentState == InputState::State::PRESSED ||
mInputStates[(int) id].mCurrentState == InputState::State::HOLD;
}
halnf EventHandler::getScrollY() const { return 0; }

View file

@ -96,6 +96,8 @@ bool Window::shouldClose() const { return glfwWindowShouldClose(mContext->window
void Window::processEvents() { void Window::processEvents() {
glfwWaitEvents(); glfwWaitEvents();
checkAxisUpdates(); checkAxisUpdates();
glViewport(0, 0, mSize.x, mSize.y);
} }
void Window::draw() { void Window::draw() {
@ -134,7 +136,7 @@ void Window::checkAxisUpdates() {
RectF windowRec = { { 0, 0 }, mSize }; RectF windowRec = { { 0, 0 }, mSize };
if (windowRec.isInside(mPointerPos)) { if (windowRec.isInside(mPointerPos)) {
mEventHandler->postEvent(InputID::MOUSE1, { {}, {}, mPointerPos }); mEventHandler->postEvent(InputID::MOUSE1, { InputEvent::Type::MOUSE_POS, {}, mPointerPos });
} }
} }
} }
@ -149,15 +151,11 @@ static void keyCallback(GLFWwindow* window, int key, int scancode, int action, i
self->checkAxisUpdates(); self->checkAxisUpdates();
// post key event
/*
if (action == GLFW_PRESS) { if (action == GLFW_PRESS) {
inputManager->keyStates[key] = GLFW_PRESS; eventHandler->postEvent((InputID) key, { InputEvent::Type::BUTTON_ACTION, InputEvent::ButtonAction::PRESS, {} });
} else if (action == GLFW_RELEASE) { } else if (action == GLFW_RELEASE) {
inputManager->keyStates[key] = GLFW_RELEASE; eventHandler->postEvent((InputID) key, { InputEvent::Type::BUTTON_ACTION, InputEvent::ButtonAction::RELEASE, {} });
} }
inputManager->isEvent = true;
*/
} }
static void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { static void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
@ -169,9 +167,14 @@ static void mouseButtonCallback(GLFWwindow* window, int button, int action, int
self->checkAxisUpdates(); self->checkAxisUpdates();
// post mouse button event auto id = (InputID) ((int) InputID::MOUSE1 + button);
// inputManager->mouseButtonStates[button] = action;
// inputManager->isEvent = true; if (action == GLFW_PRESS) {
eventHandler->postEvent(id, { InputEvent::Type::BUTTON_ACTION, InputEvent::ButtonAction::PRESS, {} }
);
} else if (action == GLFW_RELEASE) {
eventHandler->postEvent(id, { InputEvent::Type::BUTTON_ACTION, InputEvent::ButtonAction::RELEASE, {} });
}
} }
static void scrollCallback(GLFWwindow* window, double xOffset, double yOffset) { static void scrollCallback(GLFWwindow* window, double xOffset, double yOffset) {

View file

@ -11,14 +11,14 @@ namespace tp {
struct InputEvent { struct InputEvent {
enum class Type { enum class Type {
NONE, NONE = 0,
BUTTON_ACTION, BUTTON_ACTION,
MOUSE_DELTA, MOUSE_DELTA,
MOUSE_POS, MOUSE_POS,
} type = Type::NONE; } type = Type::NONE;
enum class ButtonAction { enum class ButtonAction {
NONE, NONE = 0,
PRESS, PRESS,
RELEASE, RELEASE,
REPEAT, REPEAT,
@ -28,18 +28,6 @@ namespace tp {
}; };
class InputState { class InputState {
// Transitions from state to state is strict and defined by state order in the enum
// If posted event conflicts with that automata, we need to emulate intermediate states
enum State {
NONE, // Button is inactive
PRESSED, // Button is pressed
HOLD, // Button is still pressed
RELEASED, // Button is released
};
State mCurrentState;
State mPreviousState;
public: public:
InputState() = default; InputState() = default;
@ -49,6 +37,19 @@ namespace tp {
// true if event is no longer needed and false if we need to emulate some transitions further // true if event is no longer needed and false if we need to emulate some transitions further
bool isHandled(); bool isHandled();
public:
// Transitions from state to state is strict and defined by state order in the enum
// If posted event conflicts with that automata, we need to emulate intermediate states
enum State {
NONE = 0, // Button is inactive
PRESSED, // Button is pressed
HOLD, // Button is still pressed
RELEASED, // Button is released
};
State mCurrentState;
State mPreviousState;
}; };
// Assumes that user and event poster use different threads // Assumes that user and event poster use different threads
@ -62,32 +63,16 @@ namespace tp {
public: // Event Poster Interface public: // Event Poster Interface
// Record event // Record event
void postEvent(InputID inputID, InputEvent inputEvent) { void postEvent(InputID inputID, InputEvent inputEvent);
mMutex.lock();
mEventQueue.pushBack({ inputID, inputEvent });
mMutex.unlock();
}
public: // User interface public: // User interface
bool isEvents() { bool isEvents();
mMutex.lock(); void processEvent();
auto res = mEventQueue.length();
mMutex.unlock();
return res;
}
void processEvent() {
mMutex.lock();
mEventQueue.popFront();
mMutex.unlock();
}
const Vec2F& getPointer() const; const Vec2F& getPointer() const;
bool isPressed(InputID id) const; bool isPressed(InputID id) const;
bool isReleased(InputID id) const; bool isReleased(InputID id) const;
bool isDown(InputID id) const; bool isDown(InputID id) const;
halnf getScrollY() const; halnf getScrollY() const;
private: private:
@ -100,7 +85,7 @@ namespace tp {
Vec2F mPointer; Vec2F mPointer;
halnf pressure = 0; halnf pressure = 0;
InputState mInputStates[(int) InputID::LAST_KEY_CODE]; InputState mInputStates[(int) InputID::LAST_KEY_CODE]{};
}; };
} }

View file

@ -142,8 +142,6 @@ namespace tp {
MOUSE3 = 503, MOUSE3 = 503,
MOUSE4 = 504, MOUSE4 = 504,
MOUSE5 = 505, MOUSE5 = 505,
MOUSE_UP = 506,
MOUSE_DOWN = 507,
LAST_KEY_CODE = 508, LAST_KEY_CODE = 508,

View file

@ -6,7 +6,7 @@ file(GLOB HEADERS "./public/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/) target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Math Graphics Strings) target_link_libraries(${PROJECT_NAME} PUBLIC Math Graphics Imgui Strings)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing() enable_testing()
@ -18,6 +18,6 @@ add_test(NAME Test${PROJECT_NAME} COMMAND Test${PROJECT_NAME})
### -------------------------- Applications -------------------------- ### ### -------------------------- Applications -------------------------- ###
add_executable(ExampleGui examples/Entry.cpp) add_executable(ExampleGui examples/Entry.cpp)
target_link_libraries(ExampleGui ${PROJECT_NAME} Graphics Imgui Nanovg glfw ${GLEW_LIB}) target_link_libraries(ExampleGui ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(ExampleGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR}) target_include_directories(ExampleGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

View file

@ -7,9 +7,7 @@ using namespace tp;
class ExampleGUI : public Application { class ExampleGUI : public Application {
public: public:
ExampleGUI() { ExampleGUI() = default;
gGlobalGUIConfig = &mConfig;
}
void processFrame(EventHandler* eventHandler) override { void processFrame(EventHandler* eventHandler) override {
auto rec = RectF( { 0, 0 }, mWindow->getSize() ); auto rec = RectF( { 0, 0 }, mWindow->getSize() );
@ -21,7 +19,6 @@ public:
} }
private: private:
GlobalGUIConfig mConfig;
ComplexWidget<EventHandler, Canvas> mGui; ComplexWidget<EventHandler, Canvas> mGui;
}; };
@ -35,8 +32,13 @@ int main() {
} }
{ {
ExampleGUI gui; GlobalGUIConfig mConfig;
gui.run(); gGlobalGUIConfig = &mConfig;
{
ExampleGUI gui;
gui.run();
}
} }
binModule.deinitialize(); binModule.deinitialize();

View file

@ -22,7 +22,7 @@ namespace tp {
this->mArea.w = 30; this->mArea.w = 30;
this->mVisible = areaParent.isOverlap(aArea); this->mVisible = areaParent.isOverlap(aArea);
if (!this->mVisible) return; if (!this->mVisible) return;
mIsHover = aArea.isInside(events.getPos()); mIsHover = aArea.isInside(events.getPointer());
} }
void draw(Canvas& canvas) override { void draw(Canvas& canvas) override {
@ -62,7 +62,7 @@ namespace tp {
this->mArea.w = 50; this->mArea.w = 50;
this->mVisible = areaParent.isOverlap(aArea); this->mVisible = areaParent.isOverlap(aArea);
if (!this->mVisible) return; if (!this->mVisible) return;
mIsHover = aArea.isInside(events.getPos()); mIsHover = aArea.isInside(events.getPointer());
} }
void draw(Canvas& canvas) override { void draw(Canvas& canvas) override {
@ -181,7 +181,7 @@ namespace tp {
} }
public: public:
Buffer<MessageWidget<Events, Canvas>> mMessages; Buffer<MessageWidget<Events, Canvas>*> mMessages;
ScrollableWindow<Events, Canvas> mHistoryView; ScrollableWindow<Events, Canvas> mHistoryView;
TextInputWidget<Events, Canvas> mMessage; TextInputWidget<Events, Canvas> mMessage;
ButtonWidget<Events, Canvas> mSend; ButtonWidget<Events, Canvas> mSend;
@ -190,34 +190,34 @@ namespace tp {
template <typename Events, typename Canvas> template <typename Events, typename Canvas>
class ChattingWidget : public Widget<Events, Canvas> { class ChattingWidget : public Widget<Events, Canvas> {
public: public:
explicit ChattingWidget() { ChattingWidget() {
this->createConfig("Chatting"); this->createConfig("Chatting");
this->addColor("Back", "Background"); this->addColor("Back", "Background");
this->addValue("Padding", "Padding"); this->addValue("Padding", "Padding");
// todo : fetch code // todo : fetch code
mUsers.append(UserWidget<Events, Canvas>()); mUsers.append(new UserWidget<Events, Canvas>());
mUsers.append(UserWidget<Events, Canvas>()); mUsers.append(new UserWidget<Events, Canvas>());
mUsers.append(UserWidget<Events, Canvas>()); mUsers.append(new UserWidget<Events, Canvas>());
mUsers[0].mArea = { 0, 0, 100, 100 }; mUsers[0]->mArea = { 0, 0, 100, 100 };
mUsers[1].mArea = { 0, 0, 100, 100 }; mUsers[1]->mArea = { 0, 0, 100, 100 };
mUsers[2].mArea = { 0, 0, 100, 100 }; mUsers[2]->mArea = { 0, 0, 100, 100 };
for (auto message : mUsers) { for (auto message : mUsers) {
mSideView.mContents.append(&message.data()); mSideView.mContents.append(message.data());
} }
mActive.mMessages.append(MessageWidget<Events, Canvas>()); mActive.mMessages.append(new MessageWidget<Events, Canvas>());
mActive.mMessages.append(MessageWidget<Events, Canvas>()); mActive.mMessages.append(new MessageWidget<Events, Canvas>());
mActive.mMessages.append(MessageWidget<Events, Canvas>()); mActive.mMessages.append(new MessageWidget<Events, Canvas>());
mActive.mMessages[0].mArea = { 0, 0, 100, 100 }; mActive.mMessages[0]->mArea = { 0, 0, 100, 100 };
mActive.mMessages[1].mArea = { 0, 0, 100, 100 }; mActive.mMessages[1]->mArea = { 0, 0, 100, 100 };
mActive.mMessages[2].mArea = { 0, 0, 100, 100 }; mActive.mMessages[2]->mArea = { 0, 0, 100, 100 };
for (auto message : mActive.mMessages) { for (auto message : mActive.mMessages) {
mActive.mHistoryView.mContents.append(&message.data()); mActive.mHistoryView.mContents.append(message.data());
} }
} }
@ -240,7 +240,7 @@ namespace tp {
} }
public: public:
Buffer<UserWidget<Events, Canvas>> mUsers; Buffer<UserWidget<Events, Canvas>*> mUsers;
ScrollableWindow<Events, Canvas> mSideView; ScrollableWindow<Events, Canvas> mSideView;
ActiveChatWidget<Events, Canvas> mActive; ActiveChatWidget<Events, Canvas> mActive;
SplitView<Events, Canvas> mSplitView; SplitView<Events, Canvas> mSplitView;

View file

@ -29,6 +29,9 @@ namespace tp {
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override { void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
this->mArea = aArea; this->mArea = aArea;
this->mVisible = areaParent.isOverlap(aArea);
if (!this->mVisible) return;
mIsHover = false; mIsHover = false;
if (!areaParent.isOverlap(aArea)) { if (!areaParent.isOverlap(aArea)) {
@ -37,13 +40,13 @@ namespace tp {
return; return;
} }
mIsHover = aArea.isInside(events.getPos()); mIsHover = aArea.isInside(events.getPointer());
if (events.isPressed() && mIsHover) { if (events.isPressed(InputID::MOUSE1) && mIsHover) {
mIsPressed = true; mIsPressed = true;
} }
if (mIsPressed && mIsHover && events.isReleased()) { if (mIsPressed && mIsHover && events.isReleased(InputID::MOUSE1)) {
mIsReleased = true; mIsReleased = true;
mIsPressed = false; mIsPressed = false;
} }
@ -54,6 +57,8 @@ namespace tp {
} }
void draw(Canvas& canvas) override { void draw(Canvas& canvas) override {
if (!this->mVisible) return;
if (mIsPressed) { if (mIsPressed) {
canvas.rect(this->mArea, this->getColor("Pressed"), this->getValue("Rounding")); canvas.rect(this->mArea, this->getColor("Pressed"), this->getValue("Rounding"));
} else if (mIsHover) { } else if (mIsHover) {

View file

@ -15,9 +15,13 @@ namespace tp {
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override { void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
this->mArea = aArea; this->mArea = aArea;
this->mVisible = areaParent.isOverlap(aArea);
if (!this->mVisible) return;
} }
void draw(Canvas& canvas) override { void draw(Canvas& canvas) override {
if (!this->mVisible) return;
canvas.text( canvas.text(
mLabel.read(), mLabel.read(),
this->mArea, this->mArea,

View file

@ -22,9 +22,11 @@ namespace tp {
// takes whole area // takes whole area
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override { void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
this->mArea = aArea; this->mArea = aArea;
this->mVisible = areaParent.isOverlap(aArea);
if (!this->mVisible) return;
auto area = getHandle(); auto area = getHandle();
mHovered = getHandleHandle().isInside(events.getPos()); mHovered = getHandleHandle().isInside(events.getPointer());
if (mSizeFraction > 1.f) { if (mSizeFraction > 1.f) {
mPositionFraction = 0; mPositionFraction = 0;
@ -36,7 +38,7 @@ namespace tp {
return; return;
} }
if (events.getScrollY() != 0 && areaParent.isInside(events.getPos())) { if (events.getScrollY() != 0 && areaParent.isInside(events.getPointer())) {
auto offset = events.getScrollY() < 0 ? 1.0f : -1.0f; auto offset = events.getScrollY() < 0 ? 1.0f : -1.0f;
if (scrollInertia * offset > 0) { if (scrollInertia * offset > 0) {
scrollInertia += offset; scrollInertia += offset;
@ -53,14 +55,14 @@ namespace tp {
return; return;
} }
if (events.isPressed() && area.isInside(events.getPos())) { if (events.isPressed(InputID::MOUSE1) && area.isInside(events.getPointer())) {
mIsScrolling = true; mIsScrolling = true;
} else if (!events.isDown()) { } else if (events.isReleased(InputID::MOUSE1)) {
mIsScrolling = false; mIsScrolling = false;
} }
if (mIsScrolling) { if (mIsScrolling) {
tp::halnf pos = events.getPos().y; tp::halnf pos = events.getPointer().y;
pos = (pos - area.y - mSizeFraction * area.w / 2.f) / area.w; pos = (pos - area.y - mSizeFraction * area.w / 2.f) / area.w;
mPositionFraction = tp::clamp(pos, 0.f, 1.f - mSizeFraction); mPositionFraction = tp::clamp(pos, 0.f, 1.f - mSizeFraction);
} }
@ -69,6 +71,8 @@ namespace tp {
} }
void draw(Canvas& canvas) override { void draw(Canvas& canvas) override {
if (!this->mVisible) return;
auto area = getHandle(); auto area = getHandle();
if (mSizeFraction > 1.f) return; if (mSizeFraction > 1.f) return;
@ -142,7 +146,6 @@ namespace tp {
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override { void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
this->mArea = aArea; this->mArea = aArea;
this->mVisible = areaParent.isOverlap(aArea); this->mVisible = areaParent.isOverlap(aArea);
if (!this->mVisible) return; if (!this->mVisible) return;
updateContents(); updateContents();
@ -170,6 +173,7 @@ namespace tp {
void draw(Canvas& canvas) override { void draw(Canvas& canvas) override {
if (!this->mVisible) return; if (!this->mVisible) return;
mScroller.draw(canvas); mScroller.draw(canvas);
canvas.pushClamp(this->mArea); canvas.pushClamp(this->mArea);

View file

@ -17,8 +17,9 @@ namespace tp {
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override { void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
this->mArea = aArea; this->mArea = aArea;
this->mVisible = areaParent.isOverlap(aArea);
if (!areaParent.isOverlap(aArea)) { if (!this->mVisible) {
mResizeInProcess = false; mResizeInProcess = false;
return; return;
} }
@ -27,12 +28,12 @@ namespace tp {
if (events.isPressed(InputID::MOUSE1) && mIsHover) { if (events.isPressed(InputID::MOUSE1) && mIsHover) {
mResizeInProcess = true; mResizeInProcess = true;
} else if (!events.isDown()) { } else if (events.isReleased(InputID::MOUSE1)) {
mResizeInProcess = false; mResizeInProcess = false;
} }
if (mResizeInProcess) { if (mResizeInProcess) {
halnf pos = events.getPos().x; halnf pos = events.getPointer().x;
auto diff = pos - (this->mArea.x + mFactor * this->mArea.z); auto diff = pos - (this->mArea.x + mFactor * this->mArea.z);
mFactor += diff / this->mArea.z; mFactor += diff / this->mArea.z;
} }
@ -46,6 +47,8 @@ namespace tp {
// takes whole area // takes whole area
void draw(Canvas& canvas) override { void draw(Canvas& canvas) override {
if (!this->mVisible) return;
if (mResizeInProcess) canvas.rect(getHandle(), this->getColor("Resizing")); if (mResizeInProcess) canvas.rect(getHandle(), this->getColor("Resizing"));
else if (mIsHover) canvas.rect(getHandle(), this->getColor("Hovered")); else if (mIsHover) canvas.rect(getHandle(), this->getColor("Hovered"));
else canvas.rect(getHandle(), this->getColor("Handle")); else canvas.rect(getHandle(), this->getColor("Handle"));

View file

@ -20,6 +20,13 @@ namespace tp {
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override { void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) override {
this->mArea = aArea; this->mArea = aArea;
this->mVisible = areaParent.isOverlap(aArea);
if (!this->mVisible) return;
}
void draw(Canvas& canvas) override {
if (!this->mVisible) return;
nChanged = false; nChanged = false;
const auto col = this->getColor("Accent"); const auto col = this->getColor("Accent");
@ -58,10 +65,6 @@ namespace tp {
ImGui::PopStyleVar(3); ImGui::PopStyleVar(3);
} }
void draw(Canvas& canvas) override {
// canvas.rect(this->mArea, this->getColor("Base"));
}
enum { mMaxBufferSize = 512 }; enum { mMaxBufferSize = 512 };
char mBuff[mMaxBufferSize] = ""; char mBuff[mMaxBufferSize] = "";
bool nChanged = false; bool nChanged = false;