Document the layout fix and the return of the title bar
AGENTS.md: die WinAPI-Regel praeziser gefasst -- verboten bleiben Fenstermanipulation und globale Hooks, passive Layout-Abfragen sind es nicht (und die offizielle VersaGUI macht dasselbe). Dazu die neue Aufloesungsreihenfolge samt Warnung, nicht auf "Zeichen auswerten" zurueckzubauen, die Kollisionsregel bei Tastennamen, der Mehrmonitor-Fix und die Folgen der Titelleiste (welche Eigenbauten dadurch entfallen sind und warum randlos nicht ohne Ruecksprache zurueckkommt). README.md und docs/architecture.md entsprechend: Position statt Zeichen, layoutrichtige Beschriftungen inkl. der Anzeigeaenderung an bestehenden Belegungen, Taskleisten-Eintrag statt randlosem Fenster. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
e1e3d0e466
commit
78e9640dec
3 changed files with 152 additions and 61 deletions
112
AGENTS.md
112
AGENTS.md
|
|
@ -59,6 +59,13 @@ Nutzerorientierte Einführung: [`README.md`](README.md).
|
|||
`read_profile_names()` liest nur die Namen (kein Board-Zugriff, fuer
|
||||
Tab-Beschriftungen im Nur-Lese-Modus, siehe Installierbarkeit-Notiz).
|
||||
|
||||
- `versapad_keylayout.py` (seit 2026-08-29) — passive Abfragen ans aktive
|
||||
Windows-Tastaturlayout: `hid_for_vk()` (Virtual-Key → Scan-Code → HID über
|
||||
`MapVirtualKeyW`) und `key_name()` (Beschriftung über `GetKeyNameTextW`).
|
||||
Optional: auf Nicht-Windows/ohne ctypes bleibt `AVAILABLE` False und
|
||||
`versapad_data` faellt auf seine US-Naeherung zurueck. Zur Abgrenzung
|
||||
gegen die WinAPI-Verbote siehe Domaenenregeln.
|
||||
|
||||
**UI:**
|
||||
- `desktop_viewer.py` — Tkinter, flaches Design, drei unabhängige Modi
|
||||
(Nur lesen / Live-Sync / Programmiermodus, siehe Kritische Domänenregeln),
|
||||
|
|
@ -235,24 +242,47 @@ Protokoll) liegt in `docs/`, siehe „Dokumentation und Verifikation" unten.
|
|||
`vcomb.DEFAULT_PATH`, fällt nur bei fehlender/kaputter Datei auf
|
||||
`default_combined()` zurück. Bei jedem "komisches Layout"-Report hier immer
|
||||
ALLE 3 Profile prüfen, nicht nur das gemeldete.
|
||||
- **Randloses Fenster (seit 2026-08-15):** Die Titelleiste ist per reinem Tk
|
||||
`overrideredirect(True)` ausgeblendet -- **niemals** per ctypes/WinAPI
|
||||
nachhelfen (kein `SetWindowLongW`/`ShowWindow` auf das eigene Fenster),
|
||||
siehe Speicher-Notiz "keine Selbstversteck-Fenstertricks": genau diese
|
||||
Kombination hat in einem anderen Projekt Bitdefender-Fehlalarme
|
||||
ausgelöst. Konsequenzen, die mitgebaut werden müssen: kein
|
||||
Taskleisten-Eintrag (Rückweg nur über das Tray-Icon), kein Ziehen am
|
||||
Rahmen (Kopfzeile ist deshalb per `<B1-Motion>` verschiebbar), keine
|
||||
System-Buttons (eigene `✕`/`—` in der Kopfzeile, beide legen ins Tray)
|
||||
und **keine Resize-Ränder**.
|
||||
- **Der Größen-Anfasser hängt per `place()` an der Fensterecke, nicht
|
||||
gepackt am Ende des Inhalts.** Gepackt verschwindet er, sobald das Fenster
|
||||
kleiner als der Inhalt ist -- also exakt dann, wenn man ihn zum
|
||||
Vergrößern bräuchte. `MIN_W/MIN_H` begrenzen das Verkleinern.
|
||||
- **WinAPI: was verboten bleibt und was nicht.** Verboten sind
|
||||
**Fenstermanipulation am eigenen Fenster** (`SetWindowLongW`,
|
||||
`ShowWindow`) und **globale Eingabehooks** (`SetWindowsHookEx`) — genau
|
||||
diese Kombination hat laut Speicher-Notiz „keine Selbstversteck-
|
||||
Fenstertricks" in einem anderen Projekt Bitdefender-Fehlalarme ausgeloest.
|
||||
Erlaubt und seit 2026-08-29 in Benutzung sind **passive Layout-Abfragen**
|
||||
(`MapVirtualKeyW`, `GetKeyNameTextW` in `versapad_keylayout.py`): sie
|
||||
lesen nur die Tastaturbelegung, fassen weder Fenster noch Prozesse noch
|
||||
den Eingabestrom an; die offizielle VersaGUI (C#) benutzt
|
||||
`GetKeyNameText()` fuer denselben Zweck. Die Grenze verlaeuft also nicht
|
||||
bei „ctypes", sondern bei „greift ins System ein".
|
||||
- **Normales Fenster mit Titelleiste (seit 2026-08-29).** Von 2026-08-15
|
||||
bis dahin lief das Fenster randlos (`overrideredirect(True)`) — das kostet
|
||||
unter Windows zwingend den Taskleisten-Eintrag. Als der ausdruecklich
|
||||
gebraucht wurde, gab es nur drei Wege: Titelleiste zurueck,
|
||||
`SetWindowLongW`+WS_EX_APPWINDOW (siehe Verbot oben) oder ein
|
||||
unsichtbares Proxy-Fenster. Der User hat sich fuer die Titelleiste
|
||||
entschieden. Damit sind ersatzlos entfallen: ziehbare Kopfzeile
|
||||
(`_start_move`/`_on_move`), Groessen-Anfasser unten rechts
|
||||
(`_start_resize`/`_on_resize`, ersetzt durch `self.minsize()`), die
|
||||
eigenen `✕`/`—`-Knoepfe und der `<Unmap>`-Handler, der Minimieren ins Tray
|
||||
umgeleitet hat. **Wer das Fenster wieder randlos machen will, nimmt dem
|
||||
User den Taskleisten-Eintrag weg** — nicht ohne Ruecksprache.
|
||||
- Schliessen (`✕`) legt weiterhin ins Tray statt zu beenden (wie die
|
||||
offizielle VersaGUI, das Programm laeuft im Hintergrund weiter),
|
||||
Minimieren geht jetzt aber ganz normal in die Taskleiste. `Escape`
|
||||
minimiert ebenfalls, statt wie frueher ins Tray zu legen — mit
|
||||
Taskleisten-Eintrag waere „verschwindet spurlos" die unangenehmere
|
||||
Ueberraschung.
|
||||
- Fensterhöhe muss zum Karteninhalt passen: seit die Karten eine Notizzeile
|
||||
haben (`CARD_H` 84 → 114) braucht das Fenster ~960px, sonst liegen
|
||||
Encoder-Bereich und Fußzeile unterhalb des Rands. Wer `CARD_H` ändert,
|
||||
muss `geometry()` mit anpassen.
|
||||
- **Dialogposition wird gegen das ELTERNFENSTER begrenzt, nie gegen
|
||||
`winfo_screenwidth()` (2026-08-29):** Tk meldet dort nur den
|
||||
Hauptbildschirm. Die erste Fassung hat damit geklemmt — lag das
|
||||
Hauptfenster auf einem zweiten Monitor, zog genau diese Begrenzung den
|
||||
Dialog zurueck an den Rand des ersten. Passt der Dialog nicht ins
|
||||
Elternfenster (der Makro-Schritte-Dialog ist breiter als der
|
||||
Action-Dialog), wird er an dessen linker oberer Ecke ausgerichtet statt
|
||||
zentriert.
|
||||
- **Dialoge öffnen über dem Hauptfenster, nicht in der Bildschirmecke
|
||||
(2026-08-28):** `_ModalDialog` baut sich `withdraw()`n auf, positioniert
|
||||
sich in `run()` per `_center_on_parent()` und wird erst dann
|
||||
|
|
@ -290,6 +320,23 @@ Protokoll) liegt in `docs/`, siehe „Dokumentation und Verifikation" unten.
|
|||
baut genau dieses Risiko ein. Konsequenzen, die so bleiben müssen:
|
||||
- Erkannt wird nur, was das fokussierte Fenster erreicht — Win+L,
|
||||
Strg+Alt+Entf und andere vom System abgefangene Kombinationen nicht.
|
||||
- **Welche Taste gemeint ist, wird ueber die PHYSISCHE POSITION
|
||||
aufgeloest, nie ueber das erzeugte Zeichen** (2026-08-29, nach
|
||||
Fehlermeldung aus der Praxis). HID-Keycodes *sind* Positionen: das Board
|
||||
sendet eine Position, erst Windows macht daraus ein Zeichen. Die erste
|
||||
Fassung ging ueber keysym/Zeichen und war auf deutschem Layout
|
||||
entsprechend kaputt — Y und Z landeten vertauscht auf dem Board, ÄÖÜ
|
||||
und #/+ waren gar nicht erfassbar. Reihenfolge jetzt: (1) benannte
|
||||
Tasten ueber den keysym, (2) Zeichentasten ueber
|
||||
`versapad_keylayout.hid_for_vk()`, (3) die alte Naeherung nur noch als
|
||||
Fallback ohne WinAPI. **Nicht auf "Zeichen auswerten" zurueckbauen** —
|
||||
das ist genau der Fehler, der hier behoben wurde.
|
||||
- Schritt (1) ist keine Bequemlichkeit, sondern noetig: `MapVirtualKeyW`
|
||||
liefert fuer die Pfeiltasten denselben Scan-Code wie fuer ihre
|
||||
Numpad-Zwillinge (gemessen: VK_LEFT und VK_NUMPAD4 beide 0x4B, das
|
||||
E0-Praefix von `MAPVK_VK_TO_VSC_EX` bleibt dort aus). Ueber den
|
||||
Scan-Code allein waeren Pfeiltasten nicht von Numpad-Tasten zu
|
||||
unterscheiden.
|
||||
- Das Dropdown bleibt daneben stehen (Korrekturmöglichkeit), es ersetzt
|
||||
die Erkennung nicht und wird von ihr nicht ersetzt.
|
||||
- Modifier werden doppelt ermittelt (selbst mitgeführte Press/Release-Bits
|
||||
|
|
@ -300,19 +347,30 @@ Protokoll) liegt in `docs/`, siehe „Dokumentation und Verifikation" unten.
|
|||
würden die laufende Aufnahme mit ihrem eigenen Klick beantworten.
|
||||
- Makro-Schritte filtern das Win-Bit weg (`allow_win=False`), passend zur
|
||||
Firmware-Regel oben.
|
||||
- Zeichentasten-Labels zeigen eine US-Layout-Näherung, nicht das tatsächlich
|
||||
aktive Windows-Tastaturlayout (die echte VersaGUI löst das über
|
||||
`GetKeyNameText()`, das bilden wir ohne WinAPI-Call nicht nach).
|
||||
**Dieselbe Näherung gilt für die Tastendruck-Erkennung:** HID-Keycodes
|
||||
sind physische US-Tastenpositionen, Tk liefert aber nur keysym/VK-Code des
|
||||
aktiven Layouts — die Position (Scan-Code) wäre dafür nötig und ist ohne
|
||||
WinAPI nicht zu bekommen. Auf deutschem Layout landen Y und Z deshalb
|
||||
vertauscht auf dem Board, und die einzige echte Keysym-Kollision (`minus`:
|
||||
US-Position 0x2D vs. deutsche Position 0x38) ist bewusst zugunsten der
|
||||
US-Position aufgelöst, damit Erkennung und Dropdown-Beschriftung dasselbe
|
||||
sagen. Bei gehaltenem Shift zählt zuerst der VK-Code, weil der keysym dann
|
||||
das verschobene Zeichen ist (deutsch: Shift+7 → `slash`, was sonst
|
||||
fälschlich auf Taste 0x38 zeigen würde).
|
||||
- **Tastenbeschriftungen kommen vom aktiven Layout** (seit 2026-08-29).
|
||||
`versapad_data.hid_key_name()` fragt fuer Zeichentasten
|
||||
`GetKeyNameTextW` (deutsch: HID 0x1C → „Z", 0x34 → „ä"); fuer alles
|
||||
andere bleiben die gepflegten deutschen Namen aus `_SPECIAL_KEYS`
|
||||
(„Enter", „Bild↑", „Num5") — die lesen sich besser als das, was Windows
|
||||
liefert („EINGABE", „4 (ZEHNERTASTATUR)"). Ohne Layout-Abfrage
|
||||
(Nicht-Windows) faellt alles auf die alte US-Naeherung zurueck.
|
||||
Konsequenzen, die man kennen muss:
|
||||
- Bestehende Belegungen aendern ihre **Anzeige**, nicht ihre Daten. Wer
|
||||
frueher im Dropdown „Z" gewaehlt hat, bekam 0x1D — das steht so in der
|
||||
Config und zeigt jetzt wahrheitsgemaess „Y", weil es auf dieser Tastatur
|
||||
ein Y tippt. Das ist keine Regression, sondern der sichtbar gewordene
|
||||
Altfehler.
|
||||
- Namen sind Schluessel (Dropdown, `hid_key_code_for_name()`) und muessen
|
||||
eindeutig bleiben. Es gibt echte Kollisionen: auf deutschem Layout heisst
|
||||
HID 0x31 schlicht „#" — den Namen trug bisher HID 0x32. Der Layoutname
|
||||
gewinnt, der verdraengte US-Name wird als „# (US-Layout)" gekennzeichnet
|
||||
statt verworfen, damit die Taste ansprechbar bleibt.
|
||||
- `hid_key_code_for_name()` akzeptiert weiterhin beide Schreibweisen, bei
|
||||
Kollision gewinnt das Layout. Fuer den MCP-Server heisst das:
|
||||
`set_button_key(key="Z")` trifft die Taste, die auf dieser Tastatur ein
|
||||
Z tippt (0x1C), nicht mehr die US-Position 0x1D.
|
||||
- Ein Layoutwechsel zur Laufzeit wird nicht bemerkt (Namen werden einmal
|
||||
ermittelt und behalten).
|
||||
- Tk-Aufrufe (`self.after()`, Widget-Konfiguration) NIE direkt aus einem
|
||||
Fremdthread (Serial-Thread, pystray-Thread) — hat in einer früheren
|
||||
Version einen stillen Absturz verursacht. Threads legen Ergebnisse nur in
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue