Files
SimpleRemoter/server/2015Remote/PreviewTipWnd.h
yuanyuanxiang 566f5b8d42 Feature: screen preview thumbnail on host double-click
Server sends COMMAND_SCREEN_PREVIEW_REQ when user double-clicks an
active (non-Locked/Inactive) host that advertises CLIENT_CAP_SCREEN_PREVIEW.
Client BitBlts primary screen, encodes to JPEG via GDI+ and replies. The
existing STATIC tooltip is replaced with a self-drawn CPreviewTipWnd
showing the thumbnail above the host info text, with wide-character
rendering so the popup also works on non-Chinese servers.

- Quality tiers reuse QualityProfile pattern: PreviewProfile + 6 levels
  driven by GetTargetQualityLevel (FRP-aware), with 4K/ultrawide auto
  upscale on Ultra/High tiers up to min(screenWidth/4, 1280).
- Client limits to 1 in-flight capture via atomic counter to defend
  against flood/DoS; Send2Server is already mutex-serialized.
- Server validates responses by reqId only (single in-flight tip);
  4s arrival timeout marks "preview unavailable" without blocking the
  text fallback path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:17:28 +02:00

58 lines
2.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// PreviewTipWnd.h
// 双击在线主机时弹出的浮窗:上方 JPEG 缩略图,下方主机信息文本。
// 无图片时显示"加载预览…"占位,供 OnListClick 即时弹窗、收到响应后再 SetImage 重画。
#pragma once
#include <afxwin.h>
#include <memory>
#include <vector>
namespace Gdiplus { class Bitmap; }
class CPreviewTipWnd : public CWnd
{
public:
CPreviewTipWnd();
virtual ~CPreviewTipWnd();
// 创建浮窗。
// anchor 屏幕坐标,浮窗左上角
// text 下方显示的主机详情文本(宽字符,确保跨语言系统正确渲染)
// imageReserveW 上方图像区域预留宽度(即将到来的预览最大宽度,仅作初始布局)
// 为 0 表示不预留 — 与老 STATIC 路径行为一致(仅文本)
BOOL Create(CWnd* pParent, CPoint anchor, const CStringW& text, int imageReserveW);
// 收到 JPEG 后调用:解码并重画。线程安全前提是只在主 UI 线程调用。
void SetImageFromJpeg(const BYTE* data, size_t bytes);
// 标记预览不可用(请求超时 / 客户端报错)。
void MarkPreviewUnavailable();
WORD GetReqId() const { return m_reqId; }
void SetReqId(WORD id) { m_reqId = id; }
protected:
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
DECLARE_MESSAGE_MAP()
private:
void RecalcLayoutAndResize();
void DrawImageArea(CDC& dc, const CRect& rc);
void DrawTextArea(CDC& dc, const CRect& rc);
CStringW m_text;
int m_imageReserveW = 0; // 预留图像宽度(图像未到达时占位)
int m_imageReserveH = 0; // 预留图像高度(按 16:9
int m_imageDrawW = 0; // 实际图像绘制宽度
int m_imageDrawH = 0; // 实际图像绘制高度
int m_textW = 0;
int m_textH = 0;
bool m_hasImage = false;
bool m_unavailable = false;
std::unique_ptr<Gdiplus::Bitmap> m_image;
CFont m_font;
WORD m_reqId = 0;
};