Amiga C

23. Bölüm

Cenker Özkurt

Evet Amiga'cılar, yazımız devam ediyor. Hatırlayacağınız gibi, geçen ay 'MenuDesign' adlı bir program hazırlamıştım. Şimdi bu programımızı biraz daha derinlemesine incelemeye başlayabiliriz. Açıklamalara başlamadan önce, vermiş olduğum örneği yazıp denediğinizi farzediyorurn. Eğer denemediyseniz bu açıklamaları anlamanız zor olabilir. Ayrıca programın ana mantığını da ancak programı kullanarak daha rahat anlayabilirsiniz. Evet, artık programımızın incelemesine geçebiliriz.

#include <exec/types.h> 
#include <exec/memory.h>
#include <graphics/gfxbase.h> 
#include <intuition/intuitionbase.h> 
#include <libraries/dos.h>
Hatırlayacağınız gibi ilk satırlarda her zaman olduğu gibi #include satırları mevcut. Bu dosyalarda kulladığımız yapı değişkenleri ve tipleri tanımlanarak program sürüyor. Bu dosyalar bildiğiniz gibi 'include' kütüphanesinde bulunuyor. Eğer dosyalar bu kütüphane içerisinde bulunamazsa compile sırasında program hata verecektir. Bu nedenle gerek yazım sırasında gerekse de çalışma disketinizde bu dosyaların doğru yazıldığına ve bulunduğuna dikkat etmeniz gerekir.
struct IntuitionBase *IntuitionBase; 
struct GfxBase *GfxBase; 
struct DosBase *DosBase;

struct RastPort *rp; 
struct ViewPort *vp;
struct Window *window;
Diğer satırlarda ise kulladığımız yapı değişkenlerini tanımlıyoruz. Burada mesela 'IntuitionBase' yapı değişkeni #include dosyaları içerisinden '#include ' dosyası içerisinde tanımlanıyor. IntuitionBase yapı değişkenini incelemek isterseniz, bu dosyayı yükleyip inceleyebilirsiniz. Bunun yanında 'GfxBase', 'gfxbase.h' ve 'DosBase' 'dos.h' içerisinde tanımlanıyor.

Sonraki satırlarda ise kullanacağımız ekran özelliklerini taşıyan RastPort ve ViewPort, Window özelliklerini taşıyan window yapı değişkenleri bir pointer yapı olarak tanımlanıyor. Burada pointer olarak tanımlamamızın sebebi, daha önceki yazılarımda da belirttiğim gibi bu yapıların orijinal yapıları görüntülemesi amacıyla kullanılmasından doğuyor.

char title[80];
char view[30][20];
char disk[30]|30];
char *name[]={ " ",0 };
char *list;
int no=0;
int fin;
Bundan sonraki değişkenlerimiz bize ait. Bu değişkenleri diğer rutinlerin de tanıyabilmesi için main dışında 'global değişken' olarak tanımladık. Değişkenlerin görevleri ise şöyle;
title -> Menu listesi içerisindeki 'menu=' ifadesinden bir sonraki satıra kadar olan cümleyi saklıyor. Burada 'menu-' ifadesinden sonraki cümle 'Amiga Dünyası Utility Disk' ise, 'title' değişkeninin her indisi bu cümleyi tutacak şekilde bir karakter saklıyor. Bu örneğe göre 'title' değişkenin içeriği de şöyle oluşuyor:
title[0]='A'
title[1]='m'
title[2]='i'
title[3]='g'
title[4]='a'
...
olarak devam eder. Dikkat ettiyseniz her indise bir karakter düşüyor. Buna göre 'title' dizisi en fazla 80 karakterlik bir diziyi taşıyabilir. Tabii ki bu sayıyı arttırmak elinizde.
view -> Menu listesi içerisindeki '-' ifadesinden '+' ifadesine kadar olan cümleleri saklıyor. Tanıma göre;
view[30][20];
30, saklayabileceği cümle sayısını, 20 ise cümlenin uzunluğunu belirtiyor. 'view' değişkeni, disketteki programların listesini görüntülemek amacıyla kullanılıyor.
disk -> Menu listesi içerisindeki '+' ifadesinden satır sonuna kadar olan cümleleri saklıyor. Burada gene 30, saklayabileceği cümle sayısını, diğer 30 ise uzunluğunu belirtiyor, 'disk' değişkeni disketteki programların asıl isimlerini saklamak için kullanılıyor.
name -> mouse ile seçilen programın disketteki ismini 'execv' komutunun kullanabileceği şekilde tutmak için kullanılıyor.
no -> Menu listesi içerisindeki Utility program isimlerinin sayısını saklıyor. Bu aynı zamanda disketteki programların sayısını da gösterir.
fin -> Disketten yüklenen menu listesinin file olarak uzunluğunu belirtiyor. Bu değişkene göre utility isimleri 'list' değişkeninden 'list+fin'e kadar olan bölgede aranıyor.
list -> Pointer, değişken olarak menu listesinin yüklendiği hafıza bölgesinin başlangıç adresini saklıyor. Bu değer AllocMem() fonksiyonundan alınıyor.

Programda kullandığımız ana değişkenler bu kadar. Bu değişkenler bütün alt rutinlerde tanımlıdır. Diğer değişkenler ise geçici (local) değişken olarak tanımlanıyor.

Bu tanımlardan sonra şimdi gelelim ana programın icra edildiği main() fonksiyonuna.

main() içerisindeki 'argc' ve 'argv' değişkenleri, verilen parametreleri ve parametre sayılarını saklıyor. 'argc' parametre sayısı, 'argv' ise parametrenin kendisini oluşturuyor. Bir örnek vermek gerekirse;

MenuDesign df0:s/menulist
Burada, MenuDesign programımızın ismi, df0:s/menulist ise yüklenecek olan listenin bulunduğu directory ve ismini belirtiyor. Bu girişe göre, argc=1, argv[1]="df0:s/menulist" olarak tanımlanır. 'argc' kontrolü ile gerekli parametrelerin girilip girilmediğini öğrenmemiz mümkün. Bu olayları,
main(argc,argv)
int argc;
char *argv[];
{
int a; /* local degisken */
if(argc==1) /* menu listesi kontrol ediliyor */
   printf("Usage : menudesign [list]\n ");
   return(1);
}
satırları ile kontrol ediyoruz. Daha sonra,
IntuitionBase=OpenLibrary("intuition.library",0); 
GfxBase=OpenLibrary("graphics.library",0); 
DosBase=OpenLibrary("dos.library",0);

window=IntuitionBase->ActiveWindow; 
rp=window->RPort;
satırları ile kullanılacak olan Library'ler ve değişkenler tanımlanıyor. Yalnız burada programın daha anlaşılır olması için, Library'lerin açılıp açılmadığını kontrol etmedim. İsterseniz siz güvenilirlik açısından kontrol edebilirsiniz.
SetDrMd(rp,JAM1);
satırında, yazı modu tanımlanıyor. Bu modlar ekrana yazarken kullanılacak mantık işlemini belirtiyor. JAM1 modu, bize üstüste yazılan karakterlerde birbirlerini yoketmemesi için kullanılıyor. Diğer bir deyişle OR işlemi yapıyor. Bu modu kullanmamdaki sebep, print() rutini ile gölgeli yazı oluşturmak içindi. Diğer kullanılabilecek modlar ise, JAM2 ve COMPLEMENT modlarıdır. Bu modların işlevlerini deneyerek görebilirsiniz.

Daha sonraki satırlarda, programın işlevini gerçekleştiren bizim hazırlamış olduğumuz alt rutinler mevcut. Bu rutinler belirli bir sırada çağrılarak adım adım program işleniyor. Bu sıra,

clear();       
load(argv[1]); 
scr_clear();   
draw_box():    
search();      
menudesign();  
flood():       
a=mouse();     
if(a==0) {     
    quit();   
    scr_clear();
    exit(0);
}
quit();
scr_clear();
printf("Loading %s...\n",view[a]);
strcpy(name[0],disk[a]); 
execv(name[0],name);     
}
şeklindedir. Şimdi her bir rutini derinlemesine inceleyebiliriz.

clear(), 'view' ve 'disk' değişkenlerini boş karakterlerle dolduruyor ve temizliyoruz. Bunu yaparken strcpy() fonksiyonu işimizi rahatlıkla görüyor, 'i' değişkenini de register değişken olarak kullanarak işlemimizin hızını arttırıyoruz.

clear()
{
register int i;

for(i=0;i<30;i++) {
strcpy(view[i],"	");
	strcpy(disk[i],"	");
}
}
Sonraki fonksiyon olan load(argv[1]), verilen parametreyi yüklemek için kullanılıyor. Buradaki argv[1]'nin disketindeki ismini saklıyor.
load(a)
char [a];
{
struct FileHandle *file;
file=Open(a,MODE_OLDFILE); 
if(file==NULL) {
      printf("%s dosyası bulunamadı..!\n",a);
      quit();
      exit(1);
}
satırlarında 'file' yapı değişkeni dosya pointer göstergesi olarak tanımlanıyor. 'file' FileHandle tipinde bir yapıyı oluşturuyor. Bu yapı ile dosya işlemleri yürütülüyor. Sonraki satırda dosya açılarak file yapı değişkeni kontrol ediliyor. Eğer dosya açılamadıysa bir mesajla program sona eriyor. Bu nedenle yüklerken MenuDesign isminin yanındaki menu liste ismini doğru vermemiz gerekiyor.
list=AllocMem(1000,MEMF_CHIP|MEMF_CLEAR)İ; 
if(list==NULL) {
      printf("Hafıza yetersiz..!\n");
      quit();
      exit(1); 
} 
fin=Read(file,list,1000);
Close(file); 
return(0);
}
Yukarıdaki satırlarda, menü listesinin bulunduğu hafıza değişkeni olan 'list' değişkenine, AllocMem ile 1000 byte'lık bir yer ayrılarak bu bölgenin ilk adresi saklanıyor. Biz diğer rutinlerde bu adresten yararlanacağız.

Sonraki satırlarda hafızanın yeterliliğini kontrol ederek menu listesini yüklemeye başlıyoruz. Read() fonksiyonu yükleme işlemini gerçekleştiriyor ve 'fin' değişkenine, yüklenen byte sayısını veriyor. Bu satırlar sonunda menü listesinin bulunduğu hafıza bölgesinin başlangıcını 'list' uzunluğunu ve sonunu 'fin' değişkeni ile saptamış oluyoruz.

draw_box()
{
register int x,y;

for(x=0;x<3;x++) {
 for(y=0;y<10;y++) {
   box(70+x*170,25+y*16,1);
 }
}
}
Bu rutinde x ve y değişkenleri kutunun çizileceği koordinatı saptıyor. c değişkeni ise kutu rengini belirtiyor. Bu renk değişkeni vasıtasıyla istediğimiz renkte kutuyu çizebiliyoruz. Böylece kullanıcı mouse pozisyonundaki programı yüklemesi gerektiğini rahatlıkla anlayabiliyor.

Search() rutini, en önemli rutini oluşturuyor. Burada menu listesinin ekrandaki ve disketteki isimleri, 'list' adresinden 'list+fin' adresine kadar aranarak kaydediliyor. Bu işlemler şu kademelerden geçiyor:

İlk olarak 0'dan 'fin'e kadar bir 'i' döngüsü oluştutuluyor. Bu bizim arayacağımız hafıza bölgesinin uzunluğunu belirtiliyor. Sonraki satırda, listede 'menu=' ifadesinin bulunup bulunmadığını strncmp() fonksiyonu ile karşılaştırarak öğreniliyor. Dikkat ettiyseniz 'list+i'den işleme başlıyoruz. strncmp()'deki 5 sayısı karşılaştırılan karakter sayısını belirliyor. Eğer sonuç 0 ise bu parametrelerin içeriklerinin eşit olduğu anlamına gelmektedir. Böyle bir durumda. 'menu='den sonraki karakterler satır sonuna kadar kontrol edilerek 'title' değişkenine aktarılıyor. Satır sonu return ile bittiğine göre, decimal olarak 10 değerini arıyoruz.

Bir sonraki kademe ise '-' ve ' + ' karakterlerinden sonraki cümleleri 'view' ve 'disk' değişkenlerine aktarmakla geçiyor. Bu işlemler menu isminin bulunduğu gibi yapılıyor. Her eşitleme sonunda değişkenlerin uzunluğunu belirtmek için son karaktere '\0', satırsonu karakteri koyduğumuza dikkat edin. Bu, dizinin uzunluğunu bulmamızı sağlıyor.

search()
{
int a;
register int i,j,k;

for(i=0;i<=fin;i++) {
     a=strncmp(list+i,"menu=",5); 
     if(a==0) {
	 i+=5;
	 j=i;
	 while(*(list+i)!=10)i++;
	 for(k=0;k<i-j;k++) title[k] =(char)*(list+j+k);
	 title[k]='0';
     }
a=strncmp(list+i,"-",1);
     if(a==0) {
	 no++;
i++; j=i;
while(*(list+i)!='+')i++;
	 for(k=0;k<i-j;k++) view[no][k]=(char)*(list+j+k);
	 view[no][k]='0';
	 j=i+1;
	 while(*(list+i)!=10)i++;
	 for(k=0;k<i-j;k++) disk[no][k]=(char)*(list+j+k);
	 disk[no][k]='0';
     }
}
}
search() rutininin ardından, artık 'view' ve 'disk' değişkenleri utility isimleriyle hazır bekliyor. Ayrıca 'title' değişkeni de menu ismimizi zaten saklıyordu. Artık menüyü yazmanın tam sırası. Bu işlemi menu() rutini ile gerçekleştirebiliriz.
menu()
{
char a[50];

strcpy(a,"MenuDesign (c) 1992 Programming by Cenker OZKURT");
print(320-4*(strlen(a)),192,a,strlen(a)); 
print(320-4*(strlen(title)),22,title,strlen(title));
}
Menü içerisinde kullandığımız print() fonksiyonu da gölgeli yazı yazmamızı sağlıyor.
print(x,y,a,len)
int x,y; 
char a[];
int len;
{
SetAPen(rp,2);
Move(rp,x,y);
Text(rp,a,len);
SetAPen(rp,1);
Move(rp,x-1,y-1);
Text(rp,a,len);
}
print() fonksiyonunun kullanımı oldukça kolay. x ve y, koordinatları; a, yazılacak değişkeni; len ise değişkenin uzunluğunu belirtiyor. Sonraki fonksiyon olan flood() ise 'view' değişkeni 'view' değişkeninin içeriklerini daha önce çizilmiş olan kutuların içerisine yazıyor. Böylece diskette bulunan utility isimleri ekrana dökülmüş oluyor. Bu fonksiyon içerisinde de print() rutininin kullanıldığına dikkat.
flood()
{
register int x,y,i;

x=0; y=0;
for(i=1;i<=no;i++) {
	print(73+x*170,34+y*16,view[i],strlen(view[i]));
	y++; if(y==10) { y=0; x++; }
}
}
a=mouse() satırı ile, mouse ile seçilen utility ismi 'no' değişkenine atılarak belirleniyor.
mouse()
{
int ret;
int q,w,p=0;
int a,b,c,d;
register int x,y;
register int i,j;
UBYTE *m=0xbfe001;

 while(1) {
  x=window->GZZMouseX; 
  y=window->GZZMouseY+10; 
  for(i=0;i<3;i++) {
     for(j=0;j<10;j++) { 
	a=70+i*170; 
	b=25+j*16; 
	c=a+160;
	d=b+11;
	if(x>a&&x<c&&y>b&&y<d&&(q!=a||w!=b)) { 
	    if(p==1)box(q,w,1); else p=1; 
	    box(a,b,2); q=a; w=b;
	}
	if(!(x>70&&x<570&&y>25&&y<185)) { 
	    if(p=1) { box(q,w,1); q=0; w=0; p=0; }
	}
	if(p==0&&!(*m&64)) return(0);
	ret=i*10+j+1
     if(ret<=no&&x>a&&x<c&&y>b&&y<d&&y!(*m&64))
	return(ret);
	}
     }
 }
}
Rutin içerisinde mouse pozisyonunu almak için,
x=window->GZZMouseX ve 
y=window->GZZMouseY
kullanıyoruz. Bu satırlar sonunda mouse pozisyonu x ve y değişkenine aktarılmış oluyor. Bu değerleri almak için window yapı değişkenini kullandığımıza dikkat edin. Bu değişken içerisindeki diğer alt değişkenleri include dosyalarına bakarak incelemenizi tavsiye ederim.

Sonraki satırlarda mouse() fonksiyonu bir sonsuz while döngüsüne giriyor. Bu döngü içerisinde mouse oynatıldığı sürece kutuların renkleri değiştirilerek mouse pozisyonunun belirtmek istediği kutu kullanıcıya gösteriliyor. Bunları yaparken box() rutinini tekrar kullandığımıza dikkat edin.

Mouse tuşuna basıldığında, seçilen kutunun değeri mouse'un bulunduğu pozisyona göre hesaplanarak return ile 'a' değişkenine aktarılıyor. Artık yüklenmek istenen utility isminin indisi belirlendiğine göre sıra geldi yükleme işlemine.

İlk olarak 'a' değişkeninin değerinin sıfırdan farklı olup olmadığını kontrol ediyoruz. Bu bize seçimin doğru yapılıp yapılmadığını veriyor. Eğer yapılmadıysa program sona eriyor.

Seçim doğru yapıldığı takdirde, quit() ile açılan Library'ler ve hafızalar terk ediliyor.

quit()
{
if(list)FreeMem(list,1000);
CloseLibrary(IntuitionBase);
CloseLibrary(GfxBase);
CloseLibrary(DosBase);
}
Sonraki işlemler ise ekranı temizlemek, yüklenecek olan programın ismini ekrana yazmak, name değişkenine program ismini taşımak ve yüklemek.

Programın açıklaması burada sona eriyor. Bu program ile Amiga C'de basit bir Utility programının nasıl yapıldığını gördünüz.

Gelecek ay yeni örneklerle buluşmak üzere, hoşçakalın...