From 6e45d64ee065b34e049095fa5eed6f9d82a92ad7 Mon Sep 17 00:00:00 2001 From: Lucas Rayan Date: Sat, 3 Oct 2026 09:46:53 -0300 Subject: [PATCH] Comandos 'copiado consertar' e 'copiado ajuda' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - consertar: recria/recarrega o atalho Win+V e inicia o app se estiver parado - instalar.sh e desinstalar.sh passam a usar os comandos do próprio app Co-Authored-By: Claude Opus 5.5 --- README.md | 20 ++++----- copiado.py | 116 +++++++++++++++++++++++++++++++++++++++++++++++-- desinstalar.sh | 11 +---- instalar.sh | 28 ++---------- 4 files changed, 125 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 92d9bfd..1e47a3c 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,6 @@ A área de transferência pode ter senhas, tokens e dados pessoais, então o Cop pasta do Copiado são descartadas. - **Não usa rede** e não executa nada do que foi copiado. -Para apagar tudo, inclusive os fixados: `copiado esquecer` - O instalador confere o checksum SHA-256 do pacote antes de instalar, e a esteira de CI/CD roda o [Trivy](https://trivy.dev) (vulnerabilidades, segredos e configurações) em todo push, em toda release e toda segunda-feira. Os resultados ficam na aba @@ -54,16 +52,16 @@ em todo push, em toda release e toda segunda-feira. Os resultados ficam na aba curl -fsSL https://raw.githubusercontent.com/lucasrguerra/copiado/main/desinstalar.sh | bash ``` -## Se algo der errado +## Comandos -- **Win+V não abre nada:** faça o Cinnamon recarregar os atalhos: - ```bash - gsettings set org.cinnamon.desktop.keybindings custom-list "[]"; gsettings set org.cinnamon.desktop.keybindings custom-list "['custom0']" - ``` - Se ainda assim não abrir, veja se outro atalho usa Win+V em - *Configurações do Sistema → Teclado → Atalhos*. -- **Abre, mas o histórico está vazio:** o app pode não estar rodando. Rode - `copiado &` +| Comando | Para quê | +|---|---| +| `copiado consertar` | **Win+V não abre ou o histórico está vazio?** Recria o atalho, faz o Cinnamon recarregá-lo e inicia o app se estiver parado. | +| `copiado esquecer` | Apaga todo o histórico, inclusive os fixados. | +| `copiado ajuda` | Lista os comandos. | + +Se mesmo depois do `copiado consertar` o Win+V não abrir, veja se outro atalho usa +Win+V em *Configurações do Sistema → Teclado → Atalhos*. ## Publicar uma versão nova diff --git a/copiado.py b/copiado.py index 2380b2b..d15b4e3 100755 --- a/copiado.py +++ b/copiado.py @@ -2,9 +2,11 @@ """Copiado: histórico da área de transferência no estilo Windows 11 (Win+V). Uso: - copiado.py inicia em segundo plano (vigia o Ctrl+C) - copiado.py show abre a janelinha do histórico - copiado.py esquecer apaga todo o histórico, inclusive os fixados + copiado inicia em segundo plano (vigia o Ctrl+C) + copiado show abre a janelinha do histórico + copiado esquecer apaga todo o histórico, inclusive os fixados + copiado consertar recria o atalho Win+V e inicia o app se estiver parado + copiado ajuda mostra esta ajuda """ import hashlib import json @@ -406,5 +408,111 @@ class App(Gtk.Application): return False +# ---------- comandos de manutenção (não abrem janela) ---------- +ATALHOS = "org.cinnamon.desktop.keybindings" +CAMINHO_ATALHO = "/org/cinnamon/desktop/keybindings/custom-keybindings/{}/" + + +def gsettings(*args): + return subprocess.run(["gsettings", *args], capture_output=True, + text=True, check=True).stdout.strip() + + +def ler_lista(valor): + # gsettings devolve algo como "['custom0']" ou "@as []" + return json.loads(valor.replace("@as ", "").replace("'", '"')) + + +def esquema(ident): + return f"{ATALHOS}.custom-keybinding:" + CAMINHO_ATALHO.format(ident) + + +def configurar_atalho(): + """Cria (ou corrige) o atalho Win+V e faz o Cinnamon recarregá-lo.""" + lista = ler_lista(gsettings("get", ATALHOS, "custom-list")) + ident = next((i for i in lista + if gsettings("get", esquema(i), "binding") == "['v']"), None) + if ident is None: + ident = next(f"custom{n}" for n in range(1000) if f"custom{n}" not in lista) + lista.append(ident) + gsettings("set", esquema(ident), "name", "Copiado") + gsettings("set", esquema(ident), "command", "copiado show") + gsettings("set", esquema(ident), "binding", "['v']") + # o Cinnamon só relê os atalhos quando a lista muda + gsettings("set", ATALHOS, "custom-list", "[]") + time.sleep(0.5) + gsettings("set", ATALHOS, "custom-list", json.dumps(lista).replace('"', "'")) + return ident + + +def remover_atalho(): + lista = ler_lista(gsettings("get", ATALHOS, "custom-list")) + nossos = [i for i in lista if "copiado" in gsettings("get", esquema(i), "command")] + for ident in nossos: + gsettings("reset-recursively", esquema(ident)) + restantes = [i for i in lista if i not in nossos] + gsettings("set", ATALHOS, "custom-list", json.dumps(restantes).replace('"', "'")) + + +def esta_rodando(): + bus = Gio.bus_get_sync(Gio.BusType.SESSION) + resp = bus.call_sync("org.freedesktop.DBus", "/org/freedesktop/DBus", + "org.freedesktop.DBus", "NameHasOwner", + GLib.Variant("(s)", ("io.github.copiado",)), + GLib.VariantType("(b)"), Gio.DBusCallFlags.NONE, -1, None) + return resp.unpack()[0] + + +def iniciar_em_segundo_plano(): + subprocess.Popen([sys.executable, os.path.realpath(__file__)], + stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, start_new_session=True) + for _ in range(30): + time.sleep(0.2) + if esta_rodando(): + return True + return False + + +def consertar(): + try: + ident = configurar_atalho() + print(f"✓ Atalho Win+V configurado e recarregado ({ident}).") + except (OSError, subprocess.CalledProcessError) as erro: + print(f"✗ Não consegui configurar o atalho: {erro}") + return 1 + if esta_rodando(): + print("✓ O Copiado já está rodando.") + elif iniciar_em_segundo_plano(): + print("✓ O Copiado estava parado e foi iniciado.") + else: + print("✗ Não consegui iniciar o Copiado. Rode 'copiado' para ver o erro.") + return 1 + print("Pronto! Aperte Win+V.") + print("Se ainda não abrir, veja se outro atalho usa Win+V em") + print("Configurações do Sistema → Teclado → Atalhos.") + return 0 + + +def main(argv): + comando = argv[1] if len(argv) > 1 else "" + if comando in ("ajuda", "-h", "--help"): + print(__doc__.strip()) + return 0 + if comando == "consertar": + return consertar() + if comando == "instalar-atalho": + configurar_atalho() + return 0 + if comando == "remover-atalho": + remover_atalho() + return 0 + if comando not in ("", "show", "esquecer"): + print(f"Comando desconhecido: {comando}\n") + print(__doc__.strip()) + return 2 + return App().run(argv) + + if __name__ == "__main__": - sys.exit(App().run(sys.argv)) + sys.exit(main(sys.argv)) diff --git a/desinstalar.sh b/desinstalar.sh index 687528a..baa8c8b 100755 --- a/desinstalar.sh +++ b/desinstalar.sh @@ -1,15 +1,6 @@ #!/usr/bin/env bash # Remove o Copiado e o atalho Win+V -BASE=org.cinnamon.desktop.keybindings -LISTA=$(gsettings get $BASE custom-list) -for ID in $(python3 -c "import ast,sys; print(' '.join(ast.literal_eval(sys.argv[1].replace('@as ',''))))" "$LISTA"); do - SCHEMA="$BASE.custom-keybinding:/org/cinnamon/desktop/keybindings/custom-keybindings/$ID/" - if gsettings get "$SCHEMA" command | grep -q copiado; then - LISTA=$(python3 -c "import ast,sys; l=ast.literal_eval(sys.argv[1].replace('@as ','')); print([x for x in l if x!='$ID'])" "$LISTA") - gsettings set $BASE custom-list "$LISTA" - gsettings reset-recursively "$SCHEMA" - fi -done +command -v copiado >/dev/null && copiado remover-atalho pkill -f "/usr/bin/copiado" 2>/dev/null sudo apt-get remove -y copiado rm -rf "$HOME/.local/share/copiado" diff --git a/instalar.sh b/instalar.sh index 74a38a4..57a5659 100755 --- a/instalar.sh +++ b/instalar.sh @@ -2,7 +2,6 @@ # Instala (ou atualiza) o Copiado a partir do pacote publicado no GitHub set -e URL="https://github.com/lucasrguerra/copiado/releases/latest/download" -BASE=org.cinnamon.desktop.keybindings TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT @@ -34,31 +33,10 @@ if [ -f "$HOME/.local/share/clipboard-winv/historico.json" ]; then rm -rf "$HOME/.local/share/clipboard-winv" fi -echo "==> Criando atalho Win+V..." -LISTA=$(gsettings get $BASE custom-list) -ID="" -for i in $(python3 -c "import ast,sys; print(' '.join(ast.literal_eval(sys.argv[1].replace('@as ',''))))" "$LISTA"); do - B=$(gsettings get "$BASE.custom-keybinding:/org/cinnamon/desktop/keybindings/custom-keybindings/$i/" binding) - [ "$B" = "['v']" ] && ID=$i && break -done -if [ -z "$ID" ]; then - for i in $(seq 0 99); do - echo "$LISTA" | grep -q "'custom$i'" || { ID="custom$i"; break; } - done - LISTA=$(python3 -c "import ast,sys; l=ast.literal_eval(sys.argv[1].replace('@as ','')); l.append('$ID'); print(l)" "$LISTA") -fi -SCHEMA="$BASE.custom-keybinding:/org/cinnamon/desktop/keybindings/custom-keybindings/$ID/" -gsettings set "$SCHEMA" name "Copiado" -gsettings set "$SCHEMA" command "copiado show" -gsettings set "$SCHEMA" binding "['v']" -# só agora registra a lista (e força o Cinnamon a recarregar os atalhos) -gsettings set $BASE custom-list "[]" +echo "==> Criando atalho Win+V e iniciando..." +pkill -f "/usr/bin/copiado" 2>/dev/null || true sleep 0.5 -gsettings set $BASE custom-list "$LISTA" - -echo "==> Iniciando..." -pkill -x copiado 2>/dev/null || pkill -f "/usr/bin/copiado" 2>/dev/null || true -setsid copiado >/dev/null 2>&1 & +copiado consertar >/dev/null echo echo "Pronto! Versão instalada: $(dpkg-query -W -f='${Version}' copiado). Aperte Win+V."