Dzisiaj, po dłuższej tym razem przerwie, *funkcje*
A w zasadzie to, co w fvwm nazywane jest "complex functions". Czyli nie jakieś gotowe polecenia, ale zestawione samodzielnie listy poleceń. To, co każdy lubi w WindowManagerach i co każdy uważa za funkcjonalność bez której absolutnie nie można się obejść ;)
Funkcje definiuje się, i dodaje do nich kolejne pozycje, za pomocą "AddToFunc". Składnia jest, o dziwo, bardzo prosta i nie ma tym razem 40+ dodatkowych modyfikatorów ani opcji.
AddToFunc foo + X bar + X shmoo + X slrnrnkhrn
Pierwsza linia to wywołanie "AddToFunc", "foo" to nazwa funkcji. Jeśli taka funkcja już istniała, to po prostu kolejne polecenia zostaną doklejone do jej zadka.
Następnie podążają znane już "+ ", zawsze z dwoma argumentami - pierwszy to modyfikator działania "X", a drugi to nazwa polecenia/funkcji które należy wywołać. Oczywiście wywoływane polecenie/funkcja często będzie się składało z więcej niż jednego słowa, jak np. "GotoPage 0 0". Chwilkę trzeba poświęcić na "modyfikator X". Przyjmuje on jedynie kilka wartości: "I", "H", "C", "D", "M". Sterują one sposobem wykonania polecenia w obrębie funkcji, a w zasadzie to decydują o tym, czy funkcja się wykona.
"I"
oznacza "Immediate" - polecenie zostanie bezwarunkowo wykonane. Najpopularniejszy modyfikator.
"C"
oznacza "Click". Mysz musi kliknąć. A konkretnie, to jest to Click+Release. Różnica istnieje przede wszystkim w odniesieniu do "I". Podczas gdy "I" wykona funkcję od razu po naciśnięciu myszy, to "C" wykona funkcję dopiero po puszczeniu klawisza. Ma to przeróżne zastosowania, z czego jedno pokażę za chwilę...
"H"
oznacza "Hold". Polecenie zostanie wykonane dopiero wtedy, gdy klawisz/przycisk myszy zostanie przytrzymany określoną długość czasu. Coś jak "tester cierpliwości". Moim zdaniem to bardzo ciekawy modyfikator, którego prawie nikt nie wykorzystuje (nie, ja też nie ;). Ale szybki przykład, żeby pokazać co można tym zdziałać:
AddToFunc xxx + H menu WindowOpsMenu + C Iconify
Czyli: jeśli "H", to pokaż menu o nazwie "WindowOpsMenu". Jeśli "C", to wywołaj "Iconify". Teraz tylko przybindować to do odpowiedniego przycisku w dekoracji "mozilla".
Mouse 1 4 N function xxx
Przycisk myszy pierwszy, na 4. przycisku okna, bez modyfikatorów (None), wywołuje zdefiniowaną wcześniej funkcję "xxx". Tutaj szybka notka - słówko "function" można sobie darować, fvwm normalnie i tak sprawdzi, czy chodzi o wbudowane polecenie, moduł czy funkcję. Ale używając "module" i "function" można zaznaczyć o co nam chodzi. To by oznaczało, że teoretycznie możliwe jest dublowanie się wpisów w namespace, czyli można by mieć moduł o nazwie identycznej co jakaś funkcja...
Dobra, mamy naszą funkcję podczepioną pod Mouse1 na 4. przycisku okna (iconify). Teraz test - pojedyncze kliknięcie ikonifikuje, jak dawniej, ale jeśli przytrzyma się przycisk myszy, to po pewnym czasie wyskoczy WindowOpsMenu (zakładając, że je wcześniej zdefiniujemy). Jak dla mnie - bomba. Można mieć np. menu z zaawansowanymi opcjami dotyczącymi maksymalizacji czy zamykania okienek wygodnie podczepione pod przyciski, nie zmieniając wcale ich dotychczasowej funkcjonalności. Gdyby zamiast
+ C Iconify
było
+ I Iconify
to funkcja by była bezużyteczna, gdyż "I" by wykonywało swoje zadanie od razu po wciśnięciu przycisku, nie czekając na żadne "release" czy "hold. Ale wracam już do głównego tematu...
"D"
DoubleClick. Do wykonania opcji konieczny jest podwójny klik. Przypuszczam, że to też da się pogodzić z "hold", choć już zapewne ożenienie tego (sensowne) z "C" by było nieco trudniejsze.
"M"
Motion. Ta regułka się aktywuje gdy mysz zacznie się ruszać. Zwykle używana w funkcjach typu "Raise-or-Move" czy "Raise-or-Resize" przybindowanych do paska tytułowego czy narożników okna.
Acha, do regulowania czasu oczekiwania na "H" służy globalne polecenie ClickTime, np. "ClickTime 1500" ustawi czas oczekiwania na 1.5s (1500ms). Jak ustawić DoubleClickTime - nie wiem. Nie ma takiego polecenia. Znowu niekonsekwencja. Być może wykorzystuje po prostu jakąś prostą wielokrotność ClickTime. Zresztą, chyba nie trzeba specjalnie się starać i ustalać sobie DoubleClickTime, przynajmniej ja zawsze pasowałem w ten "komputerowy kanon podwójnego kliku"...
Naszej funkcji można przekazywać parametry przy wywołaniu. Są one dostępne w ciele funkcji przez zmienne $0..$9, oraz OIDP $*, odpowiadającego shellowemu $@ - czyli liście wszystkich parametrów.
Do AddToFunc istnieje "parka" - DestroyFunc. Jak nietrudno zgadnąć, kasuje ona funkcję. Tutaj notka: Do dobrych nawyków, jak już wspominałem przy opisie menu, należy profilaktyczne zniszczenie obiektu przed jego stworzeniem. Ot, tak na wszelki wypadek. Czyli:
DestroyFunc xxx AddToFunc xxx + I ...
może to nadmiar paranoi, ale naprawdę zdarzają się dni, gdy tradycyjna czapeczka z folii aluminiowej na głowie nie wystarcza. W takie dni warto sięgnąć po DestroyFunc. I to by było wszystko co dotyczy funkcji jako takich. W manualu są dodatkowe informacje na temat pewnych specjalnych funkcji, które są wykorzystywane przez fvwm. Np. "InitFunction" wykonywana przy inicjalizacji fvwm. Taka funkcja nie musi istnieć, ale jeśli przypiszemy do niej np. "+ I Exec xterm", to przy starcie zostanie automatycznie otworzony xterm. To oczywiście najprostsze co przyszło mi do głowy...
No. To by było z głowy... O, to może od razu wspomnę o innych dostępnych w funkcjach (i nie tylko) zmiennych "specjalnych": $c - Class (dostępne oczywiście tylko w kontekście okna), $d - numer biurka, $. - absolutna ścieżka określająca położenie aktualnie wczytywanego pliku (konfiguracyjnego lub za pomocą polecenia Read i pewnie PipeRead), $w - windowId, $[vp.height] - wysokość viewportu (zwykle to samo, co pionowa rozdzielczość ekranu), $[w.x] - koordynaty poziome narożnika okna, $[cw.x] - to samo, tyle że po odliczeniu dekoracji fvwm, $[pointer.y] - pozycja myszy (na osi Y), $[pointer.wy] - to samo, tyle że względem aktywnego okna, itd. itp. Nie są to może oszałamiające ilości danych, ale i tak sporo ich w manualu.
O, i teraz coś przydatnego: Instrukcje warunkowe. Język skryptowy fvwm jest bardzo prymitywny, i na próżno doszukiwać się w nim tego, co oferował sawfish swoim dialektem lispa. Ale jest kilka poleceń, które można ewentualnie podciągnąć pod instrukcje warunkowe. Choć klasycznego if..then nie ma :)
All [warunki] polecenie
Wykonuje <polecenie> na wszystkich oknach spełniających określone warunki. Warunek w najprostszym wariancie jest nazwą okna/klasą okien. Np. "All [gimp] iconify" spowoduje ikonifikowanie wszystkich okien gimpa. "All [XTerm] MoveToDesk 0 1" spowoduje przeniesienie wszystkich okien XTerm na biurko pierwsze, tfu, drugie. Off by one, w końcu liczy się od zera. O ile polecenie MoveToDesk istnieje i ma taką akurat składnię, bo teraz sobie po prosty płynę i nie sprawdzam ;)
Ciekawsze rzeczy zaczynają się, gdy oprócz (albo zamiast) nazwy/klasy/resource okna zechcemy użyć innych kwalifikatorów. Dostępne są np. AcceptsFocus, Visible, Iconic, Shaded, StickyAcrossPages, Transient, CurrentDesk, CurrentPage, State, Layer [n], HasHandles, FixedSize, itp. Praktycznie każdy z tych warunków można zanegować za pomocą "!". Przykład:
All [gimp, !CurrentDesk] close
zamknie wszystkie okna gimpa, oprócz tych na aktualnym biurku.
Albo:
All [Iconic] MoveToDesk 0 3
przeniesie wszystkie ikonki na biurko trzecie, znaczy się, czwarte :)
Jasne? To sympatyczne narzędzie do operowania na grupach okien. Można zrobić sobie np. jakiś klawisz "gather", który zbierze na jednej stronie wszystkie okna należące do klasy okna akurat aktywnego. Albo magiczne "iconify" które jednym kliknięciem ikonifikuje wszystkie gimpy czy xtermy. Warto tutaj zwrócić uwagę na polecenie "State" i 32 "rejestry", które można ustawiać i sprawdzać np. właśnie przy okazji All. Czyli "ikonifikuj wszystkie xtermy, oprócz tych którym ustawiłem rejestr 18".
Kolejne polecenie warunkowe to
"Any"
Składnia identyczna do "All", ale działanie inne - polecenie nie jest wykonywane na oknach, a "ogólnie". "Jeśli istnieją określone okna, to zrób cośtam".
Następne w kolejce:
"Current"
Składnia identyczna, tyle że odnosi się do aktywnego okna. Np. "Jeśli to okno to xterm, to zrób z nim fooshmoo". Albo np. poprawka na okno jakiegoś edytora, który często niechcący minimalizujemy. No bo np. mod Mod4-z mam "iconify", ale obok mam Alt który podbindowałem sobie jako dający polskie litery, i w Gvimie czasem zamiast Alt-z nacisnę Mod4-z i gvim mi się ikonifikuje, co mnie drażni. Więc zmieniam definicję Mod4-z na:
Key z WT 4 Current [!gvim] Iconify
Przez co klawisz zadziała zawsze, chyba że okno to gvim. Wiem, wydumane i nierealistyczne, no ale nie mam jakoś dzisiaj chwytliwych pomysłów :)
Inne polecenie:
"Direction"
coś jak "Current", tylko że nie operuje na aktualnym oknie, a na najbliższym oknie które spełnia warunki i jest położone na Róży Wiatrów.
"Przenieś fokus i kursor myszy na to nieaktywne okno na północny wschód, licząc od pozycji aktualnego okna (lub kursora myszy)". Istnieje specjalny kierunek "Center", który działa jak rozwijająca się spirala - znajduje absolutnie najbliższe odpowiadające okno. Zastosowanie zależy tylko i wyłącznie od fantazji autora. Jestem pewien, że da się z tym zrobić coś sensownego. Oprócz fajnego przełączania fokusu między oknami za pomocą klawiatury numerycznej. A dlatego fajnego, że oprócz góra/dół/lewo/prawo są także obsługiwane "ukosy".
Następne w kolejce:
"Next"
Coś jak "Current", tylko że jeśli aktywne okno nie pasuje, to zaczyna szukać pasującego. Kolejność poszukiwań jest owiana tajemnicą, zapewne chodzi o mistyczną "fvwm internal window list". Przykładowe zastosowanie: "Znajdź mi jakiś xterm! Pamiętam, że gdzieś tu jakiś położyłem..." (fakt, że bez sensu :)
"None"
przeciwieństwo "Any". Np. "None [gkrellm] Exec exec gkrellm" Można wywoływać ile razy się chce, a i tak uruchomi tylko 1 gkrellm, po czym przestanie robić cokolwiek.
"Pick"
jeśli wywoła się w kontekście okna, to działa jak "Current". Jeśli wywoła się w kontekście RootWindow, to kursor zamieni się w krzyżyk i trzeba będzie wskazać okno, na którym polecenie ma operować.
"PointerWindow"
"Current", tylko że dla okna nad którym wisi mysz a nie dla okna które ma fokus.
"Prev"
jak "Next", tylko zapewne przeszukuje listę okien w odwrotnej kolejności.
"ScanForWindow"
bardzo podobne do Direction, ale ulepszone. Algorytmy o udowodnionej skuteczności, "secondary fallback direction" które jest używane jeśli dwa okna będą równouprawnione, itp. Specjalizowane polecenie do przełączania fokusu.
"ThisWindow"
kosmetycznie zmieniony "Current"
"WindowId"
wykonuje, po uwzględnieniu ew. dodatkowych warunków, operacje na oknie o określonym WindowId.
"NoWindow"
prefiks dla poleceń, "maskuje" kontekst okna. Np. pozwala wywołać polecenie "Pick" w kontekście okna bez tego niechcianego często efektu, jak to Pick bez gry wstępnej dobiera się do łechtaczki jak wilk do owieczki. "NoWindow Pick" wywołane w kontekście okna zawsze pokaże najpierw "crosshair" którym trzeba będzie wybrać okno. To oczywiście tylko przykład.
Oprócz tego istnieją 3 polecenia których można użyć w funkcjach, w połączeniu z powyższymi poleceniami:
"Cond" i "CondCase"
operują w oparciu o "return code" poprzedniego polecenia warunkowego. Kody warunkowe mają 3 stany: Match, NoMatch, Error.
Jeśli np. jakieś polecenie "All" nie znajdzie żadnego okna na którym by mogło operować, to następne w kolejce "Cond (NoMatch) foobar" wykona polecenie "foobar". Bo "All" nie znalazło żadnego "match". A "CondCase" działa bardzo podobnie, z tym że nie zmienia kodu powrotu (bo po "Case" kod powrotu by się zmienił na to, co zwróci "foobar"). Tak że CondCase nadaje się do sprawdzenia kilka razy pod rząd wyniku jednej operacji, bez potrzeby grupowania instrukcji w osobnej funkcji. "Cond" z kolei pozwala zbudować rozgałęziające się drzewa przepływu wykonywania (można tak w ogóle powiedzieć?)
No i ostatnie polecenie:
"Break"
Powoduje natychmiastowe wyjście z funkcji, nie przyjmuje parametrów i w zasadzie nadaje się tylko do używania z Cond lub CondCase.
Mam nadzieję, że to pokazało jeden z ciekawszych aspektów fvwm - funkcje i ich zastosowanie. W manualu znajduje się nieco przykładów i innych takich... w każdym bądź razie powinno być już jasne, że oprócz głupiego definiowania menu czy ikonek można sobie też zestawić kilka inteligentnych, zależnych od kontekstu funkcji które coś tam będą robić. Np. funkcja, która wychodzi z fvwm. Ale tylko jeśli nie ma żadnych otwartych okien - jeśli jakieś są, to przy próbie wyjścia przenosi je wszystkie na aktualną stronę, jeśli to ikonki to je deikonifikuje, tak żeby było od razu widać o czym się zapomniało. Dwie-trzy linijki, które mogą się okazać czasem bardzo przydatne. Albo cokolwiek innego.
Dobra, to by było wszystko co dotyczy funkcji i poleceń warunkowych. Ponieważ ten list jeszcze nie ma tradycyjnych minimalnych 300 wierszy, to coś jeszcze mogę dodać... hmm... może dokończyć Style? Nie, to zbyt wiele... to może Colorsets? To nie jest długie...
Kolorsety. Kiedyś kolorsety były tylko sparowanymi kilkoma kolorami (stąd też ich nazwa). Kolor tła, kolor pierwszoplanowy, kolor podświetlenia, kolor cienia. Ot, wygodny sposób na używanie na określonych elementach określonych kolorów. A cały ten mechanizm był obsługiwany przez moduł FvwmTheme. Potem jednak przedostał się do głównego kodu, i rozrósł. Zaczął obsługiwać piksmapy. np.
Colorset 0 Pixmap /usr/share/porno/stary_człowiek_i_może.png
spowoduje, że kolorset o numerze "0" będzie zawierał odpowiedni obrazek .png. Zamiast słówka "Pixmap" można użyć też "TiledPixmap" (obrazek będzie kafelkowany), albo "AspectPixmap" - obrazek będzie rozciągany, ale z zachowaniem proporcji. Zwykłe "Pixmap" rozciąga obraz w razie potrzeby, ale nie zwraca uwagi na proporcje.
Istnieją też 2 specjalne "piksmapy": "Transparent" i "RootTransparent". Prawidłowe używanie jest dosyć skomplikowane... dodatkowo są to dosyć młode opcje, ale można dzięki nim uzyskać kilka miłych efektów. Ja osobiście ich nie używam - nie mam cierpliwości do takiej grzebaniny. No i nie będzie to nigdy tak szybkie jak "zwykłe" piksmapy. Choć np. taki FvwmIconMan który wygląda jak niebieskawe szkiełko na pulpicie jest całkiem fajny (screenshot w moim repo). Akurat włączyłem sobie przezroczyste menu. Przy odpowiedniej tapecie jest całkiem fajne. Jeśli kogoś takie rzeczy bardziej interesują, to tutaj jest niezły screenshot - "wszystko transparentne"
Przykład?
Colorset 0 transparent
tworzy "przezroczysty" kolorset. Można na to nałożyć "farbę", np.
Colorset 0 transparent, tint blue 20
tworzy przezroczysty kolorset, z 20% domieszką koloru "blue". Mam właśnie ustawione tło z dużą ilością wszelkich odcieni czerwieni, i taki "zabarwiony" kolorset w menu jest naprawdę prześliczny. Naprawdę. Ale powolny, bo "transparent" ma swoje wady, zwłaszcza gdy łączy się go z "tint". Wtedy lepiej jest użyć "roottransparent", najlepiej buforowanego:
Colorset 0 roottransparent buffer, tint blue 20
O, i to działa i szybko, i ładnie. tyle że na okres wygenerowania kolorsetu zamroziło mi fvwm na 3 sekundy, ale potem już poszło gładko. To zamrożenie można zniwelować podając ręcznie wartości dla cienia i uśrednionych barw obrazu, które fvwm zwykle samemu zlicza z obrazka. Można też używać "kafelkowanych" tapet, wtedy też idzie to szybciej.
Dodatkowe opcje w kolorsetach to Shape, TiledShape i AspectShape - wczytują jakiś obrazek i używają jego przezroczystości jako maski dla kolorsetu. Nie wiem po co to komu.
Można też definiować gradienty, dostępne typy to: Horizontal, Vertical, Diagonal, BackDiagonal, Square, Circular, Radar, YinYang (nieprawda! wygląda raczej jak dwa naprute kwadraty...)
Dostępne są przeróżne opcje Tint i Alpha, odnoszące się również explicite do ikonek renderowanych w kolorsetach. Bardzo przydatna jest też opcja Dither, której polecam używać przy głębiach do 16 bitów włącznie. Kolorsety wyglądają wtedy dużo, dużo "gładziej".
Piksmapy i przezroczystości są w gruncie rzeczy proste, bo nie ma wiele możliwości by coś z nimi wykombinować. Bardziej interesujące są klasyczne gradienty.
Gradient w najprostszej postaci definiuje się tak:
Colorset 0 VGradient 64 blue black, dither
co tworzy pionowy (vertical) gradient od niebieskiego po czerń, używając 64 kolorów. Dodatkowo z ditheringiem. Ale to jest baardzo prosty gradient. Fvwm ma jednak możliwości tworzenia takich gradientów, jak te używane np. przez gimpa (pomijając jednak przezroczystość, chyba że czegoś nie wiem - efekty przezroczystości to działka na której się po prostu nie znam, więc mogę sobie właśnie radośnie bredzić). A więc można np. zrobić gradient wielokolorowy, gdzie każdy kolor będzie zajmował określony "kawałek" przestrzeni w gradiencie. Przykład:
Colorset 0 VGradient 64 2 red 5 black 20 blue, dither
Poczynając od "VGradient" w prawo:
Pierwsza liczba to ilość kolorów do zaalokowania na potrzeby gradientu. Im więcej kolorów tym większe zużycie ramu, ale to pestka. Na wyświetlaczach 8bit można po prostu szybko wyczerpać sobie paletę barw jeśli się nie rozdziela uważnie kolorów :( Druga liczba to liczba "progów" w gradiencie. Czyli ile kolorów-wyznaczników chcemy zdefiniować. Uwaga, liczy się razem z zerem, więc jeśli używam 3 kolorów to wpisuję "2". Potem lista kolorów do użycia, po każdym kolorze następuje jego "długość". Długości są potem sumowane i z nich przeliczany jest procentowy udział każdego koloru. W moim przykładzie: 3 kolory. Pierwszy to czerwony. Przejście z czerwonego do czarnego to 5 jednostek. Przejście z czarnego do niebieskiego to 20 jednostek. Dzięki temu wiadomo, że w gradiencie czerwono-czarny będzie raptem 1/4 długości tego, co dostanie przejście czarny-niebieski.
To może i jest dziwaczne ("Nie można by tak po prostu procentami?"), ale jednego nie można temu rozwiązaniu odmówić - jest logiczne, działa, i można się szybko przyzwyczaić.
I wystarczy wymienić "VGradient" na "CGradient" i już ma się koncentryczne okręgi... bardzo proste.
Dobra, wystarczy tą razą. Na koniec tylko jedna uwaga - gdy używa się piksmap, gradientów czy czegokolwiek innego, fvwm wylicza sobie "kolory pomocnicze". Te kolory są potem używane przy decydowaniu np. o tym, jak ma wyglądać cień tekstu pisanego na tym kolorsecie, albo jaki kolor ma w ogóle mieć tekst czy też jak ma być podświetlany. Fvwm wyciąga jakoś średnie z wszystkich pikseli i dobiera ich przeciwwagi tak, aby to estetycznie i kontrastowo wyglądało. I zwykle odwala przy tym kawał dobrej roboty (zmieniasz kolorset w menu, a kolor fontu się automagicznie dostosowuje). Ale jeśli nawali, albo po prostu masz inny pomysł jak to zrobić, to możesz wymusić swoje zdanie. Np. ustawiłem ten trójkolorowy gradient w swoim menu, jako tło (MenuStyle i MenuColorset).
A fvwm uznał, że tekst na takim (dosyć ciemnym) kolorsecie powinien być biały. I faktycznie, pasuje bardzo dobrze. Ale problem jest taki - ja sobie udumałem, że biały to ten tekst będzie dopiero wtedy, gdy będzie nad nim kursor myszy (hilight color). I np. chciałbym, by jednak fvwm używał na tym kolorsecie tekstu w kolorze żółtym. Nic trudnego:
Colorset 1 fg yellow, VGradient 64 2 red 5 black 20 blue, dither
Czyli wymuszam jeszcze swój "foreground color". BTW, algorytmy fvwm są niezłe jeśli idzie o wydajność - w takim przypadku powinny wykryć że narzucam "fg color" i nie będą go same wyliczały z kolorsetu - czyli pójdzie to nieco szybciej (ważne przy RootTransparent, gdzie mogą to być sekundy!)
Tradycyjnie już: Pytania, propozycje, cokolwiek? Jakiś odzew?