5 1 9 Näyttötilat.IFF 56 0* {3 Järjestelmäohjelmoinnin alkeiskurssi - Osa 3: Intuition {3 ------------------------------------------------------- Sami Klemola Kurssin tässä osassa jatkuu teema ohjelmoinnin perusasioiden ympäriltä. Katsomme, miten C-kieliseen ohjelmaan voidaan sisällyttää konekielisiä aliohjelmia, miten parametrit välitetään niille ja miten muuttujia osoite- taan ohjelman osien välillä. Käyttöjärjestelmän tutkiminen jatkuu Intuitio- niin. Sen tehtävänä on ylläpitää graafinen käyttöliittymä ja paljon muuta. Tutustumme nyt siihen alustavasti. Tässä osassa käsittelyssä ovat Intuitionin ruudut ja ikkunat. Seuraavassa osassa jatkamme käsittelemällä Intuitionin muita toimintoja, kuten reques- tereita ja alertteja. Myös ikkunoihin liittyvät toiminnot, kuten valikot ja nappulat käsitellään jatkossa. Tässä osassa oleva koodi tarvitsee yleensä toimiakseen Release 2:n. Osa koodista kuitenkin toimii myös 1.x:llä. Kurssin viimekertaisessa osassa oli heti johdannossa osa eräästä virkkeestä kadonnut jonnekin. Virkkeen ensimmäinen lause kuului: "sitten käännetään vielä makroassemblerilla". Ilmiselvästi jotakin puuttuu alusta. Ilmeisesti tarkoitin C-kääntäjän tuottamaa assemblykoodia, mutta olin jotenkin onnis- tunut hukkaamaan virkkeen alun. Se puuttui jo itselläni olevasta raakakap- paleesta, joten tällä kertaa syy ei ollut päätoimittajan (kun oppisit eka toimittamaan juttusi aikataulun puitteissa, voisit valitella virheistäni - kiiressä kun niitä sattuu helposti, toim. huom.). {3Konekielikoodi C-ohjelmassa {3--------------------------- Monesti C-kieliseen ohjelmaan kirjoittaa mielellään joitakin toimintoja ko- nekielellä. Jotkut asiat on vain helpompi tehdä sillä. Erikoisimmat silmu- kat voivat olla selvästi nopeampiakin suoraan konekielellä kirjoitettuina, koska C-kääntäjä ei koskaan osaa optimoida koodia optimaalisesti! Konekie- lisen koodin voi kirjoittaa funktioksi, jota kutsutaan samalla tavalla kuin C-kielisiäkin funktioita. Sille voidaan antaa parametrejä, ja siltä saadaan mahdollisesti palautusarvo. Oman koodin kirjoittaminen C-kääntäjän luomaan ympäristöön voi olla jok- seenkin kiperää. Pääohjelman rekisterimuuttujia ei saa sotkea, ja kaiken on oltava kohdallaan palattaessa sinne. Perinteisesti parametrit välitetään pinossa, josta ne on noukittava. Erityisen vaikeaksi homma menee, jos kone- kielikoodista kutsutaan C-kielistä funktiota. Tällöin meidän pitää itse työntää sen parametrit pinoon ja muistaa huolehtia pinon palauttamisesta alkuperäiseen tilaansa. Lisäksi tulee kutsun ajaksi palauttaa alkuperäiset rekisterit, jos niitä vaikka tarvittaisiin. Ainakin A4:n sisältö on palautettava, koska sitä käytetään data-alueen osoittamiseen. A4:ssä on osoitin datahunkkiin, ja sen kautta tehdään kaikki osoitukset sinne. Tässä on ongelma. Konekielikoodista voisi osoittaa C-kielisessä osassa määriteltyjä muuttujia suhteessa A4:ään, mutta ilmeisesti vain PhxAss osaa merkitä objektikoodiin tällaisen osoituk- sen oikein. Aikaisemmin kehumani SNMA ei sitä osaa, kuten ei mikään muukaan kokeilemani assembleri. PhxAss vain taas on muuten lähinnä tarkoitettu de- mojen tekemiseen. Se ei edes ymmärrä exec/types.i-tiedoston määrittelyjä! Tässä vaiheessa on jo syytä käydä toimeen. Ajatelkaamme, että meillä on C- ohjelma, josta kutsutaan konekielistä calc()-funktiota. Sen tehtävä on las- kea kaksi lukua yhteen ja vähentää kolmas. Funktio on syytä prototyypätä oikein, eli ohjelman alussa määritellään tarkalleen sen olemus. Tässä ovat olennaiset osat C-kielisestä osuudesta: extern long calc(long,long,long); <...> printf("2 + 3 - 4 = %ld\n",calc(2,3,4)); Koodi tulostaa calc()-funktion palauttaman luvun, joka siis on ensimmäinen parametri plus toinen parametri miinus kolmas parametri. Tässä on funktion konekielinen lähdekoodi kokonaisuudessaan: section calc,code xdef _calc ; sisällyttää rutiinin osoitteen objektikoodiin _calc move.l 4(sp),d0 ; ensimmäinen parametri add.l 8(sp),d0 ; lisätään siihen toinen parametri sub.l 12(sp),d0 ; ja vähennetään summasta kolmas rts ; tulos on valmiiksi D0:ssa Eli ei tämä niin vaikeaa ole. Funktion parametrit työnnetään pinoon viimei- sestä alkaen, joten kun ensimmäisenä 0(sp):ssä oleva paluuosoite jätetään väliin, ovat parametrit pinossa ensimmäisestä alkaen, ja sieltä ne haetaan sp:n kautta indeksin avulla. Palautuskoodi sijoitetaan D0:aan aivan kuin ohjelmankin. Normaalisti parametreillä ei operoida suoraan pinosta, vaan ne ladataan ensin rekistereihin. On myös mahdollista käyttää nk. regargs-toi- mintoa, jolloin C-kääntäjä sijoittaa parametrit suoraan rekistereihin. Vaikeammaksi koodin kirjoittaminen menee sitten, kun funktiossa tarvitaan enemmän rekisterejä. Tällöin alkuperäiset rekisterit on tallennettava pi- noon, jolloin pino-osoittimen arvo luonnollisesti muuttuu. Tämä on otettava huomioon parametrejä noukittaessa. Jokainen pinoon tallennettu rekisteri kasvattaa indeksin arvoa neljällä. Tässä on toinen edelliseen kuulumaton esimerkki, jossa tallennetaan kaksi rekisteriä pinoon: movem.l a2/a3,-(sp) movea.l 12(sp),a2 <...> movem.l (sp)+,a2/a3 rts Nyt ensimmäinen parametri onkin +12:ssa, koska pinossa ovat ensin rekiste- rien A2 ja A3 sisällöt ja paluuosoite. Konekielikoodista voidaan kutsua kirjastofunktioita C-stubien kautta. Stubit ovat koodinpätkiä, jotka poimi- vat pinosta funktion parametrit ja lataavat ne rekistereihin, joissa funk- tiot ne haluavat. Esimerkiksi Delay(10) hoituu konekieliosuudesta näin: move.l #10,-(sp) jsr _Delay(pc) addq.l #4,sp Lopuksi pinorekisterin arvo korjataan tilannetta edeltäneeseen kuntoon. Ar- volla ei ole meille mitään merkitystä, joten sitä ei tarvitse ladata pinos- ta takaisin, vaan riittää, että pino-osoitin on kunnossa. Yllä oleva koodi ei kyllä ole mitenkään erityisen tehokas. On huomattavasti parempi kutsua systeemifunktioita suoraan, mutta silloin meidän tarvitsee saada selville kirjaston kantaosoite. Se onnistuu PhxAssilla kääntäen näin: section waitsec,code near a4 xref _DOSBase xref _LVODelay xdef _waitasec _waitasec move.l a6,-(sp) movea.l _DOSBase(a4),a6 moveq #50,d1 jsr _LVODelay(a6) movea.l (sp)+,a6 rts Funktio waitasec() odottaa sekunnin. DOS-funktio Delay() viivyttää ohjelman suoritusta halutun ajan, tässä tapauksessa 50 ruudunpiirtoa eli yhden se- kunnin verran. Koko toiminnan kulmakivi on toisen rivin near-direktiivi, joka määrittää rekisterin a4 toimimaan indeksirekisterinä osoitettaessa toisessa moduulissa olevaa dataa short-data modelissa. Normaalisti osoituk- set ovat long-data modelin mukaisia eli suoraan 32-bittisiä osoittimia, jotka täytyy asettaa kohdalleen ohjelmaa ladattaessa. DICE käyttää oletuksena short-data modelia, jossa dataa osoitetaan A4:n kautta. Kuten yllä näkyy, näin voidaan tehdä myös omassa konekielikoodissa, mutta kuten mainittua, tämä onnistuu vain PhxAssilla, koska se on ainut near-direktiivin osaava makroassembleri. Ilman near-direktiiviä viittaukset tehtäisiin normaaleina 32-bittisinä osoittimina, joilla small-data model ei toimi, koska siinä ulkoinen muuttuja saa indeksin arvon eikä suoraan osoi- tetta. Short-data modelista on myös se hyöty, että koodihunkin sisällä tapahtuvat osoitukset ja hypyt voidaan niin ikään tehdä suhteellisina, suhteellisina ohjelmalaskuriin. Kuten edellisessä esimerkissä näkyi, aliohjelmia voidaan kutsua PC-relatiivisesti. Näin ollen myöskään esimerkiksi käytetty _Delay ei ole osoitin funktioon, vaan indeksi siihen suhteessa ohjelmalaskuriin. Tästä kaikesta on rutkasti hyötyä. Ohjelma nopeutuu ja tulee lyhyemmäksi ja residentattavaksikin. Hunkkien koko ei kuitenkaan saa ylittää 32 kilotavua, koska indeksit ovat 16-bittisiä lukuja. Nämä asiat eivät kuitenkaan varsinaisesti kuulu tämän kurssin piiriin, vaan pikemminkin C-kurssiin, joten näistä ei tässä sen enempää. Nyt on aika kat- soa aivan oikean funktion lähdekoodia. Olen julkaissut ohjelman nimeltä TechUserMask, jonka avulla voi manipuloida käyttäjien oikeuksia. Käytin siinä _main()-entrypointia, enkä halunnut tuoda sisään hirveää määrää ylimääräistä koodia vain käyttääkseni printf():ää, joten kirjoitin kaksi omaa tulostusfunktiotani. Toinen niistä toimii kuin printf(), ja tässä ole- va ennemminkin kuin puts(). Se ei siis käsittele mahdollisia argumentteja. Alussa määritellään makro Lib, jota käytetään systeemifunktion kutsumiseen. Lib MACRO * function[,basereg] xref _LVO\1 ifnc '\2','' movea.l \2,a6 endc jsr _LVO\1(a6) ENDM section TechUserMask,code near a4 xref _DOSBase ; External reference to the base of dos.library xdef _rawprint ; prints an unformatted string _rawprint ; LONG rawprint(string); movea.l 4(sp),a1 _raw1 movem.l d2/d3/a6,-(sp) ; registered entry point (A1=string) move.l a1,d2 moveq #-1,d3 _raw2 addq.l #1,d3 tst.b (a1)+ bne _raw2 Lib Output,_DOSBase(a4) move.l d0,d1 Lib Write _raw3 movem.l (sp)+,d2/d3/a6 rts Palautuskoodi on tulostettujen merkkien lukumäärä, joka saadaan Wri- te():ltä. Funktio laskee _raw2-silmukassa merkkijonon pituuden ja tulostaa sen oletustulostuskanavalle, johon saadaan handle funktiolla Output(). DO- Sin kantaosoite saadaan C-kääntäjän toimittamasta muuttujasta DOSBase. Tämänkin koodin kääntämiseen tarvitaan PhxAss. PhxAss MC680x0/68851/6888x Macro Assembler V4.00 Written by F.Wille (Herford, GERMANY) in 1991-94 Pass 1 Pass 2 00 No error. 92 lines in 0.08 sec = 69000 lines/min. Global symbols: 19 Local symbols: 0 Bytes gained by optimization: 10 Code: 1 section(s) 114 bytes Data: none BSS: none Kokonaisuudessaan koodi jopa lyheni 10 tavua, koska PhxAss osaa myös opti- moida koodia erittäin hyvin, ja kuten näkyy, se on myös hyvin nopea. {3Ruudut {3------ Amigan graafisen käyttöliittymän ydin on Intuition. Sen tehtävänä on mm. huolehtia näytöstä. Näytöllä voi olla useita ruutuja, joilla kaikilla voi olla oma olemuksensa. Jokaisella ruudulla voi olla useita ikkunoita, jotka jakavat ruudun pienempiin osiin. Samalla ruudulla olevat ikkunat toimivat samanlaisessa ympäristössä, mutta toisella ruudulla voi olla erilaiset värit ja erilainen resoluutio. Jokaisella ikkunalla voi olla valikkonauha, ja siinä voi olla nappuloita ja valitsimia. Intuition tekee kaiken ylläpi- don, eikä ohjelman tarvitse olla huolissaan esimerkiksi ikkunoiden olemi- sesta päällekkäin. Ohjelman ei tarvitse avata omaa ruutua, vaan se voi avata ikkunansa Work- bench-ruudulle tai oletusruudulle, joka usein on Workbench. Käyttäjän kan- nattaa antaa valita ruudun ominaisuudet, ellei niitä sido jokin erityinen seikka. Yksi hyvä tapa on kloonata Workbench eli avata oma ruutu samanlai- seksi kuin Workbench-ruutu on. Tällainen esimerkki on luvassa. Ruutuja on kolmea tyyppiä: Workbench, public ja custom. Ainoastaan Workbench-ruutu on tyyppiä Workbench. Vanhat omat ruudut ovat tyyppiä custom, mutta nykyään ruudut avataan useimmiten julkisiksi eli tyyppi on public. Julkiselle ruu- dulle saa jokin toinen ohjelma avata oman ikkunansa. Mikäli sallii tämän, on otettava huomioon monia asioita, joilla ei tarvitse normaalisti päätään vaivata. Ruudun ominaisuudet on myös tiedettävä ennen sen avaamista. Omi- naisuudet sisältävät mm. ruudun koon, resoluution ja bittikarttojen määrän, joka määrää käytettävissä olevien värien määrän. Mikäli et todella tarvitse tarkkaa grafiikkaa, älä edellytä uudempia pii- rejä ja multisync-monitoria. Useimmat ohjelmat pärjäävät normaalilla hi- res-grafiikalla. Ruudun ominaisuudet kootaan NewScreen-struktuuriin sekä Tag-struktuureihin. Käyttöjärjestelmä 2.0 toi mukanaan tagit. Niitä käytetään joka puolella käyttöjärjestelmää parametrien välittämiseen. Tag- struktuuri koostuu kahdesta longwordista, joista ensimmäinen kertoo para- metrin tyypin ja jälkimmäinen sisältää itse tiedon. Tag-struktuureissa teh- dyt määrittelyt ohittavat NewScreen-struktuurin asetukset. NewScreenissä kannattaa määritellä niin paljon asioita kuin mahdollista, koska tagit ku- luttavat mielettömästi muistia. Yhden bitin tilan määrittäminen vie kahdek- san tavua! Vastaavasti ikkunan voi avata NewWindow-struktuurin ja mahdollisen laajen- nuksen avulla tai pelkkiä tageja käyttämällä. Struktuureista on olemassa laajennus 2.0-käyttöjärjestelmästä alkaen. Ne ovat nimeltään ExtNewScreen ja ExtNewWindow. Niissä on lisänä uudet kentät, joiden avulla niihin voi- Kuva 1. Mahdolliset grafiikkatilat. daan liittää tagijono lisäominaisuuksien määrittämiseen. V36 toi tullessaan ikkunoille mm. 3d-newlookin eli kolmiulotteisemman näköisen käyttöliit- tymän. Se täytyy ottaa huomioon jo ruutua avattaessa ja kertoa Intuitionil- le, että ohjelma ymmärtää newlook-liittymän. Muuten ruutu avataan samanlai- seksi kuin vanhoille ohjelmille. Jokaista ruutua varten on olemassa Screen-struktuuri sekä Graphics-kirjas- ton ylläpitämä ViewPort-struktuuri. Nämä sisältävät ruudun tiedot. Kakkos- julkaisusta alkaen Intuition tekee jokaiselle ruudulle myös DrawInfo-struk- tuurin, joka on tavallaan Intuitionin vastine Graphicsin RastPortille. Ne sisältävät piirtämiseen liittyvää tietoa. Ruudun avaamiseen on peräti kolme funktiota, jotka ottavat parametrikseen osoittimen NewScreen-struktuuriin sekä mahdollisesti tageja: struct Screen *OpenScreen( struct NewScreen * ); struct Screen *OpenScreenTagList ( struct NewScreen * , struct TagItem * ); struct Screen *OpenScreenTags ( struct NewScreen * , ULONG , ULONG , ... ); BOOL CloseScreen( struct Screen *screen ); Osoitin TagItemiin on itse asiassa osoitin taulukkoon TagItemejä, joka päätetään TAG_DONE:iin. Kutsuista viimeinen on kaikkein hirvein resurssien kulutuksen kannalta, koska se työntää tagit pinoon. Itse asiassa se ei ole edes oikea funktio, vaan pelkkä stubi OpenScreenTagList():lle. Tehokkain tapa on käyttää laajennusta, jossa ExtNewScreen-struktuurissa on osoitin tagilistaan. NewScreen-struktuurin voi jättää poiskin, jos oletukset kel- paavat, mutta normaalisti näin ei kannata tehdä. Erittäin pelkistetysti ruudun voi avata näin: struct Screen *ourscreen; UWORD pens[] = { ~0 }; ULONG tags[] = { SA_Pens,(ULONG)pens,SA_Depth,2,TAG_DONE }; <...> if(ourscreen = OpenScreenTagList(NULL,(struct TagItem *)tags)) { <...> CloseScreen(ourscreen); } Käytetyt kaksi tagia, SA_Pens ja SA_Depth, tarvitaan 3d-newlookin saamisek- si. Kyniä ei määritellä, vaan taulukossa on heti terminaattori, mutta In- tuition katsoo juuri SA_Pens-tagista, voiko ohjelmalle tehdä newlook-ruu- dun. Niitä on itse asiassa olemassa kolmea tyyppiä. Täyden newlookin lisäksi on mustavalkoversio, jota käytetään, jos ruudulla on vain yksi bit- tikartta eli kaksi väriä. V34-yhteensopivaa taas käytetään, kun ohjelma avaa custom-ruudun ilman SA_Pens-tagia, jolloin Intuition olettaa, että ky- seessä on uusista jutuista tietämätön vanha ohjelma. Ruutu suljetaan funktiolla CloseScreen(). Vanha 1.x ei palauta mitään arvo- ja, mutta 2.0:sta alkaen CloseScreen() palauttaa toden, jos ruutu suljet- tiin, tai epätoden, jos ruutua ei saatu suljettua esimerkiksi siksi, että sillä on jonkin toisen ohjelman ikkuna. Näin ei pitäisi olla, mutta jotkin vanhat ohjelmat sisältävät mahdollisuuden siirtyä toiselle ruudulle, vaikka silloin se ei ollut sallittua. Nyt siihen on olemassa luvalliset keinot, ja niistä lisää myöhemmin. Nyt otan esimerkin suoraan elävästä elämästä. Dynaaminen tekstinnäyttimeni shml avaa oman ruudun newlook-yhteensopivasti käyttäen ExtNewScreen-struk- tuuria ja lisäksi tarpeen vaatiessa muutamaa tagia. Ensiksi kuitenkin vil- kaisu ExtNewScreen-struktuurin rakenteeseen: struct ExtNewScreen { WORD LeftEdge, TopEdge, Width, Height, Depth; UBYTE DetailPen, BlockPen; UWORD ViewModes; UWORD Type; struct TextAttr *Font; UBYTE *DefaultTitle; struct Gadget *Gadgets; struct BitMap *CustomBitMap; struct TagItem *Extension; }; Vanha NewScreen on muuten samanlainen, mutta Extension-kenttä puuttuu. Tässä se toimii laajennuksena osoittaen tagijonoon. Lisäksi Type-kentässä asetetaan NS_EXTENDED-lippu kertomaan uudelle Intuitionille, että laajennus seuraa. Vanha 1.x jättää ne huomiotta. Toinen mahdollisuus on käyttää van- haa NewScreen-struktuuria ja antaa tagijono suoraan käyttäen funktiota OpenScreenTagList(). Nyt se esimerkki: UWORD ScreenPens = 0xffff; ULONG ScreenTags[] = { SA_Pens,(ULONG)&ScreenPens, SA_PubName,(ULONG)"shml",TAG_DONE }; struct ExtNewScreen ns = { 0,0,640,256,2,0,1,HIRES,CUSTOMSCREEN|NS_EXTENDED, 0,"SHML SCREEN",0,0,ScreenTags }; Omaa fonttia, omia gadgetteja ja omaa bittikarttaa ei ole, joten vastaavat kentät jätetään nolliksi. Lopussa on osoitin tagijonoon, jossa määritellään myös julkiseksi nostettavan ruudun nimi. Ruudun avaaminen voi epäonnistua monista syistä. OpenScreen()-funkiot osaavat antaa virhekoodin, jos niille annetaan SA_ErrorCode-tagissa osoitin LONG:iin, johon sen voi kirjoittaa. Mahdolliset virhekoodit ovat: OSERR_NOMONITOR 1 pyydetty tila edellyttää monitoria, jota ei ole OSERR_NOCHIPS 2 pyydetty tila edellyttää uudempia piirejä OSERR_NOMEM 3 liian vähän muistia OSERR_NOCHIPMEM 4 ei tarpeeksi CHIP-muistia OSERR_PUBNOTUNIQUE 5 julkisen ruudun nimi on jo käytössä OSERR_UNKNOWNMODE 6 tuntematon grafiikkatila OSERR_TOODEEP 7 liian monta tasoa OSERR_ATTACHFAIL 8 ruutujen liittäminen epäonnistui OSERR_NOTAVAILABLE 9 grafiikkatila ei ole saatavilla Jokaisella julkisella eli public-ruudulla on oltava ainutlaatuinen nimi. Jos nimi on jo käytössä, saadaan virhekoodi 5. Koodi 8 liittyy 3.0-käyttöjärjestelmän uuteen toimintoon. Sen alla voidaan useita ruutuja liittää yhteen SA_Parent-tagin avulla, jolloin ne liikkuvat toisiinsa liit- tyneinä, ts. niiden väliin ei voi siirtää toista ruutua, ja ne ovat näytöllä aina samassa paikassa toisiinsa nähden. Käymme nyt läpi muutamia tärkeimpiä ruutuihin liittyviä tageja ja niiden käyttöä. {3Ruutujen tagit ja liput {3----------------------- Ruutuihin liittyy todella suuri määrä tageja. Kerron nyt tärkeimpien käytöstä. Suluissa olevat nimet ovat lippuja NewScreen-struktuurin Type- kentässä. Kannattaa aina käyttää lippuja, koska ne vievät vain yhden bitin tagien kahdeksaan tavuun verrattuna! BOOL tarkoittaa tagia, jonka data on TRUE (yksi) tai FALSE (nolla) eli toiminto on joko päällä tai ei. SA_Left, SA_Top Määrittävät ruudun sijainnin. Koordinaatit ovat suhteellisia Overscan-Pref- sissä asetetun näytön vasempaan yläkulmaan. SA_Width, SA_Height Määrittävät ruudun koon. Antamalla tässä STDSCREENWIDTH ja STDSCREENHEIGHT saa ruudusta display clip rectanglen eli fyysisesti näkyvissä olevan näytön kokoisen. SA_Depth Bittikarttojen lukumäärä. Rajoittaa värien määrän. Ruudulla voi olla bitti- karttojen määrä potenssiin kaksi väriä. SA_DisplayID Määrittää ruudun grafiikkatilan. Tämä ohittaa NewScreen-struktuurin ViewMo- des-kentän. DisplayID on uusi tapa valita moodi, ja sen avulla voidaan va- lita myös uudet tilat. SA_Pens Osoitin kynätaulukkoon, joka päättyy -1:een. Tämä tagi täytyy olla mukana, jotta ruutu saa 3d-newlookin. SA_Title Asettaa ruudun otsikon. Data on osoitin merkkijonoon. SA_ErrorCode Osoitin LONG:iin, johon kirjoitetaan virhekoodi, jos ruutua ei saada avat- tua. Virhekoodi on pätevä, kun OpenScreen() on palauttanut nollan. SA_Colors Osoitin ColorSpec-struktuuriin, jolla määritellään ruudulle värit. Muuten käytetään oletusvärejä. SA_FullPalette, BOOL Ruudulle halutaan koko paletti eikä vain osaa niin kuin V34:llä. SA_Font, SA_SysFont, BOOL Vain toinen näistä saa olla olemassa, mutta kummallakin voi määrittää ruu- dulle halutun fontin. SA_Fontin data on osoitin TextAttr-struktuuriin. SA_SysFont taas on boolean-tyyppinen tagi, jolla määritellään, voiko ruu- dulle ottaa käyttäjän toivefontin (TRUE), vai pitääkö senolla kiinteä (kui- tenkin käyttäjän valitsema) fontti (FALSE). Fonteista lisää jatkossa. SA_Type Määrittää ruudun tyypin, CUSTOMSCREEN tai PUBLICSCREEN. SA_Behind, BOOL (SCREENBEHIND) Avaa ruudun kaikkien muiden ruutujen taakse. SA_Quiet, BOOL (SCREENQUIET) Pyytää Intuitionia olemaan tekemättä otsikkoriviä ruudulle. SA_ShowTitle, BOOL (SHOWTITLE) Määrittää, näytetäänkö ruudun otsikko Backdrop-ikkunoiden päällä. Tämän voi myöhemmin muuttaa ShowTitle()-funktiota kutsumalla. SA_AutoScroll, BOOL (AUTOSCROLL) Näkyvää näyttöä suuremmat ruudut vierivät automaattisesti, kun hiiren osoittimen vie ruudun laidalle tämän ollessa päällä. SA_Parent, V39 Data on osoitin olemassa olevan ruudun Screen-struktuuriin, johon uusi ruu- tu liitetään. Toisiinsa liitetyt ruudut liikkuvat ja siirtyvät muiden eteen ja taakse yhdessä. SA_Draggable, BOOL, V39 Asettamalla tämä pois päältä voidaan ruudun raahaaminen estää, eli se on kiinteästi samassa paikassa. Tätä ei ole syytä tehdä ilman hyvää syytä. Oletus TRUE. SA_LikeWorkbench, BOOL, V39 Erittäin helppo tapa kloonata ruutu, jos edellytät OS3:n. Saat automaatti- sesti saman moodin, koon, tasomäärän, värit jne. kuin Workbench-ruudulla on. SA_MinimizeISG, BOOL, V40 Kun ruutuja on näytöllä useita, Intuition jättää niiden väliin vähintään kolme normaalia rasteririviä. Tällä OS3.1:n tagilla voit kuitenkin pyytää niin pienen välin kuin mahdollista, mikäli siitä ei koidu haittaa. ISG eli inter-screen gap mahdollistaa erityisiä toimintoja ruutujen välissä, ja siksi se pidetään vakiona. SA_PubName Osoitin julkisen ruudun nimeen. Tämä ei tarkoita ruudun otsikkoa. Julkiset ruudut yksilöidään tämän nimen avulla. SA_PubSig, SA_PubTask Nämä ovat varaamasi signaalibitti ja tehtäväsi osoitin. Nämä on annettava edellisen jälkeen. Saat signaalin Intuitionilta, kun viimeinen vieraileva ikkuna ruudullasi lähtee pois. {3Julkiset ruudut {3--------------- Vanhan käyttöjärjestelmän alla oli vain yksi julkinen ruutu, Workbench-ruu- tu. 2.0:sta alkaen ohjelmien avaamat ruudut voidaan myös tehdä julkisiksi, jolloin muut ohjelmat voivat avata ikkunansa niille. Tämä antaa käyttäjälle enemmän vapauksia, kun haluamiensa ohjelmien ikkunat voi koota haluamalleen ruudulle. Ohjelman täytyy erityisesti tehdä avaamastaan ruudusta julkinen sekä jälleen yksityinen ennen sen sulkemista. Tämä tapahtuu funktiolla PubScreenStatus(). Julkisiin ruutuihin liittyy monta muutakin funktiota, joiden merkitys selviää seuraavasta. struct Screen *LockPubScreen( UBYTE *name ); Tällä funktiolla voit lukita julkisen ruudun, ettei se pääse katoamaan sillä aikaa, kun teet sille jotakin, esimerkiksi kloonaat sen tai avaat sille ikkunan. Jos tiedät ruudun nimen, anna se, tai NULL, niin saat osoit- timen oletusruudulle, joka on yleensä Workbench, mutta voi nykyään olla muukin. void UnlockPubScreen( UBYTE *name, struct Screen *screen ); Onnistuneen LockPubScreen()-kutsun jälkeen tulee ruutu vapauttaa kutsumalla tätä funktiota saadulla osoittimella. struct List *LockPubScreenList( void ); Lukitsee public-ruutujen listan, tarkoitettu ylläpitotehtäviin. void UnlockPubScreenList( void ); Vapauttaa public-ruutujen listan. UBYTE *NextPubScreen( struct Screen *screen, UBYTE *namebuf ); Ottaa parametrikseen osoittimen nykyiseen public-ruutuun ja vähintään MAX- PUBSCREENNAME tavua pitkään puskuriin, johon kopioi seuraavan julkisen ruu- dun nimen. Tämä on erikoisfunktio, jota ei normaalisti tarvitse. Funktion toimintaan liittyy monia huomioon otettavia seikkoja. void SetDefaultPubScreen( UBYTE *name ); Tällä funktiolla voi vaihtaa oletusruutua. Parametrinä annetaan public-ruu- dun nimi, joka asetetaan oletukseksi. Tämän jälkeen kaikki oletusruutua käyttävät ikkunat avautuvat tälle ruudulle. UWORD SetPubScreenModes( unsigned long modes ); Asettaa globaaleja eli kaikkia julkisia ruutuja koskevia moodeja. Tällä hetkellä määriteltyjä ovat SHANGHAI ja POPPUBSCREEN. Näistä ensimmäinen määrää Workbench-ikkunat avautuviksi oletusruudulle ja jälkimmäinen ikkunan avautuessa jollekin julkiselle ruudulle tuomaan sen etumaiseksi. UWORD PubScreenStatus( struct Screen *screen, unsigned long statusFlags ); Tällä funktiolla voi asettaa itse avatun ruudun julkiseksi ja takaisin yk- sityiseksi ennen sulkemista. Vain yksi lippubitti on määritelty, PSNF_PRI- VATE. Ruudun saa julkiseksi antamalla lippuina nollan. Ruutu on avatessa yksityinen, ja se pitää asettaa julkiseksi kutsumalla tätä funktiota. Pa- lauttaminen yksityiseksi pitää tehdä ennen ruudun sulkemista. Se ei onnis- tu, mikäli ruudulla on muiden ohjelmien ikkunoita. Ne on suljettava tai siirrettävä pois, ennen kuin ruudun voi palauttaa yksityiseksi ja sulkea. Funktio palauttaa nollan alimmassa bitissä, jos ruutu ei ollut julkinen tai sitä ei voinut palauttaa yksityiseksi, koska sillä on vierailevia ikkunoi- ta. void GetDefaultPubScreen( UBYTE *nameBuffer ); Hakee puskuriin oletusruudun nimen. Tätä funktiota ei yleensä tarvita. Se onkin tarkoitettu lähinnä ylläpitotarkoituksiin. Ruudun tekeminen julkiseksi OpenScreen()-kutsun jälkeen tapahtuu siis osoittimen Screen-struktuuriin ollessa muuttujassa sc: PubScreenStatus(sc,0); Palauttaminen yksityiseksi voi olla hieman ongelmallista, koska on otettava huomioon, ettei se välttämättä onnistukaan: if(sc) { while(!(PubScreenStatus(sc,PSNF_PRIVATE)&1)) if(!(EasyRequestArgs(wd,&es[4],0,0))) return(); if(CloseScreen(sc)) sc=0; } EasyRequestArgs() näyttää käyttäjälle requesterin, jossa ilmoitetaan, ettei ruutua voida sulkea, koska sillä on muiden ohjelmien ikkunoita. Tässä koo- dissa voi vastata myös kieltävästi, jolloin ruudun sulkemisen annetaan ol- la. Sen voi tehdä, jos ikkunaa ollaan vain sulkemassa, mutta ei poistumassa ohjelmasta. Näin voi olla, jos ohjelma on esimerkiksi commodity. Tämä koodi on tietysti OS2:n vaativaa. Edellä ollut ruudun avaus toimii kaikilla sys- teemeillä, vaikkakin hieman eri tavalla. Requesterit käsitellään tulevissa osissa. Lopuksi otan vielä StarTech-ohjelmastani pienen pätkän varsin edistynyttä koodia, joka etsii ikkunalle paikan. Tämä on yksi tapa tehdä varsin kehit- tynyt tekniikka. Ensimmäiseksi yritetään lukita käyttäjän haluama julkinen ruutu. Sen nimi on puskurissa, jonka numeroa vastaa määritelmä BUFF_SCREEN (#define). Mikäli tätä ruutua ei saada lukittua, katsotaan eräästä valik- koasetuksesta, onko se päällä (CHECKED) eli haluaako käyttäjä, että avataan oma ruutu, jos nimettyä ruutua ei löydy. Mikäli näin on, avataan oma ruutu (käyttäen edellä olleiden kaltaisia struktuureja), ja tehdään siitä julki- nen ruutu. Mikäli käyttäjä ei halua omaa ruutua avattavan, lukitaan oletus- ruutu eli mitä todennäköisimmin Workbench-ruutu. Sitten haetaan osoitin sen DrawInfo-struktuuriin, josta mm. sen väriominaisuudet kloonataan. Täydelli- sestä kloonauksesta tulee esimerkki jatkossa. if(!(TechScreen = LockPubScreen(Buffs[BUFF_SCREEN]))) if(menuitem2[SETT_SCREEN].Flags & CHECKED) { if(!sc) if(sc = OpenScreen(&ns)) PubScreenStatus(sc,0); TechScreen = LockPubScreen("Star Screen"); } if(!TechScreen) TechScreen = LockPubScreen(NULL); if(!TechScreen) return(Err_Screen); else { din = GetScreenDrawInfo(TechScreen); <...> } {3Ruudun kloonaaminen {3------------------- Ehkäpä paras tapa oman ruudun avaamiseen on oletusruudun kloonaaminen. Tällöin ohjelman liittymällä on valmiiksi käyttäjän haluama ympäristö. Tämä onnistuu, mikäli ohjelma vain haluaa avata oman ruudun esimerkiksi käytettävyyden takia eikä mistään pakottavasta syystä. Ideana on yksinker- taisesti lukita oletusruutu, hakea sen DrawInfo ja ottaa niistä tarvittavat tiedot, jotka annetaan edelleen OpenScreen():lle. Tässä on kokonainen oh- jelma esimerkiksi kloonauksesta: /* clone.c */ #include "exec/types.h" #include "intuition/screens.h" #include "proto/graphics_protos.h" #include "proto/intuition_protos.h" int main(void); void OpenClonedScreen(void); ULONG ScreenTags[] = { SA_Width,0,SA_Height,0,SA_Depth,0,SA_Pens,0, SA_DisplayID,0,SA_Overscan,OSCAN_TEXT,SA_AutoScroll,TRUE, SA_Title,"MyScreen",SA_PubName,(ULONG)"mysc",TAG_DONE }; struct Screen *sc; int main(void) { OpenClonedScreen(); if(sc) { <...> while(!(PubScreenStatus(sc,PSNF_PRIVATE)&1)) Delay(10); while(sc) if(CloseScreen(sc)) sc = 0; else Delay(10); } return(0); }; void OpenClonedScreen(void) { struct Screen *pubsc; struct DrawInfo *din; din = 0; if(pubsc = LockPubScreen(NULL)) { if(din = GetScreenDrawInfo(pubsc)) { ScreenTags[1] = pubsc->Width; ScreenTags[3] = pubsc->Height; ScreenTags[5] = din->dri_Depth; ScreenTags[7] = (ULONG)din->dri_Pens; ScreenTags[9] = GetVPModeID(&pubsc->ViewPort); if(sc = OpenScreenTagList(NULL,(struct TagItem *)ScreenTags)) { PubScreenStatus(sc,0); } } if(din) FreeScreenDrawInfo(pubsc,din); UnlockPubScreen(NULL,pubsc); } }; Tämä OpenClonedScreen()-funktiohan tässä ohjelmassa vain on järkevä. Sen voi tästä esialustettuine tagijonoineen "kloonata" mihin tahansa ohjelmaan ja käyttää siinä ruudun avaamiseen. Ennen ruudun sulkemista tarvittaessa odotetaan, että se onnistuisi. Jälkimmäinen rivi kutsuu CloseScreen():iä niin kauan, että ruutu todella menee kiinni, koska ruudulle on voinut tun- kea itsensä esimerkiksi DarkPlay, josta ei saa tietoa PubScreenSta- tus()-funktion avulla, koska se ei ole 2.0-ohjelma eikä tee asioita niin kuin ne nykyään pitäisi tehdä. GetVPModeID() palauttaa annetun ViewPortin grafiikkatilan. Tässä on vielä muutamien käytettyjen ja muuten asiaan liit- tyvien funktioiden toimintaa: struct DrawInfo *GetScreenDrawInfo( struct Screen *screen ); Palauttaa osoittimen annetun ruudun DrawInfo-struktuuriin. void FreeScreenDrawInfo( struct Screen *screen, struct DrawInfo *drawInfo ); Vapauttaa DrawInfon. ULONG OpenWorkBench( void ); Avaa Workbenchin. LONG CloseWorkBench( void ); Sulkee Workbenchin. Onnistuu vain, jos Workbench-ruudulla ei ole muita kuin Workbenchin omia ikkunoita, eikä Workbenchin ajamia ohjelmia ole käynnissä. Käytetään yhä harvemmin - eniten käyttöä oli aikoinaan, kun koneissa oli vähän muistia, jolloin sitä saatiin lisää käyttöön sulkemalla Workbench. BOOL WBenchToBack( void ); Siirtää Workbench-ruudun takimmaiseksi. BOOL WBenchToFront( void ); Siirtää Workbench-ruudun etumaiseksi. void ScreenToBack( struct Screen *screen ); Siirtää annetun ruudun takimmaiseksi. void ScreenToFront( struct Screen *screen ); Siirtää annetun ruudun etumaiseksi. void MoveScreen( struct Screen *screen, long dx, long dy ); Siirtää annetun ruudun haluttuun paikkaan näytöllä. Annetut arvot ovat del- takoordinaatteja, eli ne eivät kerro paikkaa, johon ruutu siirretään, vaan ne ovat suhteellisia arvoja ruudun nykyisiin koordinaatteihin. void ShowTitle( struct Screen *screen, long showIt ); Asettaa ruudun otsikon joko näkymään Backdrop-ikkunoiden päällä tai ei. Jos showIt on tosi, otsikko näkyy. {3Ikkunat {3------- Ruudut sellaisenaan ovat vain pohja varsinaiselle käyttöliittymälle. Ohjel- mat toimivat ikkunoissa. Ikkunat ovat suorakulmaisia alueita, jotka jakavat ruudun osiin ja jotka voivat olla osittain tai kokonaan toistensa päällä tai rinnakkain näytöllä. Ikkunan rajaavat yleensä reunat eli borderit, joissa on ikkunan ohjaamiseen käytettäviä nappuloita eli gadgetteja. Nappulan nimi Käyttötarkoitus Close Sulkee ikkunan (tiedottaa ohjelmalle) Zoom Katso ZipWindow() Depth Siirtää ikkunan etumaiseksi tai takimmaiseksi Sizing Muuttaa ikkunan kokoa Taulukko 1. Ikkunan system-gadgetit. Yllä olevassa taulukossa näkyvät ikkunoissa yleensä olevat nappulat. Si- zing-gadget sijaitsee yleensä oikeassa laidassa, muut ylälaidassa. Borderi, jossa on gadgetteja, on selvästi leveämpi kuin sellainen, jossa niitä ei ole, esim. vasen laita. Lisäksi ikkunassa on Drag Bar, jonka muodostaa ot- sikkorivi. Siitä tarttumalla voi ikkunaa siirtää ruudulla. Nämä systeemi- gadgetit tulevat ikkunaan automaattisesti, mikäli niiden haluaa tulevan, eikä sitä pidäkään estää ilman todella hyvää syytä, koska se rajoittaa käyttäjän kontrollia. Ikkuna avataan funktiolla OpenWindow(). Sille annetaan osoitin NewWin- dow-struktuuriin, jossa voi olla laajennus. Ikkunaa avattaessa pitää tietää samoja asioita kuin ruutuakin avattaessa, esim. ikkunan paikka, koko, koko- rajat sekä ruutu, jolle se avataan. Tässä on ExtNewWindow-struktuurin määrittely, joka on muuten samanlainen kuin NewWindow, mutta lisänä on osoitin tagijonoon: struct ExtNewWindow { WORD LeftEdge, TopEdge; WORD Width, Height; UBYTE DetailPen, BlockPen; ULONG IDCMPFlags; ULONG Flags; struct Gadget *FirstGadget; struct Image *CheckMark; UBYTE *Title; struct Screen *Screen; struct BitMap *BitMap; WORD MinWidth, MinHeight; UWORD MaxWidth, MaxHeight; UWORD Type; struct TagItem *Extension; }; Laajennuksen määrittää Flags-kentässä päällä oleva lippu WFLG_NW_EXTENDED. IDCMP on Intuitionin tapa välittää tietoa ohjelmalle ulkomaailmasta. Siihen palataan kurssin tulevissa osissa. Myös muita nyt sivuutettavia kenttiä hyödynnetään jatkossa. Tähän väliin käy esimerkki ikkunan avaamisesta: struct NewWindow nw = { 0,0,640,256,0,1, IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE,WFLG_ACTIVATE| WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_SIZEGADGET| WFLG_DRAGBAR|WFLG_SIZEBBOTTOM|WFLG_SMART_REFRESH, 0,0,"My Window",0,0, 320,32,3200,1280,WBENCHSCREEN }; <...> if(!(wd = OpenWindow(&nw))) error("cannot open window"); Tässä esimerkissä käytetään IDCMP:tä, koska Intuition tiedottaa sen avulla ohjelmalle, kun Close-nappulaa painetaan. Tämä ohjelma haluaa myös tiedon ikkunan koon muuttumisesta. Liput ovat tässä nyt merkityksellisempiä. Niillä laitetaan yksitellen kukin systeemigadgetti päälle, pyydetään avaa- maan ikkuna aktiiviseksi ja sijoittamaan Sizing-nappula bottomborderiin eli alalaitaan, jolloin molemmat pystylaidat ovat kapeita ja riville mahtuu enemmän tekstiä. Esimerkin error() on oma funktio, joka näyttää virhemerk- kijonon ja poistuu ohjelmasta. Ikkuna avataan Workbench-ruudulle, mutta sen voi avata omalle ruudulle laittamalla tyypiksi esimerkiksi CUSTOMSCREEN ja asettamalla Screen-kenttään osoittimen sen Screen-struktuuriin. Pelkällä ikkunalla ei vielä juhlita. Siihen tarvitaan myös sisältö eli var- sinainen ohjelman käyttöliittymä, jonka voi tehdä ohjelman tarkoituksesta riippuen konsolin avulla tai sitten gadgeteilla ja menuilla. Näihin kaik- kiin palaamme myöhemmin tässä kurssissa. Nyt on aikaa vain ikkunoiden muu- tamien erityisominaisuuksien ja ikkunaan liittyvien laajennusten pintapuo- liseen tutkimiseen. Yllä olleessa esimerkissä ei käytetty ollenkaan tagijo- noa, koska sitä ei tarvita. Kaikki tarpeellinen voitiin määritellä NewWin- dow-struktuurissa. Myös OpenWindow():sta on olemassa Tag-versiot, joille annetaan tagit samoin kuin vastaaville OpenScreen()-funktioille: struct Window *OpenWindow( struct NewWindow *newWindow ); struct Screen *OpenScreenTagList( struct NewScreen * , struct TagItem * ); struct Window *OpenWindowTags( struct NewWindow * , ULONG , ULONG , ... ); void CloseWindow( struct Window *window ); Toisin kuin CloseScreen(), CloseWindow() ei palauta mitään. Ikkunoihin liittyy vielä yksi merkittävä asia: niiden sisällön päivitys eli refres- hing. Yleensä ikkunoiden tulisi olla Smart Refresh -tyyppiä, jolloin Intui- tion pitää yllä ikkunan sisältöä, vaikka se ei olisikaan ruudulla näkyvillä eli muut ikkunat peittävät sen osittain tai kokonaan. Simple Refresh -tyyp- pisen ikkunan joutuu ohjelma itse päivittämään, kun se tulee esiin toisen ikkunan takaa, mutta esimerkiksi konsoli tekee sen, joten sen kanssa tulee käyttää tätä tyyppiä. Näihin palaamme vielä jatkossa. {3Ikkunoiden tagit ja liput {3------------------------- Ikkunoihin liittyy todella suuri määrä tageja. Kerron nyt tärkeimpien käytöstä. Suluissa olevat nimet ovat lippuja NewWindow-struktuurin Flags- kentässä. Kannattaa aina käyttää lippuja, koska ne vievät vain yhden bitin tagien kahdeksaan tavuun verrattuna! BOOL tarkoittaa tagia, jonka data on TRUE (yksi) tai FALSE (nolla) eli toiminto on joko päällä tai ei. WA_Left, WA_Top Ikkunan vasemman yläreunan sijainti suhteessa ruudun vasempaan yläreunaan. Ruudun vasempaan yläreunaan aukeavan ikkunan arvot ovat 0,0. WA_Width, WA_Height Ikkunan leveys ja korkeus. WA_Title, WA_ScreenTitle Data on osoitin ikkunan otsikkoon tai jälkimmäisessä tapauksessa ruudun ot- sikkoon, joka näytetään aina, kun kyseinen ikkuna on aktiivinen. Näitä voi muuttaa myöhemmin SetWindowTitles()-funktiota kutsumalla. WA_CustomScreen, WA_PublicScreen Osoitin sen ruudun Screen-struktuuriin, jolle ikkuna avataan. Ikkuna on tällöin CUSTOMSCREEN- tai PUBLICSCREEN-tyyppiä käytetystä tagista riippuen. WA_PubScreenName, WA_PubScreenFallBack, BOOL Ensimmäinen määrittää sen julkisen ruudun nimen, jolle ikkuna avataan. Kun jälkimmäinen on päällä, ikkuna avautuu oletusruudulle automaattisesti, jos se ei jostain syystä voinut avautua halutulle ruudulle. WA_MinWidth, WA_MinHeight, WA_MaxWidth, WA_MaxHeight Ikkunan minimi- ja maksimikoko. Nämä voi muuttaa myöhemmin WindowLi- mits()-funktiota kutsumalla. WA_Zoom Data on osoitin neljään WORD:iin, jotka ovat ikkunan vaihtoehtoinen paikka ja koko (LeftEdge, TopEdge, Width, Height). Zoom-nappula tehdään ikkunaan aina, kun siinä on Depth- ja Sizing-nappulat, mutta tällä tagilla Zoom-nap- pulan saa ilman niitäkin. WA_CloseGadget, BOOL (WFLG_CLOSEGADGET) Halutaan Close-nappula ikkunaan. WA_DepthGadget, BOOL (WFLG_DEPTHGADGET) Halutaan Depth-nappula ikkunaan. WA_DragBar, BOOL (WFLG_DRAGBAR) Halutaan voida siirtää ikkunaa sen otsikkopalkista. WA_SizeGadget, BOOL (WFLG_SIZEGADGET) Halutaan ikkunaan Sizing-nappula. WA_SizeBRight, BOOL, WA_SizeBBottom, BOOL (WFLG_SIZEBRIGHT, WFLG_SIZEBBOT- TOM) Määrittää Sizing-nappulan joko oikeaan tai alaborderiin, voi olla kummassa- kin. WA_Borderless, BOOL (WFLG_BORDERLESS) Avaa ikkunan ilman laitoja eli Borderless-tyyppisenä. WA_Backdrop, BOOL (WFLG_BACKDROP) Avaa ikkunan Backdropina. WA_GimmeZeroZero, BOOL (WFLG_GIMMEZEROZERO) Avaa ikkunan GZZ:na eli tekee sille erillisen borderilayerin. Layerit ovat myös grafiikan matalan tason asioita, ja niistä huolehtii layers.library. Niihin emme varmaankaan tutustu ainakaan tässä alkeiskurssissa. WA_Activate, BOOL (WFLG_ACTIVATE) Avaa ikkunan aktiivisena. Tätä ei tule käyttää, jos avaa ikkunan jonnekin taakse, koska silloin käyttäjän syöte häviää sinne. WA_SimpleRefresh, WA_SmartRefresh (WFLG_SIMPLEREFRESH, WFLG_SMARTREFRESH) Määrittävät ikkunan päivitystavan. Itse asiassa WFLG_SMARTREFRESH on nolla, joten se on oletus ja sen voi jättää pois. WA_NewLookMenus, BOOL, V39 Aseta, mikäli haluat uudet OS3-newlook-menut. WA_Flags Pelastus hirveältä muistinkulukselta! Mahdollisuus määrittää liput samalla tavalla kuin NewWindow-struktuurin Flags-kentässä ilman itse struktuuria. Kannattaa ehdottomasti käyttää näitä lippuja eikä tageja. {3Ikkunoiden tyypit {3----------------- Tavallisen ikkunan lisäksi on olemassa kolme erityistyyppiä: Borderless, Backdrop ja GimmeZeroZero. Borderless-ikkuna on muuten samanlainen kuin mikä tahansa muukin ikkuna, mutta siinä ei ole bordereita eikä näin ollen myöskään systeemigadgetteja. Niiden laittaminen Borderless-ikkunaan saa ai- nakin yhden bordereista näkyviin. Tämä voi tosin helpottaa ikkunan hahmot- tamista. Borderless-ikkunalla voi tehokkaasti sekoittaa käyttäjän erityi- sesti, jos se ei ole koko ruudun kokoinen, koska sillä ei ole bordereita, jotka erottaisivat sen muista ikkunoista. Backdrop-ikkunat avautuvat muiden ikkunoiden taakse. Niitä voi olla useita, ja niiden keskinäisen järjestyksen voi muuttaa haluamakseen, mutta Backd- rop-ikkunat ovat aina tavallisten ikkunoiden takana. Backdrop-ikkuna ei ta- vallaan ole ikkuna ollenkaan, vaan sen avulla saa tunnun kuin siinä olevat kohteet olisivat suoraan ruudun pohjalla. Backdrop-ikkunan idea tulee hel- posti selville napsauttamalla Workbench Workbench-valikosta Backdropiksi. Vanhalla 1.x:llä se onkin jo valmiiksi Backdrop-tyyppinen. Yhdistämällä nämä kaksi erityistoimintoa saa ikkunattoman ruudun tunnun, jota hyödyn- netään esimerkiksi terminaaliemulaattoreissa. Ideana on avata Borderless- Backdrop-ikkuna, jossa ei ole yhtään systeemigadgettia sulkunappulaa lukuu- nottamatta. GimmeZeroZero-ikkunat ovat tavallaan kaksikerroksisia. Normaalisti ikkunan borderit ovat kiinteästi liittyneinä ikkunan varsinaiseen alueeseen, mutta GZZ-ikkunassa borderit ovat erillinen kokonaisuus. Käyttäjän kannalta eroa ei ole, mutta ohjelma voi huolettomammin tulostaa grafiikkaa ikkunaan tar- vitsematta huolehtia erikseen siitä, ettei grafiikka mene bordereiden päälle. GZZ-ikkunoista ei sen enempää, mutta edellä mainittua Borderless- Backdrop-ikkunaa voimme kokeilla myöhemmin kurssin aikana. {3Ikkunoiden ohjelmallinen kontrolli {3---------------------------------- Ohjelma voi muuttaa ikkunaan liittyviä asioita käyttäjän käskemänä tai hänestä piittaamatta. Tässä on joitakin ikkunoiden kontrolloimiseen käytettäviä funktioita: void SetWindowTitles( struct Window *window, UBYTE *windowTitle, UBYTE *screenTitle ); Asettaa ikkunalle ja ruudulle uuden otsikon. Ruudun otsikko muuttuu tässä annetuksi, kun kyseinen ikkuna aktivoidaan. Kumpi tahansa voi olla -1, joka ei muuta nykyistä otsikkoa ja jolloin voi muuttaa vain jomman kumman otsi- kon, tai nolla, joka palauttaa otsikon alkuperäiseksi eli siksi, joka an- nettiin OpenScreen()- tai OpenWindow()-kutsun yhteydessä. void SizeWindow( struct Window *window, long dx, long dy ); Muuttaa ikkunan kokoa haluttuun suuntaan. Arvot ovat taas deltakoordinaat- teja. SizeWindow(wd,-30,20) muuttaa ikkunan 30 pikseliä kapeammaksi ja 20 pikseliä korkeammaksi. struct View *ViewAddress( void ); Palauttaa osoittimen Intuitionin View-struktuuriin. Kaikki Intuitionin ruu- dut näytetään sen Viewissä, joka on grafiikkakirjaston matalan tason näytönorganisointia. Tähän palaamme joskus jatkossa. struct ViewPort *ViewPortAddress( struct Window *window ); Palauttaa osoittimen ruudun, jolla ikkuna on, ViewPortiin, joka on myös Graphicsin asioita. void WindowToBack( struct Window *window ); Siirtää annetun ikkunan takimmaiseksi. void WindowToFront( struct Window *window ); Siirtää annetun ikkunan etumaiseksi. BOOL WindowLimits( struct Window *window, long widthMin, long heightMin, unsigned long widthMax, unsigned long heightMax ); Asettaa ikkunalle uudet rajat. Alun perin rajat on annettu NewWindow-struk- tuurin Max- ja Min-kentissä. Ikkunalle kannattaa asettaa riittävän suuri minimikoko, ettei käyttäjä saa sitä sotkettua tekemällä siitä liian pientä, mutta maksimikokoa ei kannata rajoittaa ilman syytä, vaan siihen voisi laittaa vaikkapa -1:t. void MoveWindow( struct Window *window, long dx, long dy ); Siirtää ikkunaa haluttuun suuntaan. Arvot ovat taas deltana. void ZipWindow( struct Window *window ); Muuttaa ikkunan paikkaa ja kokoa. Ikkunalla on nykyinen paikka ja koko sekä toinen paikka ja koko. Näiden kahden paikan ja koon välillä voi ikkunaa vaihtaa tällä funktiolla. Vastaavan toiminnon saa aikaan Zoom-nappulan pai- naminen. Funktiota voi hyödyntää esimerkiksi antamalla käyttäjälle mahdol- lisuuden zoomaukseen näppäimistöltä.