Mapowanie klawiszy w macOS bez Karabiner Elements – HOWTO

Karabiner Elements to popularne narzędzie do mapowania klawiszy na macOS. Narzędzie jest wygodne, ale ma parę problemów. Trzeba instalować oddzielny program, były ostrzeżenia o legacy system extension, które opisywałem przy okazji opisu upgrade. Nie testowałem, ale podobno nie działa po upgrade do Big Sur.

Większość znanych mi ludzi wykorzystuje Karabiner Elements do prostego celu: zamiany prawego option z prawym command. Wszystko po to, żeby wygodnie, czyli tak samo jak na PC, wpisywać polskie znaki diakrytyczne. Robiłem tak i ja, a program był w ogóle zupełnym must have na macOS.

Mapowanie klawiszy w macOS bez Karabiner Elements - obrazek
Źródło: wygenerowane za pomocą https://thumbnail.ai/

Ponieważ znalazłem dziś kolejną osobę, która nie wiedziała, że się da, a wykorzystywała właśnie w tak prosty sposób, podzielę się sposobem, który sprzedał mi znajomy z pracy (dzięki J!). Rozwiązanie nie wymaga dodatkowych programów, wystarczy wbudowane oprogramowanie systemowe.

hidutil property --set '{"UserKeyMapping":
[{"HIDKeyboardModifierMappingSrc":0x7000000e7,
  "HIDKeyboardModifierMappingDst":0x7000000e6}]}'

Powyższe polecenie spowoduje, że klawisze zostaną przemapowane tymczasowo, do restartu systemu. Aby osiągnąć ten efekt na stałe, tworzymy plik ~/Library/LaunchAgents/mapkeys.plist o zawartości:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.nanoant.KeyRemapping</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/hidutil</string>
        <string>property</string>
        <string>--set</string>
        <string>{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x7000000e7,"HIDKeyboardModifierMappingDst":0x7000000e6}]}</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Oczywiście rozwiązanie można stosować także do bardziej skomplikowanego mapowania klawiatury. Jednak jak wspominałem na początku, większości użytkowników powinien wystarczyć ww. gotowiec. Po takim zabiegu, skoro mamy działającą aleternatywę, można odinstalować Karabiner Elements zupełnie.

UPDATE: Przydatne linki
Generator mapowania
Gotowa do użycia wersja online

Delegalizacja noży

Czy noże są legalne? Odpowiedź jest nieoczywista, w przypadku noży fizycznych i różni się mocno między państwami. Wygląda na to, że delegalizacja noży to etap do którego dotarła nasza cywilizacja. Przynajmniej w przypadku oprogramowania. Noże są metaforą, chodzi o narzędzia w postaci oprogramowania.

Tym razem wirtualnym nożem został program youtube-dl. W całej sprawie chodzi o zamknięcie jego repozytorium na GitHubie po otrzymaniu niedawnego żądania usunięcia na podstawie DMCA. Polecam lekturę, mi uzasadnienie wydaje się ciekawe – oprogramowanie służy do omijania zabezpieczeń i robienia nieautoryzowanych kopii. I rzekomo posłużyło do zrobienia nieautoryzowanych kopii artystów zrzeszonych w RIAA. W to ostatnie akurat jestem skłonny uwierzyć.

Zastanawiam się jednak nad pierwszą częścią. Pobranie z YouTube jest możliwe przy pomocy wielu narzędzi. Potrafi to każda przeglądarka internetowa. Kopię można zrobić smartfonem. O masie oprogramowania dedykowanego do odtwarzania multimediów, w tym z YouTube nie wspominam. Poza tym, w grę wchodzi dowolne oprogramowanie do rejestracji obrazu, nagrywania dźwięku czy nagrywania pulpitu. Tak wiele noży do zdelegalizowania…

Zresztą, celem umieszczania materiałów na YouTube jest udostępnianie ich publicznie. Szczególnie jeśli nie są ustawione ograniczenia widoczności, czyli materiał jest publiczny. A może znowu chodzi o reklamy i ich omijanie? Bo przecież narzędzie, czyli nóż może służyć do różnych celów. Na przykład do krojenia chleba. W przypadku youtube-dl mogę zrobić kopię własnych materiałów, które umieściłem na YouTube. Albo pobrać na dysk – legalnie w świetle polskiego prawa – materiały do ich odtwarzania bez zużywania transferu przez komórkę.

Sprawa jest głośna i zastanawiam się, jak się skończy ta próba delegalizacji noży. Czy usunięcie miało charakter automatyczny i zostanie wkrótce przywrócone? Całość jest ciekawa tym bardziej, że w grę chodzi GitHub jako miejsce do rozwijania oprogramowania. Jeśli usunięcie repozytorium zostanie utrzymane, to mogą być tu spore ruchy – ludzie mogą zacząć migrować na inne platformy. Smaczku sprawie dodaje fakt, że właścicielem GitHuba jest Microsoft, a YouTube Google.

UPDATE Program nadal można pobrać ze strony projektu, jest też dostępny w repozytoriach dystrybucji Linuksa.

UPDATE 2: O sprawie zdjęcia youtube-dl z GitHuba w kontekście legalnych zastosowań pisało EFF. Jest też artykuł w serwisie Torrentfreaks o odpowiedzi w postaci zamieszczania mp3. Oraz poruszona kwestia przeglądarek. Polecam.

UPDATE 3: Pojawił się ciekawy wpis na blogu pierwotnego autora youtube-dl w którym pisze o genezie projektu i realiach. Zachęcam do lektury.

UPDATE 4: Trwało to dłuższą chwilę, ale wygląda, że repo youtube-dl powróciło. Decyzja do przeczytania tutaj. Dłuższe uzasadnienie stanowiska GitHub. I zamierzają zmienić proces obsługi DMCA na bardziej przyjazny developerom.

711 wyrazów o optymalizacji – część 3

Była część pierwsza i część druga, pora na kolejną, niezupełnie planowaną. Jak pamiętamy w części drugiej udało się ograniczyć sprawdzane liczby do 49 sztuk. To, co chodziło mi od czasu do czasu po głowie to pytanie, czy da się rozwiązać tę zagadkę „na piechotę”, bez użycia komputera?

Rozejrzałem się za możliwymi uproszczeniami i zauważyłem kolejne potencjalne pole do optymalizacji, czyli zmniejszenia liczby potrzebnych obliczeń. Jak wiadomo, 711 jest liczbą nieparzystą. Aby suma dwóch liczb była nieparzysta, jedna z nich musi być parzysta, druga nieparzysta. Z kolei aby suma dwóch liczb była parzysta, albo obie muszą być parzyste, albo nieparzyste. Tu mamy do czynienia z sumą czterech liczb, więc są dwa przypadki. Albo jedna z liczb jest nieparzysta, a trzy są parzyste, albo odwrotnie.

Z naszych 49 liczb, 37 jest parzystych, a 12 nieparzystych. Jak to wpływa na przestrzeń rozwiązań? Z 49^4, czyli ok. 5,8 mln przechodzimy na 12*37^3 + 37*12^3 czyli ok. 672 tys. Nadal trochę dużo jak na ręczne liczenie, ale jak to wpłynie na czas obliczeń? Nasz skrypt będzie miał postać:

number = 711
iterations = 0
divs_odd = list()
divs_even = list()
for i in range(1, round(number/2) + 1):
    iterations += 1
    if 711000000 % i == 0:
        if i % 2 == 0:
            divs_even.append(i)
        else:
            divs_odd.append(i)

for a in range(0, len(divs_even)-1):
    for b in range(a, len(divs_odd)-1):
        for c in range (0, len(divs_odd)-1):
            for d in range (c, len(divs_odd)-1):
                iterations += 1
                if divs_even[a] + divs_odd[b] + divs_odd[c] + divs_odd[d] == 711:
                    if divs_even[a] * divs_odd[b] * divs_odd[c] * divs_odd[d] == 711000000:
                        print("Solved: ", divs_even[a], divs_odd[b], divs_odd[c], divs_odd[d], iterations)
                        exit()

for a in range(0, len(divs_odd)-1):
    for b in range(0, len(divs_even)-1):
        for c in range (b, len(divs_even)-1):
            for d in range (c, len(divs_even)-1):
                iterations += 1
                if divs_odd[a] + divs_even[b] + divs_even[c] + divs_even[d] == 711:
                    if divs_odd[a] * divs_even[b] * divs_even[c] * divs_even[d] == 711000000:
                        print("Solved: ", divs_odd[a], divs_even[b], divs_even[c], divs_even[d], iterations)
                        exit()

Niezależnie od kolejności bloków (najpierw 1 liczba parzysta i 3 nieparzyste, co jest teoretycznie korzystniejszym wariantem, czy odwrotnie), potrzebować będziemy poniżej 95 tys. iteracji. Czas to 0,08 sekundy dla zwykłego interpretera Pythona lub 0,12 sekundy dla Pypy.

Możliwa jest też wersja „w dół”:

number = 711
iterations = 0
divs_odd = list()
divs_even = list()
for i in range(1, round(number/2) + 1):
    iterations += 1
    if 711000000 % i == 0:
        if i % 2 == 0:
            divs_even.append(i)
        else:
            divs_odd.append(i)

for a in range(len(divs_even)-1, 0, -1):
    for b in range(len(divs_odd)-1, 0, -1):
        for c in range (b, 0, -1):
            for d in range (c, 0, -1):
                iterations += 1
                if divs_even[a] + divs_odd[b] + divs_odd[c] + divs_odd[d] == 711:
                    if divs_even[a] * divs_odd[b] * divs_odd[c] * divs_odd[d] == 711000000:
                        print("Solved: ", divs_even[a], divs_odd[b], divs_odd[c], divs_odd[d], iterations)
                        exit()

for a in range(len(divs_odd)-1, 0, -1):
    for b in range(len(divs_even)-1, 0, -1):
        for c in range (b, 0, -1):
            for d in range (c, 0, -1):
                iterations += 1
                if divs_odd[a] + divs_even[b] + divs_even[c] + divs_even[d] == 711:
                    if divs_odd[a] * divs_even[b] * divs_even[c] * divs_even[d] == 711000000:
                        print("Solved: ", divs_odd[a], divs_even[b], divs_even[c], divs_even[d], iterations)
                        exit()

Ilość potrzebnych iteracji waha się od 18 do 28 tys. w zależności od kolejności bloków. Natomiast czas wykonania to 0,06 sekundy dla zwykłego interpretera Pythona i 0,1 sekundy dla Pypy.

Nadal nie jest to optymalizacja powodująca, że da się policzyć „na piechotę”, ale… coraz bliżej.