Terve vaan kaikki...
Elikkäs, olen tässä nyt pari päivää tutkinut, että kuinka onnistuu oman WM:n luonti Linux:in X palveluun.
Noh löysin googlesta tinyWM nimisen hyvin kevyen WM:än ja aloin tutkia sen massiivista lähde koodia (noin 50riviä). No nytten tiedän kuinka voidaan luoda yksin kertainen WM:ä.
NOh nytten on suurena kysymys merkkinä, että millä tavalla ikkunoille voisi piirtää reunat?
ELi tinyWM:ssä ikkunan liikutus, koon muutokset ynnä muut tehdään näppäimistön avulla.
Eli siinä ei näytetä miten voisi piirtää ikkunalle reunat, eli ajattelin kysyä jos joku sattuisi täällä tietämään.
siis onko sun tarkoitus tehdä vaan GUI vai koko roska x:n päälle?
jos GUI niin suosittelisin ennemmin vaikka GTK+ tai vastaavaa
Noh lähinnä ainakin aluksi pelkkä GUI, mutta miks ihmeessä käyttää GTK+:ssaa jos voi käyttöö Xlib:iä?
GTK+ käyttää ymmärtääkseni pohjana Xlib:iä ja ainut sen hyvä puoli on se, että se on ehkä hieman helpompi kuin suoran Xlib:in käyttö.
oletin että näin aloitteluvaiheessasi opettelet tekemään, ja yksi helpoimmista tavoista on gtk+:lla vääntää.
yleensä kun nuo perus guit sisältää jotain nappeja ja comboboxeja sun muuta, ja harjoitellaan eventtien käyttöä.
Joo no itsekkin mietin tuota GTK+ sekä myös QT4, mutta X:än oma kirjasto on siinä hyvä, että jos sen opettelee heti alkuun sen käytön, niin WM:tä tulee varmasti kevyempi kuin esim. GTK+:lla tai QT4:lla tehtynä. Kirjastota tuovat meinaan aina kaikkea lisä sontaa softaan mukaan.
...tietääkö kukaan miten voin hakea pääikkunan kaikki aliikkunoiden osoitteet?`
miten sä olet ne tehnyt?
itse olen aina periyttänyt ali-ikkunat pääikkunan luokasta joten ei ole ollut ongelmia niiden löytämisessä ;)
itsellä ei kokemusta xlibistä ole, mutta löytyisikö täältä apua?
itse olen toistaseksi aina vaan löytänyt ikkunan sen mukaan minkä päällä hiiri on. ELi siis jos hiiri on ikkunan päällä ja painetaan hiiri pohjaan event palauttaa ikkunan pointterin ja sitten voidaan liikuttaa ks. ikkunaa.
Tuota periytymis härdelliähän tässä olen yrittänytkin pähkäillä? Miten tuo tapahtuu? Olen yrittänyt kaivaa vastausta eri WM:ien lähe koodeista, mutta mitään selvää vastausta ei ole vielä tullut esille.
Noniin, eli ikkunamankelit. Itselläni on jo jonkinmoista kokemusta näiden tekemisestä, useita erilaisia (lähes) toimivia on tullut viriteltyä. Sinulla on useita vaihtoehtoja siihen millä tavalla ikkunoiden hallinta toteuttaa. Wikipediasta löydät näin yleisesti selityksiä erilaisista tavoista, ja esimerkkejä näistä (http://en.wikipedia.org/wiki/X_window_manager). Näin olettaisin sinulle sopivimmaksi (ja millä tälläiset "normaalit" ikkunamankelit tehdään) on reparentointi. Eli luot jokaiselle ikkunalle äiti-ikkunan, jonka alle reparentoit ikkunan. Samalla luonnollisesti nappaat XSelectInputilla hallittavalta ikkunalta SubstructureRedirectMaskilla halutut eventit. Tällä tavalla ikkunan halutessa esim. muuttaa kokoaan saat tietää tästä ennen kuin se oikeasti tapahtuu. Näin voit reagoida tälläisiin haluamallasi tavalla. Useimmat (kaikki?) ikkunoille kunnon reunukset piirtävät wm:t ovat reparentoivia. Tuosta wikipedian artikkelista löydät gnomen ja kde:n oletusikkunamankelit metacityn ja kwinin Compositing Window Managersin alta. Älä kuitenkaan tästä välitä, ne ovat yhtä lailla myös reparentoivia. Tuo compositing on X:n laajennus, joka mahdollistaa monenlaisten graafisten kikkojen ja ikkunoiden sisällön kopioimiseen ja säilyttämiseen tarkoitettuja ominaisuuksia. Sinä et näitä vielä pitkään aikaan tule tarvitsemaan kun edes perusasiat eivät ole hallussa. Tuossa artikkelissa mainitaan tilettävät wm:t (kohta xmonadin käyttäjät tulevat korjaamaan näkemyksiäni). Näiden ideana on useimmiten maksimoida käytetty näyttöpinta-ala levittämällä ikkunat mahdollisimman tehokkaasti. Näissä ei perinteisesti fokuksen ilmaisevia kapeita kehyksiä kummempaa yleensä piirretä ikkunoiden reunalle. Mikään ei tietenkään estä sinua tilettämästä kunnolla kehystettyjä ikkunoita.
Tuota groovyb:n dokumentaatiota kannattaa ahkerasti lukea (miksi en ennen ole moiseen törmännyt? Olen joutunut kiikuttamaan tronchen huonommilla ohjeilla ja googlella). Sieltä löytyy apua moneen asiaan. Ja sitten ne man paget. Sieltä löytyy moneen asiaan apu myös. Ja sitten tarkempaa tietoa tuosta yleisestö toimintalogiikasta. Tutkipa twm:n sourcea. Siinä ei niin paljon ole koodia, mutta kaikki asiat mitä yksinkertaiseen ikkunamanageriin tarvitset, löytyy viimeistään sitä tutkimalla.
Öö, eli siis haen ekan pääikkunan komennolla DefaultRootWindow. Sitten avaan ruutuun vaikka konsolin (xterm):in niin mistä tiedän, että se ikkuna on ylipäätään auennut? Ilmoittaako tuoa ReparentNotify asiasta vai mitä ja mistä tiedän, että pitää luoda uusi "äiti ikkuna"?!
Otat "pääikkunasta" (root) XSelectEventillä SubstructureRedirectMaskin kanssa CreateNotifyMaskin.
tarkoitatko tähän malliin?
//Main loop alkaa for(;;) { //Haetaan seuraava tapahtuma, tapahtuma listasta XNextEvent(dpy, &ev); //Lähetetään tapahtuma joka tarkistaa onko uusia ikkunoita tullut ??? XSendEvent(dpy, root, false, SubstructureRedirectMask, &ev); //Tapahtuma käsittelijä switch( ev.type ){ // Jos uusi ikkuna on luotu ilmoitetaan siitä tulostamalla teksti... case CreateNotify: cout << "Ikkuna luotu!" << endl; break; } ....
...tuo ei ainakaan toiminut minulla :S
No ei se tietenkään toimi, tuossa sinä lähetät eventin, ja tuo maski ei edes ole eventin tyyppi. Eli ennen mainloopia varaat tarvittavat eventit rootilta, olen sen varsin hyvin kertonut aiemmissa viesteissä. Tämän jälkeen sinä saat näitä eventtejä.
Aivan!
Eli siis näinkö se tulisi olla sitten?
XSelectInput(dpy, root, SubstructureRedirectMask); for(;;) { XNextEvent(dpy, &ev); switch( ev.type ){ case CreateNotify: cout << "Morso!" << endl; break; }
...nytten kun ikkuna luodaan niin softa kaatuu.
Ja mites se kaatuu? Segfault vai x:n oma errori?
X:n oma, eli palauttaa jotain muistipaikkoja muistaakseni...
BadAccess errori jotain major opcodesta ja serialista ynnä muusta vinee...
ELi siis se ilmoittaa tällästä
X Error of failed request: BadWindow (invalid Window parameter) Major opcode of failed request: 2 (X_ChangeWindowAttributes) Resource id in failed request: 0x100 Serial number of failed request: 428 Current serial number in output stream: 431
Kristallipalloni sanoo että et ole hakenut tuota roottia ennen XSelectEventsin kutsumista.
Kylläpäs olen... :D
if(!(dpy = XOpenDisplay(0x0))) return 1; root = DefaultRootWindow(dpy); XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync); XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); // system("konsole"); XSelectInput(dpy, root, SubstructureRedirectMask); for(;;) { XNextEvent(dpy, &ev); switch( ev.type ){ case CreateNotify: cout << "Morso!" << endl; break;
...hmm, jotenkin hämärästi nytten se ei heitäkkää enää tota virhettä? Mutta nytten kun yritän avata uutta ikunaa niin ikkuna ei aukea, mutta kun suljen WM ja koitan avata uutta ikkunaa niin se aukeekin. Eli pitääkö tuonne CreateNotifyyn määritellä jotain ikkunan aukeemis shettoja???
Hyvä, eli olet jo saanut sen ohjaamaan eventit oikein. Nythän tässä käy niin, että ikkunan halutessa mapata itsensä (rtfm, XMapWindow) se ohjataan wm:llesi MapRequestina. Eli ikkunan mappaus muuttukin SubstructureRedirectMaskin kanssa pelkäksi pyynnöksi, joka lähetetään wm:lle. Tämän jälkeen on wm:n asia päättää mitä tehdään. Tässä vaiheessa (tai jo CreateNotifyn tullessa) kannattanee ensiksi grabata x (XGrabServer) jolloin mitään muutoksia ei ehdi tapahtua ikkunoihin. Tässä vaiheessa tarkistat ikkunan olemassaolon kutsumalla funktiota XGetWindowAttributes (kunnollisilla parametreilla) ja tarkistamalla paluuarvon. Muista asettaa oma error handler, tai softa kaatuu ikkunan puuttuessa (XSetErrorHandler). Tämän jälkeen tutkit ikkunaa; Minkälainen se on, onko jotain erityistä huomioitavaa (override_redirect (katso WM_HINTS), pidemmälle edistyneenä noudatetaan ewmh:ta (googleta) ja mahdollisia muita asioita. Sitten pääset vihdoinkin tekemään jotain: teet ikkunalle äiti-ikkunan jonka alle sen reparentoit ja muuta vastaavaa. Muista aina lopuksi vapauttaa serverigrab(XUngrabServer)! Muista myös mapata se ikkuna.
Ahaa, pitääpä kokeilla tuota heti kun saan taas tuon wm:än toimimaan. On meinaa aika outo juttu, kun käytän pohjan tuota tinyWM (http://incise.org/tinywm.html), niin välillä tuo koodi toimii ja välillä ei?
Entiedä mikä on vikana, laitoin .xinitrc:en exec xterm:in eli käynnistän xterm:in startx:än yhteydessä ja sitten käännän koodion ja suoritan softa ./a.out. mutta sitten välillä ei taphadu mitään ikkunoita ei voi liikuttaa eikä mitään.
En usko, että xorg.conf:ssa on jotain vikaa, koska koodi toimii kuitenkin aina väillä ja Fluxboxikin toimii aina täydellisesti. Lähdekoodissakaan ei luultavasti ole mitään vikaa, koska käänsin TinyWM:än alkuperäsen lähdekoodin ja kokeilin käynistää niin mitään ei tapahdu, ohjelma ei sammu mutta ei muutenkaan tapahdu mitään...
Outo homma...
TinyWM:än lähdekoodi (toimii välillä)??? :O
/* TinyWM is written by Nick Welch <mack@incise.org>, 2005. * * This software is in the public domain * and is provided AS IS, with NO WARRANTY. */ #include <X11/Xlib.h> #define MAX(a, b) ((a) > (b) ? (a) : (b)) int main() { Display * dpy; Window root; XWindowAttributes attr; XButtonEvent start; XEvent ev; if(!(dpy = XOpenDisplay(0x0))) return 1; root = DefaultRootWindow(dpy); XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync); XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None); for(;;) { XNextEvent(dpy, &ev); if(ev.type == KeyPress && ev.xkey.subwindow != None) XRaiseWindow(dpy, ev.xkey.subwindow); else if(ev.type == ButtonPress && ev.xbutton.subwindow != None) { XGrabPointer(dpy, ev.xbutton.subwindow, True, PointerMotionMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr); start = ev.xbutton; } else if(ev.type == MotionNotify) { int xdiff, ydiff; while(XCheckTypedEvent(dpy, MotionNotify, &ev)); 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))); } else if(ev.type == ButtonRelease) XUngrabPointer(dpy, CurrentTime); } }
Ör? Sinuna tekisin tuon niin että laittaisin vaikka exec softa_joka_ajaa_ikusessa_loopissa, ja käynnistät wm:si ja xtermin konsolissa. Kyllä tuon pitäisi toimia, olet tehnyt nyt jotain ihan hassusti.
...pitää kokeilla, mutta kyllä siis tuo aina välillä toimii. Mutta kokeilen sitä, että teen oman exec softan vaikka execvp ja fork:ia käyttäen... :)
Edit1:
Tein tuon loaderin ,mutta ei näytä toimivan. En sitten tie voisko jotenkin näppäinasetukset tai jotkut tehdä tuon ettei toimi? Meinaa tuo wm kyllä on päällä, mutta jostain syystä ei vain voi liikuttaa ikkunoit. Miten itse teet tuon wm:än kutsun, koska periaatteessa .xinitrc:hen lisättynä sen pitäisi käynnistyä ihan oikein startx:än yhteydessä....
Mistäs tiedät ettei se lähde käyntiin?
Siis se wm lähtee käyntiin, mutta jostain syystä ikkunoita ei vain voi siirtää. Tiedän sen siitä että se on käynnisttä, koska jos yritän käynnistää kosnolin kautta toista wm:ää niin valittaa, että wm on jo käynnissä.
Eli jostain syystä joku vain kusee... Kokeilen nytten vaihtaa linux distroa jos vaikka auttais. :P ...mutta pakko se on olla koodissa tai jossian, koska Fluxboxi toimii täydellisesti.
Tosin X kyllä valittaa jotain näppäimistöstä, mutta ei se näytä haittaavan silti Fluxbox:in käyttöä.
The XKEYBOARD keymap compiler (xkbcomp) reports: > Warning: Type "ONE_LEVEL" has 1 levels, but <RALT> has 2 symbols > Ignoring extra symbols Errors from xkbcomp are not fatal to the X server
Huomaa kohta "Errors from xkbcomp are not fatal to the X server"
Edit1: Tää meidän keskustelu alkaa kyllä menee sen verran chat:sen puolelle, että oisko mahdolista jos jelppisit muo irkistä tai jonkun muun softan kautta?
...vaihdoin distroa ja nytten toimii taas. Pääsen vihdoin kokeilee tuota sinun esimerkkiäsi. Sellanen kysymys kylläkin heti alkuun, että miten tuo uusi ikkuna istten luodaan, eli siirrääkö että luo vaan Window tyyppisen muuttujan ja reparentoi sen siihe vai pitääkö se luoda oikein XCreateWindow funktiolla?
Ite sähelsin tälläseen?? Ensimmäinen ikkuna aukeaa oikein, mutta toistaku nyrittää avata niin WM kaatuu??!?!? :P
void reparent(Display *dpy, Window root, XEvent ev){ Window winn; XWindowAttributes attr; XGrabServer(dpy); XGetWindowAttributes(dpy, ev.xcreatewindow.window, &attr); winn = XCreateSimpleWindow(dpy, root, 0, 0, 100, 100, 1, 0xff, 0x00); XReparentWindow(dpy, ev.xcreatewindow.window, winn, 0, 0); XReparentWindow(dpy, winn, root, 0, 0); XMapWindow(dpy, winn); XUngrabServer(dpy); } //Loopissa case CreateNotify: reparent(dpy, root, ev); break;
Olisit taas kerran voinut kertoa miten se "kaatuu". Jos se oli x:n oma errori, kristallipalloni sanoo että errorin tyyppi on BadWindow, Eli ikkunaa ei ole olemassa. Sen aiheuttajaksi veikkaisin sitä, että et aseta errorhandleria ennen XGetWindowAttributesia. Lisäksi et tarkista sen paluuarvosta onko ikkunaa olemassa. Jos se failaa, ikkunaa ei ole, ja voit vapauttaa serverigrabin ja poistua funktiosta. Lisäksi suosittelisin tuon tekemistä vasta MapRequestin saapuessa. Lisäksi edelleen suosittelisin sen override_redirectin tarkistamista. Eli jos se on true, et tee ikkunalle mitään.
Noh sain tälläseen väkerettyä, tosin wm ei kaadu nytten, mutta uutta ikkunaakaan ei pysytä luomaan?? Tai siis kun yritän käynnistää xterm:iä se ei käynnisty..
static XErrorHandler *rtHandler; static int badwindow_handler(Display *dpy, XErrorEvent * err){ if( err->error_code != BadWindow ) return (*rtHandler) (dpy, err); else return 0; } void reparent(Display *dpy, Window root, XEvent ev){ Window winn; XWindowAttributes attr; XGrabServer(dpy); if( XSetErrorHandler(badwindow_handler) != 0 ){ XGetWindowAttributes(dpy, ev.xmaprequest.window, &attr); winn = XCreateSimpleWindow(dpy, root, 0, 0, 100, 100, 1, 0xff, 0x00); //XReparentWindow(dpy, winn, root, 0, 0); XReparentWindow(dpy, ev.xmaprequest.window, winn, 0, 0); XReparentWindow(dpy, winn, root, 0, 0); XMapWindow(dpy, winn); XMapWindow(dpy, ev.xmaprequest.window); } XUngrabServer(dpy); } //main switch( ev.type ){ case MapNotify: reparent(dpy, root, ev); break; }
Ärh, mistä tuo MapNotify ilmestyi? *wink*
Ööh, ymmärsin, että se pitää hakee MapNotify:llä, koska:
kray kirjoitti:
Lisäksi suosittelisin tuon tekemistä vasta MapRequestin saapuessa.
...mutta ei tuo kyllä toimi CreateNotify:kään. :/
Jos et huomannut, MapNotify != MapRequest
Tjoo tajusin kyllä, mutta mutta... Ei tuo kyllä toimi tuossa CreateNotify:kään kutsuttuna... Ja eihän MapRequestille ole omaa Notifeensa vai onko?
/me hakkaa päätään seinään
Etkö tajua että tarkoitin tuota reparentia kutsuttuavan eventin MapRequest saapuessa?
Joo, mutta eikös se ole periaatteessa sama tehdä CreateNotifys:sä?
Ei?
Ymmärtääkseni tuo MapRequesti eventti sadaan silloin kun Mapataan ikkuna eli MapWindow funktion avulla. Sitten hän se on sama tehdäänkö reparent CreateWindow:ssa kuin MapRequest event:in saadettua. Tai ainakin näin itse ymmärtäisin asian, mutta sinähän sen kyllä varmaan paremmin tiedät...
...pitää kokeilla. :)
Argh! CreateNotify tulee kun ikkuna luodaan. MapNotify tulee kun ikkuna mapataan, MUTTA JOS rootilta on valittu SubstructureRedirectMask (TAI asianomaiselta ikkunalta StructureRedirectMask), ikkunaa ei voi mapata muu kuin maskin valinnut client; muiden clienttien mappausyritykset päättyvät MapRequestin lähettämiseen maskin valinneelle ikkunalle. Tuon reparentoinnin tekeminen heti luotua ei ole hyvä tapa, koska ikkuna ei ole vielä "valmis": kaikkia hintsejä ei ole vielä asetettu (ikkunaa ei välttämättä ole tarkoitettukaan mapattavaksi koska se on ns. tietoikkuna (http://standards.freedesktop.org on tietoa näistä stantardeista), tai ikkuna on override_redirect).
...joo nytten toimii. Kiitos!
...yksi kysymys vielä. Miten saan selvitettyä ikkunat jotka ovat auenneet ennen WM:än käynnistymistä?
Eli jos käynnistän eka X:ään xtermin ja xtermin kautta käynnistän sitten wm, niin miten saan selvitettyy sen xterm ikkunan?
Wm:n käynnistyessä grabbaat x:n, katsot kaikki rootin lapsi-ikkunat XQueryTree:llä (muistaakseni), tutkit jokaisesta Attributeseista, hintseistä yms. onko se normaali toplevel-ikkuna (eikä informaatio-, override_redirect (kannattaisi alkaa tarkistamaan sitä!) tai esim icon-ikkuna (iconit tarkistat kelaamalla kaikki ikkunat läpi ja merkkaamalla jotenkin että se on icon, jolloin jätät sen huomioimatta normaalissa reparentoinnissa)), ja sen jälkeen teet tuon reparentoinnin kaikille ikkunoille jotka osoittautuivat normaaleiksi, ja vapautat serverigrabin.
Ahaa, suur kiitos sinulle kray. En olis ilman sinua saanut mitään aikaseksi. Sellanen päättävä kysymys vielä, että miten saan tuon MotionNotifyn saapumaan vain ja ainostaan sillooin kun ikkunan ylä reunaan kosketaan, eli olen asettanut "äiti ikkunalle" 3 px paksun sinisen reunan ja nytten kun tein sellaseen xmotion.y:llä, että jos hiiri on ikkunassa alle 4 px nii sitä voidaan liikutta. Muuten toimii hienosti, mutta ikkuna lähettää sili MotionNotifyn, vaikka koskenkin yli 4px.
Eli lyhyesti ja ytimekkäästi, miten voin tehdä niin, että MotionNotify tulee vain ja ainostaan jos yläreunaan kosketaan. :)
Tuota, suosittelisin toisenlaista tapaa: jokaiselta luomaltasi äiti-ikkunalta valitset ButtonPressMaskin (ehkä tarvitsi jonkun Button1PressMaskin), ButtonReleaseMaskin ja EHKÄ tarvitsi myös StructureNotifyMaskin. Kun saat ButtonPressin, tarkistat ikkunan olevan joku äiti-ikkuna, napin olevan oikea, ja sijainnin olevan sopiva. Sitten kaappaat pointterin XGrabPointerilla, MotionNotifyn tullessa siirrät ikkunaa, ja ButtonReleasen tullessa vapautat pointterin. Tee nämä samalla tavalla kuin TinyWM:ssä, se on erinomainen esimerkki.
Olen tehnytkin juuri noin, mutta vaikka painan itse äiti ikkunan sisällä olevaa lapsi ikkunaa, ButtonPressi palauttaa aina window:na rootin ja subwindow:na äiti ikkunan. Eli siis miten sen sais sillai, että jos painetaan lapsi ikkunaa niin se palauttais myös sen lapsi ikkunan eikö aina sitä äiti ikkunaa???
Mihin sinä sitä lapsi-ikkunaa tarvitset?
Siis en tarvitsekkaan sitä mihinkään, vaan jostain syystä se palauttuu sieltä. Eli jos painan siitä lapsi ikkunasta eli tässä tapauksessa xterm:tä niin se palauttaa sen äiti ikkunan vaikka koskettiinkin oikeasti lapsi ikkunaan....
Aihe on jo aika vanha, joten et voi enää vastata siihen.