mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
feat: 光标移动到叠加层时释放捕获
This commit is contained in:
parent
b60ff120e6
commit
8dadcfe2d2
7 changed files with 160 additions and 97 deletions
|
|
@ -74,6 +74,10 @@ static POINT ScalingToSrc(POINT pt) noexcept {
|
|||
}
|
||||
|
||||
CursorManager::~CursorManager() noexcept {
|
||||
if (_isCapturedOnOverlay) {
|
||||
ReleaseCapture();
|
||||
}
|
||||
|
||||
if (_curClips != RECT{}) {
|
||||
ClipCursor(nullptr);
|
||||
}
|
||||
|
|
@ -105,9 +109,7 @@ void CursorManager::Update() noexcept {
|
|||
_hCursor = NULL;
|
||||
_cursorPos = { std::numeric_limits<LONG>::max(),std::numeric_limits<LONG>::max() };
|
||||
|
||||
const ScalingOptions& options = ScalingWindow::Get().Options();
|
||||
|
||||
if (!options.IsDrawCursor()) {
|
||||
if (!ScalingWindow::Get().Options().IsDrawCursor()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -118,31 +120,45 @@ void CursorManager::Update() noexcept {
|
|||
}
|
||||
}
|
||||
|
||||
CURSORINFO ci{ sizeof(CURSORINFO) };
|
||||
CURSORINFO ci{ .cbSize = sizeof(CURSORINFO) };
|
||||
if (!GetCursorInfo(&ci)) {
|
||||
Logger::Get().Win32Error("GetCursorPos 失败");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ci.hCursor && ci.flags != CURSOR_SHOWING) {
|
||||
if (!ci.hCursor || ci.flags != CURSOR_SHOWING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 不处于捕获状态置为 NULL
|
||||
_hCursor = _isUnderCapture ? ci.hCursor : NULL;
|
||||
_hCursor = ci.hCursor;
|
||||
// 不处于捕获状态则位于叠加层上
|
||||
_cursorPos = _isUnderCapture ? SrcToScaling(ci.ptScreenPos) : ci.ptScreenPos;
|
||||
const RECT& scalingRect = ScalingWindow::Get().WndRect();
|
||||
_cursorPos.x -= scalingRect.left;
|
||||
_cursorPos.y -= scalingRect.top;
|
||||
}
|
||||
|
||||
void CursorManager::OnCursorHoverOverlay() noexcept {
|
||||
_isOnOverlay = true;
|
||||
void CursorManager::IsCursorOnOverlay(bool value) noexcept {
|
||||
if (_isOnOverlay == value) {
|
||||
return;
|
||||
}
|
||||
_isOnOverlay = value;
|
||||
|
||||
_UpdateCursorClip();
|
||||
}
|
||||
|
||||
void CursorManager::OnCursorLeaveOverlay() noexcept {
|
||||
_isOnOverlay = false;
|
||||
void CursorManager::IsCursorCapturedOnOverlay(bool value) noexcept {
|
||||
if (_isCapturedOnOverlay == value) {
|
||||
return;
|
||||
}
|
||||
_isCapturedOnOverlay = value;
|
||||
|
||||
if (value) {
|
||||
SetCapture(ScalingWindow::Get().Handle());
|
||||
} else {
|
||||
ReleaseCapture();
|
||||
}
|
||||
|
||||
_UpdateCursorClip();
|
||||
}
|
||||
|
||||
|
|
@ -313,6 +329,13 @@ void CursorManager::_UpdateCursorClip() noexcept {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_isCapturedOnOverlay) {
|
||||
// 光标被叠加层捕获时将光标限制在输出区域内
|
||||
_curClips = destRect;
|
||||
ClipCursor(&destRect);
|
||||
return;
|
||||
}
|
||||
|
||||
const HWND hwndScaling = ScalingWindow::Get().Handle();
|
||||
const RECT scalingRect = ScalingWindow::Get().WndRect();
|
||||
const HWND hwndSrc = ScalingWindow::Get().HwndSrc();
|
||||
|
|
@ -350,10 +373,16 @@ void CursorManager::_UpdateCursorClip() noexcept {
|
|||
|
||||
_StopCapture(cursorPos);
|
||||
} else {
|
||||
// 判断源窗口是否被遮挡
|
||||
hwndCur = WindowFromPoint(hwndScaling, scalingRect, cursorPos, true);
|
||||
// 主窗口未被遮挡
|
||||
bool stopCapture = _isOnOverlay;
|
||||
|
||||
if (hwndCur != hwndSrc && (!IsChild(hwndSrc, hwndCur) || !((GetWindowStyle(hwndCur) & WS_CHILD)))) {
|
||||
if (!stopCapture) {
|
||||
// 判断源窗口是否被遮挡
|
||||
hwndCur = WindowFromPoint(hwndScaling, scalingRect, cursorPos, true);
|
||||
stopCapture = hwndCur != hwndSrc && (!IsChild(hwndSrc, hwndCur) || !((GetWindowStyle(hwndCur) & WS_CHILD)));
|
||||
}
|
||||
|
||||
if (stopCapture) {
|
||||
if (style | WS_EX_TRANSPARENT) {
|
||||
SetWindowLongPtr(hwndScaling, GWL_EXSTYLE, style & ~WS_EX_TRANSPARENT);
|
||||
}
|
||||
|
|
@ -394,25 +423,52 @@ void CursorManager::_UpdateCursorClip() noexcept {
|
|||
|
||||
if (!PtInRect(&srcRect, newCursorPos)) {
|
||||
// 跳过黑边
|
||||
POINT clampedPos = {
|
||||
std::clamp(cursorPos.x, destRect.left, destRect.right - 1),
|
||||
std::clamp(cursorPos.y, destRect.top, destRect.bottom - 1)
|
||||
};
|
||||
|
||||
if (WindowFromPoint(hwndScaling, scalingRect, clampedPos, false) == hwndScaling) {
|
||||
if (!(style & WS_EX_TRANSPARENT)) {
|
||||
SetWindowLongPtr(hwndScaling, GWL_EXSTYLE, style | WS_EX_TRANSPARENT);
|
||||
if (_isOnOverlay) {
|
||||
// 从内部移到外部
|
||||
// 此时有 UI 贴边
|
||||
if (newCursorPos.x >= srcRect.right) {
|
||||
cursorPos.x += scalingRect.right - destRect.right;
|
||||
} else if (newCursorPos.x < srcRect.left) {
|
||||
cursorPos.x -= destRect.left - scalingRect.left;
|
||||
}
|
||||
|
||||
_StartCapture(cursorPos);
|
||||
if (newCursorPos.y >= srcRect.bottom) {
|
||||
cursorPos.y += scalingRect.bottom - destRect.bottom;
|
||||
} else if (newCursorPos.y < srcRect.top) {
|
||||
cursorPos.y -= destRect.top - scalingRect.top;
|
||||
}
|
||||
|
||||
if (MonitorFromPoint(cursorPos, MONITOR_DEFAULTTONULL)) {
|
||||
SetCursorPos(cursorPos.x, cursorPos.y);
|
||||
} else {
|
||||
// 目标位置不存在屏幕,则将光标限制在输出区域内
|
||||
SetCursorPos(
|
||||
std::clamp(cursorPos.x, destRect.left, destRect.right - 1),
|
||||
std::clamp(cursorPos.y, destRect.top, destRect.bottom - 1)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 要跳跃的位置被遮挡
|
||||
if (style | WS_EX_TRANSPARENT) {
|
||||
SetWindowLongPtr(hwndScaling, GWL_EXSTYLE, style & ~WS_EX_TRANSPARENT);
|
||||
// 从外部移到内部
|
||||
const POINT clampedPos {
|
||||
std::clamp(cursorPos.x, destRect.left, destRect.right - 1),
|
||||
std::clamp(cursorPos.y, destRect.top, destRect.bottom - 1)
|
||||
};
|
||||
|
||||
if (WindowFromPoint(hwndScaling, scalingRect, clampedPos, false) == hwndScaling) {
|
||||
if (!(style & WS_EX_TRANSPARENT)) {
|
||||
SetWindowLongPtr(hwndScaling, GWL_EXSTYLE, style | WS_EX_TRANSPARENT);
|
||||
}
|
||||
|
||||
_StartCapture(cursorPos);
|
||||
} else {
|
||||
// 要跳跃的位置被遮挡
|
||||
if (style | WS_EX_TRANSPARENT) {
|
||||
SetWindowLongPtr(hwndScaling, GWL_EXSTYLE, style & ~WS_EX_TRANSPARENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool startCapture = true;
|
||||
bool startCapture = !_isOnOverlay;
|
||||
|
||||
if (startCapture) {
|
||||
// 判断源窗口是否被遮挡
|
||||
|
|
@ -439,7 +495,7 @@ void CursorManager::_UpdateCursorClip() noexcept {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!false && !_isUnderCapture) {
|
||||
if (!_isUnderCapture && !_isOnOverlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -447,32 +503,32 @@ void CursorManager::_UpdateCursorClip() noexcept {
|
|||
// 处理屏幕之间存在间隙的情况。解决办法是 _StopCapture 只在目标位置存在屏幕时才取消捕获,
|
||||
// 当光标试图移动到间隙中时将被挡住。如果光标的速度足以跨越间隙,则它依然可以在屏幕间移动。
|
||||
::GetCursorPos(&cursorPos);
|
||||
POINT hostPos = false ? cursorPos : SrcToScaling(cursorPos);
|
||||
POINT hostPos = _isOnOverlay ? cursorPos : SrcToScaling(cursorPos);
|
||||
|
||||
RECT clips{ LONG_MIN, LONG_MIN, LONG_MAX, LONG_MAX };
|
||||
|
||||
// left
|
||||
RECT rect{ LONG_MIN, hostPos.y, scalingRect.left, hostPos.y + 1 };
|
||||
if (!MonitorFromRect(&rect, MONITOR_DEFAULTTONULL)) {
|
||||
clips.left = false ? destRect.left : srcRect.left;
|
||||
clips.left = _isOnOverlay ? destRect.left : srcRect.left;
|
||||
}
|
||||
|
||||
// top
|
||||
rect = { hostPos.x, LONG_MIN, hostPos.x + 1,scalingRect.top };
|
||||
if (!MonitorFromRect(&rect, MONITOR_DEFAULTTONULL)) {
|
||||
clips.top = false ? destRect.top : srcRect.top;
|
||||
clips.top = _isOnOverlay ? destRect.top : srcRect.top;
|
||||
}
|
||||
|
||||
// right
|
||||
rect = { scalingRect.right, hostPos.y, LONG_MAX, hostPos.y + 1 };
|
||||
if (!MonitorFromRect(&rect, MONITOR_DEFAULTTONULL)) {
|
||||
clips.right = false ? destRect.right : srcRect.right;
|
||||
clips.right = _isOnOverlay ? destRect.right : srcRect.right;
|
||||
}
|
||||
|
||||
// bottom
|
||||
rect = { hostPos.x, scalingRect.bottom, hostPos.x + 1, LONG_MAX };
|
||||
if (!MonitorFromRect(&rect, MONITOR_DEFAULTTONULL)) {
|
||||
clips.bottom = false ? destRect.bottom : srcRect.bottom;
|
||||
clips.bottom = _isOnOverlay ? destRect.bottom : srcRect.bottom;
|
||||
}
|
||||
|
||||
if (clips != _curClips) {
|
||||
|
|
@ -486,6 +542,8 @@ void CursorManager::_StartCapture(POINT cursorPos) noexcept {
|
|||
return;
|
||||
}
|
||||
|
||||
OutputDebugString(L"start");
|
||||
|
||||
const Renderer& renderer = ScalingWindow::Get().Renderer();
|
||||
const RECT& srcRect = renderer.SrcRect();
|
||||
const RECT& destRect = renderer.DestRect();
|
||||
|
|
@ -527,6 +585,8 @@ void CursorManager::_StopCapture(POINT cursorPos, bool onDestroy) noexcept {
|
|||
return;
|
||||
}
|
||||
|
||||
OutputDebugString(L"stop");
|
||||
|
||||
if (_curClips != RECT{}) {
|
||||
_curClips = {};
|
||||
ClipCursor(nullptr);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,15 @@ public:
|
|||
return _cursorPos;
|
||||
}
|
||||
|
||||
void OnCursorHoverOverlay() noexcept;
|
||||
bool IsCursorOnOverlay() const noexcept {
|
||||
return _isOnOverlay;
|
||||
}
|
||||
void IsCursorOnOverlay(bool value) noexcept;
|
||||
|
||||
void OnCursorLeaveOverlay() noexcept;
|
||||
bool IsCursorCapturedOnOverlay() const noexcept {
|
||||
return _isCapturedOnOverlay;
|
||||
}
|
||||
void IsCursorCapturedOnOverlay(bool value) noexcept;
|
||||
|
||||
private:
|
||||
void _ShowSystemCursor(bool show);
|
||||
|
|
@ -48,6 +54,7 @@ private:
|
|||
bool _isOnScalingWindow = false;
|
||||
|
||||
bool _isOnOverlay = false;
|
||||
bool _isCapturedOnOverlay = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,8 +130,6 @@ void ImGuiImpl::NewFrame() noexcept {
|
|||
io.AddKeyEvent(ImGuiKey_Enter, false);
|
||||
}
|
||||
|
||||
const bool originWantCaptureMouse = io.WantCaptureMouse;
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
// 将所有 ImGUI 窗口限制在视口内
|
||||
|
|
@ -157,17 +155,7 @@ void ImGuiImpl::NewFrame() noexcept {
|
|||
ImGui::SetWindowPos(window, pos);
|
||||
}
|
||||
|
||||
CursorManager& cm = ScalingWindow::Get().CursorManager();
|
||||
|
||||
if (io.WantCaptureMouse) {
|
||||
if (!originWantCaptureMouse) {
|
||||
cm.OnCursorHoverOverlay();
|
||||
}
|
||||
} else {
|
||||
if (originWantCaptureMouse) {
|
||||
cm.OnCursorLeaveOverlay();
|
||||
}
|
||||
}
|
||||
ScalingWindow::Get().CursorManager().IsCursorOnOverlay(io.WantCaptureMouse);
|
||||
}
|
||||
|
||||
void ImGuiImpl::Draw() noexcept {
|
||||
|
|
@ -256,42 +244,35 @@ void ImGuiImpl::_UpdateMousePos() noexcept {
|
|||
}
|
||||
|
||||
void ImGuiImpl::ClearStates() noexcept {
|
||||
/*ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
std::fill(std::begin(io.MouseDown), std::end(io.MouseDown), false);
|
||||
|
||||
auto& cm = ScalingWindow::Get().CursorManager();
|
||||
if (cm.IsCursorCapturedOnOverlay()) {
|
||||
if (GetCapture() == MagApp::Get().GetHwndHost()) {
|
||||
ReleaseCapture();
|
||||
}
|
||||
cm.OnCursorReleasedOnOverlay();
|
||||
}
|
||||
|
||||
if (cm.IsCursorOnOverlay()) {
|
||||
cm.OnCursorLeaveOverlay();
|
||||
}
|
||||
CursorManager& cursorManager = ScalingWindow::Get().CursorManager();
|
||||
cursorManager.IsCursorCapturedOnOverlay(false);
|
||||
cursorManager.IsCursorOnOverlay(false);
|
||||
|
||||
// 更新状态
|
||||
ImGui::BeginFrame();
|
||||
ImGui::NewFrame();
|
||||
ImGui::EndFrame();
|
||||
|
||||
if (io.WantCaptureMouse) {
|
||||
// 拖拽时隐藏 UI 需渲染两帧才能重置 WantCaptureMouse
|
||||
ImGui::BeginFrame();
|
||||
ImGui::NewFrame();
|
||||
ImGui::EndFrame();
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiImpl::MessageHandler(UINT msg, WPARAM wParam, LPARAM /*lParam*/) noexcept {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
/*if (!io.WantCaptureMouse) {
|
||||
if (msg == WM_LBUTTONDOWN && MagApp::Get().GetOptions().Is3DGameMode()) {
|
||||
MagApp::Get().GetRenderer().SetUIVisibility(false);
|
||||
if (!io.WantCaptureMouse) {
|
||||
// 3D 游戏模式下显示叠加层会使缩放窗口不透明,这时点击非叠加层区域应关闭叠加层
|
||||
if (msg == WM_LBUTTONDOWN && ScalingWindow::Get().Options().Is3DGameMode()) {
|
||||
ScalingWindow::Get().Renderer().IsOverlayVisible(false);
|
||||
}
|
||||
return std::nullopt;
|
||||
}*/
|
||||
return;
|
||||
}
|
||||
|
||||
// 缩放窗口不会收到双击消息
|
||||
switch (msg) {
|
||||
|
|
@ -299,10 +280,7 @@ void ImGuiImpl::MessageHandler(UINT msg, WPARAM wParam, LPARAM /*lParam*/) noexc
|
|||
case WM_RBUTTONDOWN:
|
||||
{
|
||||
if (!ImGui::IsAnyMouseDown()) {
|
||||
if (!GetCapture()) {
|
||||
SetCapture(ScalingWindow::Get().Handle());
|
||||
}
|
||||
//MagApp::Get().GetCursorManager().OnCursorCapturedOnOverlay();
|
||||
ScalingWindow::Get().CursorManager().IsCursorCapturedOnOverlay(true);
|
||||
}
|
||||
|
||||
io.MouseDown[msg == WM_LBUTTONDOWN ? 0 : 1] = true;
|
||||
|
|
@ -314,10 +292,7 @@ void ImGuiImpl::MessageHandler(UINT msg, WPARAM wParam, LPARAM /*lParam*/) noexc
|
|||
io.MouseDown[msg == WM_LBUTTONUP ? 0 : 1] = false;
|
||||
|
||||
if (!ImGui::IsAnyMouseDown()) {
|
||||
if (GetCapture() == ScalingWindow::Get().Handle()) {
|
||||
ReleaseCapture();
|
||||
}
|
||||
//MagApp::Get().GetCursorManager().OnCursorReleasedOnOverlay();
|
||||
ScalingWindow::Get().CursorManager().IsCursorCapturedOnOverlay(false);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -88,11 +88,21 @@ void OverlayDrawer::SetUIVisibility(bool value) noexcept {
|
|||
}
|
||||
_isUIVisiable = value;
|
||||
|
||||
if (value) {
|
||||
Logger::Get().Info("已开启叠加层");
|
||||
} else {
|
||||
if (!ScalingWindow::Get().Options().IsShowFPS()) {
|
||||
_imguiImpl.ClearStates();
|
||||
}
|
||||
|
||||
Logger::Get().Info("已关闭叠加层");
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayDrawer::MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noexcept {
|
||||
_imguiImpl.MessageHandler(msg, wParam, lParam);
|
||||
if (_isUIVisiable || ScalingWindow::Get().Options().IsShowFPS()) {
|
||||
_imguiImpl.MessageHandler(msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
static const std::wstring& GetAppLanguage() noexcept {
|
||||
|
|
|
|||
|
|
@ -293,20 +293,22 @@ void Renderer::Render() noexcept {
|
|||
_FrontendRender();
|
||||
}
|
||||
|
||||
void Renderer::ToggleOverlay() noexcept {
|
||||
if (!_overlayDrawer) {
|
||||
_overlayDrawer = std::make_unique<OverlayDrawer>();
|
||||
if (!_overlayDrawer->Initialize(&_frontendResources)) {
|
||||
_overlayDrawer.reset();
|
||||
Logger::Get().Error("初始化 OverlayDrawer 失败");
|
||||
return;
|
||||
void Renderer::IsOverlayVisible(bool value) noexcept {
|
||||
if (value) {
|
||||
if (!_overlayDrawer) {
|
||||
_overlayDrawer = std::make_unique<OverlayDrawer>();
|
||||
if (!_overlayDrawer->Initialize(&_frontendResources)) {
|
||||
_overlayDrawer.reset();
|
||||
Logger::Get().Error("初始化 OverlayDrawer 失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_overlayDrawer->IsUIVisiable()) {
|
||||
_overlayDrawer->SetUIVisibility(false);
|
||||
} else {
|
||||
|
||||
_overlayDrawer->SetUIVisibility(true);
|
||||
} else {
|
||||
if (_overlayDrawer) {
|
||||
_overlayDrawer->SetUIVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 立即渲染一帧
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ public:
|
|||
|
||||
void Render() noexcept;
|
||||
|
||||
void ToggleOverlay() noexcept;
|
||||
|
||||
void IsOverlayVisible(bool value) noexcept;
|
||||
bool IsOverlayVisible() noexcept;
|
||||
|
||||
const RECT& SrcRect() const noexcept {
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ void ScalingWindow::Render() noexcept {
|
|||
}
|
||||
|
||||
void ScalingWindow::ToggleOverlay() noexcept {
|
||||
_renderer->ToggleOverlay();
|
||||
_renderer->IsOverlayVisible(!_renderer->IsOverlayVisible());
|
||||
}
|
||||
|
||||
LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) noexcept {
|
||||
|
|
@ -219,6 +219,7 @@ LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) n
|
|||
// 在以下情况下会收到光标消息:
|
||||
// 1、未捕获光标且缩放后的位置未被遮挡而缩放前的位置被遮挡
|
||||
// 2、光标位于叠加层上
|
||||
// 这时鼠标点击将激活源窗口
|
||||
const HWND hwndForground = GetForegroundWindow();
|
||||
if (hwndForground != _hwndSrc) {
|
||||
if (!Win32Utils::SetForegroundWindow(_hwndSrc)) {
|
||||
|
|
@ -233,12 +234,21 @@ LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) n
|
|||
prevTimePoint = now;
|
||||
|
||||
// 模拟按键关闭开始菜单
|
||||
INPUT inputs[4]{};
|
||||
inputs[0].type = INPUT_KEYBOARD;
|
||||
inputs[0].ki.wVk = VK_LWIN;
|
||||
inputs[1].type = INPUT_KEYBOARD;
|
||||
inputs[1].ki.wVk = VK_LWIN;
|
||||
inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
INPUT inputs[]{
|
||||
INPUT{
|
||||
.type = INPUT_KEYBOARD,
|
||||
.ki = KEYBDINPUT{
|
||||
.wVk = VK_LWIN
|
||||
}
|
||||
},
|
||||
INPUT{
|
||||
.type = INPUT_KEYBOARD,
|
||||
.ki = KEYBDINPUT{
|
||||
.wVk = VK_LWIN,
|
||||
.dwFlags = KEYEVENTF_KEYUP
|
||||
}
|
||||
}
|
||||
};
|
||||
SendInput((UINT)std::size(inputs), inputs, sizeof(INPUT));
|
||||
|
||||
// 等待系统处理
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue