Zhl 锁屏软件

· · 科技·工程

重磅推出!带有人脸识别的锁屏软件。

你可曾因出去上厕所惨遭机房惨案?你可曾因密码被别人偷看而 AKIOI?你可曾在一顿饭的功夫做出 +\infty 道黑题?

::::info[不可抗因素背景]

这是一款开源、免费且实用的工具,支持密码与刷脸双重识别,同时提供背景自定义及透明设计,并可在锁屏界面上显示时间。有需要的朋友,欢迎线下领取。

Github 地址。

注意:face_detection_yunet_2023mar.onnx 和 face_recognition_sface_2021dec.onnx 这两个库需要自己下,文件有点大,放不进 Github。

制作耗时不知道多少小时,点个赞吧~。

不知道是不是洛谷首发。

项目借助 DeepSeek 生成。

Tips

1. get.py – 人脸照片采集器

2. recognize.py – 人脸识别与活体检测(点头)

3. main.py – 配置界面(系统托盘程序)

4. lockscreen.py – 锁屏执行程序

5. kill.cpp – 解锁辅助程序

6. start.cpp – 全局热键启动器

整体工作流

  1. 管理员运行 start.exe(后台热键监听)。
  2. 按 Win + A → 启动锁屏(lockscreen.exe)和识别程序(recognize.exe)。
  3. 锁屏界面出现,鼠标键盘被拦截;识别程序后台等待鼠标移动。
  4. 用户移动鼠标 → 识别程序打开摄像头,要求点头 + 人脸比对。
  5. 识别成功 → 自动终止 lockscreen.exe 并切换输入法,完成解锁。
  6. 若识别失败,30 秒后自动关闭摄像头,继续等待下一次鼠标移动。

目前暂时没放编译好的文件,下面是代码,可以随便看看,等我找到合适的云盘就把编译好的 exe 放进去。

下面的代码可供爱好者参考。

::::info[get.py]

import cv2
import os
import time
import sys

save_dir = "ref_images"
os.makedirs(save_dir, exist_ok=True)

cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not cap.isOpened():
    print("[ERROR] 无法打开摄像头")
    sys.exit(1)

# ------------------------ 加载 YuNet 人脸检测模型 ------------------------
YUNET_MODEL = "model/face_detection_yunet_2023mar.onnx"
if not os.path.exists(YUNET_MODEL):
    print(f"[ERROR] 未找到 YuNet 模型: {YUNET_MODEL}")
    print("请确保 model/face_detection_yunet_2023mar.onnx 存在")
    sys.exit(1)

detector = cv2.FaceDetectorYN.create(YUNET_MODEL, "", (320, 320))
print("[INFO] YuNet 人脸检测器加载成功")

print("=" * 50)
print("【智能训练照片采集器】")
print("功能:自动检测人脸并保存,支持手动/自动抓拍")
print("操作:")
print("  - 保持人脸在绿色框内")
print("  - 按 SPACE 手动抓拍")
print("  - 自动模式:每2秒保存一张(需检测到人脸)")
print("  - 按 ESC 退出")
print("提示:请采集不同微角度(左/右偏<30°)、不同表情(微笑/严肃)")
print("      确保光线均匀,面部清晰,背景简单")
print("=" * 50)

count = 0
last_cap_time = 0

while True:
    ret, frame = cap.read()
    if not ret:
        continue

    h, w = frame.shape[:2]
    detector.setInputSize((w, h))
    result = detector.detect(frame)

    faces = []
    if result[1] is not None:
        # 将 YuNet 输出的 face 转成 (x, y, w, h) 矩形框
        for face in result[1]:
            x, y, width, height = face[0:4].astype(int)
            faces.append((x, y, width, height))
            cv2.rectangle(frame, (x, y), (x+width, y+height), (0, 255, 0), 2)

    # 显示信息
    cv2.putText(frame, f"Saved: {count}", (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
    cv2.putText(frame, "SPACE: save  |  ESC: quit", (10, 65),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)

    cv2.imshow("Train Image Collector", frame)

    key = cv2.waitKey(1) & 0xFF
    now = time.time()

    if len(faces) > 0:
        # 手动抓拍或自动抓拍
        if key == 32 or (now - last_cap_time >= 2):
            # 取面积最大的人脸
            x, y, w, h = max(faces, key=lambda r: r[2]*r[3])
            face_img = frame[y:y+h, x:x+w]
            # 保存
            filename = os.path.join(save_dir, f"me_{count:04d}.jpg")
            cv2.imwrite(filename, face_img)
            print(f"已保存: {filename}")
            count += 1
            last_cap_time = now

    if key == 27:  # ESC
        break

cap.release()
cv2.destroyAllWindows()
print(f"\n共采集 {count} 张照片,保存在 '{save_dir}' 文件夹。")
print("现在请重新运行识别程序。")

::::

::::info[recognize.py]

import cv2
import numpy as np
import time
import os
import sys
import subprocess
import ctypes
from ctypes import wintypes
from collections import deque

# ------------------------ 模型路径 ------------------------
YUNET_MODEL = "model/face_detection_yunet_2023mar.onnx"
SFACE_MODEL = "model/face_recognition_sface_2021dec.onnx"

if not os.path.exists(YUNET_MODEL):
    print(f"[ERROR] 缺少 Yunet 模型: {YUNET_MODEL}")
    sys.exit(1)
if not os.path.exists(SFACE_MODEL):
    print(f"[ERROR] 缺少 SFace 模型: {SFACE_MODEL}")
    sys.exit(1)

# ------------------------ 初始化检测器与特征提取器 ------------------------
detector = cv2.FaceDetectorYN.create(YUNET_MODEL, "", (320, 320))
feature_extractor = cv2.FaceRecognizerSF.create(SFACE_MODEL, "")
print("[INFO] YuNet 检测器 + SFace 特征模型加载成功")

# ------------------------ 人脸对齐函数 ------------------------
def align_face(img, face):
    std_points = np.array([
        [30.2946, 51.6963],   # 左眼
        [65.5318, 51.6963],   # 右眼
        [48.0252, 71.7366],   # 鼻尖
        [33.5493, 92.3655],   # 左嘴角
        [62.7299, 92.3655]    # 右嘴角
    ], dtype=np.float32)

    landmarks = face[4:14].reshape(5, 2).astype(np.float32)
    src_points = np.array([
        landmarks[0],   # 左眼
        landmarks[1],   # 右眼
        landmarks[2],   # 鼻尖
        landmarks[4],   # 左嘴角
        landmarks[3]    # 右嘴角
    ])

    M, _ = cv2.estimateAffine2D(src_points, std_points)
    aligned = cv2.warpAffine(img, M, (112, 112))
    return aligned

# ------------------------ 加载参考人脸特征 ------------------------
def get_ref_feat():
    features = []
    if os.path.exists("my_photo.jpg"):
        img = cv2.imread("my_photo.jpg")
        if img is not None:
            h, w = img.shape[:2]
            detector.setInputSize((w, h))
            result = detector.detect(img)
            if result[1] is not None and result[1].shape[0] > 0:
                face = result[1][0]
                aligned = align_face(img, face)
                feat = feature_extractor.feature(aligned).ravel()
                features.append(feat)
                print("[INFO] 已添加 my_photo.jpg 特征")
    ref_dir = "ref_images"
    if os.path.exists(ref_dir):
        for f in sorted(os.listdir(ref_dir)):
            if f.lower().endswith(('.jpg', '.jpeg', '.png')):
                img_path = os.path.join(ref_dir, f)
                img = cv2.imread(img_path)
                if img is None: continue
                h, w = img.shape[:2]
                detector.setInputSize((w, h))
                result = detector.detect(img)
                if result[1] is not None and result[1].shape[0] > 0:
                    face = result[1][0]
                    aligned = align_face(img, face)
                    feat = feature_extractor.feature(aligned).ravel()
                    features.append(feat)
    if not features:
        return None
    avg_feat = np.mean(features, axis=0)
    norm = np.linalg.norm(avg_feat)
    if norm > 0:
        avg_feat = avg_feat / norm
    avg_feat = avg_feat.reshape(1, -1)
    print(f"[INFO] 使用 {len(features)} 张照片的平均特征")
    return avg_feat

ref_feat = get_ref_feat()
if ref_feat is None:
    print("[ERROR] 未找到参考人脸")
    sys.exit(1)

THRESHOLD = 0.7

# ------------------------ 点头检测参数 ------------------------
NOD_WINDOW_SEC = 2.0
NOD_DIFF_THRESH = 10.0
ASPECT_RATIO_CHANGE_MAX = 0.1

# ------------------------ 鼠标移动检测(低CPU轮询)-----------------------
user32 = ctypes.windll.user32
point = wintypes.POINT()
last_x, last_y = -1, -1

def mouse_moved():
    global last_x, last_y
    user32.GetCursorPos(ctypes.byref(point))
    if last_x == -1 and last_y == -1:
        last_x, last_y = point.x, point.y
        return False
    moved = (point.x != last_x) or (point.y != last_y)
    last_x, last_y = point.x, point.y
    return moved

# ------------------------ 执行 kill.exe ------------------------
def run_kill_exe():
    # 获取当前脚本所在目录
    base_dir = os.path.dirname(os.path.abspath(__file__))
    kill_path = os.path.join(base_dir, "kill.exe")
    if not os.path.exists(kill_path):
        kill_path = "kill.exe"   # 尝试当前路径
    try:
        subprocess.run(kill_path, check=False, shell=False)
        print("[INFO] kill.exe 已执行")
        return True
    except Exception as e:
        print(f"[ERROR] 执行 kill.exe 失败: {e}")
        return False

# ------------------------ 输入法切换 ------------------------
def switch_to_sogou_or_ms_pinyin():
    """
    优先切换至搜狗输入法,如果不存在则切换至微软拼音
    """
    def enum_keyboard_layouts():
        num = user32.GetKeyboardLayoutList(0, None)
        if num == 0:
            return []
        hkls = (wintypes.HKL * num)()
        user32.GetKeyboardLayoutList(num, hkls)
        return list(hkls)

    layouts = enum_keyboard_layouts()
    for hkl in layouts:
        name_buf = ctypes.create_unicode_buffer(9)
        if user32.GetKeyboardLayoutNameW(name_buf):
            if "sogou" in name_buf.value.lower():
                user32.ActivateKeyboardLayout(hkl, 0)
                print("[INFO] 已切换至搜狗输入法")
                return True

    # 没有搜狗,切换到微软拼音
    ms_hkl = user32.LoadKeyboardLayoutW("00000804", 1)
    if ms_hkl:
        user32.ActivateKeyboardLayout(ms_hkl, 0)
        print("[INFO] 已切换至微软拼音输入法")
        return True
    else:
        print("[WARN] 无法切换输入法")
        return False

# ------------------------ 30秒识别窗口 ------------------------
def run_recognition_window():
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    if not cap.isOpened():
        print("[ERROR] 无法打开摄像头")
        return False

    start_time = time.time()
    detect_end_time = start_time + 30
    print(f"[INFO] 识别窗口已开启,持续30秒(至 {time.strftime('%H:%M:%S', time.localtime(detect_end_time))})")
    print("[INFO] 请面对摄像头,做出点头动作(低头再抬头)")

    history = deque(maxlen=300)
    verify_frames_left = 0

    try:
        while time.time() < detect_end_time:
            ret, frame = cap.read()
            if not ret:
                time.sleep(0.05)
                continue

            current_time = time.time()
            h, w = frame.shape[:2]
            detector.setInputSize((w, h))
            _, faces = detector.detect(frame)

            pitch = None
            aspect_ratio = None
            face = None
            if faces is not None:
                face = faces[0]
                bbox = face[:4].astype(int)
                x, y, w_face, h_face = bbox
                aspect_ratio = w_face / h_face
                pitch = face[5]
                history.append((current_time, pitch, aspect_ratio))

            while history and history[0][0] < current_time - NOD_WINDOW_SEC:
                history.popleft()

            # 点头检测
            if verify_frames_left == 0 and len(history) >= 5:
                pitches = [p for _, p, _ in history]
                aspects = [a for _, _, a in history]
                pitch_diff = max(pitches) - min(pitches)
                aspect_change = max(aspects) - min(aspects)
                if pitch_diff >= NOD_DIFF_THRESH and aspect_change <= ASPECT_RATIO_CHANGE_MAX:
                    print(f"[{time.strftime('%H:%M:%S')}] 检测到点头 (pitch差值={pitch_diff:.1f})")
                    verify_frames_left = 4

            # 验证阶段
            if verify_frames_left > 0 and face is not None:
                aligned = align_face(frame, face)
                feat = feature_extractor.feature(aligned)
                sim = feature_extractor.match(ref_feat, feat, cv2.FaceRecognizerSF_FR_COSINE)
                print(f"[{time.strftime('%H:%M:%S')}] 验证剩余{verify_frames_left}帧,相似度={sim:.3f}")
                if sim >= THRESHOLD:
                    print(f"[{time.strftime('%H:%M:%S')}] 识别成功!相似度 {sim:.3f} ≥ 0.7")
                    return True
                verify_frames_left -= 1
                if verify_frames_left == 0:
                    print("[INFO] 4帧验证结束,未达标,继续等待点头...")
            elif verify_frames_left > 0 and face is None:
                print(f"[{time.strftime('%H:%M:%S')}] 验证帧剩余{verify_frames_left},未检测到人脸,跳过")
                verify_frames_left -= 1

            time.sleep(0.01)
    finally:
        cap.release()
    return False

# ------------------------ 主循环 ------------------------
def main():
    print("[INFO] 程序已启动,移动鼠标将触发30秒人脸识别窗口")
    print("[INFO] 识别成功后先执行 kill.exe,再切换输入法,然后退出")
    print("[INFO] 按 Ctrl+C 退出程序")

    while True:
        try:
            if mouse_moved():
                print(f"[{time.strftime('%H:%M:%S')}] 检测到鼠标移动,启动识别窗口")
                success = run_recognition_window()
                if success:
                    # 1. 执行 kill.exe
                    run_kill_exe()
                    # 2. 切换输入法
                    switch_to_sogou_or_ms_pinyin()
                    print("[INFO] 任务完成,程序退出")
                    break
                else:
                    print(f"[{time.strftime('%H:%M:%S')}] 30秒内未识别到目标人脸,回到空闲状态,等待下次鼠标移动")
            else:
                time.sleep(0.05)
        except Exception as e:
            print(f"[ERROR] 主循环异常: {e}")
            time.sleep(0.5)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n[INFO] 用户中断,程序退出")
    finally:
        cv2.destroyAllWindows()

::::

::::info[main.py]

# config.py - 最终稳定版 | 托盘图标 | 4:3 | 自动配色 | @Zhl2010
import os
import sys
import json
import hashlib
import subprocess
import threading
import customtkinter as ctk
from tkinter import filedialog
from PIL import Image, ImageDraw

# 确保 pystray 已安装
try:
    import pystray
except ImportError:
    print("请先安装 pystray: pip install pystray")
    sys.exit(1)

# ------------------------------- 路径与目录 -------------------------------
LOG_DIR = "log"
PASSWORD_FILE = os.path.join(LOG_DIR, "pass")
SETTINGS_FILE = os.path.join(LOG_DIR, "settings.json")
ICON_FILE = os.path.join(LOG_DIR, "icon.png")

if not os.path.exists(LOG_DIR):
    os.makedirs(LOG_DIR)

# ------------------------------- 密码管理器 -------------------------------
class PasswordManager:
    PASSWORD_FILE = PASSWORD_FILE
    SALT = b"lockscreen_secure_salt"

    @classmethod
    def _hash(cls, pwd):
        h = hashlib.sha256()
        h.update(cls.SALT)
        h.update(pwd.encode('utf-8'))
        return h.hexdigest()

    @classmethod
    def initialize(cls):
        if not os.path.exists(cls.PASSWORD_FILE):
            cls.save_password("123456")
            print("初始密码已设为 123456")

    @classmethod
    def save_password(cls, new_pwd):
        with open(cls.PASSWORD_FILE, "w") as f:
            f.write(cls._hash(new_pwd))

    @classmethod
    def verify(cls, pwd):
        if not os.path.exists(cls.PASSWORD_FILE):
            cls.initialize()
        try:
            with open(cls.PASSWORD_FILE, "r") as f:
                stored = f.read().strip()
            return stored == cls._hash(pwd)
        except:
            return False

# ------------------------------- 设置管理器 -------------------------------
class SettingsManager:
    SETTINGS_FILE = SETTINGS_FILE

    @classmethod
    def load(cls):
        if os.path.exists(cls.SETTINGS_FILE):
            try:
                with open(cls.SETTINGS_FILE, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except:
                pass
        return {"wallpaper": "", "show_time": False, "transparent_bg": False, "theme": "auto"}

    @classmethod
    def save(cls, settings):
        with open(cls.SETTINGS_FILE, 'w', encoding='utf-8') as f:
            json.dump(settings, f, indent=4, ensure_ascii=False)

# ------------------------------- 主应用程序 -------------------------------
class ConfigApp(ctk.CTk):
    def __init__(self):
        super().__init__()
        self.title("锁屏设置")                     # 修改标题
        self.geometry("800x600")                  # 4:3
        self.resizable(False, False)
        self.center_window()
        self.attributes('-alpha', 0.97)

        ctk.set_default_color_theme("green")      # 绿色主题,自动适配深浅色

        PasswordManager.initialize()
        self.settings = SettingsManager.load()

        theme = self.settings.get("theme", "auto")
        self._apply_theme(theme)

        self._notify_after_id = None

        # 主容器 (透明背景,让主题完全控制)
        self.main_frame = ctk.CTkFrame(self, corner_radius=28, fg_color="transparent")
        self.main_frame.pack(fill="both", expand=True, padx=16, pady=16)

        # 顶部栏
        top_bar = ctk.CTkFrame(self.main_frame, fg_color="transparent")
        top_bar.pack(fill="x", pady=(0, 12))
        title_lab = ctk.CTkLabel(top_bar, text="锁屏设置", font=ctk.CTkFont(size=26, weight="bold"))
        title_lab.pack(side="left", padx=10)

        self.theme_var = ctk.StringVar(value=self._theme_to_display(theme))
        theme_seg = ctk.CTkSegmentedButton(
            top_bar, values=["浅色", "深色", "自动"],
            variable=self.theme_var, command=self._change_theme,
            font=ctk.CTkFont(size=12), width=170, corner_radius=18
        )
        theme_seg.pack(side="right", padx=10)

        # 分段选择器
        self.seg_btn = ctk.CTkSegmentedButton(
            self.main_frame, values=["修改密码", "壁纸与样式", "关于"],
            command=self._switch_tab,
            font=ctk.CTkFont(size=14),
            corner_radius=24
        )
        self.seg_btn.pack(pady=(6, 18), padx=20, fill="x")
        self.seg_btn.set("修改密码")

        # 卡片容器
        self.card_container = ctk.CTkFrame(self.main_frame, fg_color="transparent")
        self.card_container.pack(fill="both", expand=True, padx=12, pady=(0, 12))

        self.card_pw = ctk.CTkFrame(self.card_container, corner_radius=22)
        self.card_wall = ctk.CTkFrame(self.card_container, corner_radius=22)
        self.card_about = ctk.CTkFrame(self.card_container, corner_radius=22)

        for card in (self.card_pw, self.card_wall, self.card_about):
            card.pack(fill="both", expand=True)

        self._build_password_ui()
        self._build_wallpaper_ui()
        self._build_about_ui()

        self._switch_tab("修改密码")

        # 底部通知栏
        self.notify_label = ctk.CTkLabel(self.main_frame, text="", font=ctk.CTkFont(size=12), corner_radius=14, padx=14, pady=6)
        self.notify_label.pack(pady=(0, 10))
        self.notify_label.pack_forget()

        # 延迟启动托盘线程 (确保图标出现)
        self.after(100, self._start_tray_thread)

        self.protocol("WM_DELETE_WINDOW", self.hide_window)

        self.after(50, self._bring_to_front)
        self.after(200, self._bring_to_front)

    def _apply_theme(self, mode):
        if mode == "light":
            ctk.set_appearance_mode("light")
        elif mode == "dark":
            ctk.set_appearance_mode("dark")
        else:
            ctk.set_appearance_mode("system")

    def _theme_to_display(self, mode):
        mapping = {"light": "浅色", "dark": "深色", "system": "自动"}
        return mapping.get(mode, "自动")

    def center_window(self):
        self.update_idletasks()
        sw, sh = self.winfo_screenwidth(), self.winfo_screenheight()
        self.geometry(f"800x600+{sw//2-400}+{sh//2-300}")

    def _bring_to_front(self):
        self.lift()
        self.focus_force()
        self.attributes('-topmost', True)
        self.after(200, lambda: self.attributes('-topmost', False))

    def hide_window(self):
        self.withdraw()

    def show_window(self):
        self.deiconify()
        self._bring_to_front()

    def _change_theme(self, choice):
        mapping = {"浅色": "light", "深色": "dark", "自动": "system"}
        mode = mapping[choice]
        self._apply_theme(mode)
        self.settings["theme"] = mode
        SettingsManager.save(self.settings)
        self._show_notification(f"主题已切换为{choice}")

    def _show_notification(self, msg, is_err=False):
        if self._notify_after_id:
            self.after_cancel(self._notify_after_id)
        color = "#E67E22" if is_err else "#2E8B57"
        self.notify_label.configure(text=f"• {msg}", fg_color=color, text_color="white")
        self.notify_label.pack(pady=(0, 10))
        self._notify_after_id = self.after(2500, self._hide_notification)

    def _hide_notification(self):
        self.notify_label.pack_forget()
        self._notify_after_id = None

    def _switch_tab(self, tab_name):
        for card in (self.card_pw, self.card_wall, self.card_about):
            card.pack_forget()
        if tab_name == "修改密码":
            self.card_pw.pack(fill="both", expand=True)
        elif tab_name == "壁纸与样式":
            self.card_wall.pack(fill="both", expand=True)
        else:
            self.card_about.pack(fill="both", expand=True)

    # ==================== 修改密码页面 ====================
    def _build_password_ui(self):
        self.card_pw.grid_columnconfigure(0, weight=1)
        self.card_pw.grid_columnconfigure(1, weight=1)

        ctk.CTkLabel(self.card_pw, text="原密码", font=ctk.CTkFont(size=15)).grid(row=0, column=0, padx=10, pady=(45,20), sticky='e')
        self.old_entry = ctk.CTkEntry(self.card_pw, show="*", width=320, corner_radius=18, border_width=1)
        self.old_entry.grid(row=0, column=1, padx=10, pady=(45,20), sticky='w')

        ctk.CTkLabel(self.card_pw, text="新密码", font=ctk.CTkFont(size=15)).grid(row=1, column=0, padx=10, pady=18, sticky='e')
        self.new_entry = ctk.CTkEntry(self.card_pw, show="*", width=320, corner_radius=18, border_width=1)
        self.new_entry.grid(row=1, column=1, padx=10, pady=18, sticky='w')

        ctk.CTkLabel(self.card_pw, text="确认密码", font=ctk.CTkFont(size=15)).grid(row=2, column=0, padx=10, pady=18, sticky='e')
        self.confirm_entry = ctk.CTkEntry(self.card_pw, show="*", width=320, corner_radius=18, border_width=1)
        self.confirm_entry.grid(row=2, column=1, padx=10, pady=18, sticky='w')

        btn_frame = ctk.CTkFrame(self.card_pw, fg_color="transparent")
        btn_frame.grid(row=3, column=0, columnspan=2, pady=45)
        ctk.CTkButton(btn_frame, text="确认修改", command=self._change_password, width=160, corner_radius=28, font=ctk.CTkFont(size=13)).pack(side='left', padx=20)
        ctk.CTkButton(btn_frame, text="清空字段", command=self._clear_password_fields, width=160, corner_radius=28, fg_color="#B85C5C", hover_color="#9E4646", font=ctk.CTkFont(size=13)).pack(side='left', padx=20)

    def _change_password(self):
        old = self.old_entry.get()
        new = self.new_entry.get()
        cf = self.confirm_entry.get()
        if not PasswordManager.verify(old):
            self._show_notification("原密码错误", is_err=True)
            self.old_entry.delete(0, 'end')
            return
        if new != cf:
            self._show_notification("两次密码不一致", is_err=True)
            self.confirm_entry.delete(0, 'end')
            return
        PasswordManager.save_password(new)
        self._show_notification("密码修改成功,下次锁屏生效")
        self._clear_password_fields()

    def _clear_password_fields(self):
        self.old_entry.delete(0, 'end')
        self.new_entry.delete(0, 'end')
        self.confirm_entry.delete(0, 'end')

    # ==================== 壁纸与样式页面 ====================
    def _build_wallpaper_ui(self):
        scroll_frame = ctk.CTkScrollableFrame(self.card_wall, fg_color="transparent", corner_radius=0)
        scroll_frame.pack(fill="both", expand=True)

        self.trans_var = ctk.BooleanVar(value=self.settings.get("transparent_bg", False))
        trans_sw = ctk.CTkSwitch(
            scroll_frame, text="透明背景模式 (忽略壁纸)", variable=self.trans_var,
            command=self._on_transparent_toggle, font=ctk.CTkFont(size=14), switch_width=50
        )
        trans_sw.pack(anchor='w', pady=(28, 16), padx=28)

        self.wall_group = ctk.CTkFrame(scroll_frame, fg_color="transparent")
        self.wall_group.pack(fill='x', padx=28, pady=6)

        path_frame = ctk.CTkFrame(self.wall_group, fg_color="transparent")
        path_frame.pack(fill='x', pady=8)
        self.wall_entry = ctk.CTkEntry(path_frame, placeholder_text="选择壁纸图片", width=500, corner_radius=18)
        self.wall_entry.pack(side='left', padx=(0, 12))
        self.wall_entry.insert(0, self.settings.get("wallpaper", ""))
        self.browse_btn = ctk.CTkButton(path_frame, text="浏览", command=self._browse_wallpaper, width=90, corner_radius=18)
        self.browse_btn.pack(side='left')

        preview_container = ctk.CTkFrame(self.wall_group, fg_color="transparent")
        preview_container.pack(fill='x', pady=(20, 16))
        self.preview_label = ctk.CTkLabel(preview_container, text="壁纸预览", anchor='w', font=ctk.CTkFont(size=13))
        self.preview_label.pack(anchor='w', pady=(0, 8))
        self.preview_img = ctk.CTkLabel(
            preview_container, text="", width=680, height=180,
            corner_radius=22, fg_color=("#EFF3F8", "#2D2D3F")
        )
        self.preview_img.pack()

        if not self.trans_var.get() and self.settings.get("wallpaper"):
            self._update_preview(self.settings["wallpaper"])
        else:
            self.preview_img.configure(text="(暂无预览)")

        self.time_var = ctk.BooleanVar(value=self.settings.get("show_time", False))
        time_sw = ctk.CTkSwitch(
            scroll_frame, text="锁屏时显示当前时间 (左下角)", variable=self.time_var,
            command=self._on_time_toggle, font=ctk.CTkFont(size=14), switch_width=50
        )
        time_sw.pack(anchor='w', pady=(18, 30), padx=28)

        self._update_wallpaper_access()

    def _update_wallpaper_access(self):
        if self.trans_var.get():
            self.wall_entry.configure(state="disabled")
            self.browse_btn.configure(state="disabled")
            self.preview_img.configure(image=None, text="透明模式已开启,不显示壁纸")
        else:
            self.wall_entry.configure(state="normal")
            self.browse_btn.configure(state="normal")
            wp = self.settings.get("wallpaper", "")
            if wp:
                self._update_preview(wp)
            else:
                self.preview_img.configure(image=None, text="(未选择壁纸)")

    def _on_transparent_toggle(self):
        new_val = self.trans_var.get()
        self.settings["transparent_bg"] = new_val
        SettingsManager.save(self.settings)
        self._update_wallpaper_access()
        self._show_notification("透明模式已" + ("开启" if new_val else "关闭"))

    def _browse_wallpaper(self):
        if self.trans_var.get():
            return
        path = filedialog.askopenfilename(filetypes=[("图片", "*.png *.jpg *.jpeg *.bmp *.gif")])
        if path:
            self.wall_entry.delete(0, 'end')
            self.wall_entry.insert(0, path)
            self._save_wallpaper_path(path)

    def _save_wallpaper_path(self, path):
        if not path or not os.path.isfile(path):
            self._show_notification("文件不存在", is_err=True)
            return
        self.settings["wallpaper"] = path
        if self.settings["transparent_bg"]:
            self.settings["transparent_bg"] = False
            self.trans_var.set(False)
            self._update_wallpaper_access()
        SettingsManager.save(self.settings)
        self._update_preview(path)
        self._show_notification("壁纸已保存")

    def _update_preview(self, path):
        try:
            img = Image.open(path)
            img.thumbnail((660, 170), Image.Resampling.LANCZOS)
            photo = ctk.CTkImage(img, size=(img.width, img.height))
            self.preview_img.configure(image=photo, text="")
            self.preview_img.image = photo
        except Exception:
            self.preview_img.configure(image=None, text="预览失败,请检查图片文件")

    def _on_time_toggle(self):
        self.settings["show_time"] = self.time_var.get()
        SettingsManager.save(self.settings)
        self._show_notification("时间显示" + ("已开启" if self.time_var.get() else "已关闭"))

    # ==================== 关于页面 ====================
    def _build_about_ui(self):
        info = """锁屏设置 · 4:3 版本

• 自定义壁纸 / 透明背景
• 锁屏实时时钟
• 自动保存 / 双主题
• 系统托盘控制

@Zhl2010"""
        about_lab = ctk.CTkLabel(self.card_about, text=info, justify='left', font=ctk.CTkFont(size=13), padx=40, pady=30)
        about_lab.pack()

    # ==================== 系统托盘 ====================
    def _start_tray_thread(self):
        self.tray_thread = threading.Thread(target=self._setup_tray, daemon=True)
        self.tray_thread.start()

    def _create_tray_icon(self):
        size = 64
        img = Image.new('RGBA', (size, size), (0, 0, 0, 0))
        draw = ImageDraw.Draw(img)
        draw.arc((20, 12, 44, 36), start=0, end=180, fill=(100, 150, 100), width=5)
        draw.rounded_rectangle((14, 30, 50, 58), radius=10, fill=(120, 180, 120), outline=(60, 100, 60))
        draw.ellipse((28, 42, 36, 50), fill=(40, 70, 40))
        draw.rectangle((30, 48, 34, 55), fill=(40, 70, 40))
        return img

    def _setup_tray(self):
        # 确保图标存在
        if os.path.exists(ICON_FILE):
            try:
                icon_img = Image.open(ICON_FILE).resize((64, 64), Image.Resampling.LANCZOS)
            except:
                icon_img = self._create_tray_icon()
        else:
            icon_img = self._create_tray_icon()

        menu = pystray.Menu(
            pystray.MenuItem("启动锁屏", self._launch_lockscreen),
            pystray.MenuItem("显示窗口", self.show_window),
            pystray.MenuItem("退出程序", self._quit_app)
        )
        icon = pystray.Icon("lock_config", icon_img, "锁屏设置", menu)
        icon.run()

    def _launch_lockscreen(self):
        base = os.path.dirname(os.path.abspath(sys.argv[0]))
        exe = os.path.join(base, 'lockscreen.exe')
        py = os.path.join(base, 'lockscreen.py')
        if os.path.exists(exe):
            subprocess.Popen([exe])
        elif os.path.exists(py):
            subprocess.Popen([sys.executable, py])
        else:
            self._show_notification("未找到锁屏程序", is_err=True)

    def _quit_app(self):
        self.quit()
        os._exit(0)

if __name__ == "__main__":
    app = ConfigApp()
    app.mainloop()

::::

::::info[lockscreen.py]

"""
锁屏程序 - 普通版(完整鼠标拦截,强制英文,允许 Shift)
彻底重构钩子与日志,解决卡顿、钩子失效、窗口抖动偏移问题
功能:
- 全屏置顶,拦截键盘鼠标,仅ESC唤出密码框
- 自动切换至美式英语键盘布局,杜绝中文输入
- 允许 Shift 键(大小写切换)
- 支持自定义壁纸、透明背景、时间显示
- 密码错误抖动
- 调试模式(10秒自动解锁)
- 解锁后恢复原始键盘布局
- **精确鼠标拦截**:密码框显示时只能点击密码框内控件,无法点击其他区域
- 彻底禁用右键菜单
- 低CPU占用,无频繁磁盘I/O
- 【已删除托盘图标功能】
- **钩子永不失效**:ESC直接触发主线程回调,无需鼠标干预
"""
import os
import sys
import json
import ctypes
import ctypes.wintypes
import hashlib
import threading
import subprocess
import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk
import time
import atexit
import signal
import traceback
from datetime import datetime

# ---------- 加载 Windows API ----------
user32 = ctypes.WinDLL('user32', use_last_error=True)
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

# 输入法相关
imm32 = ctypes.WinDLL('imm32')
ImmAssociateContext = imm32.ImmAssociateContext
ImmAssociateContext.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.HANDLE]
ImmAssociateContext.restype = ctypes.wintypes.HANDLE

# 键盘布局
LoadKeyboardLayoutW = user32.LoadKeyboardLayoutW
LoadKeyboardLayoutW.argtypes = [ctypes.c_wchar_p, ctypes.c_uint]
LoadKeyboardLayoutW.restype = ctypes.wintypes.HANDLE
ActivateKeyboardLayout = user32.ActivateKeyboardLayout
ActivateKeyboardLayout.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_uint]
ActivateKeyboardLayout.restype = ctypes.wintypes.HANDLE

# 窗口句柄相关
WindowFromPoint = user32.WindowFromPoint
WindowFromPoint.argtypes = [ctypes.wintypes.POINT]
WindowFromPoint.restype = ctypes.wintypes.HWND
IsChild = user32.IsChild
IsChild.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.HWND]
IsChild.restype = ctypes.wintypes.BOOL

KLF_ACTIVATE = 1
KLF_SETFORPROCESS = 0x100
ENGLISH_US_LAYOUT = "00000409"  # 美式英语

LOG_DIR = "log"
BG_ALPHA = 0.0
PASSWORD_FILE = os.path.join(LOG_DIR, "pass")
SETTINGS_FILE = os.path.join(LOG_DIR, "settings.json")
ERROR_LOG_PATH = os.path.join(LOG_DIR, "crash.log")
LOCK_SIGNAL_FILE = os.path.join(LOG_DIR, "lock_signal.txt")

if not os.path.exists(LOG_DIR):
    os.makedirs(LOG_DIR)

def log_error(msg):
    """仅记录严重错误,不记录正常信息"""
    try:
        with open(ERROR_LOG_PATH, 'a', encoding='utf-8') as f:
            f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {msg}\n")
            f.flush()
    except:
        pass

def load_settings():
    if os.path.exists(SETTINGS_FILE):
        try:
            with open(SETTINGS_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except:
            pass
    return {"wallpaper": "", "show_time": False, "transparent_bg": False}

# ---------- 钩子常量 ----------
WH_KEYBOARD_LL = 13
WH_MOUSE_LL = 14
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101
WM_SYSKEYDOWN = 0x0104
WM_SYSKEYUP = 0x0105
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205
WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP = 0x0208
WM_MOUSEWHEEL = 0x020A
WM_XBUTTONDOWN = 0x020B
WM_XBUTTONUP = 0x020C
WM_NCRBUTTONDOWN = 0x00A4
WM_NCRBUTTONUP = 0x00A5

VK_SHIFT = 0x10
VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1
VK_CTRL = 0x11
VK_ALT = 0x12
VK_LWIN = 0x5B
VK_RWIN = 0x5C
VK_ESC = 0x1B
VK_BACK = 0x08
VK_TAB = 0x09
VK_ENTER = 0x0D
VK_CAPSLOCK = 0x14
VK_DELETE = 0x2E
VK_HOME = 0x24
VK_END = 0x23
VK_LEFT = 0x25
VK_RIGHT = 0x27
VK_UP = 0x26
VK_DOWN = 0x28

class KBDLLHOOKSTRUCT(ctypes.Structure):
    _fields_ = [
        ('vkCode', ctypes.wintypes.DWORD),
        ('scanCode', ctypes.wintypes.DWORD),
        ('flags', ctypes.wintypes.DWORD),
        ('time', ctypes.wintypes.DWORD),
        ('dwExtraInfo', ctypes.POINTER(ctypes.c_ulong))
    ]

class MSLLHOOKSTRUCT(ctypes.Structure):
    _fields_ = [
        ('pt', ctypes.wintypes.POINT),
        ('mouseData', ctypes.wintypes.DWORD),
        ('flags', ctypes.wintypes.DWORD),
        ('time', ctypes.wintypes.DWORD),
        ('dwExtraInfo', ctypes.POINTER(ctypes.c_ulong))
    ]

HOOKPROC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM)

g_lock = None

# ---------- 密码管理 ----------
class PasswordManager:
    PASSWORD_FILE = PASSWORD_FILE
    SALT = b"lockscreen_secure_salt"

    @classmethod
    def _hash(cls, pwd):
        h = hashlib.sha256()
        h.update(cls.SALT)
        h.update(pwd.encode('utf-8'))
        return h.hexdigest()

    @classmethod
    def initialize(cls):
        if not os.path.exists(cls.PASSWORD_FILE):
            cls.save_password("123456")
            print("初始密码已设为 123456")

    @classmethod
    def save_password(cls, new_pwd):
        with open(cls.PASSWORD_FILE, "w") as f:
            f.write(cls._hash(new_pwd))

    @classmethod
    def verify(cls, pwd):
        if not os.path.exists(cls.PASSWORD_FILE):
            cls.initialize()
        try:
            with open(cls.PASSWORD_FILE, "r") as f:
                stored = f.read().strip()
            return stored == cls._hash(pwd)
        except:
            return False

# ---------- 钩子回调 ----------
@HOOKPROC
def keyboard_proc(nCode, wParam, lParam):
    try:
        if nCode >= 0:
            kb = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
            vkCode = kb.vkCode
            if wParam in (WM_KEYDOWN, WM_SYSKEYDOWN):
                if g_lock and g_lock.should_block_key(vkCode):
                    return 1
    except Exception:
        pass
    return user32.CallNextHookEx(None, nCode, wParam, lParam)

@HOOKPROC
def mouse_proc(nCode, wParam, lParam):
    try:
        if nCode >= 0 and g_lock and g_lock.is_locked:
            ms = ctypes.cast(lParam, ctypes.POINTER(MSLLHOOKSTRUCT)).contents
            is_click = wParam in (WM_LBUTTONDOWN, WM_LBUTTONUP,
                                   WM_RBUTTONDOWN, WM_RBUTTONUP,
                                   WM_MBUTTONDOWN, WM_MBUTTONUP,
                                   WM_XBUTTONDOWN, WM_XBUTTONUP,
                                   WM_MOUSEWHEEL,
                                   WM_NCRBUTTONDOWN, WM_NCRBUTTONUP)
            if not is_click:
                return 0

            if g_lock.dialog_shown:
                hwnd_clicked = WindowFromPoint(ms.pt)
                if hwnd_clicked and g_lock.password_dialog:
                    if hwnd_clicked == g_lock.password_dialog.winfo_id() or IsChild(g_lock.password_dialog.winfo_id(), hwnd_clicked):
                        return 0
                return 1
            else:
                return 1
    except Exception:
        pass
    return user32.CallNextHookEx(None, nCode, wParam, lParam)

# ---------- 主类 ----------
class LockScreen:
    def __init__(self, debug_mode=False):
        self._init(debug_mode)

    def _init(self, debug_mode):
        self.debug_mode = debug_mode
        self.is_locked = True
        self.dialog_shown = False
        self.password_dialog = None
        self.timer_id = None
        self.timeout_id = None
        self.keyboard_hook = None
        self.mouse_hook = None
        self.settings = load_settings()
        self.canvas = None
        self.time_text_id = None
        self.bg_image_id = None
        self.signal_check_id = None
        self.reload_id = None
        self.original_layout = None
        self.keepalive_id = None

        if not ctypes.windll.shell32.IsUserAnAdmin():
            messagebox.showerror("错误", "请以管理员身份运行!")
            sys.exit(1)

        # 保存原始键盘布局
        try:
            self.original_layout = ActivateKeyboardLayout(0, 0)
        except Exception:
            pass

        # 强制英文布局
        self._force_english_layout()

        PasswordManager.initialize()

        self.root = tk.Tk()
        self.root.title("")
        self.root.overrideredirect(True)
        self.root.attributes('-topmost', True)
        self.root.geometry(f"{self.root.winfo_screenwidth()}x{self.root.winfo_screenheight()}+0+0")
        self.root.focus_force()
        self.root.protocol("WM_DELETE_WINDOW", lambda: None)
        self.root.grab_set_global()

        self.canvas = tk.Canvas(
            self.root,
            width=self.root.winfo_screenwidth(),
            height=self.root.winfo_screenheight(),
            highlightthickness=0,
            bg='#000000'
        )
        self.canvas.pack()

        # 设置背景
        transparent_bg = self.settings.get("transparent_bg", False)
        if transparent_bg:
            self.root.attributes('-alpha', BG_ALPHA)
        else:
            wallpaper = self.settings.get("wallpaper", "")
            if wallpaper and os.path.isfile(wallpaper):
                self._set_wallpaper(wallpaper)
                self.root.attributes('-alpha', 1.0)
            else:
                self.root.attributes('-alpha', BG_ALPHA)

        if self.settings.get("show_time", False):
            self._create_time_display()

        self._check_lock_signal()
        if self.debug_mode:
            self.timer_id = self.root.after(10000, self.debug_unlock)

        self._reload_settings_periodic()

        global g_lock
        g_lock = self

        atexit.register(self.cleanup)
        signal.signal(signal.SIGTERM, self.signal_handler)
        signal.signal(signal.SIGINT, self.signal_handler)

        self.hook_thread = threading.Thread(target=self._hook_message_loop, daemon=True)
        self.hook_thread.start()

        # 保活机制:每5秒调用一次空函数,确保主线程消息循环活跃
        self._keepalive()

    def _keepalive(self):
        """空函数,仅用于唤醒主线程消息循环"""
        self.keepalive_id = self.root.after(5000, self._keepalive)

    def _force_english_layout(self):
        try:
            hkl = LoadKeyboardLayoutW(ENGLISH_US_LAYOUT, KLF_ACTIVATE | KLF_SETFORPROCESS)
            if hkl:
                ActivateKeyboardLayout(hkl, 0)
        except Exception:
            pass

    def _reload_settings_periodic(self):
        """每隔30秒重新加载设置,且仅在文件真正改变时才更新壁纸等,不写日志"""
        try:
            new_settings = load_settings()
            if new_settings != self.settings:
                # 设置发生变化,需要更新
                old_wall = self.settings.get("wallpaper")
                new_wall = new_settings.get("wallpaper")
                if new_wall != old_wall:
                    if new_wall and os.path.isfile(new_wall):
                        self._set_wallpaper(new_wall)
                    else:
                        # 清除壁纸
                        if self.bg_image_id:
                            self.canvas.delete(self.bg_image_id)
                            self.bg_image_id = None
                # 更新时间显示
                if new_settings.get("show_time") != self.settings.get("show_time"):
                    if new_settings.get("show_time"):
                        if not self.time_text_id:
                            self._create_time_display()
                    else:
                        if self.time_text_id:
                            self.canvas.delete(self.time_text_id)
                            self.time_text_id = None
                # 更新透明背景
                if new_settings.get("transparent_bg") != self.settings.get("transparent_bg"):
                    alpha = BG_ALPHA if new_settings.get("transparent_bg") else 1.0
                    self.root.attributes('-alpha', alpha)
                self.settings = new_settings
        except Exception:
            pass
        self.reload_id = self.root.after(30000, self._reload_settings_periodic)

    def _set_wallpaper(self, path):
        try:
            img = Image.open(path)
            sw = self.root.winfo_screenwidth()
            sh = self.root.winfo_screenheight()
            img = img.resize((sw, sh), Image.Resampling.LANCZOS)
            self.bg_photo = ImageTk.PhotoImage(img)
            if self.bg_image_id:
                self.canvas.delete(self.bg_image_id)
            self.bg_image_id = self.canvas.create_image(0, 0, image=self.bg_photo, anchor='nw')
            self.canvas.tag_lower(self.bg_image_id)
        except Exception:
            pass

    def _create_time_display(self):
        self.time_text_id = self.canvas.create_text(
            50, self.root.winfo_screenheight() - 50,
            text='', fill='white', font=('Segoe UI', 60, 'normal'), anchor='sw'
        )
        self._update_time()

    def _update_time(self):
        if self.time_text_id:
            now = datetime.now()
            self.canvas.itemconfig(self.time_text_id, text=f"{now.strftime('%H:%M')}\n{now.strftime('%Y-%m-%d')}")
            self.root.after(1000, self._update_time)

    def _check_lock_signal(self):
        try:
            if os.path.exists(LOCK_SIGNAL_FILE):
                self.show_password_dialog()
                os.remove(LOCK_SIGNAL_FILE)
        except Exception:
            pass
        self.signal_check_id = self.root.after(500, self._check_lock_signal)

    def signal_handler(self, signum, frame):
        self.cleanup()
        sys.exit(0)

    def cleanup(self):
        try:
            if self.keyboard_hook:
                user32.UnhookWindowsHookEx(self.keyboard_hook)
            if self.mouse_hook:
                user32.UnhookWindowsHookEx(self.mouse_hook)
            if self.signal_check_id:
                self.root.after_cancel(self.signal_check_id)
            if self.reload_id:
                self.root.after_cancel(self.reload_id)
            if self.keepalive_id:
                self.root.after_cancel(self.keepalive_id)
        except Exception:
            pass

    def _hook_message_loop(self):
        """钩子线程,无限重连"""
        while True:
            try:
                self.keyboard_hook = user32.SetWindowsHookExW(
                    WH_KEYBOARD_LL, keyboard_proc,
                    kernel32.GetModuleHandleW(None), 0
                )
                if not self.keyboard_hook:
                    raise Exception("Keyboard hook failed")

                self.mouse_hook = user32.SetWindowsHookExW(
                    WH_MOUSE_LL, mouse_proc,
                    kernel32.GetModuleHandleW(None), 0
                )
                if not self.mouse_hook:
                    raise Exception("Mouse hook failed")

                msg = ctypes.wintypes.MSG()
                while user32.GetMessageW(ctypes.byref(msg), None, 0, 0) != 0:
                    user32.TranslateMessage(ctypes.byref(msg))
                    user32.DispatchMessageW(ctypes.byref(msg))
                # 正常退出WM_QUIT - 重启线程
            except Exception:
                pass
            finally:
                if self.keyboard_hook:
                    user32.UnhookWindowsHookEx(self.keyboard_hook)
                    self.keyboard_hook = None
                if self.mouse_hook:
                    user32.UnhookWindowsHookEx(self.mouse_hook)
                    self.mouse_hook = None
                time.sleep(1)  # 等待后重试

    def should_block_key(self, vk):
        """仅做逻辑判断,不调用任何Tk方法,避免跨线程问题"""
        if not self.is_locked:
            return False
        if self.dialog_shown:
            # 密码输入模式:放行字母、数字、Shift、功能键等
            if 0x30 <= vk <= 0x39 or 0x41 <= vk <= 0x5A or 0x60 <= vk <= 0x69:
                return False
            if vk in (VK_SHIFT, VK_LSHIFT, VK_RSHIFT):
                return False
            allowed = {VK_BACK, VK_TAB, VK_ENTER, VK_DELETE, VK_HOME, VK_END,
                       VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_CAPSLOCK}
            if vk in allowed:
                return False
            if vk in (VK_LWIN, VK_RWIN):
                return True
            if vk == VK_ESC:
                # 直接通过 after 调用主线程方法(线程安全)
                self.root.after(0, self.show_password_dialog)
                return True
            return True
        else:
            # 锁屏模式:仅ESC有效
            if vk in (VK_LWIN, VK_RWIN):
                return True
            if vk == VK_ESC:
                self.root.after(0, self.show_password_dialog)
                return True
            return True

    def show_password_dialog(self):
        if self.dialog_shown:
            return
        try:
            self.dialog_shown = True
            dlg = tk.Toplevel(self.root)
            dlg.title("")
            dlg.overrideredirect(True)
            dlg.attributes('-topmost', True)
            dlg.transient(self.root)
            dlg.grab_set_global()
            dlg.configure(bg='#1e1e1e')
            dlg.attributes('-alpha', 0.95)

            w, h = 380, 200
            sw, sh = self.root.winfo_screenwidth(), self.root.winfo_screenheight()
            x = sw // 2 - w // 2
            y = sh // 2 - h // 2
            dlg.geometry(f"{w}x{h}+{x}+{y}")

            # 保存原始坐标供抖动使用
            self.dialog_orig_x = x
            self.dialog_orig_y = y

            canvas = tk.Canvas(dlg, width=w, height=h, bg='#1e1e1e', highlightthickness=0)
            canvas.place(x=0, y=0)
            canvas.create_round_rect(2, 2, w-2, h-2, radius=25, fill='#2d2d2d', outline='')
            canvas.create_text(w//2, 35, text="解锁屏幕", fill='#e0e0e0', font=('Segoe UI', 16, 'bold'))

            ex, ey = w//2-120, 70
            canvas.create_round_rect(ex, ey, ex+240, ey+40, radius=10, fill='#3c3c3c', outline='')

            def validate_input(char, action):
                if action == '0':
                    return True
                if len(char) == 0:
                    return True
                return 32 <= ord(char) <= 126

            vcmd = (self.root.register(validate_input), '%S', '%d')
            entry = tk.Entry(canvas, show='●', font=('Segoe UI', 14), bg='#3c3c3c',
                             fg='white', insertbackground='#6ab04c', bd=0, highlightthickness=0,
                             validate='key', validatecommand=vcmd)
            entry.place(x=ex+10, y=ey+8, width=220, height=24)

            hwnd = entry.winfo_id()
            ImmAssociateContext(hwnd, None)

            entry.bind('<Button-3>', lambda e: 'break')
            canvas.bind('<Button-3>', lambda e: 'break')
            dlg.bind('<Button-3>', lambda e: 'break')

            def on_focus_in(event):
                self._force_english_layout()
                ImmAssociateContext(entry.winfo_id(), None)
            entry.bind('<FocusIn>', on_focus_in)

            btn_y = 130
            unlock = tk.Button(canvas, text="解锁", command=lambda: self.check_password(entry),
                               bg='#6ab04c', fg='white', font=('Segoe UI', 11),
                               relief='flat', bd=0, cursor='hand2')
            unlock.place(x=w//2-110, y=btn_y, width=100, height=35)
            cancel = tk.Button(canvas, text="取消", command=self.hide_password_dialog,
                               bg='#d63031', fg='white', font=('Segoe UI', 11),
                               relief='flat', bd=0, cursor='hand2')
            cancel.place(x=w//2+10, y=btn_y, width=100, height=35)

            close_btn = tk.Button(canvas, text="✕", bg='#2d2d2d', fg='#a0a0a0',
                                  font=('Segoe UI', 10), bd=0, command=self.hide_password_dialog,
                                  activebackground='#c42b1c')
            close_btn.place(x=w-35, y=5, width=25, height=25)

            dlg.lift()
            dlg.focus_force()
            dlg.update()
            dlg.lift()
            dlg.focus_force()
            entry.focus_set()

            def reset_timeout():
                if self.timeout_id:
                    dlg.after_cancel(self.timeout_id)
                self.timeout_id = dlg.after(3000, self.hide_password_dialog)
            entry.bind('<Key>', lambda e: reset_timeout())
            reset_timeout()
            entry.bind('<Return>', lambda e: self.check_password(entry))

            self.password_dialog = dlg
            self.password_entry = entry
        except Exception as e:
            log_error(f"show_password_dialog error: {traceback.format_exc()}")
            self.dialog_shown = False

    def hide_password_dialog(self):
        try:
            if self.password_dialog:
                if self.timeout_id:
                    self.password_dialog.after_cancel(self.timeout_id)
                    self.timeout_id = None
                self.password_dialog.grab_release()
                self.password_dialog.destroy()
                self.password_dialog = None
            self.dialog_shown = False
        except Exception:
            pass

    def check_password(self, entry):
        try:
            if PasswordManager.verify(entry.get()):
                self.unlock()
            else:
                self.shake_window()
                entry.delete(0, tk.END)
                if self.password_dialog:
                    self.password_dialog.lift()
                    self.password_dialog.focus_force()
                    entry.focus_set()
                    self._force_english_layout()
                    ImmAssociateContext(entry.winfo_id(), None)
        except Exception as e:
            log_error(f"check_password error: {e}")

    def shake_window(self):
        if not self.password_dialog:
            return
        dlg = self.password_dialog
        orig_x = getattr(self, 'dialog_orig_x', None)
        orig_y = getattr(self, 'dialog_orig_y', None)
        if orig_x is None or orig_y is None:
            orig_x = dlg.winfo_x()
            orig_y = dlg.winfo_y()
        offsets = [5, -5, 5, -5, 3, -3, 2, -2, 0]
        idx = 0
        def step():
            nonlocal idx
            if idx >= len(offsets):
                dlg.geometry(f"+{orig_x}+{orig_y}")
                return
            off = offsets[idx]
            dlg.geometry(f"+{orig_x + off}+{orig_y}")
            idx += 1
            dlg.after(30, step)
        step()

    def unlock(self):
        self.is_locked = False
        if self.timer_id:
            self.root.after_cancel(self.timer_id)
        self.cleanup()
        if hasattr(self, 'original_layout') and self.original_layout:
            try:
                ActivateKeyboardLayout(self.original_layout, 0)
            except Exception:
                pass
        self.root.quit()
        self.root.destroy()
        os._exit(0)

    def debug_unlock(self):
        if self.is_locked:
            print("调试模式自动解锁")
            self.unlock()

    def open_config(self):
        try:
            base = os.path.dirname(os.path.abspath(sys.argv[0]))
            exe = os.path.join(base, 'config.exe')
            py = os.path.join(base, 'config.py')
            if getattr(sys, 'frozen', False):
                subprocess.Popen([exe] if os.path.exists(exe) else [py])
            else:
                subprocess.Popen([sys.executable, py] if os.path.exists(py) else [exe])
        except Exception:
            pass

    def run(self):
        try:
            self.root.mainloop()
        except Exception as e:
            log_error(f"run error: {traceback.format_exc()}")
        finally:
            self.cleanup()

def _create_round_rect(self, x1, y1, x2, y2, radius=20, **kwargs):
    points = [x1+radius, y1,
              x2-radius, y1,
              x2, y1,
              x2, y1+radius,
              x2, y2-radius,
              x2, y2,
              x2-radius, y2,
              x1+radius, y2,
              x1, y2,
              x1, y2-radius,
              x1, y1+radius,
              x1, y1]
    return self.create_polygon(points, smooth=True, **kwargs)

tk.Canvas.create_round_rect = _create_round_rect

if __name__ == "__main__":
    try:
        print("锁屏程序启动(普通版,无托盘图标,钩子直接回调)")
        lock = LockScreen(debug_mode=False)
        lock.run()
    except Exception as e:
        log_error(traceback.format_exc())

:::: ::::info[kill.cpp]

#include<bits/stdc++.h>
#include <conio.h>
#include<windows.h>
#include<time.h>
#include<tlhelp32.h>
#include<iostream>
#include<map>
#include<string>
#include<thread>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "kernel32.lib")

using namespace std;


const int Year=2025,Month=10,Day=22;


void killProcess(DWORD pid) {
    HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,FALSE,pid);
    if(hProcess) {
        TerminateProcess(hProcess,0);
        CloseHandle(hProcess);
    }
}
struct node {
    string rode,name;
};
unordered_map<string,int>mp;
vector<string>vv;
string S;
void bfs_kill() {
    HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    if(hSnapshot) {
        PROCESSENTRY32 p;
        p.dwSize=sizeof(PROCESSENTRY32);
        if(Process32First(hSnapshot,&p)) {
            do {
                string s=p.szExeFile;
//              cout<<s<<endl;
                if(s==S) {
                    killProcess(p.th32ProcessID);
                }
            } while(Process32Next(hSnapshot,&p));
        }
        CloseHandle(hSnapshot);
    }
}

int main(int argc,char* argv[]) {
    S="lockscreen.exe";
    bfs_kill();

    return 0;
}

::::

::::info[start.cpp]

#include <windows.h>
#include <shellapi.h>
#include<bits/stdc++.h>
using namespace std;
string EXE_PATH = "lockscreen.exe";
HHOOK g_hHook = NULL;
bool g_winPressed = false;
DWORD g_lastTriggerTime = 0;
const DWORD COOLDOWN_MS = 10000;

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        KBDLLHOOKSTRUCT* pKb = (KBDLLHOOKSTRUCT*)lParam;
        if (pKb->vkCode == VK_LWIN || pKb->vkCode == VK_RWIN) {
            if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
                g_winPressed = true;
            else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
                g_winPressed = false;
        }
        if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && pKb->vkCode == 'A' && g_winPressed) {
            DWORD now = GetTickCount();
            if (g_lastTriggerTime == 0 || (now - g_lastTriggerTime) >= COOLDOWN_MS) {
                string cmd= "start " + EXE_PATH;
                g_lastTriggerTime = now;
                system(cmd.c_str());
//              Sleep(5000);
                cmd="start recognize.exe";
                system(cmd.c_str());
//              ShellExecuteW(NULL, L"open", EXE_PATH, NULL, NULL, SW_SHOW);
            }
        }
    }
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

int main() {
HWND hWnd = GetConsoleWindow(); // 获取控制台窗口的句柄
ShowWindow(hWnd, SW_HIDE); // 隐藏控制台窗口
// ... 其他代码

    g_hHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleW(NULL), 0);
    if (!g_hHook) return 1;

    MSG msg;
    while (GetMessageW(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    UnhookWindowsHookEx(g_hHook);
    return 0;
}

::::

未经允许禁止转载、套用。