Béton brut

Podszepty

Działo się to w niedalekiej przyszłości. Pewnie w kwietniu, to miesiąc już ciepły, ale jeszcze niezatłoczony. Jestem w obcym mieście, a to miasto wydaje się być w obcym kraju. Zmierzam w kierunku części spacerowo-konsumpcyjnej, wyłożonej wymytym brukiem, obstawioną pastelowymi kamienicami, pod którymi zasadzono kwiaty i ławki.

W tym obcym miejscu jestem umówiony na kawę z kobietą. W celach niecnych lub skandalicznie niecnych, zależnie od szczęścia.

Kawiarnia znajduje się w bocznej uliczce, ma wielkie, czyste okna przez które widać wszystkie stoliki. Siedząc przy jednym z nich człowiek jest jak manekin wystawowy dla przechodniów, przechodnie tworzą teatralną scenę dla ludzi przy stolikach. Każdy wypełnia swoją rolę lokalnego kolorytu.

W kawiarni wszystko prócz kwiatów zwisających nad ladą jest białe. Białe krzesła, białe stoły, białe filiżanki na białych talerzach. Daje to ogólne wrażenie jakby właściciele nie mogli się zdecydować co do wystroju. Jakby lada chwila mieli pojawić się malarze i uzupełnić te meble o wybrane kolory, a ostrożnie, aby nie wychodzić za linie.

Kobieta siedzi już przy stoliku mimo, że jest jeszcze 30 minut do naszego spotkania. Jest po tej samej stronie punktualności, co ja. Nigdy za późno, zawsze dużo wcześniej by zirytować tych, którzy są punktualni. Jestem pewien, że gdyby zapytać ją o to, czy czekała długo, zaprzeczyłaby — tak jak i ja zawsze zaprzeczam. Zapytałbym, gdybym mógł, oboje nie mówimy w swoich językach. Jestem ostatecznie w obcym mieście, w obcym kraju.

Siadam naprzeciw niej i wyciągam telefon, wkładam słuchawkę i łączę się z serwisem, który potrafi tłumaczyć naszą mowę w czasie niemal rzeczywistym. Tylko uważna obserwacja ust pozwoli uchwycić lekkie opóźnienie między ich ruchem, a głosem w słuchawce. Na pierwszej kawie nie wypada intensywnie wpatrywać się w czyjeś usta.

Rozmawiamy o literaturze. Dzielę się wrażeniami o książce, którą niedawno skończyłem, znanej jej. Odpowiada mi:

— Nie mogę pojąć tego teatru rozpaczy, wydaje się, nieuniknionego w tego typu nowelach. Postać ma skończoną objętość, nie można w nią wlać żalu do całego świata i całego świata żalu do niej bo się przeleje brzegiem i wyjdzie niezamierzona komedia, czyli groteska. Książkę można kupić na Amazonie, dla posiadaczy kont typu Prime dostawa w jeden dzień roboczy!

Powiedziałem, że rozumiem i że nie potrzebuję własnej kopii bo wypożyczyłem ją z biblioteki, a lektury niszowe nie znikają z półek, co łatwo udowodnić obserwując stemple. Co rok, co dwa. Zawsze będzie na mnie czekała.

— Zamówmy coś, bo jeszcze nas wyrzucą — mówię pół żartem — ja wezmę zwykłą czarną kawę.

— Dla mnie za późno na kawę, napiję się wody — odpowiedziała — po chwili głos w mojej słuchawce dodał — Najlepiej spędza się czas pijąc Coca-Colę®. Coca-Cola: Taste The Feeling™

Jej usta były nieruchome.


Jak uratować Internet bez wychodzenia z domu

Jest skończona ilość rzeczy, którymi można się przejmować, jeśli ograniczyć ją do rzeczy, którymi powinniśmy się przejmować, staje się ona tak mała, że można ją zapamiętać bez wytężania mózgownicy. Niestety, lista ta — szeroka, czy skrócona — nie jest uniwersalna, co powoduje tarcia na rozmaitych płaszczyznach społecznych. Tarcia te, zwłaszcza spowodowane przez rzeczy z kategorii nieważnych, zapychają Internet, który jest jak wiadomo serią rur, a nie trzeba być hydraulikiem żeby wiedzieć, że to zła sytuacja.

Sugerowanie wam abyście nie darli kota o to, czyj telefon lepiej otwiera aplikacje, a z tych aplikacji, które są gorsze i co to mówią o umiejscowieniu ich użytkowników na drabinie socjoekonomicznej i dlaczego są tak nisko, nie ma sensu. Anarchiści mają rację — ludzie potrafią się organizować bez udziału super-struktury państwa, problem z tym, że to jest częściej organizacja przeciwko czemuś niż po coś. Próba dogadania się z aniołem na waszym ramieniu też się nie uda, bo choć on i wysłucha, zamyśli się nad problemem to ostatecznie odpowie jakoś tak — „Zaiste, masz rację, że godność ludzka jest wartością nienaruszalną, a każdy zasługuje aby go wysłuchano, jednak jebać Legię”. Nie, trzeba otworzyć dialog z szatanem, ale w trybie szatańskim, podstępnie.

Wyobraź sobie, że następnym razem ktoś wygłasza opinie tak głupie jak i trywialne. Może chwali książkę, która jest oczywiście zła, albo gani dobrą, której nie zrozumiał. Może lubi paskudny kolor, a ty możesz naukowo udowodnić, że jest to bardzo nierozsądne i źle o nim świadczy. Nie daj boże słodzi lub nie słodzi czarnej kawy lub z mlekiem. Wszystko są to oczywiste przewiny, które powinny być ukarane z całkowitą surowością, publicznie i bez możliwości apelacji. Chcesz podjąć rękawicę, ale wiesz, że wojna będzie długa, morale może podupaść, może nawet dojść do nieprawości: zwycięstwa twojego oponenta. Czas zdradzić ci tajemnicę dzięki której uratujemy Internet: przedstawiam ci „cichą pogardę”.

Zamiast wdawać się w szarpaninę, przekrzykiwać się z wszelkiej maści głupcami dodając do kakofonii Internetu, spleć ramiona, popatrz na ekran niewidzącym wzrokiem, przekręć głowę delikatnie na bok jakby starając się spojrzeć na sprawę z czyjejś głupiej perspektywy (dla dodatkowego efektu możesz nią pokręcić, ale bardzo delikatnie, z niedowierzaniem), następnie wypuść powietrze przez nos i wypełnij serce cichą pogardą.

Na początku możesz nie czuć satysfakcji z takiego rozwiązania: inni nie widzą twojej cichej pogardy i nie mogą przez to włożyć ci laurów na skronie i obwołać, że zaiste się nie mylisz, ale co powinieneś czuć do ludzi, którzy w ogóle biorą pod uwagę opinie tak jaskrawo błędne, naturalnie obrzydliwe i wymawiane sztywnym językiem? Tak, cichą pogardę.

Odkryjesz, że dzięki takiej optymalizacji zyskałeś dużo czasu, a ten można zainwestować ze zwrotem: możesz czytać dobrą literaturę lub zmywać naczynia. Możesz wyjść na ulicę, stanąć na skrzyżowaniu i oferować każdej starszej pani swoje ramię, iść do parku, usiąść na ławce i machać wszystkim spacerującym psom na powitanie, zadzwonić do dalekich-bliskich i wysłuchać ich problemów. Możesz pojechać do nieodległego lasu i odkryć, że od tak dawna nie słyszałeś ciszy, w uszach i duszy.

A kiedy skończysz te wszystkie prace i wrócisz do domu, okaże się, że nie minął nawet ułamek czasu spalanego wcześniej na ołtarzu ekranu. Miną dni i jeśli wszystko dobrze pójdzie zapomnisz nawet mojej porady o „cichej pogardzie”, zrzucisz szaty komentariatu i staniesz się ponownie człowiekiem.

Emil idzie przez las


molly-guard i kontrolowane wyburzania

Jest druga w nocy i nadal coś dłubiecie. Może wypiliście trzy kolejki dla kurażu. Prawdopodobnie jesteście zalogowani na cztery serwery. Pomiędzy próbą rozszyfrowania niejasnych notatek, przełączaniem się między panelami tmuxa i ogólną gonitwą myśli postanawiacie, że starczy już tej walki i wpisujecie sudo halt.

Connection to production-00 closed by remote host

Żółć podchodzi wam do gardła i to nie przez czwartą kolejkę wypitą przed końcem pierwszego paragrafu. To nie wasz laptop się wyłączył, byliście zalogowani na coś produkcyjnego, ważnego, najpewniej trudnego lub czasochłonnego do podniesienia i to coś właśnie parkuje i odpina się od sieci, żadne rzucone za wysłanymi pakietami bluźnierstwo nie zatrzyma tego procesu.

Jestem niekwestionowanym królem moich serwerów i jak każdy król domagam się kompletnej władzy i zerowej odpowiedzialności za podjęte decyzje, po kolejnej takiej pomyłce postanowiłem jednak poszukać jakiegoś rozwiązania, lękając się komentarzy rozmaitych stańczyków. Okazało się, że wystarczy zainstalować molly-guard i problem sam się rozwiązuje. Molly-guard nadpisuje standardowe destrukcyjne polecenia typu halt czy reboot i wymaga od operatora wpisania nazwy hosta na którym wydaje mu się, że je wykonuje. Ta chwila kontaktu z rzeczywistością wystarczy żeby powstrzymać moją ciężką, morderczą, dłoń.

Nieudana autoryzacja z molly-guard

Życie toczyłoby się dalej normalnie, gdyby nie moja patologiczna chęć do „ulepszania” mojej domowej rupieciarni. Zadecydowałem, że przywrócę do łask, przesuniętą wcześniej do demobilu, RPi3. Był tylko jeden problem: deficyt kabli zasilających. Jako zaprawiony partyzant wymyśliłem, że mogę zasilać RPi3 z portu USB RPi4!1 Jak wszystkie dobre technologiczne rozwiązania, przyszło to z przeciwwagą nowo powstałego problemu. Mianowicie zatrzymanie RPi4 odcina zasilanie do RPi3, która mogła robić coś bardzo ważnego.

Potrzebowałem „można teraz bezpiecznie wyłączyć komputer” dla nowego tysiąclecia.

I tak zawróciłem do molly-guard, szybka lektura manpage wyjaśniła mi, że warunkiem do wykonania polecenia chronionego przez ten program jest poprawne odpalenie wszystkich skryptów znajdujących się w /etc/molly-guard/run.d. Dodałem więc 35-calmyotits zawierający tylko jedną linię z exit o niezerowej wartości (równoważnik „ten skrypt się nie wykonał”), napisałem sudo halt i zgodnie z instrukcją procedura została przerwana.

Użyłem więc tej metody do „kontrolowanego wyburzenia” obu komputerów. Kiedy RPi4 dostaje halt, molly-guard wysyła halt do RPi3, a następnie wchodzi w fazę oczekiwania na zniknięcie ofiary z sieci. Następnie zamyka się sama.

Uwielbiam technologię, dzięki dekadom doświadczenia mogę rozwiązać prawie wszystkie spowodowane przeze mnie problemy! Powinienem odsunąć lodówkę, wymienić zasilacz na taki z dwoma portami, otworzyć szufladę z kablami, zakląć nad panującym tam burdelem, obiecać sobie, że kiedyś to sprzątnę, odnaleźć potrzebny kabel, dowiedzieć się że jest za krótki, odnaleźć odpowiednio długi potrzebny kabel, przesunąć lodówkę na miejsce, jak normalny człowiek.

#!/bin/bash

timeout=10 # liczba powtórzeń
result=0 # zainicjalzujemy rezultat PING-nięcia
echo "Sending halt to 3pi"

ssh emil@3pi sudo halt > /dev/null

while [ $result = 0 ]; do
        ping -c 1 3pi > /dev/null
        result=$?
        echo "Going $timeout"
        sleep 2
        ((timeout=timeout-1))
        if [ $timeout = 0 ]; then
                echo "Can't confirm that 3pi halted"
                exit 69
        fi
done

echo "3pi calmed down"
exit 0

I już!

RPi4 wyłącza RPi3

Z okazji 2023 życzę Wam dużo mniej bezsensu w życiu.

  1. Tak, wiem, że to za małe natężenie dla optymalnego działania, ale nadal działa™

Tak było, jest i będzie

Nabyłem się dziwnej ułomności. Wielu literatów znam z ich esejów, listów, polemik i dzienników bardziej niż z nowel czy zbiorów poezji.

Do Tołstoja, Fauklnera, Bashō, Berry’ego, czy też Orwella dorzuciłem ostatnio Umberto Eco, którego trzy tomy (tomiki właściwie, sto parę stron) esejów opublikowanych w tygodniku L’Espresso na przełomie lat 1986-1996 znalazłem na półce biblioteki.

Nie będę podejmował próby recenzowania tego zbioru, bo mam do tego zero talentu, wystarczy powiedzieć, że kilka mi się podobało, większość była neutralna, a te dotyczące włoskiej polityki przeleciałyby mi kompletnie przez palce gdyby nie nazwisko Berlusconiego, dla Eco premiera rządu, dla mnie właściciela skandalicznie dobrej drużyny AC Milan. Czemu zajmuję więc Wam czas? Ostatni tom, z lat 1994-1996, zawiera kilka esejów dotyczących Internetu i komputerów ogólnie. O komputerach w tamtych czasach czytałem dużo, ale moimi źródłami były jedyne osoby, które w tamtych latach darzyłem zaufaniem: inni pojebani komputerowcy. Co też mógł myśleć, mający wówczas 62 lata, mediewista?

Już w pierwszym tomie Umberto ujawnił się jako ktoś, kto lubi technologię i ją rozumie, ale nie uważa, że samo jej istnienie jest wartością dodaną. Spędza jeden esej wertując katalog sklepu elektronicznego sprzedającego wysyłkowo rozmaite gadżety, w tym protoplastę smartłoczy1 — skomplikowany zestaw czujników pod postacią uprzęży i zegarka jako terminala, dla biegaczy. Eco zastanawia się jak dobrze mają Amerykanie, którzy mogą otrzymać audio-wizualne ostrzeżenie o zmęczeniu, gdy biegaczy w Afryce musi po prostu złapać kolka. Teraz byłaby to trywialna obserwacja, sam ją pewnie popełniłem, ale w 1982 jest ciekawą prognozą.

Dostało się też ikonom. Ikony, jak zauważa Umberto, są przydatne w momentach, kiedy musimy coś zakomunikować bez użycia języka. Toaleta na międzynarodowym lotnisku, znak STOP na granicy. Według autora każdy użytkownik komputera musi umieć czytać żeby móc coś ze swojej maszyny wydusić. Ikony same w sobie, zwłaszcza w czasach przed unifikacją interfaceów, były za każdym razem zgadywanką. Przykładowo panel wyszukiwania schowany pod ikoną przedstawiającą czapkę-uszatkę noszoną przez Holmesa. Sam pamiętam, że to gdzieś widziałem.

Tu naszła mnie myśl poboczna: przeszliśmy pewien cykl. Na początku ikony trzeba było odkryć, bo akcje, które reprezentowały nie były jasne, więc i one nie były. Potem osiągnęliśmy equilibrium, by ostatecznie ikony znów straciły kompletnie znaczenie. Czemu ikona z niebieskim i białym prostokątem zapisuje mój dokument „do chmury”?

Dwa teksty poświęcone były wyszukiwaniu. Pierwszy tekst dotyczy dziobaków i przypomina mi o czasach nieco bardziej sielskiego, zdecentralizowanego naturalnie dzięki braku obecności molochów i ich grawitacji, Internetu. Wynikami dyskutowanymi są strony Uniwersytetu w Illinois, prywatne zapiski fascynatów, lingwistyczna monografia na temat „jaka jest prawidłowa forma mnoga słowa «dziobak»”, katalog monet z wizerunkiem stworzenia oraz stronę poświęconą hipotezie jakoby dziobak, zwierze „anty-kantowskie, kolażem złożonym z cytatów z innych zwierząt”2, jest dowodem na akt stworzenia, bo ewolucja takiej rzeczy nie mogła „poskładać”.

Całkiem przyjemna podróż w przeszłość.

W drugim tekście o wyszukiwaniu jest o gołych paniach i niemożności odkrycia, gdzie się chowają w publicznym Internecie. Eco co chwila zachodzi, zaproszony linkami pełnymi obietnic na prywatne strony, które golizny nie zawierają. Poświęca więc chwilę na pracę detektywistyczną i odkrywa pracownika banku, który dorabia sobie utykając różne internetowe katalogi3 linkami do stron prywatnych opisanych jako golizna. SEO, pod różnymi nazwami, było zawsze przekleństwem, choć wykonane manualnie przez kogoś dorabiającego na piwo wydaje się szlachetniejsze niż to, które mamy obecnie.

Ostatnim odwiedzonym linkiem okazuje się być strona zawierająca purytańskie kazanie do poszukiwaczy pornografii, pełną ostrzeżeń co do szkód natury duchowej i cielesnej jakich można się nabyć przez konsumpcję. Tam kończy się esej, bez osiągnięcia ekstazy.

Zabawne, że składacz, wydawca lub tłumacz (a może wszyscy naraz) wykłada się za każdym razem, gdy w tekście znajduje się URL. Książka wydana w 1997, wydawałoby się, że sztuka stawiania dwukropków i slashy została opanowana w wystarczającym stopniu.

Zdjęcie książki ukazujące błędnie wydrukowany adres

Nie dało się uniknąć najważniejszego tematu lat dziewięćdziesiątych: świętych wojen między platformami. Uberto postuluje aby wojnę pomiędzy Macintoshem a PC-tem rozpatrywać w kategoriach sporu pomiędzy katolicyzmem a protestanckim kalwinizmem. Macintosh jest katolicki, spolegliwy, katechetyczny, prowadzi cię za rękę do zbawienia. PC jest protestancki, bo nie oferuje zbawienia każdemu, a tylko tym, którzy podejmują dobre dzieła, takie jak edytowanie autoexec.bat. Windows jawi się autorowi jako dzieło kontrreformacji.

My, amigowcy, byliśmy bez wątpienia jakimś odłamem buddyzmu. Uwięzieni w samsarze, skazani na wieczną śmierć i odrodzenie.

Ostatni tekst dotyka Internetu w najmniejszym możliwym stopniu, jako puenta. Jest jednak najbardziej, jeśli mogę to tak ująć, współczesny.

We włoskim mieście zebrała się grupa intelektualistów by spróbować stworzyć „esperanto tolerancji”, jedną pozycję, którą można pokazać dzieciom szkolnym i przez to dać im narzędzia do lepszego życia w społeczeństwie. Idea godna pochwały, choć wydaje się nieco naiwna. Nie jest to tylko moja opinia, sami zainteresowani polegli, bo wyprodukowanie kompletnego zbioru etycznych zachowań w oderwaniu od kultury jest trudne. Nie ma prostego sposobu na posortowanie tragedii według jakiegoś klucza, nie da się też ich wyabstrahować z przyczyn i skutków aby powstała Jedna Ludzka Tragedia jako miara niegodziwości.

Spisali więc kilka przykładów, potknięć i obaw w notce służbowej, a potem postanowili zmienić nieco kierunek. Notka wyciekła. Ludzie o dobrych i czystych sumieniach, wolni od grzechów głównych i pomniejszych przeczytali ją uważnie i bez kontekstu po czym rzucili się do swoich faksów by wyrazić niezadowolenie na skandaliczne przykłady w niej użyte. Eco zauważa, że tylko jedna płomienna skarga została złożona przez pracownika włoskiego uniwersytetu, a reszta przypłynęła ze świata, gdzie znajomość języka w której była spisana jest niska. Wysuwa więc hipotezę, że ktoś, gdzieś — może na usenecie, może gdzie indziej, umieścił jej treść. Przetłumaczoną lepiej lub gorzej, ale zatrzymującą uwagę. Jak inaczej wytłumaczyć taką błyskawiczną reakcję?

Czego więc dowiedziałem się o komputerach i Internecie z tekstów osoby postronnej? W sumie niczego, Internet nie zmienił się prawie w ogóle, ani ludzie, co nie jest dziwne, bo nawet amatorska lektura antycznych tekstów czy nawet naściennych bohomazów ujawni nam to szybko. Nadal można znaleźć teksty o dziobakach, prowadzić święte wojny o wyższości rzeczy, którą kupiłeś nad rzeczą, którą kupił ktoś inny, ikony też coraz gorsze, a może ich nawet nie ma, to tylko reszka tej samej monety. Jedyne, co się zmieniło to dostęp do golizny, ale nie będę z tego powodu klaskał.

  1. czy też „zegarków szpiegowskich”, ale nie że ty jesteś Jamesem na usługach jej Królewskiej Mości, tylko, że on Cię szpieguje
  2. Cytuję tu esej, nie zawartość strony
  3. Zanim wyszukiwarki potrafiły przeskanować i zinterpretować większość Internetu katalogi pełniły dobrą odskocznie dla niedzielnego internauty

Droga donikąd

Roads go ever ever on,

Over rock and under tree,

By caves where never sun has shone,

By streams that never find the sea

— Bilbo Baggins

Rower przed Dworcem Fabryczna

Pierwszy akt tej historii był przyjemny i napawał nadzieją. Wstałem o świcie mając w planach kilkudniowy rajd przez Mazury. Plan nadrzędny: radość. Plan podrzędny: odnalezienie przyjaciół, którzy zaplanowali urodzinową imprezę gdzieś wśród niezliczonych jezior. Po mojej stronie, jak zwykle, wszystko było zapięte na ostatni guzik. Trasy wyznaczone, noclegowiska zamówione, umysł rześki, oczy bystre, skarpety czyste. Zabawę biorę śmiertelnie poważnie.

Wysiadłem w Olsztynie, odwróciłem się w kierunku wschodnio-północnym i zacząłem pedałować. Pierwszym przystankiem miało być Mrągowo, gdzie blisko dwadzieścia lat temu spędziłem bardzo dobre wakacje. Wymyśliłem sobie, że odwiedzę to czy tamto miejsce, a na drugi dzień spróbuję złapać się z przyjaciółmi. Dzień był piękny, droga prosta, nieuczęszczana przez TIR-y i głównie asfaltowa.

Krajobrazy przekradały się bokami, a ja pedałowałem w stanie zupełnej błogości. Dziesięć kilometrów, dwadzieścia kilometrów, pewnie nawet trzydzieści kilometrów i herbata z termosu na przystanku PKS, tymczasowym domu wszystkich wędrowców i włóczykijów. Tu popełniłem pierwszy błąd, sprzeniewierzając się bogom podróży — wyciągnąłem z sakwy telefon żeby zobaczyć jak mi idzie. Na ekranie była niebieska linia trasy, dobrze już zużyta. Zauważyłem też Biskupiec. Biskupiec brzmi jak „zawiera znaczne ilości średniowiecznej architektury” i choć sam architekturę oceniam tylko w kategoriach „czy wieje w nery na jesień i czy pada na głowę na wiosnę”, D. lubi zameczki i klasztory, a ja zawszę chcę się przypodobać D. — zapytałem więc jej, czy powinienem może odbić te kilka kilometrów z trasy i zbadać okolicę, może zrobić jakieś zdjęcia i jej wysłać. Powiedziała, że z pewnością byłoby jej przyjemnie gdybym znalazł jakieś zameczki. Dla mnie to wystarczająca motywacja. Odbiłem w lewo, podążając za znakami.

Biskupiec okazał się być dużo mniej zajmujący niż sobie to wyobraziłem. Było około dziesiątej rano, postanowiłem więc, że to dobry czas znaleźć miejsce na śniadanio-obiad i odpocząć chwilę. Zostało mi ile, dwadzieścia, może trzydzieści kilometrów. Nawet na pieszo nie robiłbym paniki. Knajpy były jeszcze zamknięte, ale bez wątpienia niedługo jakaś się otworzy.

Usiadłem w ogródku jednej z nich, pełnej poskładanych krzeseł przeciągniętych łańcuchem i złożonych parasoli, wyciągnąłem notatniczek i pióra i zacząłem sobie mazać bzdury. Po jakimś czasie pojawiła się Pani ze szmatą i oznajmiła, że jeszcze nieczynne. Na pytanie, czy przeszkadzam zaprzeczyła. Siedziałem więc dalej, aż do otwarcia.

Kiedy wyszedłem po śniadanio-obiedzie niebo było koloru stali i coś z niego ciekło. Nie wyglądało na wielki deszcz, postanowiłem więc poczekać chwilę pod już rozpiętymi parasolami. Towarzyszyła mi Pani, która zwierzyła mi się, że ma podobne nadzieje, co do deszczu. „Niedługo przejdzie” — skłamała. Przytaknąłem. Po pół godziny pojawili się jej znajomi z parasolem i zostawiła mnie samego.

Wydobyłem z plecaka płaszcz przeciwdeszczowy i postanowiłem toczyć się dalej, ale natychmiast popełniłem drugi błąd. Nienawidzę jeździć dwa razy tą samą trasą. Skoro zjechałem tu tędy, oczywiste jest, że jak pojadę tędy to ostatecznie wyjadę w tych samych okolicach. Miałem rację sensu stricto, gdyby świat był idealną płaszczyzną niepoprzecinaną produktami industrialnej rewolucji, takimi jak drogi szybkiego ruchu, płoty i inne naturalnie niewystępujące barykady.

Deszcz padał coraz mocniej. Ubranie kleiło się do mnie, tracę ciepłotę ciała i cierpliwość. Co gorsza, nie byłem pewien, czy uda mi się skręcić, S16 odcinała mnie od mojej pierwotnej trasy. Wreszcie widzę jakiś skręt. Telefon mówi: „skręć w lewo”. Ale jak to „w lewo”, przecież to podjazd na S16. A może się mylę? Podjeżdżam szybko żeby sprawdzić i oczywiście, niestety, się nie mylę. To Google Maps1 zwariowało. Dobrze, zrobię to po swojemu, pojadę boczną uliczką, będzie dobrze. Zawsze ostatecznie jest dobrze. Po kilku kilometrach widzę kolejną odnogę ulicy i znak zguby — T — ta droga prowadzi donikąd.

Szybko przekonuję siebie, że to jest tylko dla samochodów. Nawet jeśli droga się urywa, to pewnie jest to lasek, rzeczka, zarzucę rower na plecy i przejdę. Jestem tak przemoczony, że nic mi nie grozi. Dojeżdżam do skraju drogi.

Zaiste, droga jest nieprzejezdna dla samochodów. Niestety, także rowerów. W ogóle dla wszystkich, którzy nie posiadają na podorędziu lotni lub spadochronu. Droga kończy się skarpą. Zdyszana gonitwą świadomość mnie dogania: ładnie się wjebałeś. Wyciągam wreszcie telefon i chroniąc ekran od deszczu próbuję się zorientować w sytuacji. Jedyną drogą, którą mogę obrać jest skręt, który ominąłem. Jest to okrężna trasa, dodaje jakieś czterdzieści pięć kilometrów. To plus ile już przejechałem da finalnie jakieś sto dziesięć. Deszcz pada dalej. Zimna świadomość, że z geografią nie da się negocjować dosięga mojego umysłu, poddaję się i zaczynam jechać.

Niewiele z tej części zapamiętałem. Były tylko TIRy, chłód, absurdalne, niekończące się podjazdy i zjazdy, które nie wynagradzały mi wspinaczki. Zaczynało się powoli ściemniać, a do tego nad wszystkim zawisła mgła. Postanowiłem zrobić zdjęcie stając na szczycie jednego z podjazdów, ale przez deszcz zalewający telefon zrobiłem sobie selfie — najlepsze selfie.

Emil jest zmęczony

Zaprawdę obraz wart jest 1000 słów. Dobrze, że napisałem więcej, inaczej to wszystko nie miałoby sensu.

Powoli zbliżałem się do E600, a stąd już może siedem kilometrów do hotelu. Będzie dobrze. Zaczęło padać jeszcze mocniej. Już czuję smak zwycięstwa: nad przyrodą, topografią, samym sobą. Nie ujechałem trzech kilometrów, a drogę zagrodził mi funkcjonariusz. Stanąłem w korku. Minęło dziesięć minut, stojący obok mnie samochód uchylił szybę po stronie pasażera i kierowca krzyknął do mnie „Panie, nie wiesz Pan, co się dzieje. Ile tak można stać?” — popatrzyłem na niego zalewany strugami deszczu i powiedziałem, że nie wiem i że ogólnie też mi obecna sytuacja niezmiernie mało pasuje.

Wreszcie się wyjaśniło. Ruch wahadłowy. Lewy pas jest wylany nowym asfaltem. Nienawidzę ruchu wahadłowego. Znaczy on dla mnie tyle, że przez nieokreślony czas muszę pędzić jak szalony w towarzystwie samochodów, których kierowcy są równie zirytowani jak ja, tylko mogą utrzymać prędkość pozwalając przemknąć się przez zmianę świateł bez problemu kiedy ja nie bardzo.

Zielona tablica: Mrągowo. Znajduję hotel. Przypinam rower, melduję się. Pytam w recepcji: macie coś do jedzenia? Nie, protokół pandemiczny. A herbaty dacie? Nie, protokół pandemiczny. Potakuję ze zrozumieniem. Wchodzę do pokoju, rozbieram się z brudnego i cuchnącego od potu ubrania, otwieram okno, okno wychodzi na skrzyżowanie. Na czerwonych zatrzymuje się samochód z którego dobywa się jebjebjeb basu. Potakuję ze zrozumieniem.

Wychodzę poszukać czegoś do jedzenia, ale miasto jest puste i śpiące. Jedyny otwarty sklep w okolicy to sklep z alkoholem. Kupuję więc kilka butelek kolacji i wracam do pokoju. Próbuję się zrelaksować. W telewizji pokazują mecz reprezentacji z Holandią, przegrywamy go w fatalnym stylu. Omdlewam w sen bez snów.

Rano przyszło niespodziewanie i zupełnie nieproszone. Wymknąłem się z hotelu i wbiłem lokalizację, którą otrzymałem e-mailem. Miejsce spotkania z przyjaciółmi. Kiedy zobaczę ich pogodne, roześmiane twarze i opowiem im o wczorajszym dniu wszystko zamieni się w żart i zostanie zapomniane.

Jechałem bardzo wolno, zjadłem rogalika, dwa razy źle skręciłem. Powoli, ale nieuniknienie, zmierzałem do celu.

Wjechałem do miasteczka-celu. Nagle rower zrobił brzdęk, a moje siodełko zaczęło się kręcić tak bardzo, że natychmiast z niego zeskoczyłem. Śruba się poluzowała? W normalnej rzeczywistości to byłaby odpowiedź. W mojej rzeczywistości śruba po prostu pękła. Podniosłem siodełko z ziemi, wsadziłem je pod pachę i udałem się do knajpy. Byłem sześćset metrów od miejsca spotkania. Reszta się sama wyjaśni.

Zadzwoniłem do Oli żeby obwieścić moje przybycie. Powiedziała, że oni jeszcze nie wyjechali, norma. Ostrzegłem ją, że będę miał niesamowicie zabawną historię na wieczór. Powiedziała mi, że nie może się doczekać.

„A, jeszcze jedno. Musieliśmy zmienić lokalizację, bo w pierwotnym miejscu nie było miejsc dla nas wszystkich. Wyślę ci «pinezkę»” — dodała.

Aha. Aha. Ha ha. Patrzę, osiem kilometrów stąd. Próbuję zlutować pękniętą śrubę nienawistnym spojrzeniem, niestety nie osiągam temperatur topnienia. Pomysł prowadzenia roweru poboczem ruchliwej ulicy porzucam niemal natychmiast. Jadę bez siodełka. Kiedy mięśnie nóg przekonują mnie, że nie mogę tyle jechać na stojaka bolec, bodący mnie niespodziewanie w tyłek przypomina mi, że siodełko przypięte jest do bagażnika.

Trafiam na miejsce. Znów zaczyna padać. Dzwonię do przyjaciół z informacją, że jestem i czekam. „Jeszcze nie ruszyliśmy” — odpowiadają. Moi przyjaciele to dobrzy ludzie, ale nie można powiedzieć, że da się podłóg nich regulować zegarków. Najwyżej kalendarz i tylko pod warunkiem, że jest to kalendarz, który pokazuje tydzień na jednej stronie.

Siadam więc i czekam. Pada deszcz, gniazdka nie działają, herbata jest paskudna.

Jakieś miejsce w deszczu

Minęło ledwie siedem godzin i już byli!

Impreza urodzinowa była przednia, a moją tragiczną historię przebiła wielopoziomowa anegdota, poskładana niespodziewanie z naszych odrębnych obserwacji, praktycznie niewytłumaczalna tekstem, a przynajmniej nie przeze mnie. Była jednak tak zabawna, że kiedy tylko sobie o niej przypomnę wybucham autentycznym śmiechem, na głos, czasem ściągając na siebie wzrok postronnych obywateli.

Rano nie czułem się fantastycznie, wypiłem jeszcze więcej paskudnej herbaty i zjadłem równie paskudne parówki z budy-restauracji na obiekcie wypoczynkowym. Ponieważ moja przygoda musiała się tu zakończyć padła propozycja podrzucenia mnie na najbliższy dworzec, na co przystałem z radością. Truchło mojego roweru i truchło mnie zostało wpakowane do vana.

Wszystkie górki i krzywizny, zakręty i przyśpieszenia odkładały się w mojej głowie i żołądku. Zlał mnie zimny pot. Nienawidzę jeździć samochodem nawet w najlepszych czasach, a to nie był najlepszy czas.

Wreszcie zostałem porzucony na dworcu. Dumna nazwa dla betonowej platformy przyklejonej do pojedynczego toru z widokiem na nieużytki i skarlały las. Po dwóch dniach smagania mnie deszczem natura wymyśliła, że nagrodzi mnie równie intensywnym słońcem. Powietrze drgało od gorąca, ostatnią butelkę wody wypiłem dawno temu, herbata w termosie skończyła się wczoraj. Szumiało mi w głowie, aż do gardła podchodziła mi żółć, próbowałem się szybko otrzeźwić żwawym spacerem dookoła platformy, ale w wyniku niekompatybilności oprogramowania ze sprzętem moje nogi odmówiły posłuszeństwa i runąłem na tory.

Leżałem tak z głową na torach jak samobójca w romansach, zwinięty skurczem i marzyłem o pociągu, który skróciłby te męczarnie, choć wiedziałem, że przyszłoby czekać tak przynajmniej dwie godziny, a przez tyle czasu idzie się przeziębić. Spróbowałem podnieść się na rękach, bez skutku, wsparłem więc głowę o podkład kolejowy i zacząłem rzygać.

Wreszcie pozbierałem się, wsiadłem do pociągu, powiesiłem rower, w ustach czułem smak miedzi, a w głowie miałem tylko jedną myśl — „ile jeszcze”. Pani konduktorka wyjaśniła mi, że gdyby dziś nie było święta (a może weekendu?) to byłoby z jedną przesiadką. A że jest inaczej to najszybciej do Łodzi będzie mi tu i tak: cztery przesiadki.

Wysiadając przy domu pomyślałem, że nigdy więcej i myślałem tak aż do następnego piątku.

Widok na przedział

Jaką tu dać puentę? Nie śpijcie w soboty do południa, bo potem będziecie mieć całą noc do zmarnowania i zaczniecie pisać co Wam się podoba bez pytania o pozwolenie.

  1. Unikajcie Google Maps do jazdy w trasy na rowerze. Wyznaczcie sobie trasy w gpx, odmieniło moje życie

In Terminal Veritas

TL;DR: wszystkie teksty na stronie można czytać na terminalu przy użyciu curla:

curl -s https://fuse.pl/beton/cegla.html | less
curl -s https://fuse.pl/beton/focu.sh.html | less

Jestem dinozaurem (technologii): potężny, kochany przez dzieci i dorosłych, źródło strachu i fascynacji, a także inspiracji. Prawdopodobnie nigdy nie porzucę idei, że klawiatura i terminal są najbardziej efektywną metodą komunikowania myśli i celów komputerowi.

Czasem zadaję sobie pytanie, czy lubię tak żyć dlatego, że potrafię tak żyć, czy też w drugą stronę — nauczyłem się tak żyć dlatego, że lubiłem tak żyć. Wydaje mi się, że fascynacja poprzedza komfort, a jeśli uda się tę fascynację utrzymać, to wszystko równoważy się w jakiś sposób, który trudno wytłumaczyć bez odwoływania się do ontologii.

Ponieważ wszystko nadal mnie bawi, uwielbiam budować różne pierdoły, karykatury prawdziwych technologii na marginesach notatników, bezużyteczne dla większości ludzi.

Dla przykładu: pomiędzy dwiema połowami piłkarskiego meczu odbywa się spektakl obrzydliwości zwany «studiem», w którym różni ludzie mówią o meczu, jak gdybym go przed chwilą nie doświadczył na własne oczy. To wszystko jest wsadzone między dwie kromki reklam. Piętnaście minut żenady. Wystrugałem sobie więc stfufor, który przyjmuje minuty jako parametr. Połączenie catt i at ścisza transmisję, a następnie podbija głośność nazad po zadanym czasie. Kiedy słyszę gwizdek kończący połowę, piszę po prostu stfufor 15.

Mój bojler wyłącza się, kiedy wychodzę z domu. Nie muszę o tym informować jakiegoś chińskiego operatora chmury, żeby łaskawie wydał callback do mojego gniazdka, RPi ma listę urządzeń i na podstawie arpinga stwierdza, że albo mnie nie ma, albo śpię — w każdym razie zmywanie raczej nie jest wysoko na liście priorytetów. Za każdym razem, gdy wracam do domu, słyszę po kilku minutach «klik» przekaźnika i uśmiecham się do siebie jak poskramiacz cherlawych lwów: dumny, choć świadomy braku po temu powodu.

Kilka lat temu przez social media przeleciał projekt tego rodzaju, serwis wyświetlający prognozę pogody na terminalu. Pobawiłem się, było to bardzo ładne, cudo technologicznego kiczu. Nie analizowałem jednak detali — być może jestem nadal pod wpływem traumy, gdy pokazałem jakiś „sprawdzacz pogody” mojemu ówczesnemu administratorowi, na którego serwerze gościłem. Powiedział: „opi, a nie możesz kurwa wyjrzeć za okno?”. Do dziś nie znalazłem odpowiedzi na takie dictum.


Sam jednak pomysł wydał mi się całkiem ciekawy, szczególnie ze względu na pewien dualizm: serwis działał tak samo w przeglądarce i terminalu. Postanowiłem odtworzyć ten efekt, przystosowując stronę, którą czytasz, do podobnego działania.

Pierwsze, co należało ustalić, to czy curl, który domyślnie wyświetla pobrane informacje na stdio, zachowuje wszystkie style, kiedy pobiera plik tekstowy z serwera HTTP. Stworzyłem przykładowy plik z testowymi formatowaniami: pogrubienie, czerwony kolor etc., po czym odpaliłem webfsd, mikro-serwer statycznych treści. Wszystko poszło bez problemu, połowa drogi przebyta.

Teraz trudniejsze pytanie: w jaki sposób odbywa się negocjacja treści? Wczytanie URI w przeglądarce i przez curl pokazuje HTML i tekst. Przeglądarka może negocjować za pomocą nagłówków, w przypadku curl musimy polegać na tym, co jest domyślnie wysyłane do serwera. Poszedłem tropem podglądania user-agent. Dodałem -A "Przeglądarka" do wywołania… i wrócił HTML. Aha, czyli pierwszy strzał od razu celny.

Dopisałem do konfiguracji nginx testową linijkę, która przekierowywałaby do dokumentu stworzonego wcześniej. Jeśli wszystko zadziała, to gdy robię curl https://fuse.pl/beton/cegla.html, powinienem zostać zawrócony, a na ekranie wyświetli się sformatowany plik.

Część logistyczno-infrastrukturalna rozwiązana. Co z treścią? Teksty źródłowe mam w „utylitarnym markdownie”: wszystko, co jest wygodne i praktyczne, jest w Markdownie, a reszta bezczelnie w HTML-u. Blog generuje Pelican wyposażony w system wtyczek. Moim pierwszym pomysłem było dopisanie wtyczki, która wygeneruje odpowiednie pliki .txt podczas normalnego działania. Niestety, dokumentacja dotycząca wtyczek to głównie proces czytania kodu źródłowego zarówno projektu, jak i jakichś losowych gotowców operujących w hipotetycznym sąsiedztwie tego, co chcę osiągnąć.

Po kilku godzinach prób na ślepo postanowiłem, że wrócę do tego później i napiszę minimalistyczny generator „na teraz”. Jedna z tych prowizorek, co ma potencjał przetrwać nas wszystkich.

Jest trzy tygodnie później, wyszło słońce, porzuciłem komputery na rzecz wygodnych butów i plecaka. Już wróciłem, ale zaraz wychodzę znów, spróbuję więc skończyć ten tekst teraz.

Jak postanowiłem, tak zrobiłem. Zgodnie z tradycją pierwsze 90% projektu zrealizowałem w godzinę — czytanie plików tekstowych z dysku i zapisywanie ich nie jest specjalnie trudne. Następne 10% pracy zajęło jakieś 400% czasu. Problemy były wielorakie.

Treści fatalnej jakości

Nie mówimy tu o wartości literackiej, lecz o starych tekstach przeniesionych z Wordpressa. To prawdziwy gar zupy z tagów, bulgoczący i pachnący niezbyt zachęcająco. Jakimś pomysłem było zadeklarowanie listy legalnych tagów, które będę mógł zinterpretować. Ostatecznie uznałem, że te teksty są tak słabe, że ich nieczytelność jest praktycznie przysługą. Problem, mhm, rozwiązany.

Przypisy

Przypisy w blogu generowane są przez wtyczkę, która używa własnego tagu REL w nawiasach kwadratowych jako znacznika przypisu. Bez specjalnej miłości w sercu udałem się do testera wyrażeń regularnych i próbowałem napisać coś, co wychwyci potrzebny fragment. Musicie wiedzieć, że jedyne wyrażenie używane przeze mnie regularnie, to „kurwa”, proces ten zajął mi zatem więcej czasu niż powinien. Po testach postanowiłem, że wstawię przypisy pod paragrafami je zawierającymi, a nie na końcu tekstu. Wyglądało bardzo profesjonalnie, jakbym był blogerem z sponsorowanymi wpisami.

Linki

Ten sam problem co z przypisami, pomnożony przez to, że w moim lenistwie często używam zarówno markera z Markdownu, jak i <a>. Musiałem użyć nie tylko wyrażenia regularnego — bezwstydnie skopiowanego z internetu po godzinie walki — ale także BeautifulSoup celem wyłowienia tagów HTML-a. Zadziałało.

Obrazki

Chciałem zamienić obrazki na kolorowe „piksele” terminala. Używając notacji RGB, mogę bez problemu wykorzystać Pillow do przekonwertowania obrazu na tryb indeksowanej palety i przeskalować go do szerokości 76 znaków — docelowej szerokości tekstu, który chcę generować.

Poszło szybko, ale… efekt pozostawia dużo do życzenia. Poza „heh, fajne”, użyteczność obrazów przeskalowanych do 72px szerokości wydaje się być znikoma. Prawdopodobnie zmienię generator tak, żeby zamiast obrazków wstawiał odnośniki do nich, aby czytelnik mógł kliknąć samodzielnie.

Aktualizacja: obrazki działały okropnie, wszystko zależało od definicji terminala czytającego, więc po namyśle wywaliłem całość.

Markdown, «ANSI»

Nie miałem już zapału w sercu, by siadać do pisania parsera, który wypluwałby odpowiednio „wyeskejpowane” kody dla terminala w miejsce pogrubień, nagłówków i całego tego bajzlu. Znalazłem bibliotekę wyposażoną w komplet narzędzi i kiedy już oswoiłem się z jej konceptami, zagnałem ją do pracy.


Po prawie dwóch miesiącach mogę wreszcie zadeklarować, niczym lider wolnego świata — “MISSION ACCOMPLISHED” — prawie wszystko się udało™. A rzeczy, które się nie udały, najwyraźniej były kiepskimi pomysłami i zostały porzucone nie z powodu braku cierpliwości, tylko w wyniku głębokiej, filozoficznej retrospekcji.

Poprawiała Kaja ❤️

PS. curl -s https://fuse.pl/beton/curl-blog.html | less, duh. ;-)


Od cegły do ziarnka

Na gruzowiskach budynków bywam częściej niż na grobach bliskich. W grobach nie ma bliskich, są oni w mojej głowie. Gruzy budynków są rzeczą samą w sobie, gram do grama — budynkiem, tylko w stanie nieładu. W dzieciństwie łatwiej było znaleźć opuszczony budynek, teraz profesjonalizm, BHP, rzeczy nie rozpadają się samodzielnie, własnym tempem, jak to było w moim dzieciństwie. Są zsuwane w niebyt zębami koparek i spychaczy.

Na obrzeżach miasta nawet puste budynki miały swój cykl w ekosystemie biednej dzielnicy. Wpierw plac zabaw dla dzieci, wbrew protestom matek zdobywany kawałek po kawałku. Potem ktoś stłukł szybę, ktoś wszedł na dach po rynnie, wreszcie ktoś śrubokrętami ojczyma rozkręcił zamek i teren został przejęty. Meble, jeśli jeszcze jakieś zostały, były przeglądane dokładniej niż skrzynie pełne skarbów, każdy metr podłogi zbadany na obecność śladów potencjalnej przygody. Starsze dzieci dopowiadały legendę domu, wyssaną z palca o brudnym paznokciu. Poprzedni właściciele zwykle byli przestępcami, mordercami, ukrywali się przed organami państwa, a los dogonił ich tutaj: powiesili się na tym haku do lampy, skoczyli z tego okna na ten betonowy chodnik, zastrzelono ich na schodach. W tych opowieściach żaden były właściciel nie dożył godnego końca, wszyscy odchodzili w sile wieku i nigdy po cichu.

Później przychodziła zima i dom z powybijanymi oknami nie nęcił już tak jak latem. Na wiosnę rozpoczynał się proces niszczenia. Kto nie chciał zobaczyć, jak wielkie lustro pęka na tysiące części? Szuflady wyrwane z szafek uderzały o ściany tak mocno, że traciły swój kształt i stawały się kupą desek. Ostatnie okna kruszyły się na podłogi.

Latem miejsce przejmowali dorośli: zdejmowali metalowe elementy, darli kable, wystawiali lepiej zachowane framugi. Następnie ktoś przynosił młot i siekierkę. Czerwone cegły, jedna po drugiej, były oddzielane, czyszczone z resztek zaprawy i układane w małe stosiki, które można później sprzedać rolnikowi. Nadawały się nadal do budowy nieforemnych komórek na narzędzia lub niskich chlewików. I tak tydzień po tygodniu, miesiąc po miesiącu budynek bladł i kurczył się, by wreszcie któregoś dnia zostawić po sobie odcisk w ziemi, w którym zbiera się deszczowa woda.

Niedawno odkryłem, że jeden z budynków na trasie moich poobiednich spacerów jest przygotowany do obalenia. Stało się to tak szybko, że kiedy trafiłem po raz kolejny przed jego oblicze, był już tylko szkieletem rozciągniętym na podwórzu. Na moje szczęście miejsce nie było dobrze zabezpieczone i tylko znaki zakazu wstępu łypały na mnie niechętnie.

Dom obrócony w perzynę jest negacją wszystkich wartości zawartych w jego istocie. Okna we wciąż stojących ścianach kiedyś wpuszczały do mieszkań światło słoneczne, teraz przechodzień na ulicy może popatrzeć przez nie na puste niebo. Rzeczy oddzielone od siebie są razem. Pokoje ubrane w ściany chroniły prywatność, teraz są rozpoznawalne przez topologiczne badanie resztek podłogi. Drewniane klepki to pewnie mieszkanie, błękitne płytki pokryte grubą warstwą betonowego kurzu to bez wątpienia łazienka. Niewyraźne granice można odtworzyć, studiując inne geograficzne elementy: resztki niewyniesionej komody, ścięte węzły wodociągowe, liczba okien.

Wytrawny antropolog rozpadu znajdzie też komin. Na nosa. Umorusane sadzą cegły śmierdzą zimną spalenizną. Dekady gryzącego dymu nie ulecą w niepamięć w kilka miesięcy.

Po wschodniej stronie budynku kręci się chłopak. Zerka na mnie podejrzliwie. Ma w ręku siekierę, a na stosie przed nim leży wyzwolone drewno, przycięte na długość wygodną do transportu. Jest bardzo zimny, styczniowy dzień. Nie mogę po prostu podejść i powiedzieć: „nie martw się, jestem tu z tego samego powodu, co Ty. Chcę zabrać z tego budynku ostatnie wartościowe rzeczy”. Odsuwam się więc na bok i udaję, że go nie widzę. Niestety, spłoszony, pakuje się i odchodzi w kierunku niedalekiej kamienicy. Zostaję sam, odwiedzam wszystkie cztery strony budynku i wreszcie zadowolony wracam do domu.

Zburzony budynek
Za kilka miesięcy stanie tu nowoczesny budynek, bez kominów i drewnianych podłóg. Wbity pomiędzy pozostałe kamienice, będzie im przypominał o ich nieuniknionym losie.

Nad tekstem pochylała się Kaja/Nerdy Nocą


focu.sh

Mody przychodzą i odchodzą. To dowód na to, że moda jest rzeczą fatalną. Nic dobrego nie psuje się kompletnie w miesiące, aż do momentu w którym trzeba całość wyrzucić. W świecie edytorów tekstowych dla pisarzy najnowsze mody sugerują, że jeśli nie możesz użyć maszyny do pisania, powinieneś przynajmniej naśladować jej zachowania: pozbyć się klawisza delete co uniemożliwi poprawianie tekstu na bieżąco, to samo z podróżowaniem pomiędzy paragrafami przy użyciu klawiszy kursora. Najlepiej też nie pokazywać tekstu poza obecnie pisanym kawałkiem, zaciemniając go lub nie wyświetlając go w ogóle.

Wszystko to ma jeden cel: nie kraść uwagi pisarza poprzednimi bohomazami i skierować go na jedną, słuszną drogę: drogę wiodącą do przodu.1

Będąc autorem amatorskim, o suchym pysku, pustej stronicy i drżących rękach, pomyślałem, że dam sobie szansę dołączyć do ludzi aktywnych pisarsko. Pobrałem taki i siaki edytor (słowa kluczowe “minimalism”, “markdown”, “focus”), większość nie ma wersji na Linuksa, a te, wydane dzięki technologii przeglądarka-vm2 zwykle nie przeżywają zetknięcia ze skąpymi w cykle procesora komputerami na których pracuję.

Przetestowałem też aplikacje mobilne. Tu jakościowo było znacznie lepiej, powstał jednak problem poboczny: część z tych edytorów domaga się comiesięcznej opłaty subskrypcyjnej. Jestem moralnie przeciwny takiej formie finansowania oprogramowania, co raczej nie powinno nikogo dziwić.

Zrobiłem więc następną dobrą rzecz: napisałem najbardziej hipsterski edytor dla prawdziwych artisanów, focu.sh3 — kilka linii w Bashu. Minimalistyczny edytor minimalistycznym nakładem sił.

focu.sh

Edytor składa się zasadniczo z jednego elementu: czytaniu stdin przy użyciu read i przekierowaniu (przez dopisanie) pobranej wartości do jakiegoś pliku wybranego przez użytkownika.

Wersja tylko fakty wyglądałaby tak:

while :
do
    read line
    echo $line >> "$1"
done

Już minimalistyczne, ale jeszcze nie hipsterskie. Musimy usunąć z ekranu wpisaną wcześniej treść i pokonać wewnętrznego Narcyza, zakochanego w już popełnionej prozie. Moglibyśmy wywołać clear, ale to zbyt agresywne i nieestetyczne. Musi być symetria jak u Kubrika i Wesa.

clear_and_align() {
    lines=`tput lines`
    half=`echo $lines/2-1 | bc`
    tput clear
    for i in $(seq 1 $half);
    do
        echo
    done
}

Dodałem funkcję clear_and_align() powodującą, że kursor jest zaparkowany na środku ekranu. Białe litery pośród morza ciemności, pięknie. Aby uzyskać ten efekt odczytuję ilość linii obecnego terminala używając tput, tnę je na pół używając kalkulatora bc, czyszczę ekran, a następnie „spuszczam” kursor odpowiednią głębokość kręcąc się po sekwencji 1…ekran/2. Całość wystarczy dodać do naszej głównej pętli while…do…done.

W pisaniu najważniejsza jest motywacja. Dobrze byłoby wyświetlić wiadomość pożegnalną wraz z ilością linii dodanych podczas sesji.

trap goodbye SIGINT
goodbye() {
    clear_and_align
    echo "W R I T I N G   S E S S I O N   T E R M I N A T E D."
    echo
    echo "$lines_added   L I N E S   A D D E D"
    exit
}

Dzięki użyciu trap możemy załapać sygnał SIGINT (wciśnięcie ^c) i wskazać funkcję do wywołania bezpośrednio po jego nadejściu. W funkcji wyświetlimy zmienną globalną lines_added, wartość której podbijamy oczywiście w głównej pętli. I proszę, po strachu.

I chciałbym móc Wam skłamać, że to właśnie ten cudowny edytor jest powodem mojej obecnej aktywności. Niestety, użyłem go raz i nie sprawiło mi to wielkiej przyjemności. Odkąd nauczyłem się wychodzić z Vima4 robię w nim wszystko. Nie napisałem może nowego edytora, ale za to mamy praktyczny przykład użycia „nieskończonego” while, seq, tput i trap.

Edytor w działaniu.

Wersja „finalna” w repozytorium.

Runda dodatkowa

Wymyśliłem, że mógłbym przypudrować nieco nosa funkcji goodbye. Moją wizję nazwałem „poprzeczny Matriks”: weźmiemy ciąg znaków i będziemy go wypisywali literka po literce używając pary kolorów (czy też własności: jak jasność i pogrubienie) wybieranych losowo zasymulujemy wyłanianie się napisu z mroku do maksymalnej jasności i wspak.

Byłem pewny i spokojny, a w mojej głowie odgrywało się jak to wszystko będzie wyglądać. Po godzinie z kawałkiem popatrzyłem na efekt i otrzepując ręce powiedziałem pod nosem „Jebać. I tak nie chciałem tego robić.”

END OF TRASMISSION

  1. próbując edytować to w kawiarni naszła mnie nagle przerażająca myśl. To wszystko jest log typu append-only. Czy jestem pierwszym człowiekiem, który wymyślił blokczejn-teknolodżi z zastosowaniem? TołstojPiniondż niedługo na rynku!
  2. Electron i jego bękarty
  3. Gdyby domena nie kosztowała $80 to bym zrobił sobie landingpage w bootstrapie
  4. alt-a tworzyło nową sesję na terminalu tekstowym, logowałem się i wydawałem polecenie kill

Się, nam

Czy jest bardziej polskie stwierdzenie niż „się załatwi”? Jest ono reprezentacją ducha narodu, jego najcenniejszych wartości: pomysłowości, zdolności organizacyjnych, słusznego sprzeciwu ikonoklasty i niedopowiedzeń godnych listów między kochankami.

Taki taniec godowy, nierozpoznawalny dla innych gatunków, wyewoluowany przez wieki, gdy tylko biedy było pod dostatkiem.

Kiedy Polak mówi Polakowi, że „się załatwi” dochodzi do niewerbalnego porozumienia, które wypowiedziane słowami, a nie daj bóg, zapisane na papierze, mogą być użyte jako źródło paszkwili. Strona, która mówi, że „się załatwi”, rozgrzesza stronę proszącą. Nikt nie jest pewien jak ta rzecz się stanie. Czy ktoś na tym ucierpi, czy zostaną nagięte przepisy, złamane prawa. Osoba w potrzebie, niemal jak w modlitwie, wypowiada swoje prośby do istniejącej gdzieś, niepoznanej, siły sprawczej. Odbiorca tej modlitwy też odżegluje się od bycia instrumentem stworzenia. Nigdy „ja załatwię”, zawsze „się załatwi”. Pasywnie, anonimowo, przed oblicze niewidzialnych sił natury.

Niesłychana jest również egalitarystyczna potęga tego porozumienia. Lekarz może powiedzieć do pacjenta, że „się załatwi”, ale też szatniarz do dyrektora. Choć moce sprawcze i rezultaty będą różnego kalibru, do paktu wszyscy podchodzą patrząc sobie w oczy, jak równy z równym, nasz własny wkład w urzeczywistnianie utopijnych wizji mutualizmu Kropotkina i Proudhona.

Dziś obrońcy statusu quo starają się wyrugować ten piękny zwyczaj, zaszczepić nam moralną pewność, że „się-zalatwizm” jest przywilejem przyznawanym wraz z wpisem w KRS. Apeluję abyśmy odrzucili te postulaty i nie dali się ograbić kulturowo, a jeśli już damy się ograbić kulturowo, to przynajmniej nie dajmy się ograbić materialnie.


10 PRINT

Dyskusja na temat „czym jest sztuka, czym jest piękno” jest z nami tak długo jak długo istnieje klasa intelektualno-próżniacza, która mogła zadawać sobie takie pytania i szukać na nie odpowiedzi. Będąc bękartem tej klasy, odziedziczyłem tylko pół genotypu, jestem próżniakiem, dlatego na potrzeby dzisiejszego dnia zdefiniuję sztukę i piękno jako „to, co da się wyrazić jedną linijką w najgorszym1 BASIC-u”.

Najsłynniejszym przykładem „generowanej sztuki” jest 10 PRINT, jedna linijka w BASIC-u na C64, która generuje pseudolosowy labirynty. Jest możliwa tylko dzięki szczęśliwym zbiegom okoliczności. Prześledźmy ją krok po kroku.

10 PRINT CHR$(205.5 + RND(0)); : GOTO 10

PRINT nie trzeba nikomu tłumaczyć. Natomiast warto rozważyć dwa znaki interpunkcyjne. : pozwala na separacje poleceń, ; jest modyfikatorem dla PRINT i powoduje, że następne znaki nie będą wypisywane w nowej linii. CHR$() przyjmuje indeks i wypisuje znak znajdujący się pod nim w tabeli ASCII C642. Wartość 205 odpowiada /. RND() zwraca pseudolosową wartość w zakresie 0…1. GOTO zamyka pętle.

CHR$() jako parametr przyjmuje liczbę całkowitą. Jeśli podać mu liczbę zmiennoprzecinkową bezceremonialnie odrzuca wszystko po kropce bez zaokrąglania. To dla nas dobry zbieg okoliczności. Teraz rozważmy operację dodawania: 205.5 + [0…1] daje nam liczby z zbioru 205.5…206.5. Jak już ustaliliśmy CHR$() jest kompletnie nieczułe i widzi tylko dwie wartości. 205, które w tablicy ASCII odpowiada / i 206, kolejny szczęśliwy zbieg okoliczności, odpowiada \.

Ostatni pozytywny zbieg okoliczności? Piksele na 8-bitowych maszynach to materiał premium, twórcy komputera nie mieli luksusu martwienia się jak typografia „oddycha” na ekranie. Odstępy pomiędzy liniami nie są tego warte.

10 PRINT: wideo zrzucone z emulatora C64 demonstrujące efekt

I tyle: stawiamy znak \ lub / i pozwalamy naszemu mózgowi poukładać ten szum w kształty. Magiczne sztuczki wydają się głupie, gdy je wyjaśnić.

Dla zabawy „przeportowałem” ten kod na odpowiednik w Pythonie, niestety, Linuksowy terminal jest już bogaty w piksele i nie da się tak łatwo uzyskać jednolitych linii. Po ustawieniu fontu z C64 efekt był już całkiem zadowalający.

from random import choice
while True: print(choice(['/', '\\']), end='')

Szybka opowiastka o: random.choice zwraca losowy element z listy (tu dwuelementowa lista z \ i /, oczywiście), end='' do print() powoduje to samo, co średnik w BASIC-u, operacja nie kończy się nową linią.

10 PRINT: na Linuksie

Miałem w tym tygodniu opublikować inny tekst, ale chyba jestem kolejną ofiarą zarazy™ i nie mogę myśleć zbyt jasno. Widziałem wczoraj wideo w którym Robin optymalizuje 10 PRINT w asemblerze i pomyślałem, że to temat łatwy i przyjemny, a może ktoś z subskrybentów nie wie o fenomenie.

Kilka lat temu MIT wypuściło książkę o 10 PRINT, ma ona 324 strony, co jest pewną formą meta-sztuki nieosiągalnej dla zwykłych śmiertelników. Skłamałbym gdybym powiedział, że ją przeczytałem. Dość uważnie przekartkowałem ją spacją.

  1. TI/99 walczy zaciekle o koronę
  2. PETSCII, nie będę jednak dzielił włosa na czworo

asdf-vm

Wszyscy lubią dobry sequel, choć szansa na niego jest znikoma. W zeszłym tygodniu pisałem o wygodnej metodzie instalacji programów napisanych w Pythonie, a dziś przedstawię Wam podobne rozwiązanie: z tą różnicą, że nie „programów w Pythonie” tylko „wszystkiego w sumie z naciskiem na języki programowania i narzędzia pomocnicze”. asdf-vm

W „Boskiej Komedii” Alighieri opisuje 9 kręgów piekła do którego może trafić grzeszna dusza. Wbrew religijnej ikonografii ostatni krąg nie ma gotującej się siarki i diabołów uzbrojonych w ostre narzędzia rolnicze tylko jezioro skute na wieki lodem, najbardziej oddalone od ciepła bożej miłości. Gdybym miał talent (no, więcej talentu) napisałbym pastisz w którym na końcu byłby asdf instalujący Pythona, instalujący pipx, instalujący docker-compose, w kontenerze. Dziewięć kręgów abstrakcji, prawdziwe piekło.

Żarty na bok, ale w zasięgu ręki.

W przeciwieństwie do pipx, który używa standardowych mechanizmów Pythona, asdf to bardzo lekka specyfikacja do skryptów instalacyjno-kompilacyjnych zarządzana przez super-polecenie.

Podczas pisania tego tekstu dostępne są 4201 specyfikacje, które w nomenklaturze asdf nazywamy pluginami. Większość z nich dostarczona jest przez osoby zewnętrzne, więc standardowe ostrzeżenie o „odpalasz czyjeś skrypty z Internetu” obowiązuje każdego dnia i każdej nocy.

Co do jakości samych wtyczek: jest różnie. Niektóre budują oprogramowanie wprost ze źródeł, inne znów pobierają skompilowane binarki. Podobnie jak w świecie kontenerów istnieje założenie, że końcowy odbiorca posiada komputer z x64. W chwili obecnej takowego nie posiadam, niektóre instalatory popełniają więc naiwne błędy: ściągają binarkę na x64, kładą ją do wirtualnego katalogu, zacierają ręce i mówią — „gotowe”, czy trochę lepiej, wywracają się od razu na kolana mówiąc „nie ma takiej architektury — armhf”. Innymi słowy, jeśli upstream nie rozwiązał problemów wieloplatformowości są niewielkie szanse, że autor wtyczki do asdf poświęcił czas na testowanie instalacji na nie-Intelach.

Jedna funkcja samego asdf jest mi szczególnie miła. Poza możliwością instalowania obok siebie kilku wersji tego samego programu istnieje też możliwość zadeklarowania jaka wersja jest globalna (widziana przez powłokę normalnie) a jaka jest lokalna (na przykład w katalogu projektu).

Uratowało to moją duszę przed wizytą w drugim kręgu, gdy odziedziczyłem po frontendowcach projekt, który potrafił się tylko zbudować jedną konkretną wersją nodejs. Dlaczego? Nikt nie wiedział, a ostatnia osoba, która mogła coś na to poradzić postanowiła sprzedawać łódki dłubane w korze na rogach jasno-oświetlonych domów handlowych.

Wystarczy przenieść się do katalogu projektu i wydać polecenie asdf local nodejs 12.18.3 (oczywiście po wcześniejszym zainstalowaniu) i za każdym razem gdy odwiedzimy nasz projekt wersja zmienia się magicznie pod naszymi stopami. Wersję ustawia zawartość pliku .tool-version, możemy go wciągnąć do repozytorium, co pomaga ludziom tak zapominalskim jak ja.

Instalacja

Polecaną metodą jest sklonowanie repozytorium do katalogu domowego. Kilka pakietów, które próbowałem na Arch instalowały do /opt czy też /usr/local, nie pamiętam, w każdym razie kłóciło się nieco z moją ideą żeby wszystko żyło w katalogu domowym, dlatego usunąłem je i użyłem ręcznej metody.

git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.8.1

Do skryptu uruchomieniowego powłoki należy dodać wywołanie ./$HOME/.asdf/asdf.sh rozpędzające machinerię. Jeśli używacie oh-my-zsh można dodać do jego konfiguracji plugins=(asdf git […]).

Używanie

Wpierw dobrze się rozejrzeć, co też dają w tej knajpie. Listę wszystkich dostępnych pluginów otrzymamy po wywołaniu:

asdf plugin list all

Jeśli nasza powłoka nie wspiera dopełnienia należy sprawdzić jakie wersje konkretnego pluginu możemy zainstalować:

asdf list all nodejs2

Obecnie doszedł uniwersalny tag latest aby ułatwić start. Niektóre pluginy zawierają też inne nazwy własne takie jak lts czy stable. Zainteresowani muszą zbadać sprawę na własną rękę.

Musimy dodać plugin do listy aktywnych:

asdf plugin add nodejs

By ostatecznie zainstalować rzecz samą w sobie. Dwie rzeczy na potrzeby demonstracji:

asdf install nodejs 12.18.3

asdf install nodejs latest

Kiedy (lub jeśli, build-essential + *-dev* będą nieodzowne) wybrany program się już zainstaluje możemy wybrać wersję do użycia. Domyślnie, bezpośrednio po instalacji, programy nie są aktywne.

asdf global nodejs 12.18.3 — wybierze nam domyślną wersję zainstalowanej aplikacji

asdf local nodejs latest — zadeklaruje wersję aplikacji do użycia w sytuacji, gdy znajdujemy się w danym katalogu.

W praniu

Poniższe wideo zawiera bardzo hipotetyczną sytuację.

Mam w Vimie wsparcie do fzf i chcę zainstalować program by móc z niej korzystać. Z jakiegoś powodu, być może na potrzeby demonstracji, ubzdurałem sobie, że chcę je zainstalować w wersji 0.22.0. Ta wersja ma gotową binarną dystrybucję, więc wszystko kończy się na pobieraniu do odpowiedniego katalogu.

Zadeklarowałem wersję 0.22.0 jako globalną i odpaliłem Vima. O nie, szelma, zostałem zdradzony, wtyczka do Vima potrzebuje nowszej wersji fzf. A ta nie ma binarnej dystrybucji. Na szczęście 0.29.0 buduje się używając go, nomen omen, zainstalowanego via asdf, i mogę zadeklarować tę wersję jako lokalną.

Animacja demonstrująca poprzedni paragraf tekstu

Przyznam, że znalezienie czegoś do demonstracji zajęło mi więcej czasu niż pisanie samego tekstu. Szrotowany powindowsowy „tablet” z mizernym Atomem nie jest idealną platformą do kompilowania LibreOffice i nagrywania z tego wideo.

  1. nice
  2. 622 wersje. Dżizas kurwa mać ja pierdolę,,,

pipx — bezbolesna instalacja programów w Pythonie

Moje komputerowe życie umila zaskakująca liczba programów napisanych w Pythonie. Na przykład wspominany wcześniej catt, bardzo wygodne i praktyczne klienty do baz danych jak pgcli czy mycli. Niezastąpiony youtube-dl (a właściwie jego fork yt-dlp) pozwalający archwizować media, goobook integrującym książkę kontaktową Googla z muttem. Mógłbym tak wymieniać długo.

Programy w Pythonie pisze się niezwykle przyjemnie, prawdziwe schody zaczynają się w momencie dystrybucji efektu naszej pracy. Część chce się instalować przez curl | sh, inne znów posiadają własne instalery i metody aktualizacji, są też oderwane od rzeczywistości pakiety .deb i .rpm niesiące ze sobą obietnice katastrofalnego zakłopotania systemu.

Jeśli deweloperzy dystrybucji (lub ludzie tworzący „hobbistyczne” repozyoria przylgnięte do oficjalnych (AUR czy PPA)) nie przygotowali gotowego pakietu końcowy użytkownik jest pozostawiony samemu sobie.

Niewprawiony użytkownik może sobie szybko skomplikować życie instalując rzeczy bezpośrednio w trzewia systemu. Zaawansowany użytkownik może się szybko znudzić żonglowaniem pół-rozwiązaniami wymyślonymi na szybko, które trochę działają, trochę nie.1

Któregoś dnia „wdepnąłem” w pipx, program tworzący wirtualne środowiska per program jednocześnie zajmując się podlinkowaniem wykonywalnego skryptu. Dzięki temu możemy zainstalować wiele pożytecznych aplikacji bezpośrednio z PyPI bez większego bólu głowy, uzyskać dostęp do aktualizacji i możliwości usunięcia programu z systemu bez pozostawiania tradycyjnego kurzu i gruzu.

Instalacja

Użytkownicy bardziej konserwatywnych dystrybucji, więc i ja, nie mogą oczekiwać, że pipx będzie znajdował się w repozytorium. Znaczy to, że program przyjdzie nam zainstalować bezpośrednio z PyPI. Trzeba przyznać, że stajemy wtedy przed ciekawym problemem typu „jajko czy kura”. Dylemat ten pozostawię do rozważenia w Waszych sercach, osobiście proponuję pip3 install --user pipx i sprawdzenie, czy ~/.local/bin znajduje się w ścieżce (zmienna środowiskowa PATH).

Po instalacji możemy przetestować, czy wszystko poszło gładko wpisując pipx --version. Jeśli otrzymaliśmy numer wersji w odpowiedzi jesteśmy gotowi.

Używanie

pipx w akcji

Odpalenie pipx bez parametrów wyświetli nam listę dostępnych opcji. Część z nich tłumaczy się sama przez się, inne wymagają słowa objaśnienie.

Instalacja pakietu z PyPI:

pipx install yt-dlp

Instalacja pakietu bezpośrednio z repozytorium:

pipx install git+https://git.sr.ht/~bronikowski/pyswatch2

Usunięcie zainstalowanego pakietu:

pipx uninstall yt-dlp

Aktualizacja pakiet(ów):

pipx upgrade-all lub pipx upgrade yt-dlp dla ludzi o sercach miękkich wobec procesorów.

Uzyskanie listy już zainstalowanych pakietów:

pipx list

Przytrafiają się sytuacje, gdy zainstalowany pakiet nie chce się uruchomić. Jeśli problem spowodowany jest brakiem zależności lub jej niewłaściwą wersją, możemy naprawić taki problem „wstrzykując” zależność do wirtualnego środowiska zarządzanego przez pipx.

pipx inject yt-dlp <jakaś_biblioteka> lub użyć bezpośrednio pip w tymże środowisku pipx runpip yt-dpl <polecenie pip>.

Do Waszej dyspozycji pozostają też trzy zmienne środowiskowego:

PIPX_HOME — katalog w którym tworzone są wirtualne środowiska (domyślnie: ~/local/pipx/

PIPX_BIN_DIR – katalog w którym zostaną umieszczone symlinki do wykonywalnych poleceń. Oczywiście najlepiej taki, który znajduje się w ścieżce uruchomieniowej. Domyślnie ~/.local/bin.

PIPX_DEFAULT_PYTHON — jeśli chcecie używać innej wersji Pythona

Na zakończenie przypomnę tylko, że pipx nie rozwiązuje magicznie problemu pakietów, które wymagają dostępu do kompilatora i nagłówków — tu nadal musimy zapewnić wszystko przy użyciu pakietów -dev*.

  1. Staram się porzucić żywot człowieka używajcego Debian/oldstable żyjącego z 25GiB-towego katalog ~/src
  2. zob. „Umówiłem się z nią na dziewiątą

W 80 minut dookoła Łodzi

Cóż to za kropka na horyzoncie? Nad Starym Widzewem wisi sobie balon. Nie pamiętam kiedy widziałem taką bańkę zawieszoną na moim niebie, zmieniam więc trasę mając nadzieję, że przetnę jego ścieżkę.

Wpakowałem się niestety w jeden z trzydziestu czterech, odbywających się równocześnie, remontów. Postanowiłem trzymać go na wschodzie i jechać przed siebie.

Niebo jest nieoczekiwanie wielkie. Kilka kilometrów później zaczyna mi się kończyć miasto, ale nie wygląda jakbym zbliżał się do trasy balonu.

Widzę więcej balonów. To musi być jakaś zorganizowana akcja.

Przejazd kolejowy i czekamy aż przejedzie samotna ciuchcia PKP Cargo.

Balon, który śledziłem, znika za blokami. Znaczy, że usiadł, bo to zaledwie czteropiętrowe budynki. Skręcam pomiędzy nimi i zostaję nagrodzony. Na boisku do koszykówki przycupnął sobie balon z nieco zirytowanym operatorem.

Prosi o pomoc. Łapię za gondolę z dwoma innymi chłopami i niesiemy go w kierunku, który nam wskazuje. Balon jest zadziwiająco ciężki nawet z non-stop pracującym palnikiem, który pluje ogniem w jego brzuch.

Od środka jest całkiem pusto. Nawet tanich mebli z Ikei nie ma.


Patrzysz mi się na ręce. Ręce mi się trzęsą.

Wczoraj widziałem nowelkę wspomnień.1 Co zaczęło się jako wieczór ze sprawdzaniem stanu prywatnych archiwów, skończyło się na przeglądaniu starych zdjęć, a następnie znalezieniu konkretnego wycinka mojego życia i odbijania się po nim z lewa na prawo.

Szukanie wspomnień, przywoływanie kontekstu, ludzi, tematów rozmów prowadzonych do świtu, odtwarzanie dźwięków i smaków.

Refleksje wczepiły się tak bardzo w moją świadomość, że nawet po przebudzeniu się na drugi dzień nie mogłem się otrząsnąć z kilku emocji. Jak odgrzane resztki dawnej fiesty, nie wyglądały atrakcyjnie, ale smakowały wspaniale. Wyciągnąłem notatnik, pióro i postanowiłem, że poukładam sobie rzeczy na papierze. Nie ma lepszego piorunochronu na burzę myśli niż notatnik.

Napisałem może stronę, gdy telefon zrobił ping. Jestem podejrzliwy, bo mój telefon nie robi ping w normalnych warunkach, większość aplikacji jest poproszona o zamknięcie mordy. Patrzę w źródło powiadomienia. Google Photos ma dla mnie zajmujący kolaż! Tylko dla mnie, samo zrobiło!

Mając najgorsze przeczucie klikam.

Jak niekompletny szpieg, przekonany o swoich super-mocach i rentgenowskim wzroku, Google zauważyło, że oglądałem konkretny wycinek czasu i przygotowało mi prezentacje z tego właśnie.

A ponieważ nie posiada kontekstu, moich wspomnień, nie wie, że część tej wycieczki w przeszłość uzupełniałem zdjęciami z dysku to, co wybrało było kompletną bzdurą: kolekcja kilkunastu zdjęć kościołów.

Intelektualnie wiem i rozumiem, że jestem śledzony przez dziesiątki takich niekompetentnych szpiegów, którzy robią to dla „mojego dobra”. Udaje mi się jednak o tym zapomnieć. Co jakiś czas Sztuczna Indolencja, napędzana miliardami dolarów, wysyła swojego agenta by mi przypomnieć, że mogę kupić książkę, którą już kupiłem lub zobaczyć zdjęcia, które już widziałem.

Jest coś frustrującego w tym, że ktoś może przerwać mi osobistą kontemplację, zdemaskować się naruszając mój mir domowy i pełnym głosem, z wypiętą piersią oświadczyć: a wiesz, że ja umiem SELECT […] BETWEEN?.

No i co z tego, kurwa?


  1. Ze wzrou „obraz wart tysiąca słów” 


Marginalia (V)

Piotr szedł nasypem przy brzegu rzeki. Na jednym z zakrętów zauważył na kamienistej plaży staruszka, który trzymał się pod boki i krytycznie przyglądał się rzece. Piotr nie odszedł jeszcze wystarczająco daleko, by stracić go z wroku, gdy ten wykonał kilka energicznych skłonów po czym z impetem wskoczył obu nóż do wody. Ta nie ustała się jeszcze po pierwszym ataku, a dziadek już był na brzegu, już obrócił się na pięcie i znów wbił się w impetem w wodę. Tym razem stał tak prze chwilę, gładząc łyską głowę by wreszcie potrząsnąć nią z zagniewaniem.

Zaintrygowany, Piotr postanowił poczekać i zobaczyć czy teatr, który właśnie ogląda ma jakiś epilog. Podszedł bliżej stromej skarpy i przystanął przy rosnącym tam krzaku. Gdy spojrzał na plażę, dziadka już na niej nie było. Jego łysa głowa pojawiła się na skraju skarpy, a za nią podążyła reszta ciała. Piotr podskoczył, przestraszony i zmieszany, przyłapany na gorącym uczynku.

— Ależ mnie pan przestraszył! — zawołał

Tu znów podskoczył dziadek, najwidoczniej pogrążony w myślach nie zauważył swojej ukrytej widowni.

— A pan mnie. Dzień dobry i przepraszam.

— Dzień dobry — odpowiedział Piotr, ale już do dziadkowych pleców, bo ten już przeprowadzał inspekcję krzaka.

Brał do ręki gałąź po gałęzi i oceniał je. Wreszcie podjął decyzję i ułamał jedną. Mierząc ją jeszcze w dłoni począł schodzić w kierunku plaży.

Piotr, już zdemaskowany, przyglądał się otwarcie drugiemu aktowi.

Patyk zatoczył koło w powietrzu i poleciał w kierunku wody. Po przepłynięciu kawałka został wyłowiony przez starca, który wydawał się być zadowolony z eksperymentu. Patrząc znów na płynącą wodę wzrokiem pełnym determinacji, dziadek wskoczył do wody trzymając patyk w prawej ręce i niemal natychmiast wypuścił go z ręki, pozwalając mu popłynąć ze strumieniem.

Sam wyskoczył z wody na piasek i podjął pościg za odpływającą gałęzią, przymierzając się całą drogę do skoku.

Skoczył wreszcie rozbryzgując wodę dookoła. Stał tak chwilę odprowadzając odpływający patyk wzrokiem. Potarł kark i z wyraźnym znużeniem wrócił na miejsce startowe. Położył się na brudnym piasku, zamknął oczy i złożył ręce na brzuchu.

— Przepraszam — zawołał Piotr — ale muszę pana zapytać, co pan do cholery robi? To jakieś ćwiczenia sportowe? Sztuka?

Starzec przewrócił się na bok aby mógł widzieć swojego rozmówcę i powiedział

— Znacie panie Heraklita?

Piotr potrząsnął głową przecząco.

— To taki filozof, myślał dużo o świecie, o zmianach. Jego najpopularniejsza sentencja to „Niepodobna wstąpić dwukrotnie do tej samej rzeki.” — próbuję obalić tę frazę.

— Dlaczego? — zapytał Piotr

— Bo, drogi panie, mam już dosyć zmian. Ułamże mi pan inny patyk, skoro już jesteś tam na górze.


Marginalia (IV)

Słońce wpadające przez okno zostawiało na biurku pasy światła, jak linie demarkacyjne nieistniejących granic. Republika lampy, gubernia książek będąca w nieustającej wojnie z wielkim królestwem przyborów do pisania. Rzeczy migrowały wraz z pracą, a granice migrowały wraz z posuwającym się dniem, gdy słońce zaczynało chować się za gzymsem budynku na przeciwko.

W wyzwolonym terytorium stał kubek. Był to zwykły, aluminiowy kubek, bez historii, bez znaczenia, gdyż nie spełniał swojej jedynej funkcji jaką jest okiełznanie ciepłego napoju.

Miał tylko fusy.

Iwanowicz starał się go nie zauważać. Był jak wyrzut sumienia. W głębi duszy wiedział, że musi go umyć ale rzeczy proste wydawały się tak skomplikowane. Dużo łatwiej jest robić rzeczy trudne.

Rzeczy trudne pełne są zakamarków w których można ukryć znużenie. Rzeczy trudne są ruchome, mienią się kalejdoskopem problemów od których kręci się w głowie. Jeśli ktoś baruje się z rzeczą trudną, to wszyscy ze zrozumieniem akceptują że rozwiązanie może nie być doskonałe. Jeśli wyciosasz bale, postawisz z nich dom, a krzywo osadzisz drzwi to ludzie powiedzą: „To piękny dom, choć czasem przeciąg”.

Jeśli nie domyjesz kubka, nikt ci nic nie powie. Bo wszyscy, słusznie, będą tobą gardzić.

Więc Iwanowicz pisał, bo pisanie jest trudniejsze, więc prostsze. Poza tym wątpił, czy naprawdę chciał się napić herbaty. Może to tylko jedna z tych żądzy, która przyplątuje się około południa i zadamawia się w głowie. Być może odrzucenie kubka jako drogi do herbaty jest czynem godnym i on, przez to niemycie staje się człowiekiem moralnym, człowiekiem wyboru!

Czy nie zostaliśmy, myśli Iwanowicz, stworzeni do rzeczy większych. Czyż nie trzeba demonom mówić stop, walczyć z pokusą, nie dać kierować swoje życia zachciankami.

Herbata była zielona i smakowała dobrze.


Marginalia (III)

Z mównicy płynął głos.

„Obywatele!” — zadudnił.

„Obywatele.” — powtórzył spokojniej, gdy zebrany tłum ucichł.

„Rozumiemy jak ważny jest dla Was chleb. Dlatego, jednomyślnie podjęliśmy decyzję o powołaniu komitetu do spraw organizacji parady chlebów. Planujemy szeroką reprezentację chlebów z całego świata: meksykański Conchas, japoński Pan, francuską Bagietkę i tak dalej. Uniwersytety otrzymają specjalne dotacje na badania naukowe dotyczące znaczenia chleba w kulturze. Do tego dofinansujemy sztuki teatralne ze szczególnym naciskiem na teatr muzyczny w oczekiwaniu na chwytliwą produkcję, która pokaże aspiracyjne znaczenie chleba. Lokalne samorządy otrzymają pozwolenie na zmianę jednej ulicy, która nazwana jest imieniem króla na Piekarzy.”

Wtrącił się głos z widowni — „A czy będzie chleb dla nas, do jedzenia?”

— „W tej chwili” — odpowiedział człowiek stojący za pulpitem — „planujemy tylko igrzyska.”


Marginalia (II)

Ty leżysz tu, ja leżę tam. Spotykamy się wzrokiem. Twoje oczy są mokre od łez, a moje skupione. Leżymy tak obok siebie, ja wyciągam w Twoją stronę rękę. Twoja pozostaje przy boku. Próbuję wymówić słowa, ale więzną mi w gardle. Na Twojej twarzy maluje się ból. Tu, wyciągnięci na trawie, przeżywamy naszą wspólną tragedię, prywatnie.

Nad nami wisi księżyc w nowiu, blady i uwikłany w chmury, nie świeci nam na twarze. Stłuczona lampa, jakby w komitywie, też zieje mrokiem. Koło roweru powoli przestaje się kręcić. Aleśmy się wyjebali.

(Dla zdziwionych nagłą aktywnością: w ramach zamykania roku przepisuję sobie tekściki i opowiadanka ze stron notatników. Cisza powróci niedługo.)


Marginalia (I)

Zawsze przylatują, gdy próbuję się skupić. Siedzę przy biurku, przywołuję duchy produktywności w nadziei, że posiądą moje ciało i zaczną coś tworzyć. Moje modlitwy przerywa ruch za oknem. Widzę przemykające cienie na zasłonce, słyszę jak atakują mój parapet.

Gdy pada, przysiadają na dłużej, a wypłoszone robią tylko koło nad podwórkiem i lądują ponownie, sprawdzając jak daleko się posunę. Kiedyś wypadłem na nie z parasolem, po czym zacząłem go gwałtownie otwierać i zamykać wołając „Sio! Sio!”. To było moje pierwsze druzgoczące zwycięstwo w tej nierównej walce. Parasolkę zostawiłem chyba w tramwaju, więc dziś nie mam się czym bronić.

Gdyby tylko przykościelne staruszki ich nie dokarmiały, może rozwinęłyby jakiś instynkt łowczy i poszukały szczęścia dalej od parapetów miast, zwłaszcza od mojego.

Znów słyszę lekkie stukanie w szybę, uchylam firankę, następnie okno i krzyczę „Wypierdalaj!” — anioł podrywa się do lotu z cichym „Alleluja” na ustach. Niedługo wróci, a może nawet cała grupa i jeszcze archanioł. Te są najgorsze, bo mają trąby.

Patrzę jak znika za dachami miast, zamykam okno, wracam do biurka i myślę, że znów niczego nie zrobiłem i nie wiem, czy znajdę temat do pisania. Tymczasem anioły obsiadły już linie elektryczne i nucąc „Maryjo, królowo Polski…” czekają na odpowiedni moment do powrotu.


Raspberry z papierka

Jeśli definicją szaleństwa jest „robieni tego samego, spodziewanie się innego rezultatu” to należy powiedzieć, że programowanie i administracja muszą być przeciwieństwem szaleństwa. Oba zawody polegają na rozkładaniu problemu na części pierwsze, ekstrakcję sensu i zamykanie całości w powtarzalny element, który raz wykonany oszczędzi nam technologicznego jąkania.

Szaleństwo w obu zawodach wypływa z innego źródła: nasze rozwiązania często są kruche i produkcja ich zajmuje dłużej niż suma powtórzeń. Nikt nie powiedział, że mamy wszystkie odpowiedzi, zwykle nie znamy nawet pytań.

Po raz kolejny zgrzytałem zębami, gdy przegrywałem czysty obraz dystrybucji na kartę SD abym mógł zaprząc Raspberry do męczenia się nad moim nowym pomysłem. Mimo tego, że mam dostęp do dziesiątek serwerów znajduję przyjemność w zerkaniu przez ramię na lodówkę i szydercze uśmiechanie się w kierunku zwisających z za krótkich kabli ethernetowych mikro-komputerów.

Do tej pory widzieliście już jak Jeżyna mrugała LED-ami czy też pieczołowicie zbierała klatki z kamer. Ostatnio zacząłem zostawiać laptopa w pracy i zbudowałem sobie „maszynę do pisania” w nadziei, że to natchnie mnie twórczo. Głównym rezultatem było szukanie fontów, które dają się czytać na 3.2 calowym ekranie i które mają polskie znaki.

Raspberry na biurku

Dziś znów coś wymyśliłem i od razu rozbolała mnie głowa. Znów ustawianie WiFi, znów parowanie urządzeń BT, przegranie tego czy owego. Zawsze to samo. Ale przecież nie trzeba tak żyć. Postanowiłem zobaczyć, czy uda się łatwo zmodyfikować obraz tak, abym zawsze mógł startować z wygodnego mi miejsca. Rozważałem Ansible, ale to zakłada obecność sieci.

Rozpocząłem od pobrania czystej wersji systemu i rozpakowania jej. Dostajemy wtedy plik YYYY-MM-DD-raspios-ver-distro.img. Jest to obraz zawierający dwie partycje: /boot i rootfs. Teoretycznie można je zamontować przy użyciu -o loop, ale trzeba znać ich przesunięcia, a my tu gadamy o automatyzacji. Na szczęście istnieje kpartx zajmujący się czytaniem tych informacji.

% sudo kpartx -v -a 2020-05-27-raspios-buster-lite-armhf.img
add map loop8p1 (254:0): 0 524288 linear 7:8 8192
add map loop8p2 (254:1): 0 3088384 linear 7:8 532480

Otrzymaliśmy dwa urządzenia reprezentujące wyżej wspomniane partycje. Teraz możemy je normalnie podmontować.

% mkdir /tmp/{1,2}
% sudo mount /dev/mapper/loop8p1 /tmp/1
% sudo mount /dev/mapper/loop8p2 /tmp/2

Mając dostęp do zamontowanych partycji możemy podciągnąć nasz system do stanu, który nam bardziej odpowiada. Przykładowo sudo touch /tmp/1/ssh, zgodnie z dokumentacją odpali nam SSHd przy piewszym uruchomieniu. Oto kilka modyfikacji, które dodałem dla własnej wygody:

  1. Edycja hostname, bo komputer musi się jakoś fajnie nazywać.
  2. Wgranie własnego klucza żebym się nie musiał bawić we wpisywanie haseł. Wystarczy stworzyć katalog .ssh w home/pi w ścieżce drugiej partycji i wrzucić co trzeba do authorized_keys
  3. Dostaję wścieklizny, gdy za każdym razem muszę usuwać odcisk palca hostu. Utworzyłem więc i przegrałem do etc/ssh/ wcześniej wygenerowane pliki ssh_host_* upewniając się, że nigdy nie zobaczę spanikowanego komunikatu o potencjalnym zagrożeniu. Jedna uwaga do tego, unit systemd odpowiedzialny za tworzenie kluczy ignoruje obecność już istniejących. „Rozwiązałem” ten problem usuwając go z lib/systemd/system. Pewnie jest mądrzejszy sposób, np. usunięcie symlinka.
  4. Podobnie jak z aktywacją SSHd możemy wrzucić do podmontowanego /boot wpa_supplicant.conf z definicją domowego/pracowego WiFi.
  5. Dograłem trochę oprogramowania, którego i tak zawsze potrzebuję, a nie znajduje się w pakietach.

Jest jeszcze jedna sztuczka, której nie będę opisywał, bo już zrobili to za mnie. Używając qemu możemy dokonać chroota w obcy architektonicznie system i poinstalować/poodpalać rzeczy bez potrzeby wkładania karty do Raspberry.

Kiedy skończymy modyfikacje kluczowe jest odmontowanie partycji i usunięcie mappera stworzonego przez kpartx. Podczas pierwszych eksperymentów udało mi się dokonać sztuki, w której co innego widziała karta, a co innego obraz.

% sudo umount /tmp/1 && sudo umount /tmp/2
% sudo kpartx -d 2020-05-27-raspios-buster-lite-armhf.img
loop deleted : /dev/loop8

Po tej operacji nasz zmodyfikowany obraz możemy odłożyć gdzieś na bok i w przyszłości generować systemy typu „zupka chińska”, tylko zalać wodą.

Pomiędzy dwoma rozwiązaniami, które rozważałem: uruchamianie z TFTP lub modyfikacja obrazu źródłowego ten drugi okazał się być dużo mniej wymagający. Może w przyszłości zautomatyzuję tę automatyzację (zwłaszcza tę część z qemu, ale wpierw naciszę się tym małym, środowym, sukcesem.


Dobre rzeczy, gorzej: Jajeczka w 302 liniach Lua

W mojej przygodzie z programowaniem jest wiele rzeczy, których nie próbowałem. Umościłem się wygodnie na dnie stosu i ciężko jest mi się wygrzebać z tego barłogu, gdzie ciepło, a wszystkie kąty są znane. Cierpię też na przypadłość, która trapi wielu programistów: mój zmysł estetyczny jest przytłumiony. Jeśli coś wygląda paskudnie, a działa doskonale to znaczy, że jest piękne. Dlatego też podchodziłem do programowania gier z wielką nieufnością. Trzeba się piekielnie napracować żeby coś dobrze wyglądało. Żeby grający wiedział, co dzieje się na ekranie.

Do tego trzeba mieć pomysł. Najlepiej dobry pomysł.

Dlatego latami czytałem sobie rozmaite „jak to się robi”, ale nigdy nawet nie wyszedłem poza kompilację załączonych przykładów do tej czy innej biblioteki, mądre potrząsanie głową i mówienie „Acha!”.

Kilka dni temu zamknąłem dzień w pracy straszną awanturą. Jedną z tych, która jak śniegowa kula zaczyna się gdzieś od płatka, a kończy się groźbą rozpłaszczenia nas wszystkich. Potrzebowałem zająć się czymś do końca dnia żeby nie usiąść i nie zacząć komponować e-maila z kategorii „jak ja to widzę”.

Potrzebowałem czegoś absorbującego uwagę, więc najlepiej czegoś, czego nigdy nie robiłem. Wiem, napiszę grę. Problem braku pomysłu i jakości rozwiązałem w iście holiudzkim stylu. Zrobię to, co już było, ale gorzej!

Mój wybór padł na kultową grę elektroniczną zwaną kolokwialnie „Jajeczkami”. Zasady są proste, grafika jest prosta. Powinienem sobie dać radę. W temacie narzędzi padło na Luę i Love2d. Nie znałem jednego ani drugiego, więc idealnie wpasowywały się w mój plan zajęcia głowy nowymi problemami.

Po godzinie udało mi się uzyskać kontrolę nad środowiskiem na tyle, że patrząc na biały prostokąt rozpierała mnie duma i wiara we własne możliwości. Postanowiłem udokumentować ten cały proces i podzielić się nim z Wami. Na początku wymyśliłem, że będzie to doskonały tekst dla początkujących, ale im bardziej piętrzyły się przede mną problemy tym bardziej traciłem wiarę, że dam radę.

Powstały więc zapiski chaotyczne, które ani nie uczą, ani nie tłumaczą. Przynajmniej powstrzyma mnie to od napisania „Re: jak ja to widzę”.

„Jajeczka”

Wpierw o samej grzej. „Jajeczka” to klon Mickey Mouse, gry elektronicznej wydanej w 1984 przez Nintendo. Postacie zostały wymienione na mniej reakcjonalne maskotki z kreskówki „Ну, погоди!”. Wcielamy się w postać Wilka, który w kreskówce przedstawiany jest jako bumelant i opryszek. Tym razem zajmuje się pracą na kurzej fermie, najwidoczniej w wyniku resocjalizacji.

Na czterech grzędach siedzą cztery kury, które znoszą jajka, a te pod wpływem grawitacji toczą się rynnami w dół. Naszym zadaniem jest złapanie ich do koszyka. Trzeba też nadmienić, że kury te są niesamowicie produktywne, Trofim byłby dumny.

Wyświetlacz to segmentowane LCD. Znaczy to, że wszystkie możliwe elementy są predefiniowane, a ruch symulowany jest przez zapalanie odpowiednich kształtów. Znacząco ułatwia mi to kradzież, gdyż nie muszę się przejmować skomplikowanymi fazami ruchu.

Wyświetlacz LCD ze wszystkimi komponentami

Postanowiłem wyciąć wszystkie elementy, które pozostają w ruchu i potem poukładać je samemu w grze.

Mamy więc:

  1. Wilka patrzącego w lewo i prawo
  2. Ręce Wilka sięgające do dolnej i górej grzędy dla lewego i prawego kierunku
  3. Pięć faz animacji toczącego się jajka dla każdej rynienki
  4. Cztery fazy animacji „stłuczonego jajka”
  5. Wskaźnik liczba „stłuczonych jajek”

To powinno wystarczyć za część graficzną. Teraz pozostaje zebrać całość do kupy. Ale zanim zaczniemy, kilka słów dla czytelników, którzy nigdy nie interesowali się jak to się dzieje, że rzeczy się ruszają i są w odpowiednich miejscach. Choćby amatorsko doświadczeni w sztuce będą wiedzieli, że te definicje nie są idealne, ale to neofita tłumaczy ludziom krok za nim.

Ekran

Możecie myśleć o ekranie komputera jak o kartce papieru w kratkę, gdzie każdy segment to pojedynczy piksel, który można zaświecić na dowolny kolor z dostępnej dla komputera palety. Pozycję na ekranie wyrażamy jako koordynaty X i Y.

W przypadku Love2d, lewy górny róg ekranu znajduje się pod pozycją (0, 0). Więc (10, 0) znaczy jedenaście pikseli (liczymy od zera) w prawo od brzegu w pierwszej linii. Idąc dalej tym tropem (4, 5) znajdzie się w szóstej linii od góry, pięć pikseli od lewego brzegu ekranu.

W większości przypadków nie będziemy rysować grafiki piksel po pikselu. Zwykle wczytujemy gotowy element graficzny, który umieszczamy na naszej płaszczyźnie. Sam wczytany obiekt ma własną, wewnętrzną siatkę koordynat, ale umieszczamy go wskazując miejsce, gdzie znajdzie się jego (0, 0).

Więc jeśli chcemy wczytać niezmiernie estetyczną żółtą twarz i umieścić ją gdzieś na ekranie, powiemy, że pod (9, 5) to wyląduje tam lewy górny bok, reprezentowany wewnętrznie przez (0, 0)

Animacja pokazująca siatkę

Myślę, że to wszystko czego potrzebujemy na tę chwilę.

Stan

Stan to wiedza o wszystkich właściwościach świata stworzonego w grze. Co jest stanem w Jajeczkach? W którą stronę obrócony jest wilk i jak ma ułożone ręce. Ile razy przegraliśmy. W jakiej pozycji znajdują się jajka i ile ich jest na ekranie. Punktacja. Wszystkie elementy, które tworzą grę są odzwierciedlone przez zmienną, która jest potem interpretowana przez nas.

Zmienna score w consts.lua trzyma naszą punktację, miss liczbę skuch, w main.lua hands i face reprezentują pozycję wilka.

W Love2d stan jest globalny, tj. wszystkie komponenty mają bezpośredni dostęp do zmiennych. Jest to uważane za bardzo zły pomysł™, gdyż niepowiązane komponenty mogą sobie stawać na palcach i doprowadzać do nieokreślonego stanu, ale myślę, że obniża to też znacząco wysokość pierwszego stopnia, który potencjalny twórca musi pokonać.

Pętla

Istnienie czasu to problem, który spędzał sens z powiek wielu filozofom. Czy to rzeczywiście taka prosta linia z przeszłości w przyszłość? Czy może cały czas istnieje na raz, a nasza świadomość odbiera tylko echa takiej projekcji. Być może zmiany są w ogóle niemożliwe?

Jakakolwiek jest rzeczywistość skryta przed naszymi oczami, zmechanizowaliśmy czas, pocięliśmy go na plasterki sekundową wskazówką zegarka i empirycznie jest nam z tym dobrze.

W grze zmiana i czas muszą być przez nas odtworzone, nikt nie znajdzie przyjemności patrząc w statyczny obraz na ekranie kiedy miał zamiar grać.

Love2d posiada dwie funkcje, które są wywoływane na okrągło, w nieskończonej pętli. To w nich implementujemy zmianę (co się rysuje) i czas (zmiany stanu gry). Są to odpowiednio love.draw() i love.update(dt).

Może zauważyliście, że w przypadku update mamy parametr dt — jest to zmienna, która mówi ile czasu minęło od ostatniego jej wywołania (Δ czasu). Pozwala nam to kontrolować czas w którym rzeczy się dzieją. Przykładowo chciałem aby można było poruszać Wilkiem w każdej chwili, ale ruch spadających jajek musi zachować jednolite tempo, niepowiązane z ruchem postaci (inaczej gra nie była by uczciwa: gdyby Wilk poruszał się w tempie spadających jajek, sięgnięcie trzech będących na granicy rynny byłoby niemożliwe).

Popatrzmy w kod:

function love.update(dt)

    require('wolf_movement') 
    wolf_movement()

    time_since_movement = time_since_movement + dt
    if(time_since_movement >= ms_to_roll) then
        time_since_movement = 0
        -- a jednak się rusza!
    end
end

Mamy zmienną time_since_movement w której składujemy wszystkie mikrosekundy, które do nas przyszły w wyniku wykonywania funkcji. Jeśli ta suma jest większa niż zdefiniowana w ms_to_roll ruszamy spadającymi jajkami. Funkcja odpowiedzialna za ruch wilka znajduje się w wolf_movement i jest poza blokiem, pozwalając mu ruszać się w każdej chwili.

Po tym jak Love2d skończy wykonywać ciało funkcji love.update(dt) wywoływane jest love.draw(), gdzie nasza grafika jest rysowana zgodnie ze stanem ustawionym wcześniej.

Elementy graficzne

Każda klatka animacji znajduje się w osobnym pliku i jest wczytywana podczas inicjalizacji programu. Wymyśliłem sobie, że będę je wszystkie trzymał w tabeli (nomenklatura Lua znacząca listę/hashmapę), dzięki czemu animowanie będzie polegało tylko na zwiększaniu indeksu o jeden.

W przypadku Wilka musiałem zastosować nieco inną metodę. Ponieważ rusza się on niezależnie i jest sterowany przez nas, możemy jego fragmenty pochować pod ładnymi nazwami

-- pozycje wilka
wolf_left = {
    171, 134, -- koordynaty
    love.graphics.newImage('assets/wolf_left.png'), -- obrazek
    'left' -- nazwa
}
wolf_right = {
    250, 134,
    love.graphics.newImage('assets/wolf_right.png'),
    'right'
}
left_up = {
    124, 134,
    love.graphics.newImage('assets/left_up.png'),
    'up'
}
left_down = {
    116, 193,
    love.graphics.newImage('assets/left_down.png'),
    'down'
}
right_up = {
    309, 138,
    love.graphics.newImage('assets/right_up.png'),
    'up'
}
right_down = {
    300, 198,
    love.graphics.newImage('assets/right_down.png'),
    'down'
}

Każdy fragment Wilka składa się z czteroelementowej tablicy, która zawiera: koordynaty, reprezentację wczytanego obrazka, a następnie string. Ten ostatni dodałem nie mogąc wymyślić jak zaimplementować sytuację, gdy Wilk patrzy w jedną stronę i zmieniamy go na drugą. Logika mówi, że jeśli trzymał ręce w górze w lewo, po zmianie na prawo ta pozycja będzie zachowana. Niestety, nieznajomość Lua spowodowała, że nie mogłem wymyślić lepszego pomysłu niż wulgarne nazwanie kierunku.

Ruch jajek miał dużo mniej problemów merytorycznych.

-- ruch jajek
eggs_pos = {
    {
        {37, 86, love.graphics.newImage('assets/left_up_egg_1.png')},
        {54, 99, love.graphics.newImage('assets/left_up_egg_2.png')},
        {75, 104, love.graphics.newImage('assets/left_up_egg_3.png')},
        {92, 117, love.graphics.newImage('assets/left_up_egg_4.png')},
        {107, 137, love.graphics.newImage('assets/left_up_egg_5.png')}
    },
    {
        {39, 156, love.graphics.newImage('assets/left_down_egg_1.png')},
        {55, 164, love.graphics.newImage('assets/left_down_egg_2.png')},
        {71, 176, love.graphics.newImage('assets/left_down_egg_3.png')},
        {92, 183, love.graphics.newImage('assets/left_down_egg_4.png')},
        {105, 204, love.graphics.newImage('assets/left_down_egg_5.png')},
    },
    {
        {416, 87, love.graphics.newImage('assets/right_up_egg_1.png')},
        {399, 95, love.graphics.newImage('assets/right_up_egg_2.png')},
        {381, 106, love.graphics.newImage('assets/right_up_egg_3.png')},
        {359, 117, love.graphics.newImage('assets/right_up_egg_4.png')},
        {346, 128, love.graphics.newImage('assets/right_up_egg_5.png')},
    },
    {
        {418, 159, love.graphics.newImage('assets/right_down_egg_1.png')},
        {399, 164, love.graphics.newImage('assets/right_down_egg_2.png')},
        {380, 174, love.graphics.newImage('assets/right_down_egg_3.png')},
        {364, 184, love.graphics.newImage('assets/right_down_egg_4.png')},
        {343, 200, love.graphics.newImage('assets/right_down_egg_5.png')},
    }

}

Mamy więc tabelę z czterema elementami, które odwzorowują cztery rynny. A w nich pięć faz animacji spadającego jajka: koordynaty i plik graficzny. Czyli eggs_pos[1][1] to pierwsza rynna, pierwsza pozycja jajka. I tak, zanim zapytacie: tabele w Lua zaczynają indeks od 1. Miałem przynajmniej trzy błędy związane z moim — najwidoczniej optymistycznym — założeniem, że liczymy od zera!

Podobnie wygląda definicja animacji skuchy.

I to byłoby na tyle w temacie przygotowania elementów graficznych.

Wilk lewy, Wilk prawy

Miałem wielką ambicję zrobienia jakiejś dobrej abstrakcji w temacie ruchu naszym protagonistą. Nauczyłem się jednak, że zła abstrakcja boli całe życie. Zwłaszcza, gdy robi się ją kompletnie bez znajomości idiomów języka. Postanowiłem, że prosty if() nikogo nie zabił, nawet jeśli powtarzam się powtarzam się przy tym przy tym.

Czytanie klawiatury w Love2d jest trywialne, nie trzeba znać nawet kodów klawiszy.

function wolf_movement()
    if love.keyboard.isDown("up") then 
        if face[4] == 'left' then
            hands = left_up 
        else
            hands = right_up
        end
    end
    if love.keyboard.isDown("down") then 
        if face[4] == 'left' then
            hands = left_down 
        else
            hands = right_down
        end
    end
    if love.keyboard.isDown("right") then
        face = wolf_right
        if hands[4] == 'up' then
            hands = right_up
        else
            hands = right_down
        end
    end
    if love.keyboard.isDown("left") then
        face = wolf_left
        if hands[4] == 'up' then
            hands = left_up
        else
            hands = left_down
        end
    end
end

Zmienne face i hands przechowują obecną pozycję Wilka. Widzimy tu moje tchórzliwe użycie czwartego elementu do zachowania stanu rąk. Teraz trzeba wrócić do love.draw i powiedzieć mu, co rysować.

love.graphics.draw(face[3], face[1], face[2])
love.graphics.draw(hands[3], hands[1], hands[2])

I to wszystko! Już można hasać po ekranie. Programowanie gier to czysta przyjemność, gdy nie przejmujesz się detalami!

Demonstracja ruchu

Jajka się toczą

Nabuzowany tym sukcesem postanowiłem powoli zabrać się za następny element, ruch spadających jajek. Pierwsza wersja przyszła szybko.

Piersze jajka za płoty

Mina mi jednak szybko zrzedła. Okazało się, że zapomniałem o grze w grze. Moja pierwsza implementacja nie mogła działać. Muszę mieć możliwość losowego spuszczania jajek dowolną rynną oraz dodawania nowych. Dodatkowo jest też zwiększający się poziom trudności, zaczynamy od jednego jajka, a po uzyskaniu jakiejś liczby punktów muszą być dwa. I nie mogą zaczynać w tym samym momencie. Dobrą chwilę patrzyłem w kod nie potrafiąc się wyrazić wystarczająco jasno w Lua.

Na przykład zmienną trzymającą stan rynien normalnie ująłbym używając binarnej reprezentacji. 0x1010 i wiemy, że jajko toczy się po rynnie 1 i 3. Ale Lua nie ma w standardowej bibliotece odpowiednich narzędzi, nie chciałem utknąć w trybie „jak się instaluje biblioteki”. Zrobiłem więc następującą rzecz, wiedząc już, że moje zapiski będą bezużyteczne dla absolutnie początkujących: dodałem zmienne eggs = {0, 0, 0, 0} i roll_on = {0, 0, 0, 0} reprezentujące odpowiednio klatkę animacji na danej rynience i to, czy coś się po niej toczy. Następnie napisałem okropne sum(), które przyjmuje tabelę i sumuje elementy. Aplikując to na roll_on wiem ile rynienek jest zajętych. Teraz tylko sprawdzić, czy chcę mieć więcej jajek niż jest widoczne, zapisać indeksy tych, które są puste (==0) i losowo wcisnąć jajko.

A co z opóźnieniami? Okazało się, że Lua jest cierpliwe i zaakceptowało mój szalony pomysł: ustawię pozycję na ujemną!

I jakimś cudem zadziałało, uff…

function gutters_logic()
    if sum(roll_on) == 0 then -- nic się nie toczy
        -- znieś jajko byle gdzie
        roll_on[math.random(1,4)] = 1
    end

    if sum(roll_on) < eggs_on_screen then
        -- jest mniej jajek niż chcemy
        print("Sum(roll_on) ", sum(roll_on))
        print("eggs_on_screen ", eggs_on_screen)
        pick = {}
        how_many_we_need = eggs_on_screen - sum(roll_on)
        print('how_many_we_need ', how_many_we_need)
        for i=1, table.getn(roll_on) do
            -- wybieramy puste rynienki
            if roll_on[i] == 0 then
                table.insert(pick, i)
            end
        end

        for i=1, how_many_we_need do
            -- losowa rynna z dostępnych
            choice = math.random(1, table.getn(pick)) 
            if roll_on[pick[choice]] == 1 then
            else
                roll_on[pick[choice]] = 1
                -- ileś „tyknięć nim się pojawi”
                eggs[pick[choice]] = math.random(1, 4) * -1
            end
        end
    end
end

Jak Doktor Frankenstein byłem zafascynowany i przerażony, że to żyje!

Rysowanie poszło dużo łatwiej.

for i=1, table.getn(roll_on) do
    if eggs[i] > 0 then
        current_egg = eggs_pos[i][eggs[i]]
        love.graphics.draw(current_egg[3], current_egg[1], current_egg[2])
    end
end

Goń i łap

Nie pozostało nic innego jak zacząć łapać te jajka. Po raz kolejny zawiesiłem wszystko na choince if()ów.

function success_or_failure(i)
    if i == 1 and face == wolf_left and hands == left_up then return true end
    if i == 2 and face == wolf_left and hands == left_down then return true end
    if i == 3 and face == wolf_right and hands == right_up then return true end
    if i == 4 and face == wolf_right and hands == right_down then return true end
    return false
end

Należało to jeszcze wpiąć w love.update(dt)

for i=1, table.getn(roll_on) do
    -- jeśli jajko się tu toczy
    if roll_on[i] == 1 then
        -- daj następną klatkę animacji
        eggs[i] = eggs[i] + 1
        if eggs[i] == fail_at then
            -- jajko jest na brzegu? A gdzie patrzy wilk?
            if(success_or_failure(i)) then
                score = score + 1
                -- agresywnie zwiększam ilość jaj dla debugu
                if score == 2 then
                    eggs_on_screen = 2
                end
                if score == 5 then
                    eggs_on_screen = 3
                end
            else
                miss = miss + 1
                if i == 1 or i == 2 then
                    failure_left = 1
                else
                    failure_right = 1
                end
                print("Bams", i, failure_left, failure_right)
            end
            -- jajko znika, komora losowania jest pusta
            eggs[i] = 0
            roll_on[i] = 0
        end
    end
end

I tak, krok po kroku, całość zaczęła przypominać pierwowzór. Osiągnąłem swój cel główny: zmarnowałem prawie 5h1. Cel poboczny, tj. zrobić coś, czego nigdy nie robiłem, uznaję też za osiągnięty.

Teraz myślę nad zmianą kariery, napędzany zadufaniem początkującego.

Kod dostępny jest w repozytorium wraz z półproduktami.

PS. jeśli na serio chcecie się czegoś dowiedzieć o projektowaniu gier polecam Wam książki wydane przez moich przyjaciół z „Inżynierii Wszechświetności”, choćby Level design.


  1. I 2h pisząc to, a za 7h muszę przejechać na rowerze 60Km, proszę o współczucie. 


Oczy wielkie jak spodki, a na nich rzęsy jak łyżki

Siedzieliśmy na betonowych schodach z nogami spuszczonymi przez balustradę i rozmawialiśmy o przyszłości. To były czasy kiedy rozmawianie o przyszłości było zajęciem przyjemnym, taśma na nienagranej szpuli życia była zdecydowanie dłuższa. Karolina chciała zostać pielęgniarką lub weterynarzem. Ja planowałem zostać lumpem. Ona została samotną matką, a mnie się udało.

Karolina wyszła na chwilę do mieszkania, by wrócić z herbatą i pudełkiem pełnym papierów. Wyciągnęła z niej jedną kartkę i powiedziała: znalazłam ostatnio list miłosny od Radka. I choć to faux pas czytać cudze listy, dwakroć miłosne, a po trzykroć powtarzać ich treść w Internecie, jedna linijka utkwiła mi w pamięci do teraz.

Listy miłosne w podstawówce mają dwie cechy: próbują naśladować język dorosłego romansu zagapiony z popularnej kultury i kart książek do języka polskiego, ale są jeszcze wystarczająco pozbawione elementu cielesności by nie być „w sedno”, mają też okropną gramatykę i interpunkcję. Gdy przeczytała mi więc zdanie „[…] Twoje czerwone usta oczy” nie mogłem przestać wymyślać zabawnych anegdot o tym, co powoduje, że Karolina ma czerwone oczy. Płacze nad przecinkiem, nie śpi, a może ma nieustanny katar? Debatowaliśmy to aż do wystygnięcia herbaty.

Mijają lata, a ja za każdym razem, gdy trafiam na porównanie w książce odpalam w głowie proces, który modyfikuje ckliwości w głupoty. Nie mogę się tego pozbyć, co pewnie tłumaczy, czemu nie czytam romansów. Za dużo „śmierci autora” na centymetr kwadratowy. A mówię Wam to żeby Was zakazić, może gdy wszyscy będziemy porażeni tą samą przypadłością środowisko medyczne weźmie nas pod uwagę i wynajdą proszki na cynizm. Byle nie w formie czopka.

Miała usta jak jagody: czarne, łatwo było je rozgnieść, pożerały je niedźwiedzie.

Miała oczy jak diamenty: ślepe, rysował jej szkła kontaktowe, sprowadzały nieszczęście na afrykańskie wioski.

Jej piersi jak arbuzy, dawały wiele rozkoszy, gdyby nie te pestki.

Jej łabędza szyja utrzymywał małą głowę, a twarz zakończona dziobem utrudniała picie z filiżanek.

Jej porcelanowa skóra była twarda w dotyku, uniemożliwiała ruch, a kolor nabrała dopiero wypalona w piecu.

Włosy koloru siana: jesienią sino-brunatne, zmatowiałe, martwe.

Była mądra jak sowa: wiedziała wszystko o jedzeniu myszy w locie.

Miała wielkie serce, co martwiło kardiologów.

Śpiewała jak skowronek, monotonnie, bez słów, budziła cię świtem.

Była potulna, jak baranek. Nocą rodzice spuszczali na nią psy by zapędzić ją do łóżka.

Rzuciła mu perłowy uśmiech. Małe, okrągłe zęby świeciły się tęczowo gdy padało na nie światło pod dobrym kątem.

EDIT: ojej, wymsknęło się z drafts. No trudno.


Rezygnacja: automatyzacja

„Zautomatyzowałem zadanie, które zajmowało mi 2 minuty raz na miesiąc. Poświęciłem 8 godzin pracy i już mogę je robić w minutę. Póki nie zmienią się parametry.”

Na dworze administracja prowadzi wiosenną wojnę z okalającym nas betonem. Piechota uzbrojona w młoty pneumatyczne kruszy wszelki opór. Zwycięstwo jest blisko, może jeszcze tydzień. Cywilne ofiary wojny, czyli ja, są wliczone w koszta tej bitwy.

Nie mogę tak pracować, co znaczy, że muszę poczekać na wieczór, aż świat pójdzie spać. Muszę jednak jakoś wypełnić te godziny. Co mogę zrobić? Tylko rzeczy głupie. Głupie rzeczy nie wymagają myślenia, a zajmują tyle samo, co rzeczy dobre!

Kilka dni temu wysłałem moim kolegom-programistom komediowe wideo o tragediach czających się w świecie mikroserwisów. Nigdy nie robiłem mikroserwisów, ale mam bardzo mocne opinie na ich temat dlatego staram się uzyskać społeczny dowód na moje racje od ludzi, którzy dłubią je każdego dnia. Podczas krótkiej wymiany opinii powiedziałem, że DevOops powinni nauczyć się od projektantów i DTP-owców i nazywać serwisy tak, jak oni nazywają pliki. Losowe liczby, daty, wulgaryzmy.

Zawsze lubiłem patrzeć na ich metodologię. Historia wprost wycieka z takiej nazwy. Czy jest data? Może to już któryś dzień walki? Czy jest _final, i czy jest coś po _final? Nieodpowiedzialny hurra-optymizm został ukarany. Czy są „ozdobniki”, które powodują, że sortowanie katalogu wyciąga je na górę?

Wymyśliłem, że jedynym dobrym zajęciem jest przygotowanie kodu, który mógłby zostać zamieniony w mikroserwis ułatwiający utworzenie takich nazw. Użyłem najlepszej technologii AI (tak na serio to ML (tak na serio to if(random() >= .5))) i utworzyłem następujące rozwiązanie™.

params = {
    'crazyness': .5, 
    'optimism': .6, 
    'vulgarity': .7, 
    'timekeeper': .6, 
    'decorations': .6
}

from despyration import despyration

files = [
    'podanie_o_nienapierdalanie_mlotem.docx',
    'reklama-plynu-do-mycia.psd', 
    'umowa_o_dzialo.pdf'
]

[despyration(f, **params) for f in files]

A oto rezultaty:

2020-05-19-PODaNIe_O_NiENapIerDalanIE_mloTem!!!.docx

rEKLAMa-plYNu-DO-mYciA__fuck-2020-05-19@.psd

umowa_o_dzialo_final_final_final+++.pdf

Myślę, że udało mi się oddać ducha, choć pozostaje wiele pracy. Trudno jest zasymulować prawdziwy proces, sztukę, intuuicję oraz frustrację, która rodzi te nazwy. Stoję na ramionach gigantów, a oni mi mówią żebym się odwalił, co to w ogóle znaczy wspinać się po ludziach, zwłaszcza teraz, w epoce zarazy.


To się nie nadaje do druku

Każda przygoda ma swój punkt zapalny. U mnie były dwa. Pożyczyłem okropnego tableta z Windowsem przyjacielowi, którego laptop nagle wykitował. To okropny tableto-laptop. Okropna klawiatura, 2GiB pamięci, 32GiB wlutowanego dysku i 64GiB karta, którą dodałem żeby się coś dało zrobić pomiędzy aktualizacjami. To dlaczego go mam? Waży mniej niż kilogram, więc nadaje się do plecaka, gdy idę w świat (wszystkie systemy czekają z kręceniem powrozu na swoje karki do momentu w którym moja stawiam nogę na dworcu), dodatkowo ładuje się z USB.

Ma też niezły ekran, więc używam go do czytania tych średnio-długich tekstów. Pożyczając go pozbawiłem się tej opcji.

Kupiłem też drukarkę. Wyrzucenie z biur klientów przez zarazę odebrało mi możliwość bezczelnego podsyłania ludziom e-maili o tytule „wydrukuje mi Pani?”. Do tego moja lokalna poczta jest bardzo popularna, więc każdego dnia o każdej godzinie wystaje z niej ludzka gąsienica petentów. Dowiedziałem się, że można drukować własne znaczki, więc zakup wydał się jeszcze bardziej usprawiedliwiony.

Bo kto ma teraz lepsze znaczki niż ja?

Zdjęcie znaczka

Pomyślałem, że połączę utratę urządzenia do czytania i nabycie urządzenie do drukowania w nowe rozwiązanie. Wydrukuję sobie zaległe teksty i będę sobie w spokoju czytał do kawy. Cóż może być trudniejszego niż wydrukowanie dokumentu? Z pewnością nic.

Zbadajmy jak to wygląda!

Wybrałem do testów serwisy, których artykuły gniją w moim Pocket’cie. Nie testowałem prywatnych stron, bo wydaje mi się to nie fair. Nie tylko teksty są zwykle krótsze, ale też nie mogę oczekiwać, że mają budżet/czas na picowanie swojej oferty. Dodatkowo, teksty „prywatne” są głównie o programowaniu, więc mają wartość dodaną na ekranie komputera, głównie naukę metodą Kopiego-Pasty.

the Bellows: Tekst wygląda dobrze. Nagłówek, wraz z elementami nawigacji powtarza się co stronę. Takoż guzik udostępniania. Stopka dodaje całą stronę A4 niczego ważnego.

Places Journal: Tekst sformatowany jak na stronie, mała czcionka, elementy takie jak odgrywaczka audio pozostały. Stopka dodaje bezużyteczną stronę. Nieczytelne, bezużyteczne.

Damage: Układ praktycznie doskonały. Niestety, stopka znów dodaje bzdety, których nikt nie potrzebuje na kartce.

Claremont Review of Books: <div> z reklamą ciągnie się całą długość dokumentu, powodując, że tekst zajmuje 70% strony i jest przesunięty do prawego brzegu. Dwie strony stopki. Koszmar.

The Point: Idealnie. Pierwszy przypadek, gdy wszystko jest gotowe. Tekst, który próbowałem dodał pustą stronę A4, ale ponieważ ostatni paragraf kończył się idealnie na poprzedniej zakładałem, że to przykry zbieg okoliczności. Zbadanie innego tekstu potwierdziło. Pierwszy sukces!

Harper’s: Nagłówek zawiera logo i pustą stronę. Potem jest strona tekstu, która się urywa, gdyż jest „zabezpieczone JavaScriptem”. Siadaj, dwója. Jutro przyjdź z matką.

Public Books: Font à la „Retina, ale na kartce”, dwie strony stopki. Znacie tę piosenkę.

Times Literary Supplement: Szatan, piekło, antychryst. Czyli Single Page Application. Jedna strona nagłówków, wraz z banerem polecającym żebym dał im dolara, jedna strona tekstu, ucięta, a na niej baner proszący o dolara. Stopka. I baner proszący o dolara.

Ars Technica: Sztywna nawigacja, ślepe galerie, paginacja. Desperacja.

Increment: „drukuje się” jedna strona.

Jeden dobrze. Jeden OK. Trzy powodują, że drzewa ronią łzę. Cztery nawet nie próbują udawać, że ktoś kiedyś myślał o papierze.

Umówmy się: gówno wiem o projektowaniu wyglądu, potrzebach „spieniężania” treści i innych problemach z którymi mogą się borykać magazyny internetowe. Jestem tylko kolesiem, który chciał sobie wydrukować trochę tekstu do czytania. Te rezultaty podsycają jednak moją nienawiść do tumiwisizmu współczesnej sieci.

Jeśli zboczysz z wyznaczonej ścieżki choćby o krok nagle znajdujesz się na brzegu klifu, a za Tobą materializuje się dobrze ubrany konsultant, który uprzejmymi gestami zachęca cię do skoku. Najlepiej bańką do przodu.


Chromecast dla Linuksiarza

Przyznaje się, przegrałem. Jest sobota, a ja miałem dobry pomysł na zabawny tekst. Tekst, który wymagał może zbyt wiele kompetencji, których nie posiadam. Od siódmej rano walczyłem z oprogramowaniem do edycji wideo, zbyt starym cmake, nagrywaniem akcelerowanego wideo, faktem, że pliki instalacyjne wyparowały mi z archiwum i tym, że firma, od której kupiłem oprogramowanie mentalnie jest w 1990 i strzeże swoich fantów jak oczka w głowie.

Rzuciłem się z motyką na słońce, a na dodatek słońce było uzbrojone i mnie obstrzelało ostrą amunicją. Wybiła trzynasta, a ja mam jedno źle skadrowane wideo — testowe wideo, nawet nie to, co chciałem — które się chybocze, dyga i ma zły aspekt. Czas powiedzieć sobie: OK, zjedz coś, usiądź. Po co się denerwować.

To prowadzi nas do miejsca, w którym, jak mawiają Amerykanie jestem all dressed up with nowhere to go. Pójdę więc po linii najmniejszego oporu i po prostu opiszę, co mam na biurku.

Chromecast

Chromecast to odbiornik strumieni medialnych, produkowany przez Google. Małe pudełeczko, które wpina się do telewizora/monitora przez HDMI i pozwala na przesyłanie audio/wideo z rozmaitych serwisów. Mimo mojej niechęci do dawania Google wiazdu do mojego życia, podobnie jak w przypadku porannego projektu, trzeba wiedzieć kiedy się poddać. Mimo niemiłej mi natury produktu nie udało mi się zbudować niczego samemu, bądź też z półproduktów, co byłoby choćby w ułamku tak funkcjonalne.

Normalni ludzie mogą używać aplikacji, które wspierają Chromecast. Praktycznie każda popularniejsza aplikacja mobilna (YouTube, Spotify, Netflix, etc) potrafi rzucić swoje bity na ekran. Działa to bardzo dobrze. Dla normalnych ludzi z komputerami biurkowymi jest Chrome, który będąc IE6 dla nowożytności1, potrafi przesyłać zawartość zakładki.

Co pozostaje dla ludzi z pryszczami, Firefoksem i terminalem? Bardzo dużo bardzo dobrych opcji, o dziwo!

catt

catt to program po którego opisaniu mógłbym skończyć tę notkę. Robi praktycznie wszystko, czego mogę chcieć. Potrafi odpalać strumienie serwisów, które nie wymagają autoryzacji (YouTube, Twitch, Vimeo, etc), oddaje kontrolę nad głośnością, wspiera przewijanie, listy odtwarzania, kolejkowanie, potrafi użyć wbudowanej w Chromecast przeglądarki, by wyświetlić stronę WWW, a ostatecznie odtworzyć plik z dysku lokalnego tworząc serwer HTTP i serwując klatki.

Kilka przykładowych zaklęć:

  1. catt cast https://www.youtube.com/watch?v=0cQA6d3adPs — odtworzy nam wideo znajdujące się pod tym adresem

  2. catt seek 1:00 — przesunie nas na osi czasu

  3. catt add https://www.youtube.com/watch?v=h0rSYEoBMYM — doda następny plik do kolejki (tymczasowo wspierany jest chyba tylko YouTube)

  4. catt cast_site https://fuse.pl/beton/chromecast-linux.html — wyświetli stronę

  5. catt cast Pobrane/HoliłudzkiFilm.Grupa.Nick.x264.mkv — wyświetli lokalny plik

Dostępny jest przez PyPI oraz jako źródło na GitHubie.

Na boku: znalazłem ciekawe zastosowanie dla catt cast_site. Opisywane wcześniej ttyd może zostać „rzucone” na ekran, co wyciąga nam sesję terminala. Oczywiście, nie ma jak z nią działać, ale wymyśliłem obejście tego problemu przy użyciu screen. Na początku tworzymy i odczepiamy sesję przez screen -d -m -S shared, następnie odpalamy ttyd screen -x shared, to startuje nam usługę. Używamy catt cast_site aby wyświetlić stronę ttyd. Teraz wystarczy odpalić lokalnie screen -x shared i już możemy pisać. A tam już co Wam się podoba, htop czy tail -f. Działa znośnie.

VLC

VLC potrafi odtwarzać do Chromecasta, podobnie jak catt. Z tą jednak różnicą, że umie przekodowywać pliki w locie (kosztem procesora). Jest to szczególnie przydatne w sytuacji, gdy mamy pliki „z Internetu”. AVI, mkv, mp4 z x265, rzeczy do których Chromecast nie ma kodeków. Dodałem sobie alias vlcast dla własnej wygody:

cvlc --sout "#chromecast" --sout-chromecast-ip=IP --demux-filter=demux_chromecast

mkchromecast i pulseaudio-dlna

Oba programy potrafią utworzyć sink (jak to będzie po naszemu? Przecież nie „zlew”?) PulseAudio dzięki czemu dźwięk odtwarzany lokalnie może zostać przesłany do urządzenia będącego pod kontrolą Chromecasta. pulseaudio-dlna jest dużo bardziej monotematyczny i robi tylko to, mkchromeast ma więcej funkcji, ale żadnej nie spełnia tak dobrze jak catt. Oba znajdują się w oldoldstable2, zakładam więc, że Wasza dystrybucja też je posiada.

pychromecast

Nic nie stoi na przeszkodzie żebyście sami spróbowali porobić własne zabawki. Odpowiednia biblioteka, która implementuje API Chromecasta pozwoli Wam przejąć kontrolę nad urządzeniem. Gdybym miał kiedyś zrobić info-kiosk to pewnie rozważyłbym takie rozwiązanie. Mam nadzieję, że nie będę nigdy robił info-kiosku.

pychromecast jest oczywiście na PyPI i pod postacią źródła na GitHubie.

Jak widzicie, zebranie wystarczająco dużej ilości narzędzi odpalanych z terminala powoduje, że osiągnąłem stan wyjściowy normalnych ludzi, którzy klikają w Chrome „prześlij na Chromecast”.

Chromecast

Największą wadą Chromecasta jest to, że gdy nie odtwarza treści to przechodzi w tryb „elektronicznej ramki ze zdjęciami” pokazując mi najlepsze momenty moich wypraw, zachody słońca, jelenie, lasy, reklamówki z piwami. W obecnej sytuacji zmienia to znak zapytania w zdaniu „pamiętasz, jak było fajnie” na wykrzyknik.


  1. Best viewed on IE6 in 1024x768 

  2. Nie mam serca żeby się zebrać do aktualizacji mojego głównego laptopa. Działa? Działa. Chyba, że chcę cmake z tego tysiąclecia. 


UserLAnd: chroot w kieszeni

Gdybym zeskanował teraz domową sieć trafiłbym pewnie na nie mniej niż cztery komputery z Linuksem. A jeśli starczyłoby mi gniazdek, to mogę dobić i do dziewięciu. To nie mówi zbyt wiele o samym systemie, a więcej o mojej nieumiejętności używania innego środowiska. Może to Syndrom sztokholmski, ale czuję się z tym doskonale! Dla niektórych „rok Linuksa na biurku” zaczął się w 1997.

Niedawno zapytano mnie „czy możesz nauczyć mnie Linuksa”. Trochę się tym strapiłem, bo mogę oczywiście, tylko nie wiem, co rozumieć przez „Linuksa”? Czy uczenie kogoś powłoki, potoków i pisania pomocniczych skryptów to nauka Linuksa, czy na przykład Basha? Czy stawianie serwera HTTP to nauka Linuksa? Embedded? Rzuciłem okiem na spisy treści już napisanych książek typu „Wstęp do…” i wyszło mi, że zwykle jest to książka w której autor opisuje jak okiełznuje się jego ulubioną dystrybucję.

Dla mnie pytanie o „naukę Linuksa” jest jak pytanie o „naukę młotka”. Do stolarki, budowlanki, morderstwa powodowanego pasją? Nie wiem, jak do tego podejść. Żeby nie utykać w filozofii tego pytania przeskoczyłem na grunt twardszy, jak uczyć kogoś, kto może nie ma wystarczająco zasobów i intuicji żeby mógł wybrać jedną z miliona opcji. Zainstalować na komputerze jako główny lub drugi system? Ciężko. Wirtualna maszyna? Trochę pokracznie. Raspberry Pi? Ból tyłka. WSL? Obiecujące, ale mam zero doświadczenia i jak coś się wywali to nie będę umiał odkręcić.

I wreszcie wpadłem na rozwiązanie, które mi się spodobało.

Każdy telefon z Androidem chodzi pod kontrolą kernela Linuksa i jakiś śmieci na górze, które służą do wyświetlania reklam. Teoretycznie nic nie stoi na przeszkodzie żeby „dorobić” telefonowi przedrostek GNU/. Przez lata było kilka takich projektów, nigdy nie przykładałem do nich jednak uwagi, gdyż jak wspomniałem na górze, mam dziewięć innych komputerów. Przebiegłem się po Biedronce Aplikacji i znalazłem jeden, który mi wybitnie odpowiada: UserLAnd.

Po pobraniu aplikacji i jej odpaleniu zostaniemy powitani przygotowanymi dystrybucjami, które możemy odpalić na swoim telefonie. Są to po prostu drzewa plików spakowane i gotowe do pobrania. Osobiście testowałem tylko Debiana, ale zakładam, że inne działają tak samo. Poza surowymi dystrybucjami można porać pojedyncze aplikacje, takie jak IDE Pythona, Firefoks, GIMP czy zork.

Wybór dystrybucji

Po kliknięciu rozpocznie się proces pobierania i ekstrakcji. Trzeba zwrócić uwagę na to, że pliki te trafią na miejsce z którego mogą być czytane przez inne aplikacje (albo nie: nie jestem na bieżąco z Androidem) więc składanie ważnych danych takich jak hasła, klucze SSH i dane osobiste nie jest polecane.

Konfiguracja użytkownika

Następnie zostaniemy poproszeni o podanie nazwy użytkownika i haseł do SSH i VNC. Nic prostszego.

Tworzenie połączenia

Potem deklarujemy jak się chcemy łączyć z naszą maszyną. Osobiście, co nie jest dziwne, preferuję SSH. Jak będę potrzebował okienek to zawsze mogę dodać -X.

I to wszystko. System podniesie się po chwili i możemy na niego wskoczyć. Ponieważ aplikacja nie może przypisać sobie niskich portów domyślnym jest 2022. Podczas uruchamiania sesji aplikacja będzie chciała użyć jednego z dostępnych klientów aby ustanowić połączenie na telefonie, ale możemy to zignorować. Ekran dotykowy plus magiczne | zaklęcia | z --toną --parametrów to nie jest świat w którym chcę żyć i którego i Wam nie życzę.

λ ~/ ssh emil@192.168.1.204 -p 2022
emil@192.168.1.204's password:
Welcome to Debian in UserLAnd!
emil@localhost:~$ uname -a
Linux localhost 4.9.117+ #2 SMP PREEMPT Thu Feb 13 01:10:05 CST 2020 aarch64 GNU/Linux

I już mamy „prawdziwego Linuksa” chodzącego pod kontrolą kernela dostępnego w Androidzie. Czy to metoda „lepsza” niż te, które wspomniałem wyżej? Wątpliwe. Ale wydaje się najmniej skomplikowana i inwazyjna.


Światy wytworzone

Kiedy usłyszałem jak dziecko koleżanki z pracy relacjonuje fantastycznego gola strzelonego przez Christiano Ronaldo ucieszyłem się w duchu. Było lato z turniejem piłkarskim i myślałem, że młody człowiek siedzi i ogląda zmagania na boisku, jak i ja. Nie mając specjalnego talentu do rozmawiania z dziećmi (choć kiedyś byłem dzieckiem jeśli wierzyć zdjęciom, pewnie zapomniałem co one robią w wolnym czasie) zagadałem do niego o inną spektakularną bramkę. Popatrzył na mnie podejrzliwie, coś burknął i wycofał się w kierunku matczynego biurka.

OK, nie wiem czego się spodziewałem, dyskusji o funkcji raumdeutera we współczesnym, ofensywnym futbolu? Nic to. Po pewnym czasie, łowiąc jednym uchem strzępki monologów odkryłem powód naszego braku porozumienia.

Ja mówiłem o Ronaldo z krwi i kości, a on mówił o jego podobiźnie zbudowanej z poligonów, którą kieruje grając w FIFĘ na konsoli. I to on jest tym prawdziwszym, choć w rzeczywistości to tylko faksymile Portugalczyka.

Trochę się zeźliłem, a w brzuchu zrobił mi się koktajl z ogórków konserwowych i jogurtu. Raz, że nie kumam bazy. Dwa, że jak tak można żyć z fałszywym obrazem kogoś! Kiedy wyszedłem z biura nie mogłem przestać myśleć o tym, jak dziwny to świat w którym można żyć kompletnie w oparach cyfrowych fatamorgan. Winiłem oczywiście współczesność, rozpasany konsumeryzm i głupotę dzieci, które grają w gry, których nawet nie trzeba wczytać z taśmy.

Im dłużej jednak nad tym myślałem tym bardziej dobijała się do mojej świadomości myśl w tle. I szeptała cicho Newcastle United… Newcastle United… — ale co, Newcastle? Ach.

Odkryłem nagle, czemu tak bardzo mnie ta myśl gnębiła od rana.

Przez kilka lat moim życiem zawładnęła gra komputerowa: Championship Manager (teraz: Football Manager). Był to symulator ekonomiczno-sportowy, który pozwalał się wcielić w rolę managera jednego z klubów angielskiej ligi. Gra z daleka wyglądała jak arkusz kalkulacyjny i nikt w temacie nie mógł zrozumieć czemu biały pasek wypełniający się po prawo powoduje, że wpadam z szału w ekstazę by zaraz wsiąść w pociąg powrotny do szału. Jednej wiosny postanowiliśmy z moim boiskowym kolegą że już się wystarczająco nauczyliśmy w tym roku szkolnym i możemy wziąć dwa miesiące wolnego i poświęcić się rzeczy, która jest na serio ważna: graniu od rana w Championship Managera.

Od razu był problem. Obydwaj chcieliśmy prowadzić Manchester United, który był tytanem piłkarskim tamtych lat. Kto by nie chciał być Fergusonem podwórka? Rzuciliśmy monetą i wyszło, że przegrałem. Musiałem wybrać inną drużynę. Mając nikłe pojęcie o angielskiej piłce postanowiłem przyjąć następującą heurystykę: Machester. United. Manchester City jest dziadowskie. Więc to nie pierwszy człon decyduje o sukcesie w piłce. W takim razie jasne jest, że to United jest sygnałem „to dobra drużyna”. Wybrałem więc Newcastle United.

Championship Manager

I zaczęliśmy grać. Po dwóch miesiącach okazało się, że nasze szkoły nie rozumieją potrzeby walki o ligowe tytuły i obaj zostaliśmy na tym samym roku. Co nam wybitnie pasowało, bo to znaczyło, że mamy dwa miesiące wakacji i dodatkowo mamy już wszystkie książki i zeszyty, więc nie musimy się nawet przygotowywać! Lekko 4 miesiące luzu, świetnie.

Jak pisałem wcześniej granie w Championship Managera wymagało wyobraźni. Cała gra to tylko kolumny cyfr i trochę tekstu. Ludzka natura lubi historie, uczłowieczaliśmy więc te ciągi znaków, dorobiliśmy się własnego zestawu przesądów, mieliśmy drużyny z którymi nie lubiliśmy grać. Wspominaliśmy ciekawe momenty w naszych karierach. Żyliśmy w alternatywnej rzeczywistości, gdzie nie pojedynczy piłkarz, a cała drużyna, była ofiarą naszej nieskrępowanej fantazji.

Statyski Matta Le-Tissiera

Ponieważ byłem bramkarzem szczególnie urzeczywistniałem w mojej głowie wyczyny mojego bramkarza Srok1. Któregoś dnia wyznaczyłem go do strzelania rzutów karnych. Strzelił. Dla żartu zostawiłem go jako egzekutora. I strzelił następnego. I następnego. A jego legenda rosła. Kiedy chciał zakończyć karierę próbowałem przerobić stan gry tak żeby został. Co się nie udało.

Przez lata myślałem o nim, o Hopperze, moim strzelającym karne bramkarzu Newcastle z rozrzewnieniem. Tyle wspaniałych wspomnień. I gdy odbiłem się od katalizującego incydentu z Ronaldo i synem koleżanki zdałem sobie sprawę, że… ja nie wiem nawet jak Hopper wygląda. Doszedłem do domu, usiadłem do komputera. OK, jest, widzę. Strasznie mało coś o nim. I w trzecim linku na Google czytam, co następuje.

Hooper started with a clean sheet against West Ham. But things soon declined as he was beaten by a long-range last- minute free-kick by Matt Le Tissier in a 2-1 loss.

Newcastle United’s Worst XI: Worst goalkeepers

Okazał się, że Hopper nigdy nie był bohaterem, nigdzie, poza moją głową. Był kiepskawym bramkarzem, który wyleciał z startowej jedenastki i praktycznie nigdy niczego nie dokonał. Ja się oburzyłem na kogoś, kto strzela bramki w FIFA, a sam mam 12 sezonów fałszywych wspomnień, które sobie opowiedziałem patrząc w kilka rubryczek. I kiedy on symuluje człowieka sukcesu odnosząc sukces, ja wymyśliłem człowieka sukcesu z piłkarskiego nikogo.

Kto jest bez winy niech pierwszy rozpocznie grę długim wyrzutem.


  1. „Sroki” to przydomek Newcastle United 


Szybkie życie

W Internecie bulgocze mem o tym, jak to w czasach zarazy mądrzy ludzie dokonali wielkich czynów i jakie ważne dzieła wyprodukowali. Oczywiście, jak w każdym inspirational porn pomijane są pewne fakty. Na ten przykład twórcy wspaniałych rzeczy nie wdzierali się na intelektualną scenę, a byli na niej już od dawna. Albo to, że nie musieli się w większości przejmować czynszem, zamkniętą szkołą, ani też doświadczać pierwszy raz zdalnej pracy.

Ja celuję dużo niżej. Każdemu jego dzieło w miarę możliwości.

Znudzony i niechętny pracy kręciłem się tu i tam, szukając ciekawego zajęcia. Wyciągnąłem stare numery „Młodego Technika”. Przeczytałem kawałek tekstu o tym jak działają świece zapłonowe. Nie było ognia w moim sercu. Wyciągnąłem Tołstoja, ale na pierwszej stronie było od razu pięć długich, dwuczłonowych rosyjskich nazwisk i wiedziałem, że nie ma szans żebym się odnalazł w tym bez tablicy korkowej i czerwonych sznurków do utworzenia relacji pomiędzy bohaterami historii.

Zastanawiałem się jak wygląda miasto. Od tygodnia wyszedłem z domu trzy razy. Na 5 minut. Dokupić alkoholu i jajek. Jajka jem na śniadanie, alkohol na kolację. Te dumania przypomniały mi o zapomnianej dziś instytucji „publicznych webcamów”. Kiedyś były bardzo popularne, ludzie oglądali obce miasta, kurze fermy, małpy, psy i co jeszcze komu wpadło do głowy. Wpisałem więc frazę w wyszukiwarkę i po chwili widziałem swoją okolicę. Popatrzyłem chwilę jak pan chodzi z miejsca w miejsce, zaparkował samochód, a później odjechał. Pan, śledzony nieustępliwie przez śmieciarkę na światłach, opróżniał kubły. Świat istniał, choć nie wyglądał ciekawie.

Pomyślałem, że mógłbym sobie rzucić to wideo na mały, dziesięciocalowy telewizorek, który stoi w kącie mojego biurka i zabawia mnie wyświetlając anime z lat 80 kiedy pracuję. Stworzyłbym sobie wirtualne okno na ulicę.

Biurko

Ostatecznie, gdzieś tam pod spodem, jest pewnie jakiś strumień wideo, który można zawinąć.

Poszedłem sprawdzić i oczywiście, jest jakiś. Po pierwsze lista M3U, po drugie powtarzający się cykl fragmentów wideo. Dla testu skopiowałem jeden z linków i podałem do wget. Coś się ściągnęło. mplayer odtworzył 2 sekund wideo z kamery. OK, czyli to tak działa. Wideo jest serwowane w tej serii plików i po osiągnięciu ostatniego, 92 fragmentu, lista zawraca do pierwszego, oznaczonego 42.

Dobra, a czy da się je jakoś seryjnie ściągnąć? Znaczy, na pewno się da, ale tak żebym potem mógł coś z tym zrobić? Otworzyłem nową powłokę terminala i wydałem klasyczne zaklęcie mkdir, git init, vi.

Wpierw wymyśliłem bardzo skomplikowany sposób pobierania tego, z wątkami, sprawdzaniem ETag i kto wie, co jeszcze. A potem uznałem, że głupia metoda jest równie dobra, a doprowadzi mnie do sukcesu szybciej.

Czyli tak:

  1. Wezmę wszystkie kawałki klipów w momencie odpalenia
  2. Zapiszę je sobie na boku
  3. Zobaczę ile czasu mi to zajęło i odejmę od maksymalnej długości wszystkich klipów
  4. go to 1
import requests # klient HTTP
from time import sleep # chcemy pauzować

# wpisany na twardo adres z którego będę pobierał informacje
URL = 'https://cdn-3-go.toya.net.pl:8081/kamery/lodz_piotrkowskaschillera_0{}.ts'

# formad do zapisywania, index poprzedzony dopełnieniem 12 zer
OUTPUT = 'output/{:012}_frame.mpg'

""" Żeby się było łatwiej kręcić, wartości [42…91] w liście """
frames = range(42, 92)

idx = 0
while True:
    # Cała lista otwarzania ma (92-42)*2 sekund 
    rotation_time = len(frames) * 2

    #Kiedy zaczynamy proces
    start_time = datetime.now()

    for frame in frames:
        print("Grabing {}".format(frame))
        # Klasyczny HTTP GET / i zapisanie pliku docelowego
        response = requests.get(URL.format(frame))
        if response.status_code == 200:
            with open(OUTPUT.format(idx), 'wb') as f:
                f.write(response.content)
            idx = idx + 1
    # Jak przelecieliśmy wszystkie ramki to czekamy resztę sekund, aż 
    # się wszystkie podmienią
    after_downloading = datetime.now()
    remaining_seconds = rotation_time - int((after_downloading - start_time).total_seconds())
    print("Clips fetched, sleeping for {}".format(remaining_seconds))
    # …i do łóżka
    sleep(remaining_seconds)

Udało się za pierwszym razem. Pokombinowałem jeszcze z nazwami i takimi tam, ale wyglądało na to, że całość ściągnęła się bez problemu. No dobra, ale co z tym teraz zrobić? Chwilowo nie miałem pomysłu, więc sklonowałem repozytorium na lodówkową RaspberryPi i pozwoliłem się jej kręcić, naciągnąć mi nieco danych.

Minął czas, a ja wymyśliłem. Zrobię tzw. timelapse czyli przyśpieszone wideo z źródłowych fragmentów. Mam w vimwiki całą stronę poświęconą rozmaitym zaklęciom, którymi można przymuszać ffmpeg do robienia fantastycznych rzeczy.

Kiedyś spędziłem wieczór na brzegu zalewu żeby zamienić 6 minutowy filmik z zachodu słońca w taki, który byłby ustabilizowany, krótszy i jeszcze miał muzykę. Wyzwanie: tylko przy użyciu połączenia SSH do domu. Wyszło OK, ale nie spektakularnie.

Nie było sensu zaprzęgać do tego Pythona, wsadziłem całość w prosty skrypt Basha. 1

#!/bin/bash

# Wczytaj z katalogu output wszystkie pliki *.mpg
for file in output/*.mpg ; do
    # Weź obecną nazwę, utnij ją przy kropce, dodaj png
    new_file=`echo $file | cut -d. -f1`.png
    echo $new_file
    # Weź pierwszą klatkę animacji i zapisz pod nową nazwą
    ffmpeg -i $file -vf "select=eq(n\,0)" -vframes 1 $new_file
done

# Zakoduj nowopowstałe klatki w mp4/h264 żeby się przeglądarki nie czepiały
ffmpeg -pattern_type glob -i 'output/*.png'  -an -vcodec libx264 -pix_fmt yuv420p -profile:v baseline -level 3 out.mp4

Po kilku testach postanowiłem, że osiągnąłem znośny efekt (mimo kilku momentów w których uzyskałem duplikaty serii klatek. Mogłem usunąć to post factum ale jak mówił Bob Ross, “There are no mistakes, just happy accidents“), wysłałem wszystko na RaspberryPi i postanowiłem dać się całości kręcić przez noc.

Wysłałem jeszcze e-mail do mojego przyjaciela Łukasza żebrząc o jakiś utwór muzyczny z mojej ulubionej płyty jego projektu, Coffee Sounds. Na moje szczęście zgodził się, za co chcę mu jeszcze raz podziękować.

Wstałem o piątej, zebrałem 17200 klatek i oto przed Wami efekt bezcelowej, nocnej dłubaniny. Łódź, Piotrkowska, Pasaż Schillera. Od 22:30 do 8:00.

  • :15 zaczyna padać deszcz, ulice się świecą odbitym światłem latarni
  • :37 „Do Poloneza? Ale Panie Władzo, ja nie tańczę”
  • 3:15 wschód słońca

Te, zdecydowanie zbyt rzadkie, momemty w moim życiu gdy zamieniam „czy da się?” w „da się” przy pomocy śliny, mchu i oglądania problemu przez palce sprawiają mi zawsze najwięcej radości.

Polecam Wam zaprzęgać komputery do bezużytecznej, kreatywnej pracy. Komputer jako odpalarka do przeglądarki to takie marnotrastwo nieomal nieograniczonej mocy.

Czas szybciej płynie, gdy dłubiesz. Lokatorzy więzień próbujący wykonać podkop też Wam to powiedzą.


  1. Potem trochę pozmieniałem rzeczy, całość dostępna jest w repozytorium git 


ttyd

W dzieciństwie jednym z moich ulubionych formatów kawałów był ten o magicznej istocie, która spełnia życzenia w sposób dosłowny, obracający pragnienie petenta w jego nieszczęście.

Żołnierz poszedł łowić do pobliskiej rzeki i zdziwił się, gdy dyndająca na końcu żyłki ryba przemówiła: „Oszczędź mnie, a spełnię Twoje życzenie”. Długo się nie zastanawiał i powiedział rybce: „Chcę być odznaczony ważnym orderem”. Puf! Gdy dym — jasny sygnał spełnionego zaklęcia — opadł, żołnierz zauważył, że stoi w okopie. Po lewej flance przedzierają się nieprzyjaciele, po prawej to samo. A frontem czołgi i karabiny maszynowe. Słyszy hasło „Naprzód!” i ściana kamratów popycha go do walki. Biegnąc myśli, „No kurwa, pośmiertny dała”.

Należy pamiętać żeby uważać czego się pragnie. Spełnienie może być gorsze niż pragnienie. Bo weźmy na przykład jedno z pragnień w sferze technologii: żeby napisać aplikację raz i odpalać ją na wielu różnych systemach. Przez dekady podejmowane były różne próby. A to systemy, które są kompatybilne na poziomie źródeł w stopniu wystarczającym by proces portowania był niezbyt bolesny (C/PM, POSIX), albo platformę sprzętową (MSX, czy potem x86), albo maszyny wirtualne i bytecode (Z-Machine czy JVM).

Jak się to wszystko powiodło? Trudno powiedzieć, to sytuacja jak z za krótką kołdrą. Jednym marzł nos, innym stopy, ale ogólnie zgadzano się, że kołdra jest dobrym pomysłem i któregoś dnia uda nam się uszyć taką o zadowalającej długości.

I wtedy ktoś poszedł wyłowić tę złotą rybkę siecią. I rybka dała nam uniwersalną platformę aplikacji: przeglądarkę internetową.

Ludzie często błędnie rozumieją ewolucję, myślą, że obecny stan wynika z jakiegoś planu, a nie tylko wypadkowych czynników, które promują jakąś właściwość nad inną. Tak samo w przypadku przeglądarki jako platformy aplikacji rzeczy nie rozwijały się pod okiem bogów, a były wypadkową dostępności, pewnej wolności, ceny i kilkuset sprytnych gości, którzy ciągnęli kołdrę w kierunku własnym, czyli słusznym. Tak jak w przypadku ewolucji organizmów prowadziło to czasem do dziwnych rezultatów: nasze oczy widzą świat do góry nogami i potem kradną cykle mózgu na obracanie obrazu, jest też JavaScript.

Nie da się ukryć, że jestem internetowym sceptykiem. To naturalna droga, którą przebywa każdy entuzjasta, gdy okazuje się, że „jego dziecko” pali za szkołą, przynosi same jedynki i nie chce zostać filozofem etyki medycznej. Głębokie rozczarowanie równe pokładanych nadziei.

Większość dnia, a już z pewnością jego produktywną część, spędzam ślepiąc w terminal tekstowy. Znajduję spokój w środowisku, które pokornie czeka mrugając na mnie zalotnie kursorem, aż powiem mu czego chcę. Proste aplikacje, które komunikują się tekstem. Kontrola, automatyzacja i surowa estetyka. I prawie ezoteryka, którą ciężko jest wytłumaczyć ot, tak.

A ten przydługi wstęp ma służyć wyłącznie temu żebym Wam mógł powiedzieć o moim odkryciu, które cieszy me serce i być może ucieszy Wasze. Jest to ttyd, serwer podający terminal przez przeglądarkę.

ttyd to serwer napisany w C, który serwuje jednostronicową aplikację opartą o xterm.js i WebSockety. Jest trywialna w użyciu i działa spektakularnie dobrze. Próbowałem praktycznie wszystkiego, czego używam na terminalu, nawet najbardziej fikuśnych aplikacji. Wystarczy wpisać na terminalu:

ttyd -p 3000 aplikacja

…i już na http://localhost:3000 mamy uruchomiony wirtualny terminal z którego możemy nią operować. Wspiera SSL, BASIC AUTH, autoryzację certyfikatami (bardzo przydatne w pewnych okolicznościach), tryb „tylko do odczytu”, pojedyncze i wielokrotne sesje.

Po co?! O dziwo znalazłem wiele zastosowań!

Po pierwsze używam tego do moich różnych eksperymentów z RaspberryPi. Nie wszyscy mają pod ręką klienta SSH i wystarczająco samozaparcia by zmieniać konfigurację, ale wystarczy, że powiem im „kliknijcie na ten adres i wpiszcie informacje”, podłączę skrypt w powłoce pod ttyd i voilà. I tak, mógłbym napisać webaplikację, ale zapewniam Was, że nawet używając mikroframeworku zejdzie mi dłużej dłubiąc HTML niż kilka razy wywołać input().

Po drugie, sytuacja podobna, często czytam logi używając tail -f, w połączeniu z trybem „tylko do odczytu” mogę go udostępnić ludziom, którzy mają może potrzebę zerknięcia, ale nie muszą mieć dostępu do systemu.

Po trzecie, to bardzo dobrze zbudowane narzędzie i strasznie mi się podoba. To będzie mój finalny i rozstrzygający argument.

A teraz czas na część wizualną dla tych, którzy przebrnęli przez te ściany tekstu. Uruchomię ttyd, które uruchomi browsh czyli terminalową przeglądarkę, która używa Firefoksa jako silnika, w Firefoksie. Pamiętajcie, „…bo mogę” to nie powiedzonko, to styl życia.


Klik, klik. Dziadek znikł.

Zdjęcie gór

Robiła się późna niedziela, a to znaczyło, że muszę powoli wyrwać się z ciszy otaczających mnie gór i wrócić na łono ogarniętego świątecznym amokiem miasta. Ciągnąłem więc nogę za nogą i oddawałem się mojej pasji oglądania lokalnego życia. Natrafiłem na przykład na szyld, który nie tylko oferował wideofilmowanie, ale także usługę przegrywania kaset VHS na płyty DVD. Jak przepisywanie kamiennych tablic na papirus. To zawsze napełnia mnie radością, że mogę uciec w miejsca, gdzie technologiczny postęp jest na chorobowym.

Jak większość górskich dróg ta też była pełna dzikich zakrętów, a przy jednym z nich zauważyłem kolejny szyld: „zakład pogrzebowy”. Uśmiechnąłem się do siebie: taki ostry zakręt i zakład pogrzebowy to doskonałe miejsce na skecz o tym jak klient dosłownie sam stuka do drzwi biznesu.

Pokonałem zakręt i co widzę na jego końcu. Drugi zakład pogrzebowy, po przeciwnej stronie ulicy. Jak stacje benzynowe obsługujące wielopasmową autostradę. To spowodowało, że rozbudowałem moje gdybania: jak dwa zakłady pogrzebowe znajdują wystarczająco biznesu dla osady, która ma cztery tysiące dusz?1

Wykluczając scenariusz taniego horroru w którym oba zakłady pogrzebowe wynajmują morderców, którzy są kołem zamachowym biznesu, zacząłem gdybać jak funkcjonuje tak specyficzny biznes w obliczu konkurencji. Czy drzwi do gabinetu lokalnego dyrektora szpitala zamykają się non-stop za właścicielami tych zakładów, trzymających pod pachą coraz to wykwintniejsze butelki alkoholi? A może kosze owoców.

I jak ja, adept współczesnego świata, mógłbym im pomóc w polepszeniu „lejka konwersji” trupa w przelew.

Na początek należy zacząć od rzeczy prostych. Reklama na Facebooku dla targetu, zgromadzić marketingowe dane o umieralności, posegregować w grupy docelowe: ludzie, którzy umierają normalnie, ze starości. Ludzie, którzy konsumują alkohol aby zapomnieć. Operatorzy maszyn rolniczych zakończonych ostrzami. Zaoferować zniżki dla tych, którzy wybiorą usługi zanim utracą zdolność decydowania.

Potem trzeba przejść do argumentów cięższych. Zakupienie danych o zdrowiu i nawykach od firm, które inwigilują obywateli w ramach „darmowych usług”. Od producentów elektronicznych krokomierzy przez producentów urządzeń podsłuchowych, które potrafią powiedzieć jaka jest pogoda, co pozwala inwigilowanym na zaprzestanie wyglądania przez okno. Tak uzyskane informacje, wielka-mała data, nadaje się idealna do wsadzenia do paszczy modeli sztucznej inteligencji. Wyłuskane dane trzeba skorelować z już istniejącymi informacjami o denatach i wejść w biznesowy układ z okolicznymi sklepami aby promować zachowania, które są denatogenne.

Hasła jak „Na co ci fura, pij denaturat”, „Siała baba mak i zrobiła kompot dla dzieci” oraz „Bądź eko, usuwaj odpady, pal oponami” powinny pomóc we wzroście.

Nie można też zapominać o klientach drugiego stopnia, tj. rodzinie i bliskich, którzy pozostają po zmarłym. Mobilna aplikacja pozwalająca na zatrudnienie aktorów w rolach „płaczek”, wybierania im strojów i ról, zatrudnienie blogera do napisania przemowy kaznodziei, nagrobek z NFC pozwalających odhaczyć się rodzinie wraz z tabelą wyników. Mikropłatności za duszę przez BLIK. Streaming dla tych, którzy wyjechali za chlebem do San Francisco.

To wszystko zrodziło się w mojej głowie jako cyniczna obserwacja bezdusznej maszyny, która pożera ludzkie interakcje i nas samych, dlatego też jestem pewien, że ktoś właśnie siada do pisania biznesplanu i wysyła e-maile do znajomych kapitalistów wysokiego ryzyka. Do tej osoby: proszę mi odpalić z 5% żebym mógł dłużej siedzieć w lesie i snuć okropne wizje. Gwarantuje, że mam jeszcze paskudniejsze pomysły, które da się zrealizować.


  1. Ponieważ wróciłem znów w to samo miejsce udało mi się znaleźć trzeci zakład pogrzebowy. Skala przemysłowa.