Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

Tinywm - najlżejszy menadżer okien na świecie. Cześć 1

Wprowadzenie
Witajcie!
Już dawno miałem opisać ten menadżer okien dla systemów Linux i BSD, lecz żeby był on wystarczająco długi to musiałbym przeanalizować każdą linijkę kodu źródłowego, którego na prawdę dużo nie ma. Oczywiście nie miało to sensu, więc przygotowałem wpis w trochę innym charakterze podzielony na dwie części.

Kod źródłowy

Oczywiście nie będę wszystkiego opisywał, bo nie oto chodzi. Cały kod źródłowy to 50 linijek w języku C. Mniej już się nie da. Wersja 1.3 wygląda tak:/* TinyWM is written by Nick Welch , 2005. * * This software is in the public domain * and is provided AS IS, with NO WARRANTY. */ /* much of tinywm's purpose is to serve as a very basic example of how to do X * stuff and/or understand window managers, so i wanted to put comments in the * code explaining things, but i really hate wading through code that is * over-commented -- and for that matter, tinywm is supposed to be as concise * as possible, so having lots of comments just wasn't really fitting for it. * i want tinywm.c to be something you can just look at and go "wow, that's * it? cool!" so what i did was just copy it over to annotated.c and comment * the hell out of it. ahh, but now i have to make every code change twice! * oh well. i could always use some sort of script to process the comments out * of this and write it to tinywm.c ... nah. */ /* most X stuff will be included with Xlib.h, but a few things require other * headers, like Xmd.h, keysym.h, etc. */ #include #define MAX(a, b) ((a) > (b) ? (a) : (b)) int main() { Display * dpy; Window root; XWindowAttributes attr; /* we use this to save the pointer's state at the beginning of the * move/resize. */ XButtonEvent start; XEvent ev; /* return failure status if we can't connect */ if(!(dpy = XOpenDisplay(0x0))) return 1; /* you'll usually be referencing the root window a lot. this is a somewhat * naive approach that will only work on the default screen. most people * only have one screen, but not everyone. if you run multi-head without * xinerama then you quite possibly have multiple screens. (i'm not sure * about vendor-specific implementations, like nvidia's) * * many, probably most window managers only handle one screen, so in * reality this isn't really *that* naive. * * if you wanted to get the root window of a specific screen you'd use * RootWindow(), but the user can also control which screen is our default: * if they set $DISPLAY to ":0.foo", then our default screen number is * whatever they specify "foo" as. */ root = DefaultRootWindow(dpy); /* you could also include keysym.h and use the XK_F1 constant instead of * the call to XStringToKeysym, but this method is more "dynamic." imagine * you have config files which specify key bindings. instead of parsing * the key names and having a huge table or whatever to map strings to XK_* * constants, you can just take the user-specified string and hand it off * to XStringToKeysym. XStringToKeysym will give you back the appropriate * keysym or tell you if it's an invalid key name. * * a keysym is basically a platform-independent numeric representation of a * key, like "F1", "a", "b", "L", "5", "Shift", etc. a keycode is a * numeric representation of a key on the keyboard sent by the keyboard * driver (or something along those lines -- i'm no hardware/driver expert) * to X. so we never want to hard-code keycodes, because they can and will * differ between systems. */ XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync); /* XGrabKey and XGrabButton are basically ways of saying "when this * combination of modifiers and key/button is pressed, send me the events." * so we can safely assume that we'll receive Alt+F1 events, Alt+Button1 * events, and Alt+Button3 events, but no others. You can either do * individual grabs like these for key/mouse combinations, or you can use * XSelectInput with KeyPressMask/ButtonPressMask/etc to catch all events * of those types and filter them as you receive them. */ XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); for(;;) { /* this is the most basic way of looping through X events; you can be * more flexible by using XPending(), or ConnectionNumber() along with * select() (or poll() or whatever floats your boat). */ XNextEvent(dpy, &ev); /* this is our keybinding for raising windows. as i saw someone * mention on the ratpoison wiki, it is pretty stupid; however, i * wanted to fit some sort of keyboard binding in here somewhere, and * this was the best fit for it. * * i was a little confused about .window vs. .subwindow for a while, * but a little RTFMing took care of that. our passive grabs above * grabbed on the root window, so since we're only interested in events * for its child windows, we look at .subwindow. when subwindow * None, that means that the window the event happened in was the same * window that was grabbed on -- in this case, the root window. */ if(ev.type == KeyPress && ev.xkey.subwindow != None) XRaiseWindow(dpy, ev.xkey.subwindow); else if(ev.type == ButtonPress && ev.xbutton.subwindow != None) { /* now we take command of the pointer, looking for motion and * button release events. */ XGrabPointer(dpy, ev.xbutton.subwindow, True, PointerMotionMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); /* we "remember" the position of the pointer at the beginning of * our move/resize, and the size/position of the window. that way, * when the pointer moves, we can compare it to our initial data * and move/resize accordingly. */ XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr); start = ev.xbutton; } /* the only way we'd receive a motion notify event is if we already did * a pointer grab and we're in move/resize mode, so we assume that. */ else if(ev.type == MotionNotify) { int xdiff, ydiff; /* here we "compress" motion notify events. if there are 10 of * them waiting, it makes no sense to look at any of them but the * most recent. in some cases -- if the window is really big or * things are just acting slowly in general -- failing to do this * can result in a lot of "drag lag." * * for window managers with things like desktop switching, it can * also be useful to compress EnterNotify events, so that you don't * get "focus flicker" as windows shuffle around underneath the * pointer. */ while(XCheckTypedEvent(dpy, MotionNotify, &ev)); /* now we use the stuff we saved at the beginning of the * move/resize and compare it to the pointer's current position to * determine what the window's new size or position should be. * * if the initial button press was button 1, then we're moving. * otherwise it was 3 and we're resizing. * * we also make sure not to go negative with the window's * dimensions, resulting in "wrapping" which will make our window * something ridiculous like 65000 pixels wide (often accompanied * by lots of swapping and slowdown). * * even worse is if we get "lucky" and hit a width or height of * exactly zero, triggering an X error. so we specify a minimum * width/height of 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))); } /* like motion notifies, the only way we'll receive a button release is * during a move/resize, due to our pointer grab. this ends the * move/resize. */ else if(ev.type == ButtonRelease) XUngrabPointer(dpy, CurrentTime); } }Większość w powyższym kodzie to opisy w języku angielskim do poszczególnych sekcji, dlatego wydaje się on dłuższy.

Praktyczne zastosowanie - kiosk internetowy

Normalnych praktycznych zastosowań dla tego menadżera właściwie nie ma. (pewnie się zastanawiasz po co w ogóle ja to pisze) Jednym, które ma jakiś sens jest zastosowanie w kiosku internetowym, profesjonalnie zwanym kiosk mode.
Jest to sama przeglądarka internetowa udostępniona użytkownikom końcowym.

Jako system bazowy posłużę się Debianem w wersji 6.0 beta 1. Obraz instalacyjny systemu w wersji 32 bitowej dostępny tutaj. Jako przeglądarkę internetową wykorzystam midori w wersji 0.2.9 pochodzącą z repozytorium Hadreta.

Oczywiście większość dystrybucji zawiera opisywany menadżer w swoich repozytoriach, wybrałem Debiana bo jego obraz instalacyjny miałem pod ręką.

Instalujemy system klikając dalej, dalej itd. Wybieramy tylko system podstawowy, czyli na tym ekranie nie wybieramy nic.

Po zainstalowaniu systemu logujemy się na użytkownika root i instalujemy następujące paczki:apt-get install xorg tinywm midori

Jeśli ktoś chce nowsze midori musi dodać następujące repozytorium:nano /etc/apt/sources.listi dodać to:deb http://hadret.rootnode.net/debian/ unstable main(zapisuje się ctrl +x )
oraz wykonać polecenie wyżej.
Po zainstalowaniu wylogowujemy się z roota i logujemy na użytkownika końcowego i
wykonać polecenie:nano ~/.xinitrci wkleić:
- jeśli tylko zobaczyć jak działa sam menadżer:xsetroot -solid black & xterm & tinywm- od razu z przeglądarkąxsetroot -solid black & xterm & midori & tinywmW pierwszym przypadku wygląda to tak:

zaś w drugim:
Zmieniamy rozmiar przeglądarki i działa.

Następna cześć wkrótce.

Pozdrawiam!

 

Komentarze

0 nowych
Rafal_F   3 #1 04.11.2010 19:30

Żeby zrobić kiosk internetowy nie potrzeba menadżera okien w ogóle. Wystarczy do ~/.xinitrc wpisać firefox, midori czy czego się tam używa i po sprawie.

borzole   4 #2 04.11.2010 20:22

@Rafal_F
no dobra, a co zrobić, żeby taki firefox uruchomił się zmaksymalizowany w kiosk mode?

Można też dać kiosk jako opcję w gdm:

$ cat /usr/share/xsessions/kiosk.desktop

[Desktop Entry]
Encoding=UTF-8
Name=Kiosk
Comment=Kiosk mode (without a session manager)
Exec=/usr/bin/firefox
Icon=kiosk.png
Type=XSession

  #3 04.11.2010 21:07

no dobra, a jak za blokować możliwość zamykania okna przeglądarki? Bo w kioskach należałoby wyłączyć taką opcję. Ale pomysł niezły

Rafal_F   3 #4 04.11.2010 21:19

@borzole jeżeli chodzi o firefox'a wystarczy doinstalować dodatek R-Kiosk i przeglądarka powinna startować na pełnym oknie. Bez GDM, menedżera okien itp, wystarczy podstawowy system + serwer X.

roobal   15 #5 04.11.2010 21:31

@Rafal_F

Powiedz mi jedną rzecz - czego Xserver używa do wyświetlania Firefoksa?

Z tego co mi wiadomo wraz z Xserverem instaluje się menedżer okien TVM i to w nim wyświetla się zapewne Firefox i wszelkie inne aplikacje graficzne, więc obecność menedżera okien jest obowiązkowa. Co innego przeglądarki tekstowe, które nie wymagają trybu graficznego, a grafikę na stronach można wyświetlać przy wykorzystaniu biblioteki svga.

Pozdrawiam!

Rafal_F   3 #6 04.11.2010 22:28

Nie chodzi czasem o TWM? I nie. Sam xorg chyba nie ma żadnego menadżera okna, tzn taki firefox po uruchomieniu w sposób który podałem wcześniej nie posiada żadnej belki tytułowej, ani obramowania. Wyświetla się sama zawartość, czyli to co jest w oknie. Takiego "okna" nie można przesunąć, ani zmienić jego rozmiaru. Po wyjechaniu poza obszar firefoxa kursor zmienia się na x i nie reaguje na kliknięcia. Zresztą według informacji z aptitude nie mam zainstalowanego TWM, a X tak.

roobal   15 #7 05.11.2010 11:39

Tak chodziło o TWM, literówka. Tym bardziej mnie dziwi, że pod Debianem nie zainstalował się TWM razem z Xserverem, chyba że instalowałeś ręcznie, z ręcznym uzupełnianiem zależności?

Pozdrawiam!

Rafal_F   3 #8 05.11.2010 12:00

To dawno były, ale z zdaje się, że zainstalowałem metapaczkę xserver-xorg.

  #9 05.11.2010 21:59

Da się wyświetlić i bez xservera. Są przeglądarki do buffora ramki i są programy pozwalająca uruchomic mozille w buforze ramki. Wystaczy wyciąć z firefoxa opcje gui wyłączenia i gitara.

nintyfan   11 #10 01.01.2011 19:42

@czytaczz (niezalogowany) | 04.11.2010 21:07 :
Najprosztsze rozwiązania często są najlepsze. Mi przyszło do głowy zmiana ustawień Firefoksa tak, by zawsze zapisywał obecną sesję, odebranie praw zapisu do ustawień, a następnie uruchamiać firefoksa w pętli. Miejmy tylko nadzieję, że Firefox nie odda sterowania do powłoki, zanim się nie zakończy.

nintyfan   11 #11 01.01.2011 19:47

@lukas_gab. (niezalogowany) | 05.11.2010 21:59 :
Jest GTK+ z backendem dla buffora ramki. Da się uruchomić GIMP-a. Może Firefoksa też by się udało?

@roobal | 04.11.2010 21:31 :
Menadżer okien niczego nie wyświetla. Chyba nawet pierwotne menadżery kompozycji z XGL-em niczego nie wyświetlały. XGL sam tworzył nowe okno, gdzie rysował pozostałe. Nie wiem, jak to obecnie się tak naprawdę odbywa, ale nie wydaje mi się, by menadżer kompozycji cokolwiek malował.

W każdym razie, to TWM jest lekkim menadżerem okien tworzonym przez ekipę X Window System. Nie trzeba go mieć w systemie, by X-y działały, a X-y do poprawnej pracy nie potrzebują menadżera okien. Oczywiśćie, że pojawiają się problemy typu: okno z pytaniem, gdzie zapisać plik pojawiło się pod oknem Firefoksa wyświetlanym na pełnym ekranie - takie są skutki pracy bez menadżera okien. Jednak X-y nie wymagają nic takiego.
Podobna sytuacja jest z xterm. Program xterm chyba też tworzony jest przez ekipę X-ów, natomiast taka Mandriva w domyślnej instalacji nie zawiera go.

kwpolska   6 #12 01.01.2011 21:57

Da się mniej linijek - wystarczy wywalić komentarze. [solved]