12.84K

kiosk_lock

1.

"""
=============================================================
KIOSK LOCK — Демонстрационный режим для колледжа
=============================================================
ВКЛЮЧИТЬ блокировку: Ctrl + Alt + L
СНЯТЬ блокировку:
Ctrl + Alt + U
Разрешённые приложения: Minecraft, Word/Docs
=============================================================
Установка зависимостей (запустить один раз):
pip install pywin32 keyboard pystray Pillow psutil
Запуск:
python kiosk_lock.py
"""
import sys
import time
import threading
import ctypes
import ctypes.wintypes
import psutil
import keyboard
import win32gui
import win32process
import win32con
import win32api
from PIL import Image, ImageDraw
import pystray
# ──────────────────────────────────────────────
# НАСТРОЙКИ
# ──────────────────────────────────────────────
# Хоткей для ВКЛЮЧЕНИЯ блокировки
LOCK_HOTKEY
= "ctrl+alt+l"
# Хоткей для СНЯТИЯ блокировки
UNLOCK_HOTKEY = "ctrl+alt+u"
# Ключевые слова в именах процессов разрешённых приложений
ALLOWED_PROCESSES = [
"javaw",
# Minecraft (Java Edition)
"minecraft",
# Minecraft (любая версия)
"winword",
# Microsoft Word
"soffice",
# LibreOffice Writer
"chrome",
# Google Docs в браузере — добавьте если нужно
"msedge",
# Edge с Docs
"firefox",
# Firefox с Docs
# "explorer",
# раскомментируйте только если нужен Проводник
]
# Ключевые слова в ЗАГОЛОВКАХ окон разрешённых приложений
# (дополнительная проверка поверх проверки процесса)
ALLOWED_WINDOW_TITLES = [
"minecraft",
"microsoft word",
"word",
"документ",
"document",
"google docs",
".docx",
".doc",

2.

]
# Системные процессы, которые никогда не трогаем
SYSTEM_PROCESSES = [
"explorer.exe",
"taskmgr.exe",
"python.exe",
"pythonw.exe",
"cmd.exe",
"powershell.exe",
"conhost.exe",
"dwm.exe",
"csrss.exe",
"winlogon.exe",
"services.exe",
"lsass.exe",
"svchost.exe",
"system",
]
# ──────────────────────────────────────────────
# СОСТОЯНИЕ
# ──────────────────────────────────────────────
lock_active = False
lock_lock = threading.Lock()
tray_icon = None
# ──────────────────────────────────────────────
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
# ──────────────────────────────────────────────
def is_allowed_process(proc_name: str, window_title: str) -> bool:
"""Возвращает True, если процесс/окно разрешено."""
proc_lower = proc_name.lower()
title_lower = window_title.lower()
for allowed in ALLOWED_PROCESSES:
if allowed in proc_lower:
return True
for allowed_title in ALLOWED_WINDOW_TITLES:
if allowed_title in title_lower:
return True
return False
def get_window_info(hwnd):
"""Возвращает (имя_процесса, заголовок_окна) для окна hwnd."""
try:
_, pid = win32process.GetWindowThreadProcessId(hwnd)
proc = psutil.Process(pid)
return proc.name(), win32gui.GetWindowText(hwnd)
except Exception:
return "", ""
def close_forbidden_windows():
"""Закрывает все окна, не входящие в белый список."""
if not lock_active:
return
def enum_callback(hwnd, _):

3.

if not lock_active:
return
if not win32gui.IsWindowVisible(hwnd):
return
if not win32gui.GetWindowText(hwnd):
return
proc_name, title = get_window_info(hwnd)
if not proc_name:
return
# Никогда не трогаем системные процессы
if proc_name.lower() in SYSTEM_PROCESSES:
return
if not is_allowed_process(proc_name, title):
try:
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
time.sleep(0.1)
# Если окно ещё живо — принудительно
if win32gui.IsWindow(hwnd) and win32gui.IsWindowVisible(hwnd):
win32gui.PostMessage(hwnd, win32con.WM_DESTROY, 0, 0)
except Exception:
pass
win32gui.EnumWindows(enum_callback, None)
def block_switch():
"""Блокирует Alt+Tab, Win, Alt+F4 пока активна блокировка."""
if lock_active:
# Поглощаем опасные комбинации
keyboard.block_key("left windows")
keyboard.block_key("right windows")
else:
keyboard.unblock_key("left windows")
keyboard.unblock_key("right windows")
# ──────────────────────────────────────────────
# ХУК КЛАВИАТУРЫ — перехват Alt+Tab, Alt+F4
# ──────────────────────────────────────────────
_keyboard_hook = None
def low_level_keyboard_handler(nCode, wParam, lParam):
"""Low-level keyboard hook для блокировки системных хоткеев."""
WM_KEYDOWN = 0x0100
WM_SYSKEYDOWN = 0x0104
if lock_active and nCode >= 0 and wParam in (WM_KEYDOWN, WM_SYSKEYDOWN):
vk = lParam[0] # виртуальный код клавиши
VK_TAB
= 0x09
VK_F4
= 0x73
VK_ESCAPE = 0x1B
VK_LWIN
= 0x5B
VK_RWIN
= 0x5C
alt_pressed = win32api.GetAsyncKeyState(0x12) & 0x8000
# Alt+Tab, Alt+F4, Win, Escape → заглушаем
if vk in (VK_LWIN, VK_RWIN):
return 1
# VK_MENU

4.

if alt_pressed and vk in (VK_TAB, VK_F4):
return 1
# Передаём дальше
return ctypes.windll.user32.CallNextHookEx(None, nCode, wParam, lParam)
def install_keyboard_hook():
global _keyboard_hook
HOOKPROC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_int,
ctypes.wintypes.WPARAM,
ctypes.POINTER(ctypes.c_ulong))
_hook_func = HOOKPROC(low_level_keyboard_handler)
_keyboard_hook = ctypes.windll.user32.SetWindowsHookExW(
13, # WH_KEYBOARD_LL
_hook_func,
ctypes.windll.kernel32.GetModuleHandleW(None),
0
)
# Сохраняем ссылку чтобы не собрал GC
install_keyboard_hook._ref = _hook_func
# Message loop для хука
msg = ctypes.wintypes.MSG()
while ctypes.windll.user32.GetMessageW(ctypes.byref(msg), None, 0, 0) != 0:
ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
ctypes.windll.user32.DispatchMessageW(ctypes.byref(msg))
# ──────────────────────────────────────────────
# ПОТОК МОНИТОРИНГА
# ──────────────────────────────────────────────
def monitor_loop():
"""Каждые 2 секунды проверяет и закрывает запрещённые окна."""
while True:
if lock_active:
close_forbidden_windows()
time.sleep(2)
# ──────────────────────────────────────────────
# УПРАВЛЕНИЕ БЛОКИРОВКОЙ
# ──────────────────────────────────────────────
def activate_lock():
global lock_active
with lock_lock:
if lock_active:
return
lock_active = True
block_switch()
update_tray_icon()
show_notification("
Блокировка ВКЛЮЧЕНА",
"Разрешены только Minecraft и Word/Docs.\n"
"Снять: Ctrl+Alt+U")
print("[LOCK] Блокировка ВКЛЮЧЕНА")
English     Русский Rules