Menadżer okien (ang. window manager, WM) – brzmi dumnie i bynajmniej nie wydaje się programem prostym do napisania. Okazuje się jednak, że stworzenie bardzo prostej aplikacji tego typu to nie żadne rocket science. Przy wykorzystaniu biblioteki Xlib, odpowiadającej za komunikację z serwerem X, implementacja nieskomplikowanego menadżera okien zajmie mniej niż 100 linii kodu.
Spis treści
Cel
Można oczywiście zadać pytanie „po co?”. Wiadomo, że do napisania jakiegokolwiek użytecznego (nawet do własnych zastosowań) WM droga jest dosyć długa, ale osobiście dostrzegam sens również w tworzeniu takich zabawkowych i skrajnie uproszczonych form różnych aplikacji. Nawet jeśli nie podchodzimy do nich z myślą, że staną się zaczątkiem czegoś bardziej rozbudowanego, to i tak zyskujemy pewną wiedzę. Co ważne, jest to wiedza praktyczna, a nie tylko teoretyczna i może przydać się w najrozmaitszych sytuacjach, począwszy od rozwiązywania nowych problemów programistycznych, aż po większe zrozumienie błędów zwracanych przez aplikacje zewnętrzne (np. po dzisiejszej przygodzie z menadżerem okien komunikat „cannot open display”, który można napotkać w różnych programach, stanie się mniej zagadkowy.
Założenia
Przechodząc do meritum, chciałbym zaznaczyć, że inspiracją do niniejszego artykułu stało się dla mnie spotkaniem z TinyWM – minimalistycznym menadżerem okien stworzonym przez Nicka Welcha [1]. Na swojej stronie Nick prezentuje dwie bliźniacze wersje tej aplikacji zaimplementowane w dwóch różnych językach: C oraz Pythonie. Oczywiście obie używają wspomnianej już biblioteki Xlib, stąd różnice między nimi są jedynie kosmetyczne i obie wersje zawierają tę samą logikę. Obejmuje ona przesuwanie okien oraz zmienianie ich rozmiaru. Świetny punkt wyjścia, żeby pobawić się w dorobienie we własnym zakresie chociaż jednej dodatkowej funkcjonalności. Jakiej? Menadżer okien, jak sama nazwa wskazuje, ma za zadanie przede wszystkim zarządzać oknami. Nie jest odpowiedzialny za ich rysowanie ani tworzenie, ale powinien obsługiwać wszelkie operacje na nich, takie jak powiększanie, minimalizowanie i zmienianie ich pozycji. Może też dekorować je różnymi elementami, np. paskiem tytułu, oraz dodawać efekty graficzne.
X Window System
Zanim zajmiemy się kwestiami praktycznymi, warto poświęcić chwilę na zapoznanie się z podstawami X Window System (znanego też pod nazwami X11 oraz po prostu X). Jest to bowiem system okien, dla którego będziemy tworzyć nasz menadżer. Początki „X-ów” sięgają roku 1984, a więc nie jest to już najnowsza technologia, jednak póki co wciąż obecna w większości dystrybucji GNU/Linux. Wprawdzie niektórzy wieszczą już koniec X11 i całkowite zastąpienie go przez nowe rozwiązania, takie jak np. Wayland, to raczej nie zapowiada się, by stare dobre iksy zniknęły z dnia na dzień [2].
Czym zatem jest ów X Window System? Systemem okien, czyli, najprościej mówiąc, komponentem graficznego interfejsu użytkownika, odpowiedzialnym za obsługę urządzeń wejścia/wyjścia (klawiatury, myszy) oraz grafiki [3][4]. Tworzy on okna i pozwala aplikacjom na rysowanie w nich (czym zajmują się biblioteki widżetów, takie jak Qt), zaś menadżerom okien na zarządzanie nim (obsługę przesuwania, zmiany rozmiaru itd.). Można powiedzieć, że system okien jest warstwą, która oddziela menadżera okien od sprzętu, obsługując podstawowe operacje graficzne – rysowanie linii, czy wyświetlanie napisów.
System X implementuje architekturę klient-serwer – składa się z serwera, realizującego opisane powyżej funkcje związane z tworzeniem podstawowych elementów graficznych i obsługą sprzętu, oraz klienta, który może wysyłać wiadomości z żądaniem wykonania operacji na oknach (stworzeniem, zamknięciem, zmienieniem rozmiaru itp.). Takim klientem jest dowolna aplikacja, posiadająca interfejs graficzny – np. przeglądarka internetowa, edytor tekstu, emulator terminala. Nie tylko przesyła ona żądania do serwera, ale też otrzymuje od niego różne powiadomienia, m.in. o naciśniętych lub puszczonych klawiaszach oraz przyciskach (zdarzenia KeyPress, KeyRelease, ButtonPress, ButtonRelease).
A teraz najciekawsze – menadżer okien jest w X11 zwykłym klientem. W kontekście całego systemu nie on posiada żadnych wyjątkowych uprawnień, jedyne co go wyróżnia, to dostęp do specjalnego API, przy pomocy którego komunikuje się on z serwerem X. Dostęp ten jest przyznawany pierwszemu klientowi, który o niego poprosi, a każdy kolejny, który próbowałby to zrobić, otrzyma odmowę (no bo kto to widział, żeby w jednym systemie współpracowały dwa niezależne menadżery okien). [5]
Kod początkowy
Kod, od którego zaczniemy naszą zabawę z menadżerami okien liczy zaledwie 50 linii w C lub nieco ponad 30 jeśli weźmiemy pod uwagę implementację stworzoną w Pythonie. W niniejszym wpisie będę bazował na tym napisanym w języku C. Dostępny jest on na stronie Nicka Welcha [1] w dwóch wersjach – zwykłej oraz zawierającej dokumentację w postaci komentarzy autora, wyjaśniających poszczególne fragmenty kodu. Uważam, że znajduje się tam kilka przydatnych informacji, dlatego dla kompletności tego artykułu kokusiłem się o ich przetłumaczenie. Jeśli dostrzegasz gdzieś błąd w tłumaczeniu, zachęcam do dokonania poprawki, tworząc issue lub pull request na GitHubie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
/* Komentarze zostały przetłumaczone przez Macieja Michalca * na podstawie oryginalnej wersji ze strony incise.org */ /* TinyWM zostało napisane przez Nicka Welcha <mack@incise.org>, 2005. * * To oprogramowanie jest w domenie publicznej * i jest dostarczane bez żadnych gwarancji */ /* Głównym celem tinywm jest służyć jako bardzo podstawowy przykład tego, jak * programować rzeczy związane z systemem X Window i/lub pozwolić na zrozumienie * menadżerów okien, dlatego postanowiłem umieścić w kodzie komentarze * objaśniające, ale naprawdę nienawidzę przedzierać się przez kod zawierający * za dużo komentarzy - z tego powodu tinywm ma być tak zwięzły jak to * tylko możliwe. Zbyt wiele komentarzy nie pasuje do tego. Chciałem, aby * tinywm.c było czymś, na co tylko spojrzysz i powiesz: "wow, to wszystko? * świetnie!", więc po prostu skopiowałem kod do annotated.c i skomentowałem * obszernie. Ahh, ale teraz muszę dokonywać wszystkich zmian w kodzie * dwukrotnie! Oh, dobrze. Mógłbym zawsze używać jakiegoś skryptu do wycinania * komentarzy i zapisywania tego do tinywm.c ... nah. */ /* większość rzeczy związanych z X-ami będzie zaincludowanych z pliku xlib.h, * ale kilka elementów wymaga innych nagłówków, takich jak Xmd.h, keysym.h, etc. */ #include <X11/Xlib.h> #define MAX(a, b) ((a) > (b) ? (a) : (b)) int main() { Display * dpy; Window root; XWindowAttributes attr; /* używamy tego, żeby zapisać stan wskaźnika na początku * przesuwania/zmieniania rozmiaru */ XButtonEvent start; XEvent ev; /* zwróć kod błędu jeśli nie możemy się połączyć */ if(!(dpy = XOpenDisplay(0x0))) return 1; /* zazwyczaj często będziesz się odnosił do nadrzędnego okna. Jest to * trochę naiwne podejście, które będzie działać tylko na domyślnym * ekranie. Większość ludzi ma tylko jeden ekran, ale nie wszyscy. Jeśli * uruchamiasz tryb wielomonitorowy bez xineramy, to prawdopodobnie masz * kilka ekranów [zapewne chodzi o konfigurację, w której nie można * przenosić okna między ekranami, bo nie stanowią one jednego * wirtualnego ekranu - przyp. tłum.] (nie jestem pewien implementacji * specyficznej dla producentów, np. nvidii) * * wiele, prawdopodobnie większość menadżerów okien obsługuje tylko jeden * ekran, więc w rzeczywistości nie jest to takie "naiwne" * * jeśli chcesz uzyskać dostęp do nadrzędnego okna konkretnego ekranu, * możesz użyć funkcji RootWindow(), ale użytkownik też może kontrolować * który ekran jest naszym domyślnym: jeśli ustawi zmienną $DISPLAY * na ":0.foo", to naszym domyślnym numerem ekranu jest to, co zostanie * wstawione jako "foo". */ root = DefaultRootWindow(dpy); /* mógłbyś też załączyć keysym.h i użyć stałej XK_F1 zamiast wywołania * XStringToKeysym, ale ta metoda jest bardziej "dynamiczna". Wyobraź * sobie, że masz pliki konfiguracyjne, specyfikujące bindingi klawiszy. * Zamiast parsowania nazw klawiszy i posiadania ogromnej tabeli lub * czegoś co mapuje stringi do stałch XK_*, możesz po prostu wziąć nazwę * klawisza i przekazać ją do XStringToKeysym. Funkcja ta zwróci ci w zamian * odpowiedni keysym (symbol klawisza) albo powie ci, że nazwa klawisza * jest nieprawidłowa. * * keysym to niezależna od platformy numeryczna reprezentacja klawisza, np. * "F1", "a", "b", "L", "5", "Shift" etc. Keycode jest numeryczną * reprezentacją klawisza na klawiaturze, wysłaną przez sterownik klawiatury * (albo coś w tym stylu - nie jestem ekspertem od hardware'u/sterowników) * do serwera X. Nigdy nie chcesz więc hardcodować keycodów, ponieważ mogą * one i będą różnić się między systemami. */ XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync); /* XGrabKey i XGrabButton to podstawowe sposoby powiedzenia "kiedy ta * kombinacja modyfikatorów i klawiszy/przycisków zostanie naciśnięta, * wyślij mi te zdarzenia", więc możemy bezpiecznie założyć, że otrzymamy * eventy Alt+F1, Alt+Przycisk1, Alt+Przycisk3, ale nie żadne inne. Możesz * użyć pojedynczych uchwytów, jak tych do kombinacji klawiszy/myszy lub * skorzystać z XSelectInput wraz z KeyPressMask/ButtonPressMask/etc, aby * przechwycić wszystkie zdarzenia danego typu i filtrować je w miarę * otrzymywania. */ XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); for(;;) { /* to jest najbardziej podstawowy sposób iterowania po eventach X; * możesz być bardziej elastyczny, używając XPending() lub * ConnectionNumber() wraz z select() (lub poll() albo czymkolwiek co * ci pasuje). */ XNextEvent(dpy, &ev); /* to jest nasz binding do przywracania okien. Widziałem jak kiedyś na * ratpoison wiki ktoś stwierdził, że to jest głupie, jednak chciałem * gdzieś tu wcisnąć jakieś bindowanie klawiatury i to było najlepsze * rozwiązanie. * * przez chwilę byłem nieco zmieszany w kwestii .window vs. .subwindow * ale trochę czytania manuali wyjaśniło ją. Nasze uchwyty działają * w obrębie okna nadrzędnego, więc gdy interesują nas tylko eventy * dla okien potomnych, patrzymy na .subwindow. Kiedy subwindow jest * równe None, oznacza to, że oknem, dla którego przechwycono event * jest okno, dla którego stworzyliśmy uchwyt - w tym przypadku, okno * nadrzędne. */ if(ev.type == KeyPress && ev.xkey.subwindow != None) XRaiseWindow(dpy, ev.xkey.subwindow); else if(ev.type == ButtonPress && ev.xbutton.subwindow != None) { /* teraz przejmujemy kontrolę nad wskaźnikiem, szukając zdarzeń * zwolnienia przycisku oraz ruchu. */ XGrabPointer(dpy, ev.xbutton.subwindow, True, PointerMotionMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); /* "zapamiętujemy" pozycję wskaźnika na początku przesunięcia lub * zmiany rozmiaru oraz rozmiar/pozycję okna. W ten sposób kiedy * wskaźnika się przesuwa, możemy porównać go z naszymi danymi * początkowymi i odpowiednio dokonać przeniesienia/resize'owania */ XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr); start = ev.xbutton; } /* jedyny sposób, abyśmy mogli otrzymać zdarzenie powiadamiania * o ruchu, to jeśli dokonaliśmy już przechwycenia wskaźnika oraz * jesteśmy w trybie przenoszenia/resize'owania, więc załóżmy to */ else if(ev.type == MotionNotify) { int xdiff, ydiff; /* tutaj "kompresujemy" eventy powiadomień o ruchu. Jeśli 10 z nich * jest oczekujących, należy patrzyć nie na którykolwiek z nich, * tylko na najnowszy. W pewnych sytuacjach - jeśli okno jest * naprawdę duże lub po prostu wszystko dzieje się wolno - * niezastosowanie się do tej zasady może spowodować wiele opóźnień * przeciągania ("drag lag") * * dla menadżerów okien z funkcjonalnościami w stylu przełączania * pulpitów może być użyteczne, aby kompresować eventy EnterNotify, * aby nie uzyskać "migotania focusowania" */ while(XCheckTypedEvent(dpy, MotionNotify, &ev)); /* teraz używamy tego, co zapamiętaliśmy na początku przesunięcia * lub resize'owania i porównujemy z obecną pozycją wskaźnika, aby * określić jaki powinien być nowy rozmiar okna lub jego pozycja. * * jeśli początkowo naciśniętym przyciskiem był przycisk 1, * to przesuwamy, w przeciwnym przypadku zmieniamy rozmiar * * upewniamy się też, że wyliczając wymiary okna nie uzyskaliśmy * wartości negatywnych, kończąc z jakimiś śmiesznymi wymiarami * typu 65000 pixeli szerokości (często w towarzystwie wielu zamian * i spowolnień). * * jeszcze gorzej jeśli mamy "szczęście" i trafimy na wysokość lub * szerokość równe zero, generując błąd serwera X. Ustalamy zatem * minimalną wysokość/szerokość okna na 1 pixel. */ xdiff = ev.xbutton.x_root - start.x_root; ydiff = ev.xbutton.y_root - start.y_root; XMoveResizeWindow(dpy, ev.xmotion.window, attr.x + (start.button==1 ? xdiff : 0), attr.y + (start.button==1 ? ydiff : 0), MAX(1, attr.width + (start.button==3 ? xdiff : 0)), MAX(1, attr.height + (start.button==3 ? ydiff : 0))); } /* podobnie jak w przypadku powiadomień o ruchu, jedynym przypadkiem * otrzymania zwolnienia przycisku jest moment podczas przesuwania albo * zmiany rozmiaru dzięki naszemu uchwytowi wskaźnika. To zdarzenie * kończy proces przesuwania/resize'owania. */ else if(ev.type == ButtonRelease) XUngrabPointer(dpy, CurrentTime); } } |
Uruchamianie
Środowisko, w którym postanowiłem testować tworzony menadżer okien to dystrybucja Xubuntu (działająca wewnątrz maszyny wirtualnej), w związku z czym kolejne kroki pokażę na przykładzie tego właśnie systemu. Wszystkie pliki, o których będę pisał, można znaleźć w moim repozytorium na GitHubie.
Pierwszym pytaniem, które nasuwa się na myśl po zobaczeniu kodu źródłowego tinyWM to jak w ogóle uruchomić taką aplikację? Zacznijmy od skomplikowania – ponieważ wykorzystujemy bibliotekę X11, musimy poinformować linker o tym fakcie, w związku z czym użyjemy komendy:
1 |
gcc polydev-wm.c -o polydev-wm -lX11 |
Jeśli w Waszym systemie nie istnieje wyżej wspomniana biblioteka, należy ją doinstalować – w przypadku (X)Ubuntu zrobimy to poprzez sudo apt install libx11-dev
(aczkolwiek nazwa paczki może różnić się w zależności od dystrybucji, np. w Fedorze występuje ona jako libX11-devel
).
Oprócz samego pliku wykonywalnego (który powinniśmy skopiować do katalogu /usr/bin
) potrzebujemy jeszcze skryptów związanych z sesją menadżera okien [6]:
- /usr/bin/polydev-wm-session
12345#!/bin/shxsetroot -solid "#000000"xrdb -load $HOME/.Xdefaultsx-terminal-emulator &exec /usr/bin/polydev-wm >> /tmp/polydev-wm.log 2>&1 - /usr/share/xsessions/polydev-wm-session.desktop
1234567891011[Desktop Entry]Encoding=UTF-8Name=PolyDev WMComment=Ridiculously tiny window manager based on a tinyWMExec=polydev-wm-sessionTerminal=FalseTryExec=polydev-wmType=Application[Window Manager]SessionManaged=true
W pierwszym z nich definiujemy m.in. to, jakie pliki mają być wczytywane oraz jakie aplikacje włączane przy uruchamianiu sesji. W naszym przykładzie menadżer okien przywita nas jednym okienkiem terminala, zaś dzięki przekierowaniu strumieni wyjściowych do pliku, będziemy mogli sprawdzić np. komunikaty błędów jeśli coś w trakcie działania WM pójdzie nie tak.
Drugi plik opisuje skrót, który pojawi się na ekranie logowania, i którego wybranie pozwoli nam uruchomić naszego menadżera okien (zob. rys. poniżej). Po przygotowaniu wszystkiego co powyżej pozostaje nam wylogować się z aktualnie używanego środowiska graficznego, przełączyć WM, a następnie zalogować.
Po zalogowaniu naszym oczom ukazuje się prawie całkowita pustka. Brak belek tytułowych nad oknami, brak jakichkolwiek pasków itp. Warto więc wiedzieć, że aby wylogować się z tak prostego menadżera okien, możemy po prostu zabić jego proces:
1 |
kill -9 -1 |
Testując działanie WM, pamiętajmy, że zmiana rozmiaru oraz położenia okien, która zaimplementowana jest w tinyWM działa z wciśniętym przyciskiem myszy oraz klawiszem ALT. Samemu zdarzyło mi się o tym zapomnieć pewnego razu i spędziłem trochę czasu na debugowaniu nieistniejącego problemu. 😉
Więcej skrótów
Moim pomysłem na krótką zabawę z tinyWM było po prostu dorzucenie obsługi kilku dodatkowych skrótów klawiaturowych. Na pierwszy ogień poszło ALT+F4, a poszukiwań informacji na temat potencjalnych rozwiązań rozpocząłem na stronie Tronche.com, zawierającej przygotowaną przez Christopha Tronche wersję The Xlib Manual.
Początkowo spróbowałem użyć funkcji XDestroyWindow – okno rzeczywiście zamykało się, ale oprócz tego pojawiały się komunikaty błędów. Dlaczego? Ponieważ w ten sposób w dość brutalny sposób (jak zresztą wskazuje sama nazwa – destroy) wyłączamy okno, ale nie wyłączamy klienta (czyli programu), który to okno utworzył.
Znacznie bardziej eleganckim rozwiązaniem jest wysłanie do klienta wiadomości WM_DELETE_WINDOW
, która stanowi uprzejmą prosbę o zamknięcie okna [7]:
1 2 3 4 5 6 7 8 9 10 11 12 |
focusedOnWindow = (ev.type == KeyPress && ev.xkey.subwindow != None); if (focusedOnWindow && ev.xkey.keycode == XKeysymToKeycode(dpy, XStringToKeysym("F4"))) { memset(&msg, 0, sizeof(msg)); msg.xclient.type = ClientMessage; msg.xclient.message_type = XInternAtom(dpy, "WM_PROTOCOLS", True); msg.xclient.window = ev.xkey.subwindow; msg.xclient.format = 32; msg.xclient.data.l[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); msg.xclient.data.l[1] = CurrentTime; XSendEvent(dpy, ev.xkey.subwindow, False, 0, &msg); } |
Oczywiście istnieje ewentualność, że program nie wspiera takich próśb i w takim wypadku porządny menadżer okien powinien dotrzeć do niego w jakiś inny sposób, np. funkcją XKillClient, która zamknie klienta i wyczyści jego zasoby (takie jak otwarte okna). Przykładowy WM, który sprawdza taką sytuację, można znaleźć pod tym linkiem.
Skoro mamy już zaimplementowane zamykanie okien, to przydałoby się zrobić również coś odwrotnego – użyć skrótu klawiszowego do dokonania dzieła kreacji zamiast destrukcji. W tym celu postanowiłem zaimplementować obsługę popularnej w wielu środowiskach kombinacji CTRL+ALT+t, służącej do uruchamiania terminala.
Aby powyższa kombinacja była przechwytywana dodałem takie oto wywołanie:
1 2 3 4 5 6 7 |
XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("t")), ControlMask | Mod1Mask, root, True, GrabModeAsync, GrabModeAsync); |
U mnie działa, ale… jak słusznie zauważa Steve Kemp w swoim fragmencie kodu, taki kod nie zadziała jeśli dodatkowo będzie na naszej klawiaturze uaktywniony np. NumLock albo CapsLock, więc aby w pełni obsługiwać nasze CTRL+ALT+t, trzeba dodać jeszcze parę linijek.
I to byłoby na tyle w kwestii mojego „udoskonalania” TinyWM. Jeśli Wy nie planujecie w tym momencie zakończyć swojej przygody z Xlib, to pozwolę sobie polecić program o nazwie XEV – event tester, który jest niezwykle pomocny przy developmencie X-owych aplikacji.
Dekorowanie okien i inne ficzery
Co jeszcze można dołożyć do tak prostego WM? Widząc okna pozbawione znajomej nam belki tytułowej zapewne większość osób, jako o jednej z pierwszych funkcjonalności w kolejce do zaimplementowania pomyśli o dekoracji okien – dodaniu do nich paska z tytułem, może również z paroma przyciskami (minimalizacja, zamykanie, powiększanie/pomniejszanie). Niestety okazuje się, że nie jest to już takie trywialne zadanie.
Sposoby są różne, a jednym z nich jest tzw. reparenting, polegający na tworzeniu dla każdego okna dodatkowego okienka – zawierającego właśnie belkę tytułową lub inne elementy ramki – i uczynienie z niego rodzica pierwotnego okna. Parę informacji na ten temat można znaleźc np. w tej dyskusji. Na szczęście nie jest to jedyny rozwiązanie, jednym z alternatywnych jest tworzenie dodatkowych okien z paskami tytułowymi, ale bez dokonywania reparentingu. Takie podejście zaimplementowane zostało w menadżerze KatriaWM, a autor tego oprogramowania opisał swoje przejścia na blogu (polecam!).
Garść pomysłów jeśli chodzi o funkcjonalności do zaimplementowania w WM można również znaleźć w podręczniku na Wikibooks, poświęconemu X11 [8].
Zastosowanie
Oprócz niewątpliwego zastosowania edukacyjnego, TinyWM można użyć np. w kiosku internetowym, co proponuje w swoim wpisie jeden z blogerów [9]. Co ambitniejsi mogą oczywiście pokusić się o stworzenie menadżera okien do własnego codziennego użytku, ale trzeba liczyć się z tym, że wymagany wkład czasowy będzie naprawdę spory.
Źródła
- incise.org: tinywm, Nick Welch.
- X.Org a Wayland — czyli stary król i młody następca tronu.
- Wikipedia: System okien.
- Wikipedia: X Window System.
- How X Window Managers Work, And How To Write One (Part I), Chuan Ji.
- Debian.org: TinyWM.
- StackExchange: Is closing the window of a X client application process necessarily followed by terminating the process?
- Wikibooks: Features and Facilities of Window Managers.
- Tinywm – najlżejszy menadżer okien na świecie, mati75, 2010.
- Possum – simple window manager based on TinyWM.