neues bootlogo
This commit is contained in:
parent
393ea0d190
commit
4471d845d3
|
|
@ -2,7 +2,7 @@
|
||||||
thin-client-01 ansible_host=192.168.178.241
|
thin-client-01 ansible_host=192.168.178.241
|
||||||
#thin-client-02 ansible_host=192.168.0.29
|
#thin-client-02 ansible_host=192.168.0.29
|
||||||
#thin-client-03 ansible_host=192.168.0.23
|
#thin-client-03 ansible_host=192.168.0.23
|
||||||
|
#thin-client-04 ansible host=192.168.0.
|
||||||
[rdp_clients:vars]
|
[rdp_clients:vars]
|
||||||
ansible_user=root
|
ansible_user=root
|
||||||
#ansible_ssh_private_key_file=~/.ssh/id_rsa
|
#ansible_ssh_private_key_file=~/.ssh/id_rsa
|
||||||
|
|
|
||||||
|
|
@ -201,8 +201,6 @@
|
||||||
# Start USB automount (udiskie)
|
# Start USB automount (udiskie)
|
||||||
udiskie --tray --automount --notify &
|
udiskie --tray --automount --notify &
|
||||||
|
|
||||||
# Session Watcher läuft als systemd-Service
|
|
||||||
|
|
||||||
# Start RDP Launcher
|
# Start RDP Launcher
|
||||||
/usr/local/bin/rdp-launcher.sh &
|
/usr/local/bin/rdp-launcher.sh &
|
||||||
|
|
||||||
|
|
@ -356,19 +354,6 @@
|
||||||
# Auto-generated by RDP Thin Client Setup
|
# Auto-generated by RDP Thin Client Setup
|
||||||
force: no
|
force: no
|
||||||
|
|
||||||
# === INSTALL PYTHON DEPENDENCIES ===
|
|
||||||
- name: Install python-xlib via pip
|
|
||||||
shell: pip3 install python-xlib --break-system-packages
|
|
||||||
args:
|
|
||||||
creates: /usr/local/lib/python3.11/dist-packages/Xlib
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
- name: Alternative - Install python-xlib from apt
|
|
||||||
apt:
|
|
||||||
name: python3-xlib
|
|
||||||
state: present
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
# === COPY SCRIPTS ===
|
# === COPY SCRIPTS ===
|
||||||
- name: Copy RDP Profile Manager
|
- name: Copy RDP Profile Manager
|
||||||
copy:
|
copy:
|
||||||
|
|
@ -382,60 +367,6 @@
|
||||||
dest: /usr/local/bin/rdp-launcher.sh
|
dest: /usr/local/bin/rdp-launcher.sh
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
|
|
||||||
- name: Copy Session Watcher
|
|
||||||
copy:
|
|
||||||
src: ../files/session-watcher.py
|
|
||||||
dest: /usr/local/bin/session-watcher.py
|
|
||||||
mode: '0755'
|
|
||||||
|
|
||||||
# === SESSION WATCHER SYSTEMD SERVICE ===
|
|
||||||
- name: Create systemd user service directory
|
|
||||||
file:
|
|
||||||
path: /home/{{ thin_client_user }}/.config/systemd/user
|
|
||||||
state: directory
|
|
||||||
owner: "{{ thin_client_user }}"
|
|
||||||
group: "{{ thin_client_user }}"
|
|
||||||
mode: '0755'
|
|
||||||
|
|
||||||
- name: Create Session Watcher systemd service
|
|
||||||
copy:
|
|
||||||
dest: /home/{{ thin_client_user }}/.config/systemd/user/session-watcher.service
|
|
||||||
owner: "{{ thin_client_user }}"
|
|
||||||
group: "{{ thin_client_user }}"
|
|
||||||
mode: '0644'
|
|
||||||
content: |
|
|
||||||
[Unit]
|
|
||||||
Description=RDP Session Watcher - Exit Hotkey Monitor
|
|
||||||
After=graphical.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/usr/local/bin/session-watcher.py
|
|
||||||
Restart=always
|
|
||||||
RestartSec=3
|
|
||||||
Environment=DISPLAY=:0
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target
|
|
||||||
|
|
||||||
- name: Create default.target.wants directory
|
|
||||||
file:
|
|
||||||
path: /home/{{ thin_client_user }}/.config/systemd/user/default.target.wants
|
|
||||||
state: directory
|
|
||||||
owner: "{{ thin_client_user }}"
|
|
||||||
group: "{{ thin_client_user }}"
|
|
||||||
mode: '0755'
|
|
||||||
|
|
||||||
- name: Create symlink to enable Session Watcher service
|
|
||||||
file:
|
|
||||||
src: /home/{{ thin_client_user }}/.config/systemd/user/session-watcher.service
|
|
||||||
dest: /home/{{ thin_client_user }}/.config/systemd/user/default.target.wants/session-watcher.service
|
|
||||||
owner: "{{ thin_client_user }}"
|
|
||||||
group: "{{ thin_client_user }}"
|
|
||||||
state: link
|
|
||||||
force: yes
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
# === BRANDING ===
|
# === BRANDING ===
|
||||||
- name: Check if branding files exist
|
- name: Check if branding files exist
|
||||||
stat:
|
stat:
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 44 KiB |
|
|
@ -0,0 +1,146 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
BTX-Style Boot Logo Generator
|
||||||
|
Erstellt ein professionelles Boot-Logo im BTX/Bildschirmtext Terminal-Stil
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
import os
|
||||||
|
|
||||||
|
# BTX-typische Farbpalette
|
||||||
|
BTX_BLACK = (0, 0, 0)
|
||||||
|
BTX_CYAN = (0, 255, 255)
|
||||||
|
BTX_MAGENTA = (255, 0, 255)
|
||||||
|
BTX_YELLOW = (255, 255, 0)
|
||||||
|
BTX_WHITE = (255, 255, 255)
|
||||||
|
BTX_GREEN = (0, 255, 0)
|
||||||
|
BTX_BLUE = (0, 100, 200)
|
||||||
|
BTX_DARK_CYAN = (0, 180, 180)
|
||||||
|
|
||||||
|
# Bildgröße
|
||||||
|
WIDTH = 800
|
||||||
|
HEIGHT = 600
|
||||||
|
|
||||||
|
# Erstelle Bild
|
||||||
|
img = Image.new('RGB', (WIDTH, HEIGHT), BTX_BLACK)
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
# Versuche Monospace-Font zu laden (BTX-Style)
|
||||||
|
try:
|
||||||
|
# Verschiedene Monospace-Fonts ausprobieren
|
||||||
|
font_paths = [
|
||||||
|
'/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf',
|
||||||
|
'/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf',
|
||||||
|
'/usr/share/fonts/truetype/liberation2/LiberationMono-Bold.ttf',
|
||||||
|
'/System/Library/Fonts/Monaco.ttf',
|
||||||
|
]
|
||||||
|
|
||||||
|
font_large = None
|
||||||
|
font_medium = None
|
||||||
|
font_small = None
|
||||||
|
|
||||||
|
for font_path in font_paths:
|
||||||
|
if os.path.exists(font_path):
|
||||||
|
font_large = ImageFont.truetype(font_path, 72)
|
||||||
|
font_medium = ImageFont.truetype(font_path, 42)
|
||||||
|
font_small = ImageFont.truetype(font_path, 28)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not font_large:
|
||||||
|
raise Exception("No font found")
|
||||||
|
|
||||||
|
except:
|
||||||
|
print("Monospace font nicht gefunden, nutze Default-Font")
|
||||||
|
font_large = ImageFont.load_default()
|
||||||
|
font_medium = ImageFont.load_default()
|
||||||
|
font_small = ImageFont.load_default()
|
||||||
|
|
||||||
|
# BTX-Style Border (charakteristischer Block-Rahmen)
|
||||||
|
border_width = 3
|
||||||
|
for i in range(border_width):
|
||||||
|
draw.rectangle([i, i, WIDTH-1-i, HEIGHT-1-i], outline=BTX_CYAN)
|
||||||
|
|
||||||
|
# Terminal/Computer ASCII-Art Symbol (oben)
|
||||||
|
terminal_y = 80
|
||||||
|
terminal_lines = [
|
||||||
|
"╔════════════════╗",
|
||||||
|
"║ ▓▓▓▓▓▓▓▓▓▓▓▓ ║",
|
||||||
|
"║ ▓░░░░░░░░░░▓ ║",
|
||||||
|
"║ ▓░░░░░░░░░░▓ ║",
|
||||||
|
"║ ▓▓▓▓▓▓▓▓▓▓▓▓ ║",
|
||||||
|
"╚═══════╦╦═══════╝",
|
||||||
|
" ║║ "
|
||||||
|
]
|
||||||
|
|
||||||
|
# Zeichne Terminal-Symbol zentriert
|
||||||
|
for i, line in enumerate(terminal_lines):
|
||||||
|
bbox = draw.textbbox((0, 0), line, font=font_medium)
|
||||||
|
text_width = bbox[2] - bbox[0]
|
||||||
|
x = (WIDTH - text_width) // 2
|
||||||
|
y = terminal_y + (i * 35)
|
||||||
|
# Doppelte Zeichen für BTX-Block-Effekt
|
||||||
|
draw.text((x+2, y+2), line, font=font_medium, fill=BTX_BLUE) # Schatten
|
||||||
|
draw.text((x, y), line, font=font_medium, fill=BTX_CYAN)
|
||||||
|
|
||||||
|
# Haupttext "RDP THIN CLIENT"
|
||||||
|
main_text = "RDP THIN CLIENT"
|
||||||
|
bbox = draw.textbbox((0, 0), main_text, font=font_large)
|
||||||
|
text_width = bbox[2] - bbox[0]
|
||||||
|
text_x = (WIDTH - text_width) // 2
|
||||||
|
text_y = 330
|
||||||
|
|
||||||
|
# BTX-Style Doppel-Rendering für Glow-Effekt
|
||||||
|
draw.text((text_x+3, text_y+3), main_text, font=font_large, fill=BTX_BLUE) # Schatten
|
||||||
|
draw.text((text_x, text_y), main_text, font=font_large, fill=BTX_YELLOW)
|
||||||
|
|
||||||
|
# Scanline-Effekt (BTX/CRT-Monitor-Look)
|
||||||
|
for y in range(0, HEIGHT, 4):
|
||||||
|
draw.line([(border_width+5, y), (WIDTH-border_width-5, y)], fill=(10, 10, 10), width=1)
|
||||||
|
|
||||||
|
# Firmen-Branding unten
|
||||||
|
company_text = "HackerSoft™"
|
||||||
|
bbox = draw.textbbox((0, 0), company_text, font=font_medium)
|
||||||
|
text_width = bbox[2] - bbox[0]
|
||||||
|
company_x = (WIDTH - text_width) // 2
|
||||||
|
company_y = 470
|
||||||
|
|
||||||
|
draw.text((company_x+2, company_y+2), company_text, font=font_medium, fill=BTX_BLUE)
|
||||||
|
draw.text((company_x, company_y), company_text, font=font_medium, fill=BTX_MAGENTA)
|
||||||
|
|
||||||
|
# "Hacker-Net Telekommunikation" Subtext
|
||||||
|
subtext = "Hacker-Net Telekommunikation"
|
||||||
|
bbox = draw.textbbox((0, 0), subtext, font=font_small)
|
||||||
|
text_width = bbox[2] - bbox[0]
|
||||||
|
sub_x = (WIDTH - text_width) // 2
|
||||||
|
sub_y = 520
|
||||||
|
|
||||||
|
draw.text((sub_x+1, sub_y+1), subtext, font=font_small, fill=BTX_BLUE)
|
||||||
|
draw.text((sub_x, sub_y), subtext, font=font_small, fill=BTX_DARK_CYAN)
|
||||||
|
|
||||||
|
# Dekorative Ecken (BTX-Style Blocks)
|
||||||
|
block_size = 20
|
||||||
|
positions = [
|
||||||
|
(border_width+10, border_width+10), # Oben links
|
||||||
|
(WIDTH-border_width-30, border_width+10), # Oben rechts
|
||||||
|
(border_width+10, HEIGHT-border_width-30), # Unten links
|
||||||
|
(WIDTH-border_width-30, HEIGHT-border_width-30) # Unten rechts
|
||||||
|
]
|
||||||
|
|
||||||
|
for x, y in positions:
|
||||||
|
draw.rectangle([x, y, x+block_size, y+block_size], fill=BTX_MAGENTA, outline=BTX_YELLOW, width=2)
|
||||||
|
|
||||||
|
# Status-Indicator (BTX-typisch)
|
||||||
|
status_text = "● SYSTEM READY"
|
||||||
|
bbox = draw.textbbox((0, 0), status_text, font=font_small)
|
||||||
|
text_width = bbox[2] - bbox[0]
|
||||||
|
status_x = (WIDTH - text_width) // 2
|
||||||
|
status_y = HEIGHT - 60
|
||||||
|
|
||||||
|
draw.text((status_x+1, status_y+1), status_text, font=font_small, fill=BTX_BLUE)
|
||||||
|
draw.text((status_x, status_y), status_text, font=font_small, fill=BTX_GREEN)
|
||||||
|
|
||||||
|
# Speichern
|
||||||
|
output_path = os.path.join(os.path.dirname(__file__), 'boot-logo.png')
|
||||||
|
img.save(output_path, 'PNG')
|
||||||
|
print(f"BTX-Style Boot-Logo erstellt: {output_path}")
|
||||||
|
print(f"Größe: {WIDTH}x{HEIGHT}")
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Minimal Terminal-Style Boot Logo Generator
|
||||||
|
Nur Text, wie in einem echten Terminal
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Terminal-Farbschema
|
||||||
|
BG_BLACK = (12, 12, 15)
|
||||||
|
TEXT_GREEN = (0, 255, 100)
|
||||||
|
TEXT_WHITE = (230, 230, 230)
|
||||||
|
TEXT_GRAY = (120, 120, 120)
|
||||||
|
TEXT_CYAN = (100, 200, 255)
|
||||||
|
|
||||||
|
# Bildgröße
|
||||||
|
WIDTH = 800
|
||||||
|
HEIGHT = 600
|
||||||
|
|
||||||
|
# Erstelle Bild
|
||||||
|
img = Image.new('RGB', (WIDTH, HEIGHT), BG_BLACK)
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
# Versuche Monospace-Font zu laden
|
||||||
|
try:
|
||||||
|
font_paths = [
|
||||||
|
'/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',
|
||||||
|
'/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf',
|
||||||
|
'/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf',
|
||||||
|
'/usr/share/fonts/truetype/liberation2/LiberationMono-Regular.ttf',
|
||||||
|
]
|
||||||
|
|
||||||
|
font_large = None
|
||||||
|
font_medium = None
|
||||||
|
font_small = None
|
||||||
|
|
||||||
|
for font_path in font_paths:
|
||||||
|
if os.path.exists(font_path):
|
||||||
|
font_large = ImageFont.truetype(font_path, 56)
|
||||||
|
font_medium = ImageFont.truetype(font_path, 28)
|
||||||
|
font_small = ImageFont.truetype(font_path, 22)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not font_large:
|
||||||
|
raise Exception("No font found")
|
||||||
|
|
||||||
|
except:
|
||||||
|
print("Monospace font nicht gefunden, nutze Default-Font")
|
||||||
|
font_large = ImageFont.load_default()
|
||||||
|
font_medium = ImageFont.load_default()
|
||||||
|
font_small = ImageFont.load_default()
|
||||||
|
|
||||||
|
# Start-Position
|
||||||
|
x_offset = 60
|
||||||
|
y_offset = 120
|
||||||
|
line_height = 40
|
||||||
|
|
||||||
|
# Terminal-Output simulieren
|
||||||
|
lines = [
|
||||||
|
("Booting system...", TEXT_GRAY, font_medium),
|
||||||
|
("", TEXT_GRAY, font_medium),
|
||||||
|
("RDP THIN CLIENT SYSTEM", TEXT_WHITE, font_large),
|
||||||
|
("", TEXT_GRAY, font_medium),
|
||||||
|
("[ OK ] Remote Desktop Protocol initialized", TEXT_GREEN, font_medium),
|
||||||
|
("[ OK ] Graphics subsystem ready", TEXT_GREEN, font_medium),
|
||||||
|
("[ OK ] Audio redirection enabled", TEXT_GREEN, font_medium),
|
||||||
|
("[ OK ] USB device support active", TEXT_GREEN, font_medium),
|
||||||
|
("[ OK ] Network services started", TEXT_GREEN, font_medium),
|
||||||
|
("", TEXT_GRAY, font_medium),
|
||||||
|
]
|
||||||
|
|
||||||
|
current_y = y_offset
|
||||||
|
for line_text, color, font in lines:
|
||||||
|
if line_text: # Leere Zeilen überspringen
|
||||||
|
draw.text((x_offset, current_y), line_text, font=font, fill=color)
|
||||||
|
# Größere Abstände für Title
|
||||||
|
if font == font_large:
|
||||||
|
current_y += 80
|
||||||
|
else:
|
||||||
|
current_y += line_height
|
||||||
|
|
||||||
|
# Cursor am Ende
|
||||||
|
cursor_y = current_y
|
||||||
|
draw.rectangle([x_offset, cursor_y, x_offset + 12, cursor_y + 22], fill=TEXT_GREEN)
|
||||||
|
|
||||||
|
# Footer (rechts neben dem Cursor, zentriert)
|
||||||
|
footer_text = "HackerSoft · Hacker-Net Telekommunikation"
|
||||||
|
footer_x = x_offset + 20 # 20px rechts vom Cursor
|
||||||
|
draw.text((footer_x, cursor_y), footer_text, font=font_small, fill=TEXT_GRAY)
|
||||||
|
|
||||||
|
# Speichern
|
||||||
|
output_path = os.path.join(os.path.dirname(__file__), 'boot-logo.png')
|
||||||
|
img.save(output_path, 'PNG')
|
||||||
|
print(f"Minimal Terminal Boot-Logo erstellt: {output_path}")
|
||||||
|
print(f"Größe: {WIDTH}x{HEIGHT}")
|
||||||
|
|
@ -1,201 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Session Watcher - Überwacht Tastenkombination zum Verlassen der RDP-Sitzung
|
|
||||||
Speichere als: files/session-watcher.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import configparser
|
|
||||||
from pathlib import Path
|
|
||||||
from Xlib import X, XK, display
|
|
||||||
from Xlib.ext import record
|
|
||||||
from Xlib.protocol import rq
|
|
||||||
|
|
||||||
CONFIG_FILE = Path.home() / ".config" / "rdp-profiles" / "profiles.ini"
|
|
||||||
|
|
||||||
class SessionWatcher:
|
|
||||||
def __init__(self):
|
|
||||||
self.display = display.Display()
|
|
||||||
self.root = self.display.screen().root
|
|
||||||
self.ctx = None
|
|
||||||
self.pressed_keys = set()
|
|
||||||
|
|
||||||
# Default hotkey
|
|
||||||
self.exit_hotkey = "Control+Alt+q"
|
|
||||||
|
|
||||||
# Parse hotkey from active profile (if any)
|
|
||||||
self.load_hotkey()
|
|
||||||
|
|
||||||
def load_hotkey(self):
|
|
||||||
"""Lädt Hotkey aus dem ersten aktiven Profil"""
|
|
||||||
if not CONFIG_FILE.exists():
|
|
||||||
return
|
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(CONFIG_FILE)
|
|
||||||
|
|
||||||
# Nutze ersten Profil-Hotkey als Standard
|
|
||||||
for section in config.sections():
|
|
||||||
hotkey = config[section].get('exit_hotkey', '').strip()
|
|
||||||
if hotkey:
|
|
||||||
self.exit_hotkey = hotkey
|
|
||||||
break
|
|
||||||
|
|
||||||
print(f"Exit hotkey: {self.exit_hotkey}")
|
|
||||||
|
|
||||||
def parse_hotkey(self):
|
|
||||||
"""Wandelt Hotkey-String in Keycodes um"""
|
|
||||||
parts = self.exit_hotkey.lower().split('+')
|
|
||||||
|
|
||||||
modifiers = []
|
|
||||||
key = None
|
|
||||||
|
|
||||||
for part in parts:
|
|
||||||
part = part.strip()
|
|
||||||
if part in ['control', 'ctrl']:
|
|
||||||
modifiers.append('Control_L')
|
|
||||||
modifiers.append('Control_R')
|
|
||||||
elif part in ['alt']:
|
|
||||||
modifiers.append('Alt_L')
|
|
||||||
modifiers.append('Alt_R')
|
|
||||||
elif part in ['shift']:
|
|
||||||
modifiers.append('Shift_L')
|
|
||||||
modifiers.append('Shift_R')
|
|
||||||
elif part in ['super', 'win', 'meta']:
|
|
||||||
modifiers.append('Super_L')
|
|
||||||
modifiers.append('Super_R')
|
|
||||||
else:
|
|
||||||
key = part
|
|
||||||
|
|
||||||
return modifiers, key
|
|
||||||
|
|
||||||
def check_rdp_running(self):
|
|
||||||
"""Prüft ob FreeRDP läuft"""
|
|
||||||
try:
|
|
||||||
result = subprocess.run(['pgrep', '-x', 'xfreerdp'],
|
|
||||||
capture_output=True, text=True)
|
|
||||||
return result.returncode == 0
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def kill_rdp(self):
|
|
||||||
"""Beendet alle RDP-Verbindungen - AGGRESSIV!"""
|
|
||||||
print("Killing RDP sessions...")
|
|
||||||
try:
|
|
||||||
# Erst SIGTERM versuchen (sauber)
|
|
||||||
result = subprocess.run(['pkill', '-15', 'xfreerdp'], check=False)
|
|
||||||
|
|
||||||
# Warte kurz
|
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
||||||
# Prüfe ob noch läuft
|
|
||||||
check = subprocess.run(['pgrep', '-x', 'xfreerdp'],
|
|
||||||
capture_output=True, check=False)
|
|
||||||
|
|
||||||
if check.returncode == 0:
|
|
||||||
# Immer noch da? KILL IT WITH FIRE! (SIGKILL)
|
|
||||||
print("RDP process still running, using SIGKILL...")
|
|
||||||
subprocess.run(['pkill', '-9', 'xfreerdp'], check=False)
|
|
||||||
subprocess.run(['pkill', '-9', 'xfreerdp3'], check=False)
|
|
||||||
time.sleep(0.3)
|
|
||||||
|
|
||||||
# Auch xfreerdp3 killen (falls vorhanden)
|
|
||||||
subprocess.run(['pkill', '-9', 'xfreerdp3'], check=False)
|
|
||||||
|
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
||||||
# Starte Profile Manager
|
|
||||||
subprocess.Popen(['/usr/local/bin/rdp-profile-manager.py'])
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error killing RDP: {e}")
|
|
||||||
|
|
||||||
def event_handler(self, reply):
|
|
||||||
"""Behandelt Tastatur-Events"""
|
|
||||||
if reply.category != record.FromServer:
|
|
||||||
return
|
|
||||||
if reply.client_swapped:
|
|
||||||
return
|
|
||||||
if not len(reply.data) or reply.data[0] < 2:
|
|
||||||
return
|
|
||||||
|
|
||||||
data = reply.data
|
|
||||||
while len(data):
|
|
||||||
event, data = rq.EventField(None).parse_binary_value(
|
|
||||||
data, self.display.display, None, None)
|
|
||||||
|
|
||||||
if event.type == X.KeyPress:
|
|
||||||
keysym = self.display.keycode_to_keysym(event.detail, 0)
|
|
||||||
key_name = XK.keysym_to_string(keysym)
|
|
||||||
|
|
||||||
if key_name:
|
|
||||||
self.pressed_keys.add(key_name)
|
|
||||||
self.check_hotkey()
|
|
||||||
|
|
||||||
elif event.type == X.KeyRelease:
|
|
||||||
keysym = self.display.keycode_to_keysym(event.detail, 0)
|
|
||||||
key_name = XK.keysym_to_string(keysym)
|
|
||||||
|
|
||||||
if key_name and key_name in self.pressed_keys:
|
|
||||||
self.pressed_keys.discard(key_name)
|
|
||||||
|
|
||||||
def check_hotkey(self):
|
|
||||||
"""Prüft ob Exit-Hotkey gedrückt wurde"""
|
|
||||||
modifiers, key = self.parse_hotkey()
|
|
||||||
|
|
||||||
# Check if any modifier variant is pressed
|
|
||||||
modifier_pressed = False
|
|
||||||
for mod in modifiers:
|
|
||||||
if mod in self.pressed_keys:
|
|
||||||
modifier_pressed = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not modifier_pressed:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check key
|
|
||||||
if key and key.lower() in [k.lower() for k in self.pressed_keys]:
|
|
||||||
# Hotkey matched!
|
|
||||||
if self.check_rdp_running():
|
|
||||||
print("Exit hotkey detected - killing RDP session")
|
|
||||||
self.pressed_keys.clear() # Prevent multiple triggers
|
|
||||||
self.kill_rdp()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Startet Event-Loop"""
|
|
||||||
# Setup record extension
|
|
||||||
self.ctx = self.display.record_create_context(
|
|
||||||
0,
|
|
||||||
[record.AllClients],
|
|
||||||
[{
|
|
||||||
'core_requests': (0, 0),
|
|
||||||
'core_replies': (0, 0),
|
|
||||||
'ext_requests': (0, 0, 0, 0),
|
|
||||||
'ext_replies': (0, 0, 0, 0),
|
|
||||||
'delivered_events': (0, 0),
|
|
||||||
'device_events': (X.KeyPress, X.KeyRelease),
|
|
||||||
'errors': (0, 0),
|
|
||||||
'client_started': False,
|
|
||||||
'client_died': False,
|
|
||||||
}]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.display.record_enable_context(self.ctx, self.event_handler)
|
|
||||||
self.display.record_free_context(self.ctx)
|
|
||||||
|
|
||||||
print("Session watcher started - monitoring for exit hotkey")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
event = self.display.next_event()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
try:
|
|
||||||
watcher = SessionWatcher()
|
|
||||||
watcher.run()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\nSession watcher stopped")
|
|
||||||
sys.exit(0)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
Loading…
Reference in New Issue