mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-11 10:56:10 +00:00
Only GNOME was recognised, and everything else was handed to KWin. On i3, XFCE, Cinnamon, MATE, sway and the rest, Dikte wrote an entry into kglobalshortcutsrc that nothing reads, called the session KDE, and promised that the keys would work after the next login. They never did. There is no backend to write for any of them. The /dev/input listener is already desktop-agnostic, so those sessions are the case macOS has always been: no registry, nothing to install, nothing to remove, and the combination held by the running process. One backend() function decides which of the four this session has, and the name shown, the status read back, what Install writes, what Settings explains and what the installer promises are all taken from it, so they cannot disagree. A desktop now only counts when the program that writes its registry is there too. A GNOME session without gsettings and a Plasma one without kwriteconfig6 fall to the listener rather than to a file, which is also how Plasma 5 stops erroring on a kwriteconfig6 it never had. What Settings shows on those desktops is the truth: no Install button, no KWin, no listener checkbox (it is the mechanism, not a choice), what reading /dev/input costs, that the focused application sees the keys too, and the command to bind if you would rather your desktop owned them. The evdev listener records what it is listening for the way the Carbon one does, so the status line has something to say there at all. Closes #28
86 lines
3.2 KiB
Python
86 lines
3.2 KiB
Python
"""The socket the running instance listens on, and one request over it.
|
|
|
|
A command typed at a terminal is answered rather than only obeyed: the reply
|
|
carries the transcript, the agent's answer, or the reason nothing happened,
|
|
which is what lets a script wait for a dictation instead of guessing when it is
|
|
done. One JSON object goes each way per connection. A bare verb is still
|
|
understood, because that is what earlier versions sent and what a stale KDE
|
|
shortcut may still send.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
from PyQt6.QtNetwork import QLocalSocket
|
|
|
|
SERVER_NAME = "dikte-" + str(os.getuid())
|
|
|
|
# Long enough for a process that is already running to answer, short enough that
|
|
# "nothing is running" is not a noticeable pause in front of a key press.
|
|
CONNECT_MS = 800
|
|
|
|
|
|
def script_path():
|
|
return os.path.realpath(
|
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "dikte.py")
|
|
)
|
|
|
|
|
|
def command_for(verb):
|
|
"""The command line a desktop's shortcut runs for one of the verbs.
|
|
|
|
Also what Settings shows an i3 or XFCE user to paste into their own
|
|
configuration, since there is no registry there for Dikte to write into.
|
|
"""
|
|
return f"{sys.executable} {script_path()} {verb}"
|
|
|
|
|
|
def send(cmd, wait=False, timeout=0, **args):
|
|
"""Send one request; the reply, or None when no instance is running.
|
|
|
|
`wait` asks the instance to hold its reply back until the job the request
|
|
started is over, which is how a terminal gets the transcript rather than
|
|
only the fact that recording began. `timeout` bounds that wait in seconds;
|
|
0 waits for as long as the job takes.
|
|
"""
|
|
sock = QLocalSocket()
|
|
sock.connectToServer(SERVER_NAME)
|
|
if not sock.waitForConnected(CONNECT_MS):
|
|
return None
|
|
|
|
request = {"cmd": cmd}
|
|
request.update({key: value for key, value in args.items() if value is not None})
|
|
if wait:
|
|
request["wait"] = True
|
|
# A verb carrying nothing goes as the bare word it used to be, so that an
|
|
# instance still running the older code obeys it: that is the one request
|
|
# that has to work across an update, since it is how you install the update.
|
|
line = cmd if list(request) == ["cmd"] else json.dumps(request)
|
|
sock.write((line + "\n").encode("utf-8"))
|
|
sock.flush()
|
|
sock.waitForBytesWritten(CONNECT_MS)
|
|
|
|
limit = (int(timeout * 1000) if timeout else -1) if wait else CONNECT_MS
|
|
buffer = b""
|
|
while b"\n" not in buffer:
|
|
if not sock.waitForReadyRead(limit):
|
|
break
|
|
buffer += bytes(sock.readAll())
|
|
sock.disconnectFromServer()
|
|
|
|
line = buffer.decode("utf-8", "replace").strip()
|
|
if not line:
|
|
# An instance from before replies existed answers by staying silent, and
|
|
# for a fire-and-forget verb that silence means it went through. A wait
|
|
# that ends this way did not: the run never reported back.
|
|
return ({"ok": False, "legacy": True,
|
|
"error": "the running instance is too old to answer; "
|
|
"reload it with: dikte restart"}
|
|
if wait else {"ok": True, "legacy": True})
|
|
try:
|
|
reply = json.loads(line)
|
|
except json.JSONDecodeError:
|
|
return {"ok": True, "legacy": True}
|
|
return reply if isinstance(reply, dict) else {"ok": True, "legacy": True}
|