Nel mio articolo fondativo, Darktable: schiantarsi contro il muro al rallentatore, ho presentato il disastro che era il nuovo «Grande turducken MIDI». Lo scopo di questo turducken1 era riscrivere il sistema di scorciatoie da tastiera per estenderlo ai dispositivi MIDI.
Ancora oggi sono arrabbiato per questa impresa di distruzione di massa, ecco un riepilogo delle ragioni:
- nel 2021 ha sostituito un sistema di scorciatoie da tastiera che era piuttosto buono, completo di funzioni, ben testato, stabile e scritto in meno di 1500 righe (commenti inclusi),
- …per aggiungere il supporto ai dispositivi MIDI e ai gamepad PlayStation (!?!)…
- …ma nel mio sondaggio Darktable del 2022, un anno dopo questa nuova funzione, su oltre 1251 utenti che vi hanno partecipato:
- l'81% degli utenti non aveva un dispositivo MIDI e non aveva intenzione di procurarsene uno,
- il 2% non sapeva nemmeno cosa fosse un dispositivo MIDI.
- l'8% degli utenti aveva un dispositivo MIDI ma non lo usava con Darktable,
- il 6% stava valutando l’idea di procurarsi forse un dispositivo MIDI in futuro,
- il 2% degli utenti aveva un dispositivo MIDI che usava davvero in Darktable,
- il codice era assolutamente terribile, in termini di:
- qualità del codice: istruzioni
if/switch-caseilleggibili annidate su 4 livelli, nel bel mezzo di funzioni da 1000 righe (ho pubblicato esempi di frammenti nel mio articolo), - volume del codice:
- 3546 righe di codice per Darktable 4.0,
- 4397 righe di codice per Darktable 5.0,
- l’aumento di volume è una conseguenza diretta del tentativo di correggere bug in un’architettura che non può essere corretta perché la sua complessità favorisce ulteriore complessità. Tutto ciò deriva dal design, ma risolvere problemi creati dalla complessità aggiungendo altra complessità non è una soluzione.
- complessità del codice:
- complessità ciclomatica :
- 1088 per Darktable 4.0,
- 1245 per Darktable 5.0 (dettagli ),
- complessità cognitiva :
- 1885 per Darktable 4.0,
- 2098 per Darktable 5.0 (dettagli ).
- è di gran lunga la funzione più complessa del software, anche se non opera sulle immagini. Per fare un confronto, la seconda funzione più complessa è la decodifica dei metadati EXIF, che ha una complessità cognitiva di 1348.
- complessità ciclomatica :
- qualità del codice: istruzioni
- non decodifica i modificatori dei tasti per progettazione, ma gestisce soltanto le pressioni fisiche dei tasti, il che significa che:
- l’input «1» dal tastierino numerico viene decodificato come
Keypad End, - l’input «1» da una tastiera francese AZERTY viene decodificato come
Shift+&, oShift+"su BÉPO, - devi quindi duplicare tutte le tue scorciatoie basate sui numeri per ogni modo di inserire un numero, ed essere pronto al fatto che la finestra delle impostazioni delle scorciatoie non contenga alcun numero effettivo nelle combinazioni di tasti.
- l’input «1» dal tastierino numerico viene decodificato come
- il design lato utente è assolutamente terribile, con troppe azioni ed emulazioni da configurare («effetti»), che dopo 4 anni non sono nemmeno completamente documentate (cos’è «ctrl-toggle»? «right-activate»?), e la configurazione delle scorciatoie usa una strana finestra divisa che non ha alcun senso,
- anche l’implementazione è terribile: la funzione è a conoscenza di tutta la GUI del software, e la GUI del software è a conoscenza del codice delle scorciatoie. Qui non c’è alcuna modularità, e modificare qualsiasi cosa nel codice delle scorciatoie può avere effetti inaspettati e indesiderati in qualsiasi punto del software.2 Basta guardare il grafo delle dipendenze qui sotto,
- diverse «scorciatoie» (o associazioni MIDI) possono essere collegate alla stessa azione, il che significa che ogni interazione dell’utente deve consultare l’intera lista delle azioni disponibili, comportando una gestione delle scorciatoie molto inefficiente, in alcuni casi rallentamenti della GUI e falsi positivi «combinazione di tasti sconosciuta» in casi particolari.


Il grafo delle dipendenze di src/gui/accelerators.c (Grande turducken MIDI) prima della riscrittura. Indovina perché lo chiamiamo «spaghetti code »… Questo rende evidente che esiste una dipendenza a doppio senso tra il codice degli acceleratori e il resto del codice della GUI. È un incubo da mantenere.
Quindi, detto altrimenti, un programmatore (pessimo) ha sostituito una funzione funzionante e semplice con una mostruosità, con l’approvazione del maintainer (che non scriverebbe mai codice così pessimo di suo pugno, ma è determinato a «non perdere lo slancio» sui contributi, a qualunque costo), per accontentare il 2% della base utenti. Tutto ciò per una funzione secondaria (terziaria?).
Io chiamo ciò priorità sbagliate. Tanto più che ci sono voluti diversi mesi di duro lavoro da parte di uno sviluppatore e di molti beta-tester, solo per peggiorare le cose, e poi il duro lavoro è diventato una giustificazione per non annullare mai la modifica, ciò che è noto come fallacia dei costi irrecuperabili .
Sono un utente, non mi importa del codice
Preoccuparsi del codice delle applicazioni che usi è proprio come preoccuparsi che i tubi che portano l’acqua a casa tua siano fatti di piombo. Non è compito tuo, i tubi sono sepolti fuori dalla vista, quindi hai tutte le ragioni per non preoccupartene — e le persone incaricate della rete idrica hanno tutte le ragioni perché tu non te ne preoccupi — ma avranno un effetto sulla tua salute, ed effetti che resteranno invisibili finché non sarà troppo tardi. Il software potrebbe non avere un effetto diretto sulla tua salute,3 ma il modo in cui è realizzato avrà un impatto a lungo termine su di te.
Mi ritrovo in una posizione scomoda – quella di Cassandra — dovendo spiegare agli utenti che problemi che non conoscono, non vedono e di cui non si preoccupano influenzano eccome l’usabilità e la stabilità quotidiane degli strumenti che usano, ma in modi non ovvi. In ogni caso, questi problemi influenzano direttamente la probabilità che un qualsiasi maintainer riesca, un giorno, a risolvere i bug che affronta.
Portare cattive notizie ti rende la cattiva notizia, ma portare cattive notizie di cui a nessuno importa ti rende uno stronzo, anche se quelle cattive notizie spiegano i problemi strani e casuali che diventano sempre più comuni nei bug tracker di Darktable dal 2021, che non vengono risolti per anni perché a quel punto il codice non contiene bug, il codice è un bug. Ma qui devi fare i conti con le bolle di filtraggio : a seconda di dove guardi sul web, troverai o persone profondamente contente di Darktable (prestazioni, stabilità, design), o persone profondamente scontente. La mia osservazione empirica è che i campeggiatori felici, in media, possiedono computer potenti e hanno lauree STEM.4 Le persone scontente tendono ad allontanarsi e a ridurre il tempo che perdono con il software, il che significa che, se non le stai cercando attivamente, alimenterai soltanto il tuo bias di sopravvivenza . E, sebbene non veda il senso di cercare di convertire gli utenti a Darktable (o a qualsiasi editor fotografico open-source), perché a ciascuno il suo, filtrare e scoraggiare gli utenti in base alla loro alfabetizzazione informatica è un grande fallimento per qualsiasi applicazione di editing fotografico.
Come tizio con un canale Youtube che spiega come usare Darktable, e che tiene sessioni di formazione individuali, tendo ad attirare feedback più vari rispetto ai soli bug tracker di Github (dove comunque le critiche al design vengono zittite piuttosto in fretta), e assisto in prima persona, nelle videochat, a quei bug strani, casuali e non riproducibili. Qualsiasi tizio privilegiato ha la tendenza a negare, minimizzare o ignorare le testimonianze riguardo a problemi che lui stesso non affronta, o addirittura a incolpare dei problemi la persona che li segnala. Quindi ho spesso questa impressione di due universi paralleli che non comunicano ma si guardano dall’alto in basso, quando si tratta di feedback su Darktable. Naturalmente, gli sviluppatori di Darktable hanno scelto di guardare dove splende il sole.
E mentre Darktable sembra un progetto attivo con un sacco di nuove funzioni aggiunte regolarmente da una dozzina di tizi, è tanto più difficile spiegare che ciò che in realtà accade dietro le quinte è la distruzione della base di codice, perché la qualità del codice si degrada moltissimo col passare del tempo, fino al punto in cui non sarà più possibile fare il debug affatto. Il volume dei cambiamenti non dice nulla sulla qualità, l’affidabilità o la manutenibilità. Ma 2 secoli di capitalismo ci hanno condizionato a cercare nuove funzioni scintillanti, a qualunque costo, e sotto questo aspetto Darktable soddisfa le aspettative.
Il codice nasce rotto. Qualsiasi software ha bug. Peggio ancora, usiamo molte librerie di terze parti per evitare di «reinventare la ruota», ma quelle librerie cambieranno la loro API in futuro, il che significa che il codice che funziona oggi smetterà di funzionare domani e dovrà essere rielaborato in futuro, quando le dipendenze cambiano. Il codice è come un giardino: ogni nuova stagione porta la sua dose di faccende. Si chiama manutenzione. Una volta riconosciuto ciò, la saggezza sta nel pianificare modi per rendere la manutenzione possibile, prima, e poi facile.
Il codice buono è codice facile da mantenere. Cioè, il codice buono è scritto in modi che sono gentili con le persone che devono leggerlo, comprenderlo e correggerlo. Non scriviamo codice per i computer. Non scriviamo codice affinché le funzioni «funzionino e basta» ora e nei prossimi mesi. Il codice non è qualcosa che nascondiamo sotto un bel cofano sperando di non doverlo mai più guardare. Il codice è un organismo vivente che costa energia e tempo per essere tenuto in vita.
La manutenzione ha un costo, spesso sottovalutato. Non importa se il tuo prodotto è il migliore sul mercato, se i tuoi costi di manutenzione sono proibitivi, i clienti (informati) lo eviteranno. La manutenzione è noiosa, poco interessante, per niente sexy. La manutenzione è l’opposto di introdurre novità scintillanti: è garantire che le vecchie cose impolverate continuino a funzionare silenziosamente. Nelle note di rilascio del tuo software, finisce in fondo. Gli utenti non si entusiasmano per le funzioni mantenute, ma si arrabbieranno per il codice non mantenuto e tendono a usare la metrica sbagliata per valutare quanto bene un progetto sia mantenuto (ossia: agitazione e parvenza di lavoro). Quindi non guadagnerai nulla dal mantenere il codice, ma ci rimetterai dal non mantenerlo, e ti costerà che tu lo faccia o no.
Il codice è impossibile da mantenere quando ogni correzione di un bug porta a un nuovo bug da qualche altra parte, in modo whack a mole (a colpi di talpa), e la natura delle correzioni dei bug è in realtà una toppa contestuale e un aggiramento che aggiungono soltanto complessità al software. Quando raggiungi quel punto, la tua unica opzione è riscrivere da zero il codice incriminato. Il che significa che il lavoro passato ha semplicemente creato più lavoro ora. E quando quello specifico lavoro passato stava già sovrascrivendo lavoro precedente, ci imbarchiamo nell’idiocrazia.
Più codice non è un successo o una conquista. Le conquiste stanno nel risolvere i problemi degli utenti. Anche qui, la mentalità capitalistica ci colpisce duramente: vorremmo misurare il lavoro in base al volume di codice aggiunto. Più codice è solo più responsabilità, più debito tecnico, più costi di manutenzione. È una dissonanza cognitiva, dato che il software consiste precisamente nello scrivere codice. Ma pensala così: costruire un’auto non consiste nell’aggiungere più acciaio sopra 4 ruote, perché a un certo punto l’auto dovrà muoversi. Ti serve la giusta quantità di acciaio, sagomata nel modo giusto nei posti giusti: devi essere efficiente e parsimonioso. Il software consiste nel risolvere i problemi degli utenti con i computer, non nell’accumulare codice.
Nei progetti in cui i collaboratori non sono pagati, la manutenzione noiosa non è dove le persone vogliono passare i loro sabati, ed è una preoccupazione reale per quanto riguarda la gestione del progetto. È ancora più importante rendere la manutenzione facile quando le persone lavorano gratis. Per fortuna, Darktable ha zero gestione del progetto, solo un alveare di collaboratori casuali che si grattano il proprio prurito in momenti casuali, senza obiettivo unificato, strategia, calendario o divisione del lavoro. Ecco perché vorrei che tutti smettessero di usare il termine «team di Darktable». Non esiste un team di Darktable, perché non c’è divisione del lavoro, né leadership, né calendario, né roadmap, né pianificazione, né obiettivo, né priorità, né visione, né strategia, né metodo, né design, né comunicazione prima di fare le cose, né documenti di specifiche per i problemi da risolvere. Questo è il club di informatica del liceo, fatto di individui scollegati.
Ci sono 3 cose che uno sviluppatore deve fare per garantire che la manutenzione sia la più facile possibile:
- scrivere la minor quantità di codice: rintracciare un bug tra 300 righe sarà molto più veloce che tra 3000 righe,
- scrivere il codice più semplice: «semplice» nel senso di «la logica funzionale ha pochi passaggi, poche assunzioni, pochi casi limite». Più semplice significa più facile da leggere, comprendere e correggere, ma anche che ci saranno meno casi da coprire durante i test. In pratica, significa evitare casi contestuali, opzioni utente, varianti,
if/elsenel codice. - scrivere codice autonomo: dividere la funzionalità del software tra moduli (sotto-programmi) che comunicano attraverso il nucleo e sono isolati l’uno dall’altro. Man mano che il volume del codice cresce, ciò garantisce che i cambiamenti che avvengono all’interno dei moduli non abbiano effetti imprevisti all’esterno.
Infine, devi valutare se le nuove funzioni che aggiungi valgono il costo aggiuntivo di manutenzione. Nel caso del gestore delle scorciatoie, stiamo assecondando i «bisogni» (o piuttosto, il lusso) del 2% della base utenti con un’implementazione che è (come minimo) due volte più complicata di quella che ha sostituito. Rifletti su ciò nella tua mente capitalistica…5
La complessità del codice può essere misurata usando la teoria dei grafi, tramite la complessità ciclomatica, la complessità cognitiva o la complessità N-path. Molti articoli di ricerca correlano il volume del codice e/o la complessità del codice al numero di bug nascosti, il che è piuttosto intuitivo: la riga di codice che non scrivi è l’unica in cui non avrai mai bug. La semplicità è l’obiettivo. E, come ho scoperto a mie spese, la complessità della GUI (front-end) è spesso una conseguenza diretta della complessità del back-end. Ci sono molti tizi che ululano alla luna per evocare la figura mistica dello UI/UX designer, sperando di risolvere magicamente la terribile GUI di Darktable (complicata e incoerente). La GUI non esiste in parallelo al back-end, si limita a collegare l’input atteso dal back-end a controlli grafici. Semplificare il front-end non può essere fatto senza semplificare il back-end, questo è solo uno stupido dogma inventato da persone che hanno soltanto competenze trasversali. Ma semplificare i back-end è molto più complicato che disegnare semplici mockup.
A quanto pare, ciò che è successo con il Grande turducken MIDI ha fallito su tutti e 3 i fronti: volume del codice, complessità del codice, autonomia del codice. E gli sviluppatori di Darktable non impareranno mai dai loro errori, come dimostra Darktable 5.0. Man mano che i bug delle scorciatoie da tastiera si accumulavano nel tempo in Ansel (oltre a funzioni terribili per progettazione), ho cercato di correggerli in modi che evitassero di riscrivere l’intera cosa, finché è diventato piuttosto chiaro che non sarei riuscito a evitare la riscrittura ancora a lungo.
Questo si chiama debito tecnico . L’intero codice del Grande turducken MIDI era pensato per funzionare una volta scritto, non per essere manutenibile a lungo termine. È fondamentalmente una prova di concetto che non sarebbe mai dovuta arrivare in produzione. E la prova stessa della sua impossibilità di manutenzione è quanto il volume e la complessità del codice siano cresciuti da una correzione di bug all’altra, tra Darktable 3.8 e 5.0, rendendolo ancora più impossibile da mantenere man mano che i bug vengono corretti. Questa è la definizione di incollarsi in una ragnatela: più ti muovi, più rimani bloccato.
Il mio errore è forse che ho iniziato a sollevare le mie critiche alla (mancanza di) gestione di Darktable nel 2022, dopo aver avuto la prova che il branco disorganizzato di collaboratori non avrebbe mai riconosciuto i propri errori né imparato da essi. Sebbene il ritmo di lavoro autoinflitto e insostenibile fosse più incline a creare burn-out che riflessione. Da allora, Pascal Obry, l’attuale maintainer autonominato di Darktable, ha cercato di convincere tutti che io sono un membro del team insopportabile, incapace di lavorare con altre persone che non sono d’accordo con me, che ha iniziato a insultare tutti. Naturalmente, nessuna risposta specifica e tecnica è stata data ai problemi che ho sollevato in Darktable: schiantarsi contro il muro al rallentatore, solo dichiarazioni generiche vagamente rassicuranti sul tono di «Darktable è un progetto attivo, sano perché ha molti collaboratori». Come se un numero maggiore di scimmie disorganizzate potesse alla fine equivalere a un ingegnere, se ne aggiungessi abbastanza.
Penso di essere stato niente altro che gentile per 4 anni – fin troppo gentile, in effetti — finché non ho capito quanto gravemente questi tizi abbiano danneggiato il progetto. Non imparano, e non impareranno mai dai loro errori, perché non li riconosceranno nemmeno, e l’evoluzione di Darktable 5.0 non fa che confermare la tendenza. Ma avere sempre più bug strani, casuali e non riproducibili che si accumulano nei bug tracker è un indizio piuttosto chiaro che qualcosa non va per niente. Soprattutto perché indagare su quei bug mi ha portato all’archeologia in terribili cataste di codice di merda che non aveva nemmeno 2 anni, e anche se la mia conoscenza ed esperienza sulla base di codice di Darktable è cresciuta negli anni, la mia capacità di correggere la causa alla radice dei bug è diminuita e mi trovavo di fronte a labirinti sempre più sconcertanti di indirezioni del codice .6
È difficile spiegare tutto ciò a persone che vedono i computer come scatole magiche progettate da maghi della tecnologia. Non c’è alcuna magia lì dentro. È perlopiù matematica e applicazioni. Molti sviluppatori confesseranno di essere scarsi in matematica, il che significa che sono scarsi anche nella programmazione. Perché la matematica non riguarda solo l’esecuzione di operazioni aritmetiche, è un’intera disciplina della mente che permette di astrarre problemi complessi per scomporli in problemi semplici, che porteranno a codice semplice e GUI semplice. I bravi programmatori passano un sacco di tempo a pensare a come scrivere poco codice. Perché il codice è una responsabilità, il codice è debito tecnico, il codice è costoso da mantenere. Quindi paghi il debito in anticipo, senza interessi, pensando molto al tuo codice prima di scriverlo, invece di programmare prima e poi passare anni a spegnere incendi.
Tutto ciò solleva anche la questione di chi possiede il codice open-source e i progetti open-source. Da quando il fondatore del progetto Darktable, Hanatos, ha lasciato la nave, così come tutta la prima generazione di sviluppatori, per varie ragioni, l’ultimo uomo rimasto della prima generazione si è autonominato nuovo maintainer. È uno sviluppatore molto capace e abile: il suo codice è pulito, ordinato, e finora non ho trovato alcun bug in una riga in cui git blame diceva «Pascal Obry». Ma la sua politica riguardo alla gestione del codice è terribile: crede che ogni contributo sia un buon contributo, che lo «slancio» dei contributi non debba essere scoraggiato, ed è propriamente incapace di dire «no» ai contributi, il che significa che quasi tutte le pull request vengono unite. È la Francia nel 1940: tutto entra, viene accolto con un gran sorriso e il Führer ottiene il doppio degli ebrei richiesti. Nel frattempo, i resistenti vengono chiamati terroristi.
Ma c’è un’enorme differenza tra la qualità del codice che Pascal scrive e la qualità del codice che accetta e unisce. Questo è un paradosso preoccupante che trova le sue radici tra la paura di perdersi qualcosa e il tecno-positivismo radicale , che sbanda verso il dogma e le credenze, con un totale disprezzo per il design e la prospettiva dell’utente. E forse una fiducia esagerata nella capacità della «Community» di correggere i bug in seguito.
Ci sono molti casi in cui non fare nulla è meglio che farlo male, specialmente quando stai sostituendo funzioni esistenti. Essendo Darktable un editor a codice non distruttivo, ci investi quando avvii un database di modifiche alle immagini. A meno che tu non abbia intenzione di esportare tutte le tue immagini in file ad alta risoluzione e alta profondità di bit, non appena hai finito di modificarle, per non cambiarle mai più. Ciò crea una legittima aspettativa di stabilità e coerenza a lungo termine, così che le tue vecchie modifiche possano ancora essere aperte, ritoccate ed esportate di nuovo, magari in nuovi formati, magari a risoluzioni più alte. Cambiare rotta nel design dell’applicazione è una specie di violazione del contratto, e anche se la licenza GNU/GPL annulla qualsiasi responsabilità legale, non annulla i danni agli utenti. Né mi rimborsa gli anni della mia vita che ho perso a correggere la loro merda.
Quindi, anche ignorando i problemi di manutenzione incentrati sullo sviluppatore, c’è anche una discussione da fare riguardo a chi decide come le funzioni vecchie e collaudate vengono sostituite, specialmente quelle funzioni di base e universali delle applicazioni desktop come la gestione dei file (importazione, esportazione, navigazione) o l’interazione con mouse e tastiera, che sono state così onnipresenti per così tanto tempo che gli anni 2020 arrivano 30 anni troppo tardi per fingere di reinventarle.
Un sacco di lavoro e di ore-uomo sono state investite per peggiorare le scorciatoie da tastiera, per il bene di politiche difettose e dogmi dannosi, oltre a pii desideri e ozio sociale in cui tutti sperano che La Community® (ovvero qualcun altro) si occuperà di correggere i propri errori. Gli utenti hanno anche dovuto perdere la loro configurazione della tastiera ed è stato loro richiesto di reimpostare tutto e reimparare tutto. Ma farlo nel modo giusto sarebbe in realtà costato meno lavoro e meno ore-uomo. Questo è un circolo di follia che si autoalimenta, che crea un ambiente di lavoro tossico in cui l’instabilità favorisce ulteriore instabilità, in cui la complessità favorisce ulteriore complessità, ancora una volta senza alcun tipo di roadmap delle funzioni che darebbe una direzione generale e visibilità a tutti i coinvolti.
Una breve storia del cattivo design
È solo dopo aver ricostruito da zero la funzione delle scorciatoie che ho capito cosa è andato storto nelle scorciatoie/acceleratori di Darktable.
Innanzitutto, c’è questa debilitante dissolutezza di funzioni, che rende allettante sfoltire la GUI semplicemente nascondendo le funzioni, per poi gestirle da tastiera. Il problema è poi che tali funzioni non sono sempre di nicchia e opzionali (come la scorciatoia che aggira le interazioni con la maschera durante il trascinamento dell’anteprima principale dell’immagine in camera oscura), ma sono decisamente impossibili da scoprire per gli utenti. Ansel ha risolto questo problema con il menu globale.
Alcune funzioni erano nascoste tramite un supporto di base ai vimkeys, se inizi a digitare :: :q chiuderà l’applicazione, :set seguito dal nome dello slider o della combobox ne cambierà il valore. Questo, ovviamente, non è documentato da nessuna parte nel manuale di Darktable, e come utente di Darktable da più di un decennio, non ne avevo mai sentito parlare prima di eliminarne il codice, perché questa piccola trovata ascolta tutte le tue pressioni di tasti per determinare se debba agire o meno su ciò che digiti.
Ma poi, c’è anche il fatto che i moduli usano widget Gtk fatti in casa (chiamati «Bauhaus», in src/bauhaus/bauhaus.c) che non implementano tutto ciò che ti aspetteresti da un widget GUI che cattura eventi dell’utente, in particolare non le funzioni di accessibilità.
Una delle funzioni di accessibilità più basilari è la capacità di scorrere ciclicamente tra i widget focalizzabili. Nel gergo delle GUI (e di Gtk), un widget focalizzabile è uno che può catturare gli eventi di pressione dei tasti, una volta focalizzato. I widget vengono tipicamente focalizzati una volta cliccati, ma Gtk gestisce anche internamente una catena di focus nella quale si naviga con il tasto Tab e i tasti freccia. Il primo problema è che il tasto Tab, in Darktable, era associato alla modalità «anteprima» (attivare/disattivare tutti i pannelli nella vista). E in effetti, usando gli acceleratori nativi di Gtk, ciò non sarebbe stato affatto possibile, dato che Tab è associato da Gtk e vietato nelle scorciatoie definite dall’utente, ma poiché Darktable implementava il proprio gestore delle scorciatoie, anche prima del Grande turducken MIDI, veniva sovrascritto. Quindi, lo scorrimento ciclico della catena di focus basato sulla tabulazione era disabilitato semplicemente perché il tasto Tab era assegnato a qualcos’altro. Ma il secondo problema era che anche i widget Bauhaus fatti in casa catturavano tutte le pressioni dei tasti freccia. Perciò navigare tra i controlli in maniera sequenziale (successivo/precedente) era anch’esso completamente impossibile, per progettazione. Proprio come i widget Bauhaus catturavano tutti gli eventi di scorrimento del mouse, impedendo lo scorrimento dei pannelli laterali.
Poiché la navigazione sequenziale/incrementale tra i widget focalizzabili era impossibile per progettazione, tutte le interazioni da tastiera dovevano necessariamente essere rese assolute: per ogni slider, per ogni combobox, avresti avuto una scorciatoia che aumentava/diminuiva/reimpostava il valore mappata direttamente.
Ma sono sorti altri problemi quando i moduli sono stati resi multi-istanziabili, perché i controlli erano identificati da un percorso di acceleratore come view/module/slider/increase o view/module/slider/decrease, ma tutte le istanze del modulo avrebbero ereditato lo stesso percorso. Ciò ha innescato la necessità di gestire tutto ciò a runtime, con preferenze utente per decidere se il modulo bersaglio fosse il primo, l’ultimo o l’ultimo con cui si è interagito.
Generalizzare tutto ciò a MIDI e gamepad non ha fatto che peggiorare le cose, perché oltre ad avere una scorciatoia per ogni possibile azione per ogni widget, c’era poi la necessità di gestire l’emulazione della tipica interazione desktop da altri dispositivi di input. Ma invece di gestire il livello di emulazione ad alto livello, all’interfaccia tra MIDI e le normali scorciatoie da tastiera/mouse, un pessimo sviluppatore ha totalmente sovra-ingegnerizzato un livello di astrazione di azioni, incrostato nei moduli, nei widget Gtk nativi (come sovrapposizione) e nei widget Bauhaus fatti in casa (profondamente incrostato). Il problema è che quel livello di astrazione non era davvero un livello ma piuttosto un tumore metastatico che si diffondeva ovunque. Rimuoverlo e tutte le sue dipendenze ha portato alla rimozione di 7674 righe distribuite su 163 file, anche se avrebbe dovuto essere implementato in src/gui/accelerators.c (4412 righe di codice, commenti e righe vuote).
Riprogettare da zero le scorciatoie da tastiera/acceleratori
Il passo zero della riprogettazione sono questi 2 semplici requisiti:
- Ogni azione dovrebbe essere scopribile nella GUI, le scorciatoie da tastiera non sono pensate per sfoltire la GUI; sfoltire la GUI è una questione di scomporre il flusso di lavoro in passaggi unitari e presentare solo i controlli che contano per il passaggio corrente.
- Il software dovrebbe essere completamente utilizzabile dal solo mouse e dalla sola tastiera. L’interazione mista dovrebbe essere completamente opzionale.
Il primo vincolo della riprogettazione è quindi rendere le scorciatoie assolute completamente opzionali, cioè progettare il flusso di lavoro da tastiera per un accesso sequenziale/relativo (scorrere ciclicamente tra il controllo successivo/precedente).
Interazione con i controlli focalizzati
Il paradigma diviso «focalizza» poi «interagisci» (che non ho inventato io…) è molto potente perché permette di delimitare le pressioni dei tasti nel giusto contesto, il che significa che le stesse pressioni di tasti (in particolare i tasti freccia Su/Giù/Destra/Sinistra) possono essere collegate più di una volta nella GUI, ed essere gestite in modo diverso a seconda di quale contesto ha il focus. Questo è più flessibile e in realtà più semplice7 dell’ossessione di Darktable di collegare tutto a scorciatoie globali e assolute che poi collidevano su tasti spesso riutilizzati, e portavano a dover aggiungere sempre più modificatori di tasti come aggiramento.
Finora, navigare tra i controlli ha dato loro solo il focus. E l’interazione vera e propria?
Nella vista tavolo luminoso, una volta che la griglia delle miniature è focalizzata, la navigazione tra le miniature avviene usando i tipici tasti freccia, Pag Su/Giù, ecc. (vedi la documentazione per tutti i dettagli) e le selezioni delle immagini possono essere fatte in vari modi (a lotti, in serie, individualmente) anche da tastiera.
Nella vista camera oscura, cambiare i valori su slider e combobox può essere fatto usando i tasti freccia (eventualmente usando Shift per un passo grossolano o Ctrl per un passo fine), attivare il selettore di colore (sugli slider che li supportano) si fa con Insert, ecc. (di nuovo, leggi la documentazione per i dettagli).
Quindi, ancora una volta, finora nessuna scorciatoia definita dall’utente, nessuna criptica combinazione di tasti da ricordare, sono perlopiù tasti freccia eppure si può accedere a tutto.
Focalizzazione assoluta dei controlli
Tutto ciò è ottimo, ma ti lascia su una specie di percorso Ikea quando hai bisogno di accedere direttamente a qualche area dell’applicazione ma devi navigare attraverso tutte le sezioni a partire dalla porta d’ingresso.
Per alleviare ciò, una funzione molto vecchia di Darktable era la scheda «moduli preferiti», ossia una scheda speciale che duplicava la GUI dei moduli più usati, in base alla scelta dell’utente. Questo è essenzialmente risolvere il gonfiore con altro gonfiore, eppure gli utenti si sono affezionati moltissimo a ciò. Tuttavia, se ci concentriamo sull’obiettivo, piuttosto che sul mezzo, il requisito è un accesso rapido a moduli arbitrari, il che è perfettamente comprensibile.
Quindi questo è stato reimplementato come un modo per definire una scorciatoia assoluta che focalizza immediatamente un modulo di elaborazione delle immagini o uno qualsiasi dei suoi controlli interni. Focalizzare un modulo o controllo nascosto lo farà apparire automaticamente nella GUI. Da lì, l’ulteriore interazione avviene esattamente come prima, usando combinazioni di tasti uniformi. Questo riduce di molto il numero di scorciatoie da configurare, rende il tutto più generico e la finestra delle impostazioni delle scorciatoie molto più semplice.
Le scorciatoie assolute possono anche puntare a voci di menu (ossia azioni globali), nel qual caso verranno richiamate nel menu, accanto all’etichetta dell’azione, il che è di nuovo una funzione nativa di Gtk che arriva con zero sovraccarico.
Sostituire i controller MIDI
Se lasciamo da parte tutto l’hype di avere controller dedicati e sentirsi come un pilota d’aereo, l’unica cosa che i controller MIDI hanno e che tastiera e mouse non avranno mai è la capacità di collegare potenziometri (manopole rotanti) direttamente agli slider della GUI. Ma poi, il costo di ciò è avere un dispositivo in più che occupa spazio (e polvere) sulla tua scrivania, per non parlare della futura spazzatura elettronica. Tutti i fotografi che conosco che hanno comprato MIDI o Loupedeck li hanno riposti «temporaneamente» per recuperare un po’ di spazio sulla scrivania… e non li hanno mai più tirati fuori dal ripostiglio.
I controlli di elaborazione delle immagini possono essere collegati a scorciatoie a tasto singolo, perché Ansel usa una sovrapposizione di scorciatoie personalizzata sopra le funzioni native degli acceleratori di Gtk. Ho modificato i widget Bauhaus fatti in casa in modo che, quando premi una delle scorciatoie di focalizzazione assoluta di cui sopra e la tieni premuta, lo scorrimento della rotella del mouse venga mappato direttamente al widget anche se il mouse non si trova sopra lo slider giusto.
Quindi combinando le scorciatoie a una lettera per focalizzare un controllo, con lo scorrimento del mouse (o del touchpad), ottieni tutta la bontà delle manopole rotanti MIDI senza il dispositivo in più, la libreria in più per il supporto e le meraviglie dei livelli di emulazione sovra-ingegnerizzati.
Ma tutto ciò avviene ancora con l’unica e sola scorciatoia assoluta, quindi non devi definire e ricordare diverse scorciatoie per controllo, solo per ritrovarti a corto di combinazioni di tasti disponibili.
Azioni senza scorciatoia, motore di ricerca e trigger in stile vimkeys
Il mio gestore delle scorciatoie è un sottile wrapper sopra l’API nativa degli acceleratori di Gtk. Come tale, un’azione è definita da un percorso testuale, come Ansel/Darkroom/Modules/Exposure/Black level, che è un identificatore univoco, leggibile sia da un computer che da un umano. La GUI dichiara una funzione collegata a ciascuno di questi percorsi, contenente il codice da eseguire per applicare l’azione corrispondente. Poi il gestore delle scorciatoie mappa una combinazione di tasti a questo percorso.
Quindi, ogni volta che un utente preme dei tasti, il gestore delle scorciatoie controlla se abbiamo un percorso noto per questa combinazione, e se ne trova uno, attiva la funzione ad esso collegata. Questo è un design a prova di sciocco che non sa nulla dell’interno dei moduli di Ansel, dei widget fatti in casa, ecc. Quindi può essere esteso a molte parti del software senza sovraccarico.
Ma è in realtà molto più potente di così. Perché elencando tutti i percorsi noti, possiamo poi restituire i percorsi che corrispondono a una query di ricerca testuale (cercando un controllo, un modulo o una voce di menu globale per nome), cioè trovare tutte le azioni da un elenco, ma possiamo poi anche attivarle anche se non sono collegate ad alcuna scorciatoia.

Poiché anche i moduli di elaborazione delle immagini possono essere trovati e visualizzati in questo modo, ciò sostituisce anche il motore di ricerca dei moduli, recuperando un po’ di spazio verticale per i moduli lunghi e le loro opzioni di mascheramento/fusione (e rimuovendo circa 200 righe di codice). Da lì, l’ho esteso a tutte le barre degli strumenti e l’ho reso globale, il che significa che il motore di ricerca delle azioni funziona in tutte le viste ma elencherà solo le azioni pertinenti alla vista corrente.
In camera oscura, supporta anche le multi-istanze dei moduli, permettendo di puntare direttamente a un’istanza specifica (di più su ciò nella documentazione).
Per impostazione predefinita, la ricerca globale delle azioni è mappata alla scorciatoia Ctrl+P, ed è accessibile dal menu globale Aiuto, o con un pulsante Cerca azioni al centro della barra dell’intestazione. Questo sostituisce anche in qualche modo i vimkeys che avevano un supporto molto parziale alle azioni della GUI (e avrebbero avuto bisogno di duplicare interamente gli acceleratori per estenderlo a uno stato utile), così che, invece di digitare : seguito da un comando, puoi digitare Ctrl+P e digitare il percorso dell’azione, oppure avviare una query e scegliere dall’elenco delle corrispondenze (usando i tasti freccia e poi Invio).
Le corrispondenze sono ordinate per rilevanza decrescente (dall’alto verso il basso), e la rilevanza è calcolata a partire dalla posizione della corrispondenza. L’assunzione qui è che, essendo i percorsi delle azioni da generici a specifici da sinistra a destra (view/module/control), le corrispondenze testuali che avvengono alla fine del percorso si suppone corrispondano a controlli piuttosto che a moduli o viste, che consideriamo più specifici e quindi più rilevanti. Questo può evitare di dover scorrere oltre tutto il contenuto dei moduli che potrebbe corrispondere a una query testuale che cerca un controllo.
Finestra di modifica delle scorciatoie
Poiché ora c’è solo una scorciatoia configurabile dall’utente per controllo, la GUI per elencare e modificare le scorciatoie è un semplice albero:

Basta fare doppio clic nella colonna Tasti per iniziare a registrare una nuova combinazione di tasti. Essendo così semplice, questa finestra sostituisce in un colpo solo anche il prontuario. Le scorciatoie possono essere cercate per nome dell’azione o per tasti usati, e la ricerca dei tasti dispone del completamento automatico per i modificatori (dettagli nella documentazione). La finestra delle scorciatoie può essere aperta dal menu globale Modifica → Scorciatoie da tastiera…
Documentazione all’interno del software
Il punto è questo: mantenere aggiornata una documentazione sulle scorciatoie predefinite è una seccatura, perché è troppo dettagliata. È il tipo di documento che sarà già superato nel momento in cui finisci di scriverlo. Dato che le scorciatoie predefinite devono comunque essere implementate nell’applicazione, il posto migliore per documentarle è direttamente al suo interno, così l’aggiornamento è automatico.
Darktable aveva 2 interfacce ridondanti per le scorciatoie, una per configurarle e un’altra sorta di finestra «prontuario» (che, per lungo tempo, è stata accessibile solo tramite… una scorciatoia. Bella scoperta in fatto di individuabilità). Il motivo era che la finestra delle impostazioni era troppo affollata per essere usata come una qualsiasi forma di promemoria rapido. Poi le scorciatoie sono state aggiunte nei suggerimenti di alcuni controlli… suggerimenti che compaiono solo passando il mouse sopra i controlli. Che senso ha dover usare il mouse per scoprire come usare la tastiera, caso per caso?
Ansel ha le scorciatoie (combinazioni di tasti) scritte nella finestra delle impostazioni, e riportate anche nella ricerca globale delle azioni. Ma queste scorciatoie non si limitano a quelle configurabili dall’utente: ho esteso il gestore delle scorciatoie con delle «scorciatoie virtuali», cioè scorciatoie in pratica non associate ad alcuna azione ma dichiarate esattamente come le altre, e che compaiono insieme alle altre come scorciatoie «bloccate».

Le scorciatoie descritte come interazione contestuale sul focus documentano le azioni generiche che agiscono sul controllo che ha il focus, sia che il controllo abbia ricevuto il focus in modo relativo o assoluto.
Dettagli implementativi
Il codice sorgente dell’intero sistema di gestione delle scorciatoie, comprese le parti dell’interfaccia grafica (ricerca globale delle azioni e finestra di modifica delle scorciatoie), usa 1144 righe di codice, metà delle quali dedicate all’interfaccia grafica, per una complessità ciclomatica di 216 . Si tratta di un quinto del volume di codice per un sesto della complessità (rispetto a Darktable 5.0), pur offrendo funzioni aggiuntive (ricerca dei tasti con completamento automatico, ricerca globale, obiettivi espliciti per le istanze dei moduli, ecc.). Il supporto MIDI è stato abbandonato perché, francamente, non vedo quale problema risolva che non sia già risolto dall’attuale design più semplice.
Trovare l’azione associata a una pressione di tasti richiede una decina di nanosecondi, laddove il Grande turducken MIDI impiegava da 10 a 50 millisecondi a ogni pressione.8
Il grafo delle dipendenze della funzione può essere visto nella documentazione per sviluppatori, ed è molto più pulito del precedente piatto di spaghetti. Il resto del codice dell’interfaccia grafica di Ansel interagisce con il gestore delle scorciatoie dichiarando nuovi percorsi di acceleratori, registrando le funzioni di callback ad essi associate ed eventualmente le scorciatoie predefinite, il tutto usando un unico metodo dell’API. La gestione delle scorciatoie ignora ed è immune ai meccanismi interni di Ansel, in particolare non sa nulla dei moduli o dei widget Bauhaus fatti in casa, quindi il design è interamente autocontenuto. Le preferenze sono memorizzate per lingua in ~/.config/ansel/keyboardrc-LANG, usando una mappa di acceleratori Gtk nativa. L’API è interamente documentata nella documentazione per sviluppatori di Ansel per la manutenzione e l’estensione future, così non sarà necessario alcun reverse-engineering.


È così che lavoro, perché non programmo per divertimento. Programmare non mi diverte. Risolvo problemi, cercando di non crearne di nuovi.
Conclusione
Questo è l’esempio lampante di tutto ciò che è andato storto in Darktable in termini di implacabile over-engineering e di come avrebbe dovuto essere risolto. La soluzione qui proposta è migliore perché:
- l’intera interfaccia grafica può essere navigata da tastiera senza dover ricordare una sola scorciatoia,
- i controlli, i moduli e le altre azioni hanno una sola scorciatoia diretta, assoluta e configurabile dall’utente, che attiva direttamente l’azione o dà il focus al widget del controllo, se presente, il che rende l’interfaccia delle impostazioni molto meno opprimente ed elimina la necessità di una finestra prontuario aggiuntiva,
- le azioni sono ricercabili e attivabili globalmente, che siano o meno legate a una combinazione di tasti,
- le interazioni miste tasto+scorrimento possono essere attivate senza costi aggiuntivi, emulando le manopole e i cursori rotanti MIDI senza bisogno di hardware extra,
- il codice è oggettivamente da 5 a 6 volte più semplice (a seconda della metrica che si considera),
- il tutto è interamente documentato e ampiamente commentato nel codice,
- qualunque futuro idiota decerebrato in grado di leggere il C sarà in grado di mantenere questa cosa, che comunque non dovrebbe richiedere molta manutenzione perché non fa nulla di ingegnoso e vive al di fuori del nucleo del 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. ↩︎