|
|
Bu makalenin farklı dillerde bulunduğu adresler: English Castellano ChineseGB Deutsch Francais Italiano Portugues Russian Turkce |
tarafından Reha K. Gerçeker <gerceker(at)itu.edu.tr> Yazar hakkında: Reha İstanbul Türkiye'de bir bilgisayar mühendisliği öğrencisi. Yazılım geliştirme ortamı olarak Linux'un sağladığı özgürlüğü çok seviyor. Zamanının çoğunu programcılıkla uğraşarak geçirir. Günün birinde iyi bir bilgisayar programcısı olmayı istiyor. İçerik:
|
Özet:
Ncurses metin tabanlı terminaller için pencereler oluşturma ve kullanmaya, ekranı farklı renklerde boyamaya ve fonksiyon tuşlarını kullanmaya imkan veren bir kütüphanedir.
Yazdığınız bir programın renkli ve pencere temelli bir arayüze sahip olmasını mı istiyorsunuz? Ncurses metin tabanlı terminaller için pencereler oluşturma ve kullanmaya, ekranı farklı renklerde boyamaya ve fonksiyon tuşlarını kullanmaya imkan veren bir kütüphanedir. Ncurses ile yapabilecekleriniz:
Ncurses kütüphanesini ANSI/POSIX uyumlu UNIX sistemlerde kullanmak mümkündür. Bunun yanında, hangisi kullanılıyor olursa olsun terminal özelliklerini sistemden öğrenerek terminalden bağımsız bir arayüz sunmaktadır. Bu açılardan programcının farklı platformlar ve terminaller için bile güvenerek kullanabileceği bir kütüphanedir.
Midnight Commander, ncurses kullanılarak yazılmış programlara verilecek örneklerden bir tanesidir. Ayrıca konsolda çekirdek konfigürasyonu yapmakta kullanılan arayüz ncurses ile hazırlanmıştır. Bu iki programa ilişkin ekran görüntülerini aşağıda görüyorsunuz.
Ncurses GNU/Linux altında geliştirilmektedir. www.gnu.org/software/ncurses/ adresinden güncel sürümünü, ayrıntılı bilgileri ve diğer gerekli adresleri öğrenebilirsiniz.
Ncurses kütüphanesini kullanmak için curses.h başlık dosyasını kodunuza eklemeli ve kodunuzu gcc ile derlerken -lcurses parametresini kullanmayı unutmamalısınız.
Ncurses konusuna girerken kütüphane için temel bir veri yapısından bahsetmek gerekir. WINDOW adlı bu yapı, adından da anlaşıldığı gibi, ncurses ile oluşturacağınız pencereleri temsil etmek için kullanılır. Hemen hemen tüm ncurses fonksiyonları WINDOW tipinden bir işaretçiyi parametre olarak alırlar.
Pencereler, ncurses'da en çok kullanılan bileşenlerdir. Siz hiçbir pencere yaratmasanız bile ekran tek başına bir pencere olarak kabul edilir. Standart giriş/çıkış kütüphanesinde (yönlendirmeler olmadığı sürece) ekranı stdout adlı FILE işaretçisinin temsil etmesi gibi ncurses'da da ekran WINDOW tipinde stdscr adlı bir işaretçi ile temsil edilir. stdscr işaretçisine ek olarak, kütüphane tarafından curscr adlı bir başka WINDOW işaretçisi daha tanımlanır. stdscr'nin ekranı temsil etmesi gibi, curscr de o anki ekran görüntüsünü temsil eder. "İkisinin arasında ne fark var ki?" diye düşünüyor olabilirsiniz, okumaya devam edin.
Programınızda ncurses fonksiyonlarını ve değişkenlerini kullanabilmek için herşeyden önce initscr fonksiyonunu çağırmanız gerekir. Bu fonksiyon stdscr, curscr gibi değişkenler için bellek alır ve kütüphaneyi hazır hale getirir. Dolayısıyla her ncurses fonksiyonu initscr'yi takip etmek zorundadır. Benzer şekilde ncurses ile işinizi bitirdiğinizde endwin fonksiyonunu kullanarak kütüphanenin yarattığı değişkenlerin yokedilmesini sağlayabilirsiniz. Bunu yaptıktan sonra yeniden initscr çağrısı yapmadığınız sürece ncurses fonksiyonlarını kullanamazsınız.
Programınızda initscr ve endwin çağrıları arasında standart giriş/çıkış kütüphanesinin fonksiyonlarını kullanarak ekrana çıktı göndermemelisiniz. Bu durumda beklediğiniz görüntüyü elde edemeyebilirsiniz. Ncurses aktif olduğu sürece ekran çıktısı için sadece onun fonksiyonlarını kullanmaya dikkat edin. initscr çağırmadan önce ya da endwin çağırdıktan sonra istediğinizi yapabilirsiniz.
WINDOW yapısı içinde pencerenin eni, boyu, konumu gibi bilgiler tutulduğu gibi içeriği de saklanır. Ncurses fonksiyonları ile bir pencereye yazı yazıldığı zaman bu o yazının hemen ekranda görünmesi anlamına gelmez. Yazılan yazı öncelikle ilgili pencerenin içeriğinde güncellenir. Ekran görüntüsünün güncellenmesi için refresh ya da wrefresh fonksiyonlarının çağrılması gerekir.
stdscr ile curscr arasında yukarıda kafanıza takılmış olabilecek fark buradadır. curscr ekranın o anki görüntüsünü içerirken, stdscr'nin içeriği yapılmış yeni fonksiyon çağrıları sonucu farklı olabilir. stdscr ile curscr'nin örtüşmesi için refresh fonksiyonunun çağrılması gerekir. Diğer bir deyişle, curscr'nin içeriğini değiştiren tek fonksiyon refresh fonksiyonudur. Sizin curscr'yi kurcalamamanız ve onunla ilgili işleri refresh fonksiyonuna havale etmeniz tavsiye edilir.
refresh fonksiyonu ekranı güncelleme işini mümkün olduğunca hızlı yapabilmek için değişik bir mekanizmaya sahiptir. Fonksiyon çağrıldığında görüntüsü güncellenecek olan pencerenin yalnızca değişen satırlarını günceller. Böylece değişmeyen satırları yeniden ekrana yazmak için işlemci zamanı tüketmez. Ncurses ile standart giriş/çıkış kütüphanesi fonksiyonlarının birlikte kullanılmasının istenmeyen sonuçlar doğurmasının sebebi bu mekanizmadır; ncurses fonksiyonları bir pencereye yazdığı zaman o satırın değiştiğini belirten bayrağı doğrularlar, standart giriş/çıkış fonksiyonları ise bu işi yapmaz.
refresh ile wrefresh fonksiyonları temelde aynı işi yaparlar. wrefresh fonksiyonu parametre olarak bir WINDOW işaretçisi alır ve bu pencerenin görüntüsünü günceller. refresh() ise wrefresh(stdscr) çağrısı yapmaya denktir. Daha sonra da bahsedeceğim gibi birçok ncurses fonksiyonunun (wrefresh gibi) stdscr için yazılmış makroları vardır.
Şimdi kendi pencerelerinizi yaratmanızı sağlayan subwin ve newwin fonksiyonlarından bahsedelim. Bu iki fonksiyon da parametre olarak oluşturmak istediğiniz pencerenin enini, boyunu, sol üst köşesinin koordinatlarını alırlar. Dönüş değerleri ise yeni pencereyi temsil eden bir WINDOW işaretçisidir. Bu yeni işaretçiyi yukarıda bahsettiğim wrefresh ve daha sonra bahsedeceğim diğer fonksiyonlarla birlikte kullanabilirsiniz.
"Madem aynı işi yapıyorlar, neden iki tane fonksiyon var?" diye düşünebilirsiniz. Haklısınız, tam olarak aynı işi yapmıyorlar. subwin fonksiyonu yeni yarattığı pencereyi bir başka pencerenin alt penceresi olarak yaratır. Bu şekilde yaratılan pencere ana pencerenin o anki tüm özelliklerini miras alır. Bu özellikler daha sonra ana pencereden bağımsız olarak değiştirilebilir. Bu durum ana pencere ile alt pencereyi birbirine bağlayan bir durum değildir.
Bunun dışında ana ve alt pencereyi birbirine bağlayan bir özellik vardır: Pencere içeriğinin tutulduğu karakter dizisi ana ve alt pencereler için ortaktır. Diğer bir deyişle, ana pencere ile alt pencerenin kesiştiği bir noktadaki karakter her iki pencere tarafından da değiştirilebilir. Böyle bir kareye alt pencere yazarsa ana pencerenin, ana pencere yazarsa alt pencerenin o noktadaki karakterinin üzerine yazılır.
newwin ise tamamiyle yeni bir pencere oluşturur. Böyle bir pencere, kendi alt pencereleri olmadığı sürece karakter dizisini başka pencerelerle paylaşmaz. subwin fonksiyonunu kullanmanın avantajı, ortak karakter dizisi kullanımı sebebiyle daha az bellek tüketilmesidir. Ancak pencerelerin birbirlerinin bilgilerinin üzerine yazmasının dezavantaj getirdiği durumlarda newwin kullanmak doğru harekettir.
Alt pencerelerinizi koşullar elverdiği sürece istediğiniz derinlikte yaratabilirsiniz. Her alt pencerenin de kendi alt pencereleri olabilir fakat bu durumda aynı karakter dizisini ikiden de fazla pencerenin paylaştığını unutmayın.
Yarattığınız pencerelerle işiniz bittiğinde delwin fonksiyonuyla onları yokedebilirsiniz. Fonksiyonların parametrelerini öğrenmek için man sayfalarına bakmanızı tavsiye ederim.
stdscr'den, curscr'den, ekran görüntüsünü güncellemekten ve yeni pencereler yaratmaktan bahsettik. Peki pencerelerin içeriğini nasıl değiştireceğiz? Pencerelere nasıl yazacağız ve pencerelerden nasıl bilgi okuyacağız?
Bunlar için standart giriş/çıkış kütüphanesinin fonksiyonlarına çok benzeyen fonksiyonlar kullanılmaktadır. printf yerine printw, scanf yerine scanw, putc ya da putchar yerine addch, getc ya da getchar yerine getch kullanılır. Kullanımları alışıldığı gibidir, kullanım olarak sadece isimleri değişiktir. Benzer şekilde pencereye bir katar (string) yazmak için addstr, pencereden bir katar okumak için getstr kullanılabilir. Tüm bu fonksiyonlar başlarına bir 'w' harfi eklenerek ve birinci parametre olarak bir WINDOW işaretçisi alarak belirtilen bir başka pencereye de yazabilirler. Örneğin, printw(...) ile wprintw(stdscr, ...) aynı işi yapan iki çağrıdır, aynı refresh() ve wrefresh(stdscr) durumunda olduğu gibi.
Bu fonksiyonlara çok ayrıntılı olarak değinmek fazla yer kaplayacaktır. Yaptıkları işleri, prototiplerini, dönüş değerlerini ve uyarıları öğrenmek için en iyi kaynak her zaman olduğu gibi man sayfalarıdır. Sözünü ettiğim her fonksiyon için kullanmadan önce man sayfasına bir bakmanızı tavsiye ederim. Çok detaylı bilgiler edinebilirsiniz. Ayrıca yazının son bölümünü öğretici bir örneğe ayırdım. Şu ana kadar anlattığım ve anlatacağım hemen hemen tüm fonksiyon ve kavramları bu örnek programda takip etme imkanı bulacaksınız.
Ncurses ile pencerelere yazmak ve pencerelerden okumak sözkonusu olduğu zaman mantıksal ve fiziksel imleç kavramlarına da değinmek gerekir. Fiziksel imleçten kasıt yanıp sönerek sürekli ekranda görünen bildiğimiz imleçtir ve bir tanedir. Mantıksal imleçler ise ncurses pencerelerine ait olan ve her pencerenin mutlaka sahip olduğu başka imleçlerdir. Birden çok pencere olabileceğine göre mantıksal imleç de birden fazla olabilir.
Mantıksal imlecin görevi pencereye bir yazı yazılacağı zaman yazma işleminin başlayacağı ya da pencereden bir bilgi okunacağı zaman bu bilginin okunacağı kareyi göstermektir. Mantıksal imleci isteğinize göre hareket ettirebilmeniz demek ekranın ya da yarattığınız pencerenin istediğiniz karesine istediğiniz zaman yazı yazabilmeniz anlamına gelir. Bu, standart giriş/çıkış kütüphanesinin sağlamadığı bir avantajdır.
Mantıksal imlecin hareket ettirilmesi işini yapan fonksiyon move ya da hemen tahmin edeceğiniz üzere wmove fonksiyonlarıdır. move fonksiyonu wmove fonksiyonunun stdscr için yazılmış bir makrosudur.
Bir de mantıksal imleç ile fiziksel imlecin koordinasyonunun sağlanması durumu sözkonusudur. Herhangi bir yazma işleminin ardından fiziksel imlecin bulunacağı konum, her pencerenin sahip olduğu _leave bayrağının değerine bakılarak kararlaştırılır. Eğer _leave doğruysa, mantıksal imleç yazma işleminden sonra fiziksel imlecin üzerine (son harfin yazıldığı kareye) getirilir. Eğer _leave yanlışsa, fiziksel imleç mantıksal imlecin bulunduğu kareye (ilk harfin yazıldığı kareye) geri getirilir. _leave bayrağını yöneten fonksiyon leaveok fonksiyonudur.
Fiziksel imlecin hareket ettirilmesini sağlayan fonksiyon mvcur fonksiyonudur. Diğer fonksiyonlardan farklı olarak mvcur fonksiyonu bir sonraki refresh'te değil, hemen etkin olur. Fiziksel imlecin ekranda görünmesini istemiyorsanız, curs_set fonksiyonunu ilgili parametreleri ile kullanabilirsiniz. Detaylar için man sayfalarına bakınız.
Yukarıda bahsettiğim imleci hareket ettirme ve yazı yazma işlemlerini tek bir çağrı ile de yapabilirsiniz. Bu, fonksiyon çağrılarını birleştiren makroların varlığı sayesinde mümkündür. Bu çağrılar da addch, addstr, printw, getch, getstr, scanw gibi fonksiyonların man sayfalarında detaylı olarak açıklanmıştır.
Pencelere yazmak tamam. Peki pencereleri nasıl temizleyeceğiz, istediğimiz satırları ve karakterleri nasıl sileceğiz?
Ncurses'da silme, silinecek karenin, satırın ya da pencerenin içeriğinin boşluklarla doldurulması demektir. Aşağıda değindiğim silme fonksiyonlarının yaptığı da silinecek yerleri boşlukla doldurmaktır.
Önce tek tek karakterler ya da satırlarla uğraşan fonksiyonlara değinelim. delch ve wdelch pencerede o an mantıksal imlecin altında olan karakteri siler ve aynı satırda mantıksal imlecin sağında bulunan karakterleri birer sola kaydırır. deleteln ve wdeleteln ise mantıksal imlecin bulunduğu satırı sildikten sonra alttaki satırları birer yukarı kaydırır.
clrtoeol ve wclrtoeol fonksiyonları mantıksal imlecin bulunduğu satırda imlecin sağında kalan tüm karakterleri silerler. clrtobot ve wclrtobot fonksiyonları ise önce bir wclrtoeol çağırıp satırda mantıksal imlecin solunda kalan karakterleri, daha sonra da mantıksal imlecin bulunduğu satırın altında kalan tüm satırları silerler.
Bunların dışında bir de tüm ekranı ya da pencereyi temizlemeye yarayan fonksiyonlar vardır. Tüm ekranı silecek fonksiyonların kullanabileceği iki yöntem vardır. Birincisi tüm ekranı boşluklarla doldurup refresh fonksiyonunu çağırmak, diğeri de terminale ekranı temizlemesini belirten kontrol kodunu göndermektir. Birinci yöntem ikinciden daha yavaştır çünkü tek tek her karakterin ekrana yeniden yazılmasını gerektirir. İkincisi ise tüm ekranı hemen temizler.
erase ve werase fonksiyonları bir pencerenin karakter dizisini boşluklarla dolduran fonksiyonlardır. Bir sonraki refresh çağrısında pencere temizlenmiş olacaktır. Bu fonksiyonları kullanmak temizlenmek istenen pencere tam ekran boyutunda ise çok mantıklı değildir. Çünkü bu fonksiyonlar yukarıda bahsedilen yöntemlerden ilkini kullanmaktadır. Temizlenecek pencere tam ekran boyutunda olduğu zaman aşağıda anlatacağım fonksiyonları kullanmak daha avantajlıdır.
Diğer fonksiyonlara geçmeden önce _clear bayrağından sözetmek gerekir. Her pencerede olan _clear bayrağı, eğer doğruysa bir sonraki refresh'te terminale kontrol kodunun gönderilmesini ister. Ancak refresh bunu yapmadan önce bunu isteyen pencerenin tam ekran boyutunda olup olmadığını (_FULLWIN bayrağı ile) kontrol eder. Eğer pencere tam ekran boyutundaysa, refresh terminalin ekranı temizlemesini sağlar ve sadece boşluktan farklı olan karakterleri ekrana yazar. Bu, tüm ekran boyutunda temizlemenin hızlı olmasını sağlar. Terminal yönteminin sadece tam ekran boyutundaki pencerelerin temizlenmesi için kullanılmasının sebebi, terminale kontrol kodu gönderildiği zaman tüm ekranın temizlenmesidir. Pencere tam ekran boyutunda değilse, tüm ekranın temizlenmesi istenmez. _clear bayrağını clearok fonksiyonu kontrol eder.
clear ve wclear fonksiyonları tam ekran boyutundaki pencerelerin temizlenmesi için tercih edilir. Aslında bu fonksiyonlar bir werase ve bir clearok çağrısı yapmaya denktir. Öncelikle pencerenin karakter dizisini boşluklarla doldururlar. Daha sonra da _clear bayrağını doğrulayarak pencere tam ekran boyutunda ise terminal yoluyla, değilse de tek tek tüm karelerin üzerine boşluk karakteri yazmak yoluyla pencereyi temizlerler.
Sonuç olarak, temizlenecek pencerenin tam ekran boyutunda olduğunu biliyorsanız, clear ya da wclear kullanın. Daha hızlı bir sonuç elde edersiniz. Fakat silinecek pencere tam ekran boyutunda değilse, wclear ya da werase kullanmanız arasında bir performans farkı yoktur.
Ekran üzerinde görülen renkleri renk çiftleri olarak adlandırmak gerekir. Çünkü her karenin bir arkaplan rengi, bir de yazı rengi vardır. Ncurses ile renkli yazabilmenin yolu da kendinize ait renk çiftleri yaratmak ve yazılarınızın bu çiftler kullanılarak yazılmasını sağlamaktır.
Ncurses fonksiyonlarını kullanabilmek için initscr çağrısı yapmanızın gerekmesi gibi, renk fonksiyonlarını kullanmadan önce de start_color fonksiyonunu çağırmanız gerekir. Bundan sonra renk çiftleri oluşturmak için kullanmanız gereken fonksiyon init_pair fonksiyonudur. init_pair ile bir renk çifti oluşturduğunuzda, fonksiyona aktarmış olduğunuz ilk parametre olan sayı ile bu renk çiftini ilişkilendirmiş olursunuz. Daha sonra programınızda bu çifti kullanmak istediğinizde COLOR_PAIR makrosunu bu sayı ile çağırmanız yeterli olacaktır.
Renk çiftleri oluşturmanın dışında yazılarınızın bu çiftlerle yazılmasını sağlamanız gerekir. Bunu yapan fonksiyonlar attron ve wattron fonksiyonlarıdır. Bu fonksiyonlar, attroff veya wattroff çağrıları yapılana kadar ilgili pencereye yazılacak her yazının belirtilen renk çifti ile yapılmasını sağlarlar.
Bir de pencerelerin genel arkaplan ve yazı renklerini değiştiren bkgd ile wbkgd fonksiyonları vardır. Bu fonksiyonlar çağrıldıkları zaman ilgili pencerenin renk çiftini istenen renk çifti olarak değiştirirler ve bundan sonra pencerenin tüm karelerinin arkaplan rengi ile tüm yazıların rengi otomatik olarak değişir.
Kullanabileceğiniz renkler ve adı geçen fonksiyonların kullanım detayları için man sayfalarına bakınız.
Güzel bir görüntü oluşturmak için pencerelerinizi kutu içine alabilirsiniz. Kütüphane içerisinde bunu sizin için yapacak box adlı bir makro vardır. Ancak diğer fonksiyonlardan farklı olarak wbox mevcut değildir; box makrosu zaten bir pencere işaretçisini parametre olarak alır.
Makronun kolay kullanım detaylarını man sayfasından öğrenebilirsiniz. Burada başka bir noktaya dikkat etmeniz gerekmektedir. Bu makroyla pencerelerinizi kutu içine almanızın tek anlamı, o pencerenin karakter dizisinde pencereyi sınırlayan karelere kutuyu oluşturacak karakterlerin yazılmasıdır. Eğer siz bu karakterlerin üzerine yazarsınız kutunun görüntüsünü bozabilirsiniz. Bunun için, asıl pencerenin içinde subwin ile yeni bir pencere oluşturup dıştaki pencereyi kutu içine almanız ve yazı yazmak için içteki pencereyi kullanmanız güvenilir bir yöntem olabilir.
Klavyedeki fonksiyon tuşlarını kullanabilmeniz için öncelikle klavyeden bilgi okuyacağınız pencerede _use_keypad bayrağının değerinin doğrulanması gerekir. Bunu yapan fonksiyon keypad fonksiyonudur. Bu fonksiyona çağrı yaparak fonksiyon tuşlarını kullanılır hale getirdikten sonra normal girdi fonksiyonları ile klavyeden giriş alabilirsiniz.
Ancak bu durumda, örneğin getch ile aldığınız bir bilgiyi, char tipinden değil de int tipinden bir değişkenin içerisinde tutmaya dikkat edin. Çünkü fonksiyon tuşlarının sayısal değerleri bir char tipi değişken içinde saklayabileceğiniz değerlerden büyüktür. Fonksiyon tuşlarının sayısal değerlerini bilmenize gerek yoktur, bu sayısal değerler yerine kullanabileceğiniz isimleri getch fonksiyonunun man sayfasında bulabilirsiniz.
Burada inceleyeceğimiz örnek program basit ama öğretici bir programdır. Bu programda ncurses kullanılarak menüler oluşturulmuş ve menüdeki seçeneklerden bir tanesinin seçilmesi basit bir biçimde uygulanmıştır. Bu programda önemli olan ncurses'in pencere bileşenlerini menü etkisi yaratabilecek şekilde kullanmaktır. Programın ekran görüntüsünü aşağıda görüyorsunuz:
Programda her zaman olduğu gibi önce kullanılan kütüphanelerin başlık dosyaları yazılır. Bir de program içerisinde kullanacağımız enter ve escape tuşlarının ASCII karşılıkları yerine kullanılacak sabitler tanımlanır.
#include <curses.h> #include <stdlib.h> #define ENTER 10 #define ESCAPE 27
Aşağıdaki init_curses fonksiyonu programın ilk çalıştığı zaman çağırdığı fonksiyondur. Önce initscr çağrısı yaparak ncurses çalıştırılır, daha sonra start_color ile renkler kullanılır hale getirilir ve ardından da program boyunca kullanılacak renk çiftleri tanımlanır. curs_set(0) çağrısı fiziksel imlecin ekran üzerinde görünmemesini sağlarken, noecho çağrısı da kullanıcının klavyeden girdiği karakterlerin ekrana yazılmasını engeller. noecho fonksiyonunu girdileri kontrol ederek almak ve sadece kabul edilen karakterleri ekrana aktarmak gibi bir amaç için de kullanabilirsiniz. noecho fonksiyonunun etkisini kaldırmak için gerektiği zaman echo çağrısı yapılır. Aşağıdaki fonksiyon son olarak keypad çağrısı yaparak stdscr'den alınacak girdilerde fonksiyon tuşlarının da kullanılabilmesini sağlar. F1 ve F2 ile ok tuşlarını kullabilmek için bu çağrı gereklidir.
void init_curses() { initscr(); start_color(); init_pair(1,COLOR_WHITE,COLOR_BLUE); init_pair(2,COLOR_BLUE,COLOR_WHITE); init_pair(3,COLOR_RED,COLOR_WHITE); curs_set(0); noecho(); keypad(stdscr,TRUE); }
Bundan sonraki fonksiyon üzerinde menü isimlerini bulunan satırı yazan fonksiyondur. Biraz aşağılara inerek programda yalnızca bir satır olarak görülen menü çubuğunun, main fonksiyon içerisinde stdscr'nin alt penceresi olarak tanımlanmış tek satırlık bir pencere olduğunu görebilirsiniz. İşte aşağıdaki fonksiyon o pencereyi parametre olarak alıp önce arkaplan rengini değiştirir, sonra da menü isimlerini yazar. Menü isimlerini burada waddstr ile yazıyoruz. Bu tercihin belirli bir sebebi yok, istenilen fonksiyon kullanılabilirdi. İlk satırdaki wbkgd çağrısı ile pencerenin renk çifti olarak tanımlanan 2 numaralı çiftin dışında bir renk çifti (3) ile yazmak istediğimizde wattron fonksiyonunu kullandığımıza dikkat edin. Yeniden 2 numaralı çift ile yazmak için ise wattroff kullanıyoruz.
void draw_menubar(WINDOW *menubar) { wbkgd(menubar,COLOR_PAIR(2)); waddstr(menubar,"Menu1"); wattron(menubar,COLOR_PAIR(3)); waddstr(menubar,"(F1)"); wattroff(menubar,COLOR_PAIR(3)); wmove(menubar,0,20); waddstr(menubar,"Menu2"); wattron(menubar,COLOR_PAIR(3)); waddstr(menubar,"(F2)"); wattroff(menubar,COLOR_PAIR(3)); }
Sıradaki fonksiyon F1 ya da F2 tuşlarına basıldığı zaman menülerin açılmasını sağlayan fonksiyondur. Menü açılma etkisini yaratmak için arkaplanı oluşturan pencere (mavi renkli) üzerine menü çubuğu ile aynı renkte (beyaz) yeni bir pencere açılmaktadır. Ancak bu yeni pencere açıldığı zaman altta kalan pencerede yazılmış olan yazıların silinmesini istemiyoruz. Menü kapandığı zaman bu yazıların yeniden görünmesini istiyoruz. Bu sebeple yeni yaratılan pencerenin stdscr'nin bir alt penceresi olarak yaratılması doğru değildir. Aşağıda da görüldüğü gibi items[0] adlı pencere newwin fonksiyonuyla oluşturulmuştur. Diğer 8 tane items penceresi ise items[0]'ın alt pencereleri olarak yaratılmıştır. Burada items[0] menüyü sınırlayan çerçeveyi çizmek için kullanılırken diğer pencereler ise hem çerçeveyi oluşturan karakterlerin üzerine yazmamak hem de menüde seçili olan elemanı göstermek için kullanılacaktır. Seçili olan elemanı göstermek için karşılık gelen items penceresinin arkaplan rengini diğerlerinden farklı yapmak yeterlidir. Sondan iki önceki satırda da bu yapılmaktadır ve menü ilk açıldığında birinci sıradaki eleman seçili olarak görünmektedir.
WINDOW **draw_menu(int start_col) { int i; WINDOW **items; items=(WINDOW **)malloc(9*sizeof(WINDOW *)); items[0]=newwin(10,19,1,start_col); wbkgd(items[0],COLOR_PAIR(2)); box(items[0],ACS_VLINE,ACS_HLINE); items[1]=subwin(items[0],1,17,2,start_col+1); items[2]=subwin(items[0],1,17,3,start_col+1); items[3]=subwin(items[0],1,17,4,start_col+1); items[4]=subwin(items[0],1,17,5,start_col+1); items[5]=subwin(items[0],1,17,6,start_col+1); items[6]=subwin(items[0],1,17,7,start_col+1); items[7]=subwin(items[0],1,17,8,start_col+1); items[8]=subwin(items[0],1,17,9,start_col+1); for (i=1;i<9;i++) wprintw(items[i],"Item%d",i); wbkgd(items[1],COLOR_PAIR(1)); wrefresh(items[0]); return items; }
Sıradaki fonksiyon yukarıdaki fonksiyon tarafından oluşturulan menü pencerelerini yokeden fonksiyondur. Bunun için önce pencereler tek tek delwin ile yokedilip daha sonra da items işaretçisi için alınan bellek geri verilmektedir.
void delete_menu(WINDOW **items,int count) { int i; for (i=0;i<count;i++) delwin(items[i]); free(items); }
scroll_menu fonksiyonu menüler arasında ve menü içinde gezmeyi sağlayan fonksiyondur. Sonsuz bir döngü içinde getch fonksiyonu ile klavyeden girilen tuşları okumaktadır. Kullanıcı aşağı yukarı ok tuşlarına basarsa menüde aşağıdaki ya da yukarıdaki eleman seçili olarak gösterilmektedir. Bu, yukarıda da bahsettiğim gibi, seçili olan elemanın bulunduğu pencerenin arkaplan rengini diğerlerinden farklı yaparak sağlanmaktadır. Kullanıcı eğer sağ ve sol ok tuşlarına basarsa içinde bulunulan menü kapatılmakta ve diğer menü açılmaktadır. Kullanıcı enter tuşuna basarsa seçilmiş olan elemanın değeri geri döndürülür. Escape tuşu ise herhangi bir elemanı seçmeden menüleri kapatmaya yarar. Fonksiyon diğer tuşları dikkate almamaktadır. Bu fonksiyonda getch ile klavyeden ok tuşlarının okunabilmesi için iki hatırlatma yapmakta fayda var. Öncelikle en baştaki init_curses fonksiyonunda keypad(stdscr,TRUE) çağrısı yapılarak stdscr'den fonksiyon tuşlarının okunabilmesi sağlanmıştı. Buna ek olarak bir de getch ile okunan değer bir int tipi değişkende saklanmaktadır, çünkü ok tuşları ve diğer fonksiyon tuşlarının sayısal değerleri char tipi değişkenin tutabileceğinden büyüktür.
int scroll_menu(WINDOW **items,int count,int menu_start_col) { int key; int selected=0; while (1) { key=getch(); if (key==KEY_DOWN || key==KEY_UP) { wbkgd(items[selected+1],COLOR_PAIR(2)); wnoutrefresh(items[selected+1]); if (key==KEY_DOWN) { selected=(selected+1) % count; } else { selected=(selected+count-1) % count; } wbkgd(items[selected+1],COLOR_PAIR(1)); wnoutrefresh(items[selected+1]); doupdate(); } else if (key==KEY_LEFT || key==KEY_RIGHT) { delete_menu(items,count+1); touchwin(stdscr); refresh(); items=draw_menu(20-menu_start_col); return scroll_menu(items,8,20-menu_start_col); } else if (key==ESCAPE) { return -1; } else if (key==ENTER) { return selected; } } }
Son olarak sırada main fonksiyonu var. main fonksiyonu yukarıda anlatılan fonksiyonları kullanarak programın çalışmasını sağlamaktadır. Bu fonksiyon da getch ile basılan tuş değerleri okumakta ve eğer F1 ya da F2 tuşlarına basılmışsa draw_menu ile karşılık gelen menüyü çizdirmektedir. Menü çizildikten sonra scroll_menu fonksiyonu ile menülerden bir eleman seçilmesi sağlanmaktadır. scroll_menu fonksiyonu geri döndükten sonra da önce menü pencereleri silinmekte ve ardından seçilen eleman messagebar penceresine yazdırılmaktadır.
Burada touchwin fonksiyonuna değinmekte fayda var. Menüler kapandıktan sonra touchwin çağrılmadan refresh yapılsaydı en son açılan menü ekranda durmaya devam ederdi. Bunun sebebi menüler yaratıldığı zaman stdscr üzerinde hiçbir değişiklik yapılmaması ve refresh çağrıldığı zaman stdscr hiç değişmemiş olarak görüldüğü için yeniden yazılmamasıdır. touchwin fonksiyonu parametre olarak aldığı pencerenin tüm satırlarındaki satırın değiştiğini belirten bayrakları doğrulayarak bir sonraki refresh'te pencerenin, hiç değişmemiş olsa bile, en baştan çizilmesini sağlar. Menüler yaratıldığı zaman stdscr'ye hiç dokunulmamış olması sayesinde, menüler kapandığı zaman stdscr'de yazan bilgi kaybolmamıştır.
int main() { int key; WINDOW *menubar,*messagebar; init_curses(); bkgd(COLOR_PAIR(1)); menubar=subwin(stdscr,1,80,0,0); messagebar=subwin(stdscr,1,79,23,1); draw_menubar(menubar); move(2,1); printw("Press F1 or F2 to open the menus. "); printw("ESC quits."); refresh(); do { int selected_item; WINDOW **menu_items; key=getch(); werase(messagebar); wrefresh(messagebar); if (key==KEY_F(1)) { menu_items=draw_menu(0); selected_item=scroll_menu(menu_items,8,0); delete_menu(menu_items,9); if (selected_item<0) wprintw(messagebar,"You haven't selected any item."); else wprintw(messagebar,"You have selected menu item %d.",selected_item+1); touchwin(stdscr); refresh(); } else if (key==KEY_F(2)) { menu_items=draw_menu(20); selected_item=scroll_menu(menu_items,8,20); delete_menu(menu_items,9); wprintw(messagebar,"You have selected menu item %d.",selected_item+1); if (selected_item<0) wprintw(messagebar,"You haven't selected any item."); else wprintw(messagebar,"You have selected menu item %d.",selected_item+1); touchwin(stdscr); refresh(); } } while (key!=ESCAPE); delwin(menubar); delwin(messagebar); endwin(); return 0; }
Aralara yazdığım açıklamaları çıkarıp kaynak kodunu example.c adlı dosyaya kaydettiğinizi varsayarak
gcc example.c -o example -lcurses
komutu ile kodu derleyebilir ve programı test edebilirsiniz.
Ncurses kullanarak programınız için güzel bir arayüz oluşturabilmeniz için gereken temel konulara değindim. Ancak bu kütüphaneyle yapabilecekleriniz burada anlatılanlarla sınırlı değildir. Sık sık bakmanızı tavsiye ettiğim man sayfalarında kütüphane ile ilgili başka yönler de keşfedecek ve bu anlattıklarımın yalnızca giriş düzeyinde bilgiler olduğunu göreceksiniz.
|
Görselyöre sayfalarının bakımı, LinuxFocus Editörleri tarafından yapılmaktadır
© Reha K. Gerçeker, FDL LinuxFocus.org Burayı klikleyerek hataları rapor edebilir ya da yorumlarınızı LinuxFocus'a gönderebilirsiniz |
Çeviri bilgisi:
|
2002-01-28, generated by lfparser version 2.22