X11 under cover #2: Eventy pod drobnohladom

23.07.2008 01:39 | blackhole_ventYl

Je uplne zbytocne vediet pokreslit obrazovku najroznejsimi okienkami, ak neviete, co s nimi chce robit uzivatel. Prave na informovanie aplikacie o tom, ze sa s nou uzivatel nieco snazi robit, sluzi sustava udalosti, ktore su odosielane aplikacii. V tejto casti sa podrobnejsie zameram na rozdelenie udalosti, pretoze bude klucove k pochopeniu, ako pracuju graficke toolkity, windowmanagery a niektore dalsie sluzby X servera.

V predoslej casti bolo uvedene, ze X server generuje a posiela aplikacii Eventy. V drvivej vacsine pripadov su Eventy generovane ako odpoved na konanie uzivatela, ale existuju aj udalosti, ktore su generovane systemom (prepnutie klavesnice), pripadne udalosti, ktore generuju same aplikacie. X protokol dava aplikacii moznost vygenerovat akukolvek spravu, ktora v systeme existuje a poslat ju lubovolnemu klientovi tak, ako keby mu ju poslal X server. Je teda mozne efektivne podvrhnut aplikacii trebars spravu o tom, ze sa v jej priestore nachadza kurzor a aplikacia si to ani nemusi vsimnut. Preco nemusi? Kazda sprava, ktoru aplikacia doruci, v sebe obsahuje informaciu o tom, ci je prava, teda bola vygenerovana serverom, alebo je synteticka, teda ju "podvrhla" aplikacia. Posielanie sprav medzi aplikaciami sa pomerne bezne praktizuje v niektorych konvenciach zauzivanych pri medziprocesovej komunikacii grafickych aplikacii a vie mat kopu vtipnych vyuziti.

Eventy, ktore system generuje na zaklade uzivatelskeho vstupu su v podstate rozdelene na styri zakladne druhy a to:

1. Udalosti spojene so zmenou rozlozenia okien

Tieto udalosti su obvykle dosledkom nasledujucich dvoch skupin. Medzi tieto udalosti plati napriklad udalost ExposeEvent, ktora je dorucena aplikacii v momente, ked po nej server pozaduje prekreslenie niektoreho jej okna. Dovodom moze byt to, ze bolo okno zobrazene, resiznute, pripadne ze sa ine okno, ktore ho prekryvalo, stratilo / presunulo. V klasickom X protokole udalost neobsahuje ziadne informacie o tom, ktoru cast okna je treba prekreslit. Aplikacia teda musi prekreslit cele okno. Pre jednoduche graficke prvky to nepredstavuje ziaden problem, ale ak je obsah okna vysledkom nejakeho komplikovaneho vypoctu, pripadne obsahuje bitmapu vo velkom rozliseni, je zbytocne od aplikacie k serveru prenasat zbytocne celu bitmapu a zahlcovat tak prenosove pasmo. Existuje preto rozsirenie (X Damage), ktore umoznuje aplikacii dozvediet sa, kde presne treba prekreslit. K rozsireniam sa ale dostanem v niektorom z nasledujucich dielov.
Dalsou udalostou z tejto skupiny je udalost MapNotifyEvent. Tato udalost je poslana aplikacii po tom, co na jej pokyn X server vytvori okno. Kedze X-Window nie je stavovy server, ak by aplikacia kreslila do okna hned po prikaze na vytvorenie okna, v lepsom pripade by sa okno stihlo vytvorit, ale nestihlo zobrazit, takze by nakreslene zmizlo v ciernej diere, v horsom pripade by sa okno ani nestihlo vytvorit a aplikacia by zletela s chybou BAD_WINDOW (toto by sa stat nemalo, pretoze protokol sa spracovava v takom poradi, v akom poradi spravy prisli). Tato udalost ma ale este jedno specificke vyuzitie. Existuje technika, pomocou ktorej mozno udalosti presmerovat na ineho prijemcu. Tuto techniku v spojeni s udalostou MapNotifyEvent vyuzivaju windowmanagery k tomu, aby aplikacnym oknam vedeli nakreslit ramceky. Kedze windowmanager nie je vlastnikom okna, ma zakazane do aplikacneho okna ramcek vykreslit. Ak vsak aplikacii okno "ukradne", vybere ho z plochy a vlozi do svojho vlastneho okna, kam nakresli ramcek. Ram okna je na svete. Viac o tom, ako windowmanagery ovladaju plochu bude uvedene v samostatnej casti, kedze to samo o sebe vyda na jednu cast, mozno aj viac.
K udalosti MapNotifyEvent existuje opacna udalost UnmapNotifyEvent, ktoru server posiela, ked je okno na ziadost aplikacie schovane. Tu by sa este patrilo podotknut, ze okno, ktore je schovane, stale existuje, iba sa nevykresluje. Ak sa nevykresluje okno, nevykresluju sa ani ziadne jeho podokna.
Dalsou udalostou notifikujucou zmenu v usporiadani je ConfigureNotifyEvent. Tato udalost aplikacii dorazi, ak bolo okno nejako pozmenene. Napriklad v pripade, ak bolo okno premiestnene, bola zmenena jeho velkost a podobne.
Ak je okno premiestnene do ineho okna (stane sa podoknom ineho okna, nez bolo), dostane aplikacia ReparentNotifyEvent.
Poslednymi zaujimavymi udalostami tohto typu su udalosti FocusIn a FocusOut. Ako z nazvu vyplyva, prva udalost je aplikacii zaslana, ak niektore jej okno ziska focus, druha, ak okno focus strati. Tu je nutne pripomenut, ze focusom sa nemysli graficky focus okna, ale focus klavesnicovy. X server vobec nezaujima, ktore okno focus ma. Moze nim byt trebars aj okno, ktore je schovane pod inymi oknami, musi vsak byt namapovane. Navyse v serveri existuju mechanizmy, ktorymi sa da zabezpecit, ze udalosti nedostane ani ten, kto ma focus.
Ostatne udalosti z tejto skupiny nie su pre zakladne pochopenie dolezite, avsak ak by boli potrebne, tak sa k nim niekedy neskor vratim.

2. Udalosti generovane pohybom mysi

Vsetky tieto udalosti maju spolocneho menovatela a to je sposob ich vyvolania. Udalosti vyvolava pohyb mysi, pripadne stlacanie tlacidiel.
Najzakladnejsou udalostou je MotionNotifyEvent. Tato udalost pride aplikacii vzdy, ak sa kurzor pohne a pritom sa nachadza v priestore jej okna. Aplikacia obrzi ako suradnice kurzora relativne k lavemu hornemu rohu okna, tak absolutne k lavemu hornemu rohu obrazovky.
Dalsimi udalostami priamo spojenymi s pohybom su EnterNotify a LeaveNotify. Prva z dvojice udalosti je vyvolana, ak kurzor prekroci hranice okna, pricom vstupi do okna, druha je generovana, ak kurzor okno opusti. Tieto udalosti sa pouzivaju na kreslenie hover-u u tlacidiel, pripadne pri sloppy focuse aj na indikaciu zamerania vstupnych prvkov.
Na stlacanie klavesov je aplikacia upozornena pomocou ButtonPressNotify a ButtonReleaseNotify udalosti. Prva je vyslana, ak je tlacidlo stlacene, druha, ak je pustene, pricom udalost obsahuje informaciu o tom, ktore tlacidlo mysi bolo stlacene / pustene a kde sa tak stalo (pozicia v okne).

3. Udalosti sposobene pracou s klavesnicou

Su v podstate len dve, kedze klavesnica nereaguje na pohyb (obvykle). KeyPressEvent je aplikacii poslana, ked je klavesa stlacena, KeyReleaseEvent je poslana, ked je klavesa pustena. Zaujimave na tychto eventach je, ze posielaju nie nejaky aplikacne vyuzitelny kod klavesy, ale platformovo nezavisly KeyCode, ktory sa musi doplnkovymi mechanizmami prelozit na nieco, comu aplikacia rozumie.

4. Aplikacne vyvolane udalosti

Najprimitivnejsou udalostou v tejto skupine je udalost ClientMessage. Pomocou nej si graficke aplikacie mozu vymienat spravy rozneho typu, pripadne moze aplikacia komunikovat sama so sebou (jeden zo sposobov, ako prenasat udalosti z okna na okno).
Dalsimi udalostami su SelectionClear, SelectionNotify, SelectionRequest, ktore su spojene s pracou so schrankou (nazyvanou selection). Tomu bude venovana samostatna cast, preto tu tieto udalosti rozoberat nebudem.
Dalsimi udalostami z tejto kategorie su RequestEventy. Tieto eventy dostane aplikacia, ktora si ich vyziadala v pripade, ze niekto poziada o vykonanie zmeny, ktora je sledovana. Sideefektom sledovania ziadosti je to, ze namiesto toho, aby server danu ziadost vybavil (trebars namapoval okno), ziadost bude potichu ignorovat (ziadna chyba) a zasle tomu, kto ziadost sledoval, patricnu spravu. Menovite su to tieto cinnosti:
MapRequestEvent je dorucena aplikacii, ak sa niekto pokusi namapovat okno do okna, ktora je sledovane. Bezne tuto metodu pouzivaju windowmanagery, ktore kreslia ramceky. Vyziadaju si MapRequest udalosti nad rootovskym oknom. Ak v nom aplikacia vytvori okno, windowmanager sa o tom dozvie a moze zasiahnut.
ConfigureRequestEvent je dorucena aplikacii, ak sa niekto pokusi zmenit vlastnosti okna, ktore je sledovane (pozicia, velkost, stackovanie a pod.).
Podobne je na tom aj udalost ResizeRequest, ktora ale reaguje len na pokus o zmenu velkosti okna, ostatne zmeny ignoruje.

Po nudnom rozpise udalosti nasleduje trocha zabavky a na koniec uvediem jeden priklad aplikacie, ktora pracuje vylucne s udalostami.

Aplikacia nemusi nutne prijimat vsetky udalosti, ktore pre nu server emituje. To by v pripade uplnej ignoracie tychto udalosti viedlo k zbytocnemu zahlcovaniu prenosovej kapacity. Preto aplikacia vie povedat, ktore udalosti ju pre ktore okno zaujimaju, co moze viest k vytvoreniu rychlejsie odpovedajucich aplikacii. Najma v pripade, ak aplikacia usetri prenosove pasmo neprijimanim udalosti o pohybe kurzora, ktorych je vysielanych strasne vela, ale zamera sa na podstatne informacie, napriklad klikanie mysou.
To, ktore udalosti aplikaciu zaujimaju, je mozne nastavit pomocou event-mask pola vo vlastnostiach okna. Kazde jednotlive okno a kazdy klient mozu udalost odoberat separatne, teda 5 roznych klientov moze odoberat udalost pohybu kurzora v tom istom okne. Jedinou vynimkou su niektore RequestEventy, ktore moze odoberat nad jednym oknom len jeden klient.

Poslednou moznostou, ako moze aplikacia ovplyvnit tok udalosti, je grab kurzora, alebo grab klavesnice. Ak aplikacia vytvori grab nad mysou, moze bud ukradnut aplikaciam uplne vsetky udalosti, ktore mys geruje, pripadne si zabezpecit ich kopiu. Grab moze byt dvojaky. Budto zastavi normalne prijimanie udalosti generovanych mysou, kedy udalosti dostane len ten, kto si mys grabol, pripadne sa normalne prijimanie nezastavi, eventy su dorucovane ako keby grab neexistoval, ale navyse sa ich kopia dorucuje tomu, kto grabovanie zapocal. Grabnut je mozne ako celu mys, tak jej jednotlive tlacidla. Ak si aplikacia aktivne grabne lave tlacidlo, nikdy sa nikto iny nedozvie o tom, ze bolo stlacene. Pri grabovani tlacidla je mozne nastavit, ktore modifikatory (Alt, Ctrl, Shift, Caps Lock...) musia byt stlacene, aby bol grab aktivny. Touto technikou windowmanagery implementuju presun okien pomocou Alt+klik&tahaj. Podobne plati aj pre grabovanie klavesnice, kedy je mozne grabovat ako celu klavesnicu, tak len niektore konkretne klavesy, pri ktorych je mozne vybrat, ktore modifikatory podmienia grabovanie. Touto technikou sa vytvaraju klavesove skratky, budto aplikacne, alebo celosystemove.

A na zaver, ako som v prvej casti slubil, popis realizacie jednej na prvy pohlad komplikovanej operacie, ktora sa da ale realizovat velmi jednoducho.

Keylogger je aplikacia, ktora zaznamenava vsetky vstupy z klavesnice, aby odchytila hesla, pripadne adresy, alebo obsahy tajnych dokumentov, mailov, konverzacii. Kedze X server je sietova aplikacia, moze sa na nu napojit klientska aplikacia z lubovolneho dosazitelneho miesta na internete. Po pripojeni aplikacia moze vytvorit pasivny grab klavesnice, co jej zaruci, ze bude dostavat informacie o vsetkych stlacenych klavesoch bez nutnosti vytvarat akekolvek okno, pripadne sledovat to, ake okna su vytvorene, alebo kde sa nachadza focus. Jedine, co si aplikacia musi strazit, je to, aby ju niekto nepripravil o focus. Kedze aplikacia bezi vzdialene po sieti a bez vlastnych okien, nie je nutne riesit ziadne medziukladanie dat na lokalnom pocitaci, aplikaciu nie je mozne najst ani podla vytvoreneho okna, ani podla beziaceho procesu a prakticky skoro neexistuje sposob, ako sa pomocou X servera tejto aplikacie zbavit.

Aby to ale nebolo take jednoduche, dnesne X servery vedia vypnut rozhranie, ktore reaguje na prichodzie spojenia zo sveta a navyse, aby sa vylucili lokalne utoky, musi sa kazdy klient voci serveru autentifikovat tzv. MIT-cookie. Je to maly balicek dat, ktory pri vytvoreni spojenia musi klient poslat serveru pred tym, nez mu server povoli vykonat akekolvek prikazy, alebo posle akekolvek informacie o stave servera. Tento subor je ulozeny v domovskom adresari uzivatela, takze vsetky aplikacie jedneho uzivatela mozu bezproblemovo s X serverom komunikovat, zatial co aplikacie inych uzivatelov budu odmietnute.

Ak je nutne povolit pristup k serveru aj inym uzivatelom, pripadne uplne vypnut autentifikaciu, je to mozne pomocou prikazu:

xhost + [host]

Prikaz bez zadaneho hosta sposobi uplnu deaktivaciu autentifikacie, ak je zadany host, server by danemu hostovi mal povolit pristup k serveru, co vsak nie vzdy funguje. Pre viac informacii konzultujte manual.

Kedze dnesny diel som napisal v pokrocilej nocnej hodine a podstatna cast jeho obsahu bola viacmenej nudnym vypisom udalosti, je dost mozne, ze som nieco nesformuloval presne, zrozumitelne, alebo som spravil chybu. Nebojte sa pytat, na vsetky otazky sa budem snazit odpovedat. Takisto pripomienky zapracujem, ak budu mat zmysel a nebudu presahovat rozsah tejto casti. V dalsom dieli by som sa konecne pozrel na graficke toolkity a moznosti manipulacie s oknami.

    • Fronta? 23.07.2008 | 23:24
      Avatar blackhole_srnec   Používateľ

      Existuje nieco ako fronta sprav alebo nejaky buffer eventov s ktorym dokazes pokrocilejsie pracovat?
      Ako je osetrene vlastnictvo okien na session a generovanie eventov krizom cez ne?

      +++
      • Re: Fronta? 24.07.2008 | 00:10
        Avatar blackhole_ventYl   Používateľ

        priamo protokol buffering nepozna, ale Xlib vsetky prijate spravy bufferuje, vytahujes si ich z buffru, ked mas cas. aplikacia ma vacsinou dedikovany thread, pripadne beha v main loope v ktorom caka na spravy. na vyberanie sprav existuje niekolko funkcii. klasicka funkcia xgetmessage, alebo ako sa vola (to je nepodstatne), ti vybere vzdy prvu prijatu spravu, takze pomocou nej vyberas udalosti v poradi, v akom prisli. existuju ale dalsie funkcie, mozes vyberat spravy podla toho, akeho su typu. vies si otestovat frontu, ci obsahuje spravu nejakeho typu, pripadne pre nejake okno a navyse si vies napisat filtrovaciu funkciu a potom vybrat spravu z fronty podla toho, ci prejde, alebo neprejde filtrovacou funkciou, takze vies s frontou pracovat vcelku komplexne.

        obskakovanie sprav pri vyberani z fronty sa pouziva, ak pri nejakej akcii je predpoklad, ze moze byt dorucenych viac suvisiacich sprav. ako priklad, ktory som videl implementovany, bolo stlacanie tlacidiel. ked dostanes udalost o kliku, tak sa pozries, kolko takych udalosti mas vo fronte a podla toho urcis, ci sa jedna o single click, double click, alebo tripple click, alebo pri tahani, mozes skipovat udalosti, na ktore mas uz vo fronte ich naslednikov a tak znizit zatazenie procesora pri processingu udalosti o ktorej uz teraz vies, ze jej processing je zbytocny.

        no a vlastnictvo okien a sessny. myslis tym trebars, ze vypnem pocitac a desktopove prostredie ulozi moju session? to sa riesi specialnymi protokolmi a posielanim uzivatelskych sprav typu: "idem sa vypnut, uloz si svoj stav", neskor sa to riesilo tak, ze hlavne okno malo pripnuty stitok s tym, ako program spustit a cosi sa sibrinkovalo s kniznicou ICE, ktora je pomala, derava, zabugovana a obecne zla. ako sa riesi session najnovsie, som neskumal.

        a samozrejme, ked session ukoncis a X server skonci, vsetky vazby sa pretrhaju, cize okna objektivne prestanu existovat. pri novom spusteni session sa vytvoria nove okna s novymi IDckami a nastaveniami, takze nejake riesenie traverzovania udalosti sessionami sa riesit nemusi.
        ---
        Cuchat s nadchou, to je ako sniffovat bez promiscu.

        --- Cuchat s nadchou, to je ako sniffovat bez promiscu.
        • Re: Fronta? 24.07.2008 | 21:49
          Avatar blackhole_srnec   Používateľ

          Dik za odpoved. V druhej casti otazky som myslel vlastnikov okna a uzivatelsku session, napr.: na jednom desktope mozes mat okno vlastnene interactive userom ale aj okno patriace rootovy ale niekomu inemu (sudo, su). Mozem poslat spravu oknu ktore mi nepatri? Mozem poslat spravu alebo generovat event do inej session? Napr. win32 services mozu byt interactive, to znamena za sa zobrazuju ich okna prave prihlasenemu userovy, takemuto oknu mozes posielat spravy (a injektovat tak aplikaciu, co sa vyuziva pri strikovani).

          +++
          • Re: Fronta? 25.07.2008 | 00:28
            Avatar blackhole_ventYl   Používateľ

            no, kedze X server netusi v podstate nic o tom, akemu uzivatelovi patri to - ktore okno, mozes absolutne lubovolne rozosielat eventy po celom serveri, absolutne lubovolne catchovat eventy. na tom je napriklad zalozeny ten priklad remote keyloggera z konca clanku. pripoji sa na otvoreny X server niekde na inej masine a necha si posielat vsetky eventy.

            ---
            Cuchat s nadchou, to je ako sniffovat bez promiscu.

            --- Cuchat s nadchou, to je ako sniffovat bez promiscu.
    • Re: X11 under cover #2: Eventy pod drobnohladom 28.07.2008 | 19:18
      karinqe   Návštevník

      The State of X.Org [slashdot]
      Podla komentarov je kod X serveru riadny bordel, moc ti to nezavidim. Pisalo sa tam aj nieco o spraveni forku, ale nic konkretneho.

      Super serial, dufam ze bude pokracovat (este citam).

      -----
      42

      • Re: X11 under cover #2: Eventy pod drobnohladom 28.07.2008 | 20:03
        Avatar blackhole_ventYl   Používateľ

        no, ani nie, ze je to strasny bordel, ale skor, ze ten kod nevyhovuje dnesnym poziadavkam vzhladom k tomu, ako je rozlozeny vykon. v dobach, ked sa X protokol vyvijal (roky 1984 (X1) - 1987 (X11)) v UNIXovom svete platilo, ze veskere systemove prostriedky (pamat, vypoctovy vykon, graficky akcelerator a pod.) bol tam, kde bol server. klienti boli obvykle velmi nevykonne masiny. preto sa veskery rendering pisma, veskere uskladnovanie pixmap, vypoctova logika sustredovali na serveri. Za 18 rokov sa ale situacia zmenila a klient disponuje rovnakym, pripadne vacsim vypoctovym vykonom, ako server (ma k dispozicii nepomerne viac informacii k vykreslovaniu, ako server) a je zbytocne centralizovat rendering do servera.

        preto sa vyvijaju platformy, ako Cairo pre 2D akcelerovanu grafiku, OpenGL 3.0 (alebo 2.0?) pre 2D akcelerovanu grafiku, FreeType pre renderovanie pisma, ktore vsetky bezia na klientskej strane. Tak klient ziska vacsiu kontrolu nad vykreslovanim.

        apropos, kto by mal zaujem, mam na disku rovnomenny film, zaznam z prednasky GSoC. Akurat je to dost velke.
        ---
        Cuchat s nadchou, to je ako sniffovat bez promiscu.

        --- Cuchat s nadchou, to je ako sniffovat bez promiscu.