No meu texto de referência, Darktable: batendo no muro em câmera lenta, apresentei o desastre que era o novo “Grande turducken MIDI”. O propósito desse turducken1 era reescrever o sistema de atalhos de teclado para estendê-lo a dispositivos MIDI.
Até hoje, ainda estou irritado com essa empreitada de destruição em massa; aqui vai um resumo dos motivos:
- em 2021, substituiu um sistema de atalhos de teclado que era muito bom, completo em recursos, bem testado, estável e escrito em menos de 1500 linhas (comentários incluídos),
- …para adicionar suporte a dispositivos MIDI e gamepads de PlayStation (!?!)…
- …mas na minha pesquisa sobre o Darktable de 2022, um ano após esse novo recurso, dentre os mais de 1251 usuários que participaram:
- 81% dos usuários não tinham um dispositivo MIDI e não planejavam adquirir um,
- 2% nem sabiam o que era um dispositivo MIDI.
- 8% dos usuários tinham um dispositivo MIDI mas não o usavam com o Darktable,
- 6% estavam considerando talvez adquirir um dispositivo MIDI no futuro,
- 2% dos usuários tinham um dispositivo MIDI que realmente usavam no Darktable,
- o código era absolutamente terrível, em termos de:
- qualidade de código: instruções
if/switch-caseilegíveis aninhadas em 4 níveis, no meio de funções de 1000 linhas (postei trechos de exemplo no meu artigo), - volume de código:
- 3546 linhas de código para o Darktable 4.0,
- 4397 linhas de código para o Darktable 5.0,
- o aumento de volume é uma consequência direta de tentar corrigir bugs em uma arquitetura que não pode ser consertada porque sua complexidade promove mais complexidade. Tudo isso decorre do design, mas resolver problemas criados pela complexidade adicionando mais complexidade não é solução.
- complexidade de código:
- complexidade ciclomática :
- 1088 para o Darktable 4.0,
- 1245 para o Darktable 5.0 (detalhes ),
- complexidade cognitiva :
- 1885 para o Darktable 4.0,
- 2098 para o Darktable 5.0 (detalhes ).
- é de longe o recurso mais complexo do software, mesmo não operando sobre imagens. Para comparação, o segundo recurso mais complexo é a decodificação de metadados EXIF, que tem uma complexidade cognitiva de 1348.
- complexidade ciclomática :
- qualidade de código: instruções
- ele não decodifica modificadores de tecla por design, mas lida apenas com as teclas pressionadas no hardware, o que significa:
- a entrada “1” do teclado numérico é decodificada como
Keypad End, - a entrada “1” de um teclado francês AZERTY é decodificada como
Shift+&, ouShift+"no BÉPO, - portanto, você precisa duplicar todos os seus atalhos baseados em números para cada forma de inserir um número, e se preparar para que a janela de configuração de atalhos não contenha nenhum número real nas combinações de teclas.
- a entrada “1” do teclado numérico é decodificada como
- o design voltado ao usuário é absolutamente terrível, com ações e emulações demais para configurar (“efeitos”), que nem sequer estão totalmente documentadas 4 anos depois (o que é “ctrl-toggle”? “right-activate”?), e a configuração de atalhos usa uma estranha janela dividida que não faz o menor sentido,
- a implementação também é terrível: o recurso conhece toda a GUI do software, e a GUI do software conhece o código dos atalhos. Não há modularidade alguma aqui, e mudar qualquer coisa no código dos atalhos pode ter efeitos inesperados e indesejados em qualquer parte do software.2 Basta ver o grafo de dependências abaixo,
- vários “atalhos” (ou vinculações MIDI) podem estar associados à mesma ação, o que significa que cada interação do usuário precisa consultar a lista inteira de ações disponíveis, induzindo um tratamento de atalhos muito ineficiente, travamentos da GUI em alguns casos e falsos positivos de “combinação de teclas desconhecida” em casos peculiares.


O grafo de dependências de src/gui/accelerators.c (Grande turducken MIDI) antes da reescrita. Adivinhe por que chamamos isso de “código espaguete "… Isso deixa claro que há uma dependência bidirecional entre o código dos aceleradores e o resto do código da GUI. É um pesadelo de manter.
Então, dito de outra forma, um programador (muito ruim) substituiu um recurso simples e funcional por uma monstruosidade, com a aprovação do mantenedor (que jamais escreveria um código tão ruim, mas está determinado a “não perder o ritmo” das contribuições, custe o que custar), para agradar 2% da base de usuários. Tudo isso por um recurso secundário (terciário?).
Eu chamo isso de prioridades erradas. Especialmente porque exigiu vários meses de trabalho árduo de um desenvolvedor e de muitos beta-testadores, apenas para piorar as coisas, e então o trabalho árduo se tornou uma justificativa para nunca reverter a mudança, o que é conhecido como a falácia do custo irrecuperável .
Sou um usuário, não me importo com código
Importar-se com o código dos aplicativos que você usa é como se importar se os canos que trazem água até a sua casa são de chumbo. Não é sua função, os canos ficam enterrados fora de vista, então você tem todos os motivos para não se importar — e as pessoas responsáveis pela rede de água têm todos os motivos para que você não se importe — mas eles terão um efeito sobre a sua saúde, e esses efeitos serão invisíveis até que seja tarde demais. O software talvez não tenha um efeito direto sobre a sua saúde,3 mas a forma como é feito terá um impacto de longo prazo sobre você.
Encontro-me em uma posição desconfortável – a Cassandra — tendo que explicar aos usuários que problemas que eles não conhecem, não veem e com os quais não se importam estão afetando profundamente a usabilidade e a estabilidade do dia a dia das ferramentas que estão usando, mas de formas não óbvias. De qualquer maneira, esses problemas afetam diretamente a probabilidade de qualquer mantenedor, um dia, resolver os bugs que enfrenta.
Trazer más notícias faz de você a má notícia, mas trazer más notícias com as quais ninguém se importa faz de você um babaca, ainda que essas más notícias expliquem os problemas estranhos e aleatórios que se tornam cada vez mais comuns nos rastreadores de bugs do Darktable desde 2021, que não são resolvidos ao longo dos anos porque, a essa altura, o código não contém bugs, ele é um bug. Mas aqui você tem que encarar as bolhas de filtro : dependendo de onde você olhar na web, você encontrará ou pessoas profundamente felizes com o Darktable (desempenho, estabilidade, design), ou pessoas profundamente insatisfeitas com ele. Minha observação empírica é que os satisfeitos, em média, possuem computadores potentes e têm diplomas em STEM.4 As pessoas insatisfeitas tendem a se afastar e a reduzir o tempo que perdem com o software, o que significa que, se você não estiver ativamente procurando por elas, só alimentará o seu viés do sobrevivente . E, embora eu não veja sentido em tentar converter usuários ao Darktable (ou a qualquer editor de fotos de código aberto), porque cada um sabe de si, filtrar e afastar usuários com base no seu letramento digital é uma grande falha para qualquer aplicativo de edição de fotos.
Como um cara com um canal no Youtube explicando como usar o Darktable, e que dá sessões de treinamento individuais, tendo a atrair um feedback mais variado do que apenas os rastreadores de issues do Github (onde as críticas ao design são silenciadas bem rápido, de qualquer forma), e testemunho em primeira mão, em videochamadas, aqueles bugs estranhos, aleatórios e irreproduzíveis. Qualquer sujeito privilegiado tem a tendência de negar, minimizar ou desconsiderar depoimentos sobre problemas que ele mesmo não enfrenta, ou até de culpar a pessoa que os relata pelos problemas. Então, muitas vezes tenho essa impressão de dois universos paralelos que não se comunicam mas se desprezam mutuamente, quando se trata de feedback sobre o Darktable. É claro que os desenvolvedores do Darktable escolheram olhar para onde o sol brilha.
E embora o Darktable pareça um projeto ativo, com muitos recursos novos adicionados regularmente por uma dúzia de caras, é ainda mais difícil explicar que o que realmente acontece nos bastidores é a destruição da base de código, porque a qualidade do código se degrada muito com o passar do tempo, até que não será mais possível depurar de forma alguma. O volume de mudanças não diz nada sobre qualidade, confiabilidade ou manutenibilidade. Mas 2 séculos de capitalismo nos condicionaram a buscar recursos novos e reluzentes, custe o que custar, e nesse quesito o Darktable atende às expectativas.
O código nasce quebrado. Todo software tem bugs. Pior ainda, usamos muitas bibliotecas de terceiros para evitar “reinventar a roda”, mas essas bibliotecas mudarão sua API no futuro, o que significa que o código que funciona hoje deixará de funcionar amanhã e precisará ser retrabalhado no futuro, quando as dependências mudarem. O código é como um jardim: cada nova estação traz sua cota de tarefas. Isso se chama manutenção. Uma vez reconhecido isso, a sabedoria está em planejar formas de tornar a manutenção possível, primeiro, e depois fácil.
Código bom é código fácil de manter. Ou seja, código bom é escrito de formas que são gentis com as pessoas que precisam lê-lo, entendê-lo e consertá-lo. Não escrevemos código para computadores. Não escrevemos código para que os recursos “simplesmente funcionem” agora e nos próximos meses. Código não é algo que escondemos sob um capô bonito esperando nunca mais ter que olhar para ele. Código é um organismo vivo que custa energia e tempo para se manter vivo.
A manutenção tem um custo, um custo que muitas vezes é subestimado. Não importa se o seu produto é o melhor do mercado; se os seus custos de manutenção são proibitivos, os clientes (informados) vão evitá-lo. Manutenção é chata, desinteressante, não é sexy. Manutenção é o oposto de introduzir coisas novas e reluzentes: é garantir que coisas velhas e empoeiradas continuem funcionando silenciosamente. Nas notas de versão do seu software, ela fica no final. Os usuários não se empolgam com recursos mantidos, mas ficam irritados com código não mantido e tendem a usar a métrica errada para avaliar quão bem um projeto é mantido (a saber: agitação e aparência de trabalho). Então você não ganha nada mantendo o código, mas perde por não mantê-lo, e isso vai lhe custar, quer você o faça ou não.
O código é impossível de manter quando cada correção de bug leva a um novo bug em outro lugar, ao estilo whack a mole, e a natureza das correções de bug é, na verdade, remendos contextuais e gambiarras que só adicionam complexidade ao software. Quando você chega a esse ponto, sua única opção é reescrever o código incriminado do zero. O que significa que o trabalho passado apenas criou mais trabalho agora. E quando esse trabalho passado específico já estava sobrescrevendo trabalho anterior, embarcamos na idiocracia.
Mais código não é um sucesso nem uma conquista. As conquistas estão em resolver os problemas dos usuários. Aqui, novamente, a mentalidade capitalista nos atinge com força: gostaríamos de medir o trabalho pelo volume de código adicionado. Mais código é apenas mais passivo, mais dívida técnica, mais custos de manutenção. Isso é uma dissonância cognitiva, já que software é precisamente sobre escrever código. Mas pense assim: fazer um carro não é adicionar mais aço sobre 4 rodas, porque em algum momento o carro vai ter que se mover. Você precisa da quantidade certa de aço, moldado da forma certa nos lugares certos: você precisa ser eficiente e parcimonioso. Software é sobre resolver os problemas dos usuários com computadores, não sobre acumular código.
Em projetos onde os colaboradores não são pagos, a manutenção chata não é onde as pessoas querem passar seus sábados, e isso é uma preocupação real no que diz respeito ao gerenciamento do projeto. É ainda mais importante tornar a manutenção fácil quando as pessoas trabalham de graça. Felizmente, o Darktable tem zero gerenciamento de projeto, apenas um enxame de colaboradores aleatórios coçando sua coceira em horários aleatórios, sem objetivo unificado, estratégia, cronograma ou divisão de trabalho. É por isso que eu gostaria que todos parassem de usar o termo “equipe do Darktable”. Não existe equipe do Darktable, porque não há divisão de trabalho, nem liderança, nem cronograma, nem roadmap, nem planejamento, nem objetivo, nem prioridades, nem visão, nem estratégia, nem método, nem design, nem comunicação antes de fazer as coisas, nem documentos de especificação para os problemas a resolver. Isto é o clube de informática do ensino médio, feito de indivíduos desconectados.
Há 3 coisas que um desenvolvedor precisa fazer para garantir que a manutenção seja o mais fácil possível:
- escrever a menor quantidade de código: rastrear um bug em meio a 300 linhas será muito mais rápido do que em meio a 3000 linhas,
- escrever o código mais simples: “simples” no sentido de “a lógica funcional tem poucos passos, poucas suposições, poucos casos extremos”. Mais simples significa mais fácil de ler, entender e corrigir, mas também que haverá menos casos a cobrir durante os testes. Na prática, significa evitar casos contextuais, opções de usuário, variantes,
if/elseno código. - escrever código autocontido: dividir a funcionalidade do software entre módulos (subprogramas) que se comunicam através do núcleo e são isolados uns dos outros. À medida que o volume de código cresce, isso garante que as mudanças ocorrendo dentro dos módulos não terão efeitos imprevistos fora deles.
Por fim, você tem que avaliar se os novos recursos que você adiciona valem o custo extra de manutenção. No caso do gerenciador de atalhos, estamos atendendo às “necessidades” (ou melhor, ao luxo) de 2% da base de usuários com uma implementação que é, no (bem) mínimo, duas vezes mais complicada do que a que ela substituiu. Pondere isso na sua mente capitalista…5
A complexidade do código pode ser medida usando a teoria dos grafos, por meio da complexidade ciclomática, da complexidade cognitiva ou da complexidade N-path. Muitos artigos de pesquisa correlacionam o volume de código e/ou a complexidade do código ao número de bugs ocultos, o que é bastante intuitivo: a linha de código que você não escreve é a única na qual você nunca terá bugs. A simplicidade é o objetivo. E, como descobri da forma difícil, a complexidade da GUI (front-end) é muitas vezes uma consequência direta da complexidade do back-end. Há muitos caras uivando para a lua para invocar a figura mística do designer de UI/UX, na esperança de resolver magicamente a terrível GUI do Darktable (complicada e inconsistente). A GUI não existe em paralelo ao back-end, ela apenas conecta a entrada esperada pelo back-end a controles gráficos. Simplificar o front-end não pode ser feito sem simplificar o back-end; isso é apenas um dogma estúpido inventado por pessoas que só têm soft skills. Mas simplificar back-ends é muito mais complicado do que apenas desenhar mockups.
Como se revelou, o que aconteceu com o Grande turducken MIDI falhou em todos os 3 quesitos: volume de código, complexidade de código e autocontenção do código. E os desenvolvedores do Darktable nunca aprenderão com seus erros, como comprova o Darktable 5.0. À medida que os bugs de atalhos de teclado se acumulavam ao longo do tempo no Ansel (além de recursos terríveis por design), tentei corrigi-los de formas que evitassem reescrever tudo, até ficar bem claro que eu não conseguiria evitar a reescrita por muito mais tempo.
Isso se chama dívida técnica . Todo o código do Grande turducken MIDI foi feito para funcionar uma vez escrito, não para ser mantível a longo prazo. É basicamente uma prova de conceito que jamais deveria ter chegado à produção. E a própria prova de sua impossibilidade de manutenção é o quanto o volume e a complexidade do código cresceram de correção em correção, entre o Darktable 3.8 e o 5.0, tornando-o ainda mais impossível de manter à medida que os bugs são corrigidos. Esta é a definição de se grudar em uma teia de aranha: quanto mais você se mexe, mais você fica preso.
Meu erro talvez tenha sido começar a levantar minhas críticas à (falta de) gestão do Darktable em 2022, depois de obter a prova de que o bando desorganizado de colaboradores jamais reconheceria seus erros nem aprenderia com eles. Embora o ritmo de trabalho autoinfligido e insustentável fosse mais propenso a gerar burn-out do que reflexão. Desde então, Pascal Obry, o atual mantenedor autonomeado do Darktable, vem tentando convencer todo mundo de que sou um membro insuportável da equipe, incapaz de trabalhar com outras pessoas que discordam de mim, e que passou a insultar todos. É claro que nenhuma resposta específica e técnica foi dada aos problemas que levantei em Darktable: batendo no muro em câmera lenta, apenas afirmações genéricas vagamente tranquilizadoras do tipo “o Darktable é um projeto ativo, saudável porque tem muitos colaboradores”. Como se mais macacos desorganizados finalmente equivalessem a um engenheiro caso você juntasse um número suficiente deles.
Acho que não fui nada além de gentil por 4 anos – gentil demais, na verdade — até perceber o quanto esses caras prejudicaram o projeto. Eles não aprendem, e nunca aprenderão com seus erros, porque nem sequer os reconhecem, e a evolução do Darktable 5.0 só confirma a tendência. Mas ver cada vez mais bugs estranhos, aleatórios e irreproduzíveis se acumularem nos rastreadores de bugs é uma pista bastante clara de que algo está profundamente errado. Especialmente porque investigar esses bugs me levou a uma arqueologia por terríveis pilhas de merda de código que nem tinham 2 anos de idade, e embora meu conhecimento e minha experiência com a base de código do Darktable tenham crescido ao longo dos anos, minha capacidade de corrigir a causa raiz dos bugs diminuiu, e eu enfrentava labirintos cada vez mais desconcertantes de indireções de código .6
É difícil explicar tudo isso para pessoas que veem os computadores como caixas mágicas projetadas por magos da tecnologia. Não há mágica nenhuma ali dentro. É basicamente matemática e aplicações. Muitos desenvolvedores confessam ser ruins em matemática, o que significa que também são ruins em programação. Porque matemática não é apenas executar operações aritméticas, é toda uma disciplina da mente que permite abstrair problemas complexos a fim de dividi-los em problemas simples, que levarão a um código simples e a uma GUI simples. Bons programadores passam muito tempo pensando em como escrever pouco código. Porque código é um passivo, código é dívida técnica, código é caro de manter. Então você paga a dívida antecipadamente, sem juros, pensando bastante sobre o seu código antes de escrevê-lo, em vez de programar primeiro e depois passar anos apagando incêndios.
Tudo isso também levanta a questão de quem é dono do código aberto e dos projetos de código aberto. Desde que o fundador do projeto Darktable, Hanatos, abandonou o barco, assim como toda a primeira geração de desenvolvedores, por várias razões, o último homem de pé da primeira geração se autonomeou novo mantenedor. Ele é um desenvolvedor muito capaz e habilidoso: seu código é limpo, organizado, e até agora não encontrei nenhum bug em uma linha onde o git blame dizia “Pascal Obry”. Mas suas políticas em relação à gestão de código são terríveis: ele acredita que toda contribuição é uma boa contribuição, que o “ritmo” das contribuições não deve ser desencorajado, e é propriamente incapaz de dizer “não” a contribuições, o que significa que quase todos os pull requests são mesclados. É a França em 1940: tudo entra, é recebido com um grande sorriso e o Führer ganha o dobro de judeus do que pediu. Enquanto isso, os resistentes são chamados de terroristas.
Mas há uma diferença enorme entre a qualidade do código que Pascal escreve e a qualidade do código que ele aceita e mescla. Este é um paradoxo preocupante que encontra suas raízes entre o medo de ficar de fora e o tecnopositivismo radical , que peca pelo lado do dogma e das crenças, com total desprezo pelo design e pela perspectiva do usuário. E talvez uma confiança exagerada na capacidade da “Comunidade” de corrigir os bugs depois.
Há muitos casos em que não fazer nada é melhor do que fazer errado, especialmente quando você está substituindo recursos existentes. Sendo o Darktable um editor não destrutivo de código, você investe nele quando começa um banco de dados de edições de imagens. A menos que você pretenda exportar todas as suas imagens para arquivos de alta resolução e alta profundidade de bits assim que terminar de editá-las, para nunca mais alterá-las. Isso cria uma expectativa legítima de estabilidade e consistência a longo prazo, para que suas edições antigas ainda possam ser abertas, retocadas e exportadas novamente, talvez para novos formatos, talvez para resoluções maiores. Mudar de rumo no design do aplicativo é uma espécie de quebra de contrato, e embora a licença GNU/GPL anule qualquer responsabilidade legal, ela não anula os danos aos usuários. Nem reembolsa os anos da minha vida que perdi consertando a merda deles.
Então, mesmo desconsiderando as questões de manutenção centradas no desenvolvedor, há também uma discussão a ser feita sobre quem decide como recursos antigos e comprovados são substituídos, especialmente aqueles recursos básicos e universais de aplicativos de desktop, como o gerenciamento de arquivos (importar, exportar, navegar) ou a interação por mouse e teclado, que foram tão onipresentes por tanto tempo que os anos 2020 estão 30 anos atrasados para fingir reinventá-los.
Muito trabalho e muitas horas de trabalho foram investidos em piorar os atalhos de teclado, em nome de políticas falhas e dogmas prejudiciais, somados ao pensamento mágico e à preguiça social , em que todos esperam que A Comunidade® (ou seja, outra pessoa) se encarregue de corrigir os próprios erros. Os usuários também tiveram que perder a sua configuração de teclado e foram obrigados a redefinir tudo e reaprender tudo também. Mas fazer da forma certa teria, na verdade, custado menos trabalho e menos horas de trabalho. Este é um ciclo de loucura que se autoalimenta, criando um ambiente de trabalho tóxico onde a instabilidade promove mais instabilidade, onde a complexidade promove mais complexidade, novamente sem nenhum tipo de roadmap de recursos que desse uma direção geral e visibilidade a todos os envolvidos.
Uma breve história de design ruim
Só depois de reconstruir o recurso de atalhos do zero é que entendi o que deu errado nos atalhos/aceleradores do Darktable.
Em primeiro lugar, há essa devassidão paralisante de recursos, que torna tentador desafogar a GUI simplesmente escondendo recursos, apenas para tratá-los pelo teclado. O problema então é que esses recursos nem sempre são de nicho e opcionais (como o atalho que ignora as interações de máscara ao arrastar e soltar a pré-visualização principal da imagem na sala escura), mas são definitivamente indescobríveis pelos usuários. O Ansel resolveu esse problema com o menu global.
Alguns recursos ficavam escondidos por meio de um suporte básico a vimkeys: se você começar a digitar :: :q fecha o aplicativo, :set seguido do nome do controle deslizante ou da caixa de combinação altera o valor. Isso, claro, não está documentado em lugar nenhum do manual do Darktable, e como usuário do Darktable por mais de uma década, eu nunca tinha ouvido falar disso antes de apagar seu código, porque essa piadinha escuta todas as teclas que você pressiona para determinar se deve ou não agir sobre o que você digita.
Mas há também o fato de que os módulos usam widgets Gtk caseiros (chamados “Bauhaus”, em src/bauhaus/bauhaus.c) que não implementam tudo que você esperaria de um widget de GUI que captura eventos do usuário, especialmente não os recursos de acessibilidade.
Um dos recursos de acessibilidade mais básicos é a capacidade de percorrer os widgets focalizáveis. No jargão de GUI (e do Gtk), um widget focalizável é aquele que pode capturar eventos de pressionamento de tecla, uma vez focado. Os widgets normalmente são focados ao serem clicados, mas o Gtk também gerencia internamente uma cadeia de foco na qual você navega com a tecla Tab e as teclas de seta. O primeiro problema é que a tecla Tab, no Darktable, estava mapeada para o modo “pré-visualização” (ligar/desligar todos os painéis na visualização). E, de fato, usando os aceleradores nativos do Gtk, isso não teria sido possível de forma alguma, já que Tab é mapeada pelo Gtk e proibida em atalhos definidos pelo usuário, mas como o Darktable implementou seu próprio gerenciador de atalhos, mesmo antes do Grande turducken MIDI, ela foi sobrescrita. Assim, a rotação da cadeia de foco baseada em tabulação foi desativada simplesmente porque a tecla Tab estava atribuída a outra coisa. Mas o segundo problema era que os widgets Bauhaus caseiros também capturavam todos os pressionamentos das teclas de seta. Portanto, navegar entre os controles de maneira sequencial (próximo/anterior) também era completamente impossível, por design. Assim como os widgets Bauhaus capturavam todos os eventos de rolagem do mouse, impedindo a rolagem dos painéis laterais.
Como a navegação sequencial/incremental entre widgets focalizáveis era impossível por design, todas as interações de teclado tinham necessariamente que ser feitas de forma absoluta: para cada controle deslizante, para cada caixa de combinação, você teria um atalho para aumentar/diminuir/redefinir o valor mapeado diretamente.
Mas mais problemas surgiram quando os módulos passaram a ser multi-instanciáveis, porque os controles eram identificados por um caminho de acelerador como view/module/slider/increase ou view/module/slider/decrease, mas todas as instâncias do módulo herdariam o mesmo caminho. Isso gerou a necessidade de gerenciar tudo isso em tempo de execução, com preferências do usuário para decidir se o módulo alvo seria o primeiro, o último ou o último com o qual houve interação.
Generalizar tudo isso para MIDI e gamepads só piorou as coisas, porque, além de ter um atalho por ação possível por widget, havia então a necessidade de gerenciar a emulação da interação típica de desktop a partir de outros dispositivos de entrada. Mas, em vez de gerenciar a camada de emulação em alto nível, na interface entre o MIDI e os atalhos normais de teclado/mouse, um desenvolvedor terrível superdimensionou completamente uma camada de abstração de ações, incrustada nos módulos, nos widgets Gtk nativos (como uma sobreposição) e nos widgets Bauhaus caseiros (profundamente incrustada). O problema é que essa camada de abstração não era realmente uma camada, mas mais como um tumor metastático se espalhando por toda parte. Removê-la e todas as suas dependências levou à remoção de 7674 linhas espalhadas por 163 arquivos, embora fosse supostamente implementada em src/gui/accelerators.c (4412 linhas de código, comentários e linhas em branco).
Redesenhando os atalhos/aceleradores de teclado do zero
O passo zero do redesenho são estes 2 requisitos simples:
- Toda ação deve ser descobrível na GUI; os atalhos de teclado não têm o objetivo de desafogar a GUI; desafogar a GUI é uma questão de dividir o fluxo de trabalho em etapas unitárias e apresentar apenas os controles que importam para a etapa atual.
- O software deve ser completamente utilizável apenas com o mouse e apenas com o teclado. A interação mista deve ser completamente opcional.
A primeira restrição do redesenho é, portanto, tornar os atalhos absolutos completamente opcionais, ou seja, projetar o fluxo de trabalho do teclado para um acesso sequencial/relativo (alternando entre o controle seguinte/anterior).
Interação com controles focados
O paradigma dividido “focar” e depois “interagir” (que não fui eu quem inventou…) é muito poderoso porque permite delimitar os pressionamentos de tecla no contexto certo, o que significa que os mesmos pressionamentos de tecla (especialmente as teclas de seta Up/Down/Right/Left) podem ser associados mais de uma vez na GUI, e ser tratados de forma diferente dependendo de qual contexto está com o foco. Isso é mais flexível e, na verdade, mais simples7 do que a obsessão do Darktable de conectar tudo a atalhos globais e absolutos, que então colidiriam em teclas frequentemente reutilizadas, e levariam à necessidade de adicionar cada vez mais modificadores de tecla como solução paliativa.
Até aqui, navegar pelos controles apenas lhes dava foco. E quanto à interação de fato?
Na visualização da mesa de luz, uma vez que a grade de miniaturas está focada, a navegação pelas miniaturas é feita usando as típicas teclas de seta, Page Up/Down, etc. (veja a documentação para todos os detalhes), e as seleções de imagens também podem ser feitas de várias formas (em lote, em série, individualmente) pelo teclado.
Na visualização da sala escura, alterar valores em controles deslizantes e caixas de combinação pode ser feito usando as teclas de seta (possivelmente usando Shift para passo grosso ou Ctrl para passo fino); acionar o seletor de cor (em controles deslizantes que os suportam) é feito com Insert, etc. (novamente, leia a documentação para detalhes).
Então, novamente, até aqui nenhum atalho definido pelo usuário, nenhuma combinação de teclas críptica para memorizar, são basicamente as teclas de seta e ainda assim tudo pode ser acessado.
Focalização absoluta de controles
Tudo isso é ótimo, mas te deixa numa espécie de trilha de Ikea quando você precisa acessar diretamente alguma área do aplicativo mas tem que navegar por todas as seções desde a porta de entrada.
Para amenizar isso, um recurso bem antigo do Darktable era a aba de “módulos favoritos”, ou seja, uma aba especial que duplicava a GUI dos módulos mais usados, com base na escolha do usuário. Isso é essencialmente resolver inchaço com mais inchaço, e ainda assim os usuários se afeiçoaram muito a isso. Porém, se nos concentrarmos no objetivo, em vez do meio, o requisito é um acesso rápido a módulos arbitrários, o que é perfeitamente compreensível.
Então isso foi reimplementado como uma forma de definir um atalho absoluto que foca imediatamente um módulo de processamento de imagem ou qualquer um de seus controles internos. Focar um módulo ou controle oculto fará com que ele apareça automaticamente na GUI. A partir daí, a interação seguinte é feita exatamente como antes, usando combinações de teclas uniformes. Isso reduz muito o número de atalhos a configurar, torna tudo mais genérico e a janela de configuração de atalhos muito mais simples.
Os atalhos absolutos também podem ter como alvo entradas de menu (ou seja, ações globais), caso em que serão relembrados no menu, ao lado do rótulo da ação, o que é, novamente, um recurso nativo do Gtk que vem com zero sobrecarga.
Substituindo controladores MIDI
Se deixarmos de lado todo o hype de ter controladores dedicados e se sentir como um piloto de avião, a única coisa que os controladores MIDI têm que o teclado e o mouse nunca terão é a capacidade de vincular potenciômetros (botões giratórios) diretamente aos controles deslizantes da GUI. Mas então o custo disso é ter um dispositivo extra ocupando espaço (e poeira) na sua mesa, sem falar no futuro lixo eletrônico. Todos os fotógrafos que conheço que compraram MIDIs ou Loupedecks os guardaram “temporariamente” para recuperar algum espaço na mesa… e nunca os tiraram do armário de volta.
Os controles de processamento de imagem podem ser vinculados a atalhos de uma única tecla, porque o Ansel usa uma camada personalizada de atalhos sobre os recursos nativos de acelerador do Gtk. Modifiquei os widgets Bauhaus caseiros de modo que, ao pressionar um dos atalhos de focalização absoluta acima e mantê-lo pressionado, a rolagem da roda do mouse será mapeada diretamente para o widget, mesmo que o mouse não esteja sobre o controle deslizante correto.
Então, combinando atalhos de uma única letra para focar um controle com a rolagem do mouse (ou do touchpad), você obtém todas as vantagens dos botões giratórios MIDI sem o dispositivo extra, sem a biblioteca extra de suporte e sem as maravilhas das camadas de emulação superdimensionadas.
Mas tudo isso ainda acontece com um único e mesmo atalho absoluto, então você não precisa definir e memorizar vários atalhos por controle, apenas para acabar ficando sem combinações de teclas disponíveis.
Ações sem atalho, mecanismo de busca e gatilhos no estilo vimkeys
Meu gerenciador de atalhos é um invólucro fino sobre a API de aceleradores nativos do Gtk. Assim, uma ação é definida por um caminho de texto, como Ansel/Darkroom/Modules/Exposure/Black level, que é um identificador único, legível tanto por um computador quanto por um humano. A GUI declara uma função associada a cada um desses caminhos, contendo o código a executar para aplicar a ação correspondente. Então o gerenciador de atalhos mapeia uma combinação de teclas para esse caminho.
Então, sempre que um usuário pressiona algumas teclas, o gerenciador de atalhos verifica se temos um caminho conhecido para essa combinação e, se encontrar um, aciona a função associada a ele. Este é um design à prova de erros que não sabe nada sobre o interior dos módulos do Ansel, dos widgets caseiros, etc. Assim, ele pode ser estendido a muitas partes do software sem sobrecarga.
Mas na verdade é muito mais poderoso do que apenas isso. Porque, ao listar todos os caminhos conhecidos, podemos então retornar os caminhos que correspondem a uma consulta de busca por texto (procurando um controle, módulo ou entrada de menu global pelo nome), ou seja, encontrar todas as ações de uma lista, mas também podemos acioná-las mesmo que não estejam associadas a nenhum atalho.

Como os módulos de processamento de imagem também podem ser encontrados e exibidos dessa forma, isso também substitui o mecanismo de busca de módulos, recuperando algum espaço vertical para módulos longos e suas opções de máscara/mesclagem (e removendo cerca de 200 linhas de código). A partir daí, eu o estendi a todas as caixas de ferramentas e o tornei global, o que significa que o mecanismo de busca de ações funciona em todas as visualizações, mas só listará as ações relevantes para a visualização atual.
Na sala escura, ele também suporta múltiplas instâncias de módulos, permitindo mirar diretamente uma instância específica (mais sobre isso na documentação).
Por padrão, a busca global de ações está mapeada para o atalho Ctrl+P, e pode ser acessada pelo menu global Ajuda, ou com um botão Buscar ações no centro da barra de cabeçalho. Isso também substitui de certa forma os vimkeys, que tinham um suporte muito parcial às ações da GUI (e teriam exigido a duplicação completa dos aceleradores para estendê-los a um estado útil), de modo que, em vez de digitar : seguido de um comando, você pode digitar Ctrl+P e ou digitar o caminho da ação, ou iniciar uma consulta e escolher da lista de correspondências (usando as teclas de seta e depois Enter).
As correspondências são ordenadas por relevância decrescente (de cima para baixo), e a relevância é calculada a partir da posição da correspondência. A suposição aqui é que, sendo os caminhos de ação do genérico ao específico da esquerda para a direita (view/module/control), as correspondências de texto que ocorrem no final do caminho supostamente correspondem a controles, e não a módulos ou visualizações, os quais consideramos mais específicos e, portanto, mais relevantes. Isso pode evitar ter que rolar para baixo passando por todo o conteúdo dos módulos que poderia corresponder a uma consulta de texto que busca um controle.
Janela de edição de atalhos
Como agora há apenas um atalho configurável pelo usuário por controle, a GUI para listar e editar atalhos é uma árvore simples:

Basta dar um duplo clique na coluna Teclas para começar a gravar uma nova combinação de teclas. Por ser tão simples, esta janela também substitui de uma só vez a folha de referência. Os atalhos podem ser buscados pelo nome da ação ou pelas teclas usadas, e a busca por teclas tem preenchimento automático para os modificadores de teclas (detalhes na documentação). O popup de atalhos pode ser exibido a partir do menu global Editar → Atalhos de teclado…
Documentação dentro do software
A questão é a seguinte: manter uma documentação atualizada sobre os atalhos padrão é um transtorno, porque é excessivamente detalhado. É o tipo de documento que estará desatualizado quando você terminar de escrevê-lo. Como os atalhos padrão precisam ser implementados no aplicativo de qualquer forma, o melhor lugar para documentá-los é diretamente dentro dele, de modo que a atualização seja automática.
O Darktable tinha 2 interfaces de atalhos redundantes, uma para configurar os atalhos e outra do tipo popup “folha de referência” (que, por muito tempo, só era acessível através de… um atalho. Grande coisa em termos de descoberta). O motivo era que o popup de configurações estava atravancado demais para ser usado como qualquer tipo de lembrete rápido. Então, os atalhos foram adicionados às dicas de contexto de alguns controles… dicas de contexto que só aparecem ao passar o mouse sobre os controles. Que sentido faz você ter que usar o mouse para descobrir como usar o teclado, caso a caso?
O Ansel tem atalhos (combinações de teclas) escritos no popup de configurações, e também recuperados na busca global de ações. Mas esses atalhos não se limitam aos que são configuráveis pelo usuário; eu estendi o gerenciador de atalhos com “atalhos virtuais”, ou seja, atalhos basicamente mapeados a nenhuma ação, mas declarados exatamente como os demais, e que aparecem entre os demais como atalhos “bloqueados”.

Esses atalhos descritos como interação contextual em foco documentam as ações genéricas voltadas ao controle em foco, quer o controle tenha sido focado de forma relativa ou absoluta.
Detalhes de implementação
O código-fonte de todo o sistema de gerenciamento de atalhos, incluindo as partes de interface gráfica (busca global de ações e popup de edição de atalhos), usa 1144 linhas de código, metade delas sendo a interface gráfica, para uma complexidade ciclomática de 216 . Isso é um quinto do volume de código para um sexto da complexidade (em comparação com o Darktable 5.0), mesmo oferecendo recursos adicionais (busca de teclas com preenchimento automático, busca global, alvos explícitos de instâncias de módulos, etc.). O suporte a MIDI foi abandonado porque, sinceramente, não vejo que problema ele resolve que já não seja resolvido pelo design mais simples atual.
Encontrar a ação associada a um toque de tecla leva cerca de uma dezena de nanossegundos, ao passo que o Grande turducken de MIDI levava de 10 a 50 milissegundos a cada toque.8
O grafo de dependências do recurso pode ser visto na documentação de desenvolvedor, ele é muito mais limpo do que a antiga tigela de espaguete. O restante do código de interface gráfica do Ansel interage com o gerenciador de atalhos declarando novos caminhos de aceleradores, registrando funções de callback associadas a eles e, possivelmente, atalhos padrão, tudo isso usando um único método da API. O gerenciamento de atalhos desconhece e é imune aos detalhes internos do Ansel; em particular, ele não sabe nada sobre módulos ou sobre os widgets Bauhaus caseiros, de modo que o design é inteiramente autocontido. As preferências são armazenadas por idioma em ~/.config/ansel/keyboardrc-LANG, usando um mapa nativo de aceleradores do Gtk . A API é totalmente documentada na documentação de desenvolvedor do Ansel para manutenção e extensão futuras, de modo que nenhuma engenharia reversa será necessária.


É assim que eu trabalho porque não programo por diversão. Não tenho diversão nenhuma em programar. Eu resolvo problemas, tentando não criar novos.
Conclusão
Este é o caso emblemático de tudo o que deu errado no Darktable em termos de superengenharia incessante e de como isso deveria ter sido corrigido. A solução proposta aqui é melhor porque:
- toda a interface gráfica pode ser navegada pelo teclado sem ter que memorizar um único atalho,
- controles, módulos e outras ações têm apenas um atalho direto, absoluto e configurável pelo usuário, que diretamente aciona a ação ou foca o widget de controle, se houver, o que torna a interface gráfica de configurações muito menos avassaladora e elimina a necessidade de um popup adicional de folha de referência,
- as ações podem ser buscadas e acionadas globalmente, estejam elas atreladas ou não a uma combinação de teclas,
- interações mistas de Tecla+rolagem podem ser acionadas sem custo adicional, emulando os botões e controles deslizantes giratórios de MIDI sem necessidade de hardware extra,
- o código é objetivamente de 5 a 6 vezes mais simples (dependendo de qual métrica você considera),
- tudo é totalmente documentado e fartamente comentado no código,
- qualquer futuro idiota tapado capaz de ler C será capaz de manter isso, o que, de qualquer forma, não deveria precisar de muita manutenção, porque não faz nada engenhoso e vive fora do núcleo do software.
Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.
A turducken is a chicken stuffing a duck stuffing a turkey. That’s a decadent amount of meat that will likely go to waste, unless you have 20 persons to feed. Anyway, it will take forever to cook, and the turkey will likely be dry by the time the chicken is well done. ↩︎
Developers call that “whack-a-mole bug fixing” . ↩︎
Although the stress and anxiety inflicted upon workers by ever-changing software stacks is largely underrated, since innovation is deemed to increase productivity by definition, regardless of user feedback. ↩︎
STEM: Science Technology Engineering Mathematics ↩︎
I don’t see why the capitalistic mindset is ok when it comes to getting excited about new products, but gets boring when it reaches return on investment and hidden/sunk costs… ↩︎
I’m not talking here about the Darktable way of “fixing bugs” that consists into rushing on the visible manifestation of the bug, and adding a fourth level of nested
ifto take care of the pathological corner case. That’s working around the cause of the bug by creating more technical debt, not actually fixing the root cause of the bug. And since that root cause will generally stem many visible manifestations, patching all the manifestations is actually more complicated on the long run, and leads to brittle code. ↩︎Flexibility is usually the opposite of simplicity, so you have to enjoy when you can win on both fronts. ↩︎
Remember that the shortcut handler has to listen to all keystrokes before deciding if it’s supposed to do something with them or discard them, so this part of the software runs all the time for all users, whether or not they actually use shortcuts. ↩︎