mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-11 19:06:11 +00:00
Give throwing a recording away a key of its own
Stopping a recording is the step there is no taking back. It is what sends the audio off, and a moment later the sentence you did not mean to dictate is in the clipboard and pasted into whatever window you were typing in. The tray menu was the only way out, and by the time it is open the recording has already gone. Discarding needed to be as quick as starting, which means a key. Ctrl+Alt+Space rather than Escape: the combination the recording started with, one modifier along, so the two are one gesture with a modifier between them. Escape is the obvious choice and the wrong one, because it belongs to whichever window has focus, and while you are talking something else usually has it. It works on a dictation and on a command for the agent alike, since the one you want to take back is the one that is running. That made four global shortcuts, and four is the number at which three copies of the same forty lines stop being a coincidence. The command line already had a table of them; hotkey.py now holds it, and the settings window reads it too, so a shortcut is a row rather than a combination box, two buttons, a status label and three methods written out again. The window no longer takes the commands to run as arguments either, because ipc.command_for already knows them from the verb. Adding the fourth key is what this buys: one line in the table and one call per row. Both places a key can live are told about it. The listener catching the press and the KDE shortcut arriving behind it go through the same echo guard the toggle already had, so the two are one discard rather than two, and the tray calls the inner method as it already did for the toggle. Ctrl+Space and Ctrl+Alt+Space land on the same evdev key code, so there is a test for the modifier matching that keeps them apart. install.sh takes the second key as a second argument and refuses to register two of the same. It hands both to `dikte shortcut install` rather than writing kglobalshortcutsrc itself, which is what makes it work on GNOME, and what finally puts the chosen key in the settings as well: the built-in listener reads it from there, so a key written to only one of the two places was a key that half worked. Left empty in the settings window the discard key stays empty, unlike the dictation shortcut which falls back to Ctrl+Space: a recording can always be thrown away from the tray menu, so there is nothing to guarantee here.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""GNOME/KDE global-shortcut installation plus a built-in evdev listener."""
|
||||
|
||||
import ast
|
||||
import collections
|
||||
import glob
|
||||
import os
|
||||
import pathlib
|
||||
@@ -16,6 +17,7 @@ from PyQt6.QtCore import QObject, pyqtSignal
|
||||
from i18n import t
|
||||
|
||||
DESKTOP_ID = "dikte-toggle.desktop"
|
||||
CANCEL_DESKTOP_ID = "dikte-cancel.desktop"
|
||||
MEETING_DESKTOP_ID = "dikte-meeting.desktop"
|
||||
ASK_DESKTOP_ID = "dikte-ask.desktop"
|
||||
APPLICATIONS_DIR = pathlib.Path.home() / ".local/share/applications"
|
||||
@@ -24,6 +26,25 @@ SHORTCUTS_FILE = pathlib.Path.home() / ".config/kglobalshortcutsrc"
|
||||
GNOME_MEDIA_SCHEMA = "org.gnome.settings-daemon.plugins.media-keys"
|
||||
GNOME_BINDING_SCHEMA = "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding"
|
||||
|
||||
Shortcut = collections.namedtuple("Shortcut", "verb desktop_id name setting fallback")
|
||||
|
||||
# Every global shortcut in one place, because there are four of them and the
|
||||
# command line, the settings window and the installer each used to carry their
|
||||
# own copy of the list. `fallback` is what to register when the setting is
|
||||
# empty: only the toggle has one, since it is the key the application is
|
||||
# unusable without.
|
||||
SHORTCUTS = {
|
||||
"toggle": Shortcut("toggle", DESKTOP_ID, "Dikte: start/stop recording",
|
||||
"shortcut", "Ctrl+Space"),
|
||||
"cancel": Shortcut("cancel", CANCEL_DESKTOP_ID, "Dikte: discard the recording",
|
||||
"cancel_shortcut", ""),
|
||||
"ask": Shortcut("ask", ASK_DESKTOP_ID, "Dikte: ask Claude Code",
|
||||
"assistant_shortcut", ""),
|
||||
"meeting": Shortcut("meeting", MEETING_DESKTOP_ID,
|
||||
"Dikte: start/end a meeting recording",
|
||||
"meeting_shortcut", ""),
|
||||
}
|
||||
|
||||
# --- evdev key codes (linux/input-event-codes.h) --------------------------
|
||||
|
||||
EV_KEY = 0x01
|
||||
@@ -371,14 +392,18 @@ def remove_kde_shortcut(desktop_id=DESKTOP_ID):
|
||||
(APPLICATIONS_DIR / desktop_id).unlink(missing_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
subprocess.run(
|
||||
["kwriteconfig6", "--notify", "--file", "kglobalshortcutsrc",
|
||||
"--group", "services", "--group", desktop_id, "--key", "_launch", "--delete"],
|
||||
capture_output=True, timeout=10,
|
||||
)
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
pass
|
||||
# kwriteconfig6 deletes keys rather than groups, so both of the ones KDE
|
||||
# keeps in there go and the empty group is left behind harmlessly.
|
||||
for key in ("_launch", "_k_friendly_name"):
|
||||
try:
|
||||
subprocess.run(
|
||||
["kwriteconfig6", "--notify", "--file", "kglobalshortcutsrc",
|
||||
"--group", "services", "--group", desktop_id,
|
||||
"--key", key, "--delete"],
|
||||
capture_output=True, timeout=10,
|
||||
)
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
pass
|
||||
|
||||
|
||||
def kde_shortcut_status(desktop_id=DESKTOP_ID):
|
||||
|
||||
Reference in New Issue
Block a user