PDA

View Full Version : آموزش مقدماتی ncurses در لینوکس



بهنامک‍‍
سه شنبه 30 مهر 1392, 10:11 صبح
این کتابخانه برای رسم گرافیک در text-mode هست.
نمی‌شه گفت ncurses جایگزین conio هستش. ولی خوب کسی که بخواد برنامه‌ای برای محیط متنی با رابط کاربری مناسب بنویسه، بهترین گزینه هستش.

برای استفاده از این کتابخانه لازمه که کتابخانه ی ncurses رو نصب کنین چون به طور پیشفرض تو اوبونتو(و خیلی از توزیعا)نصب نیست.
برای نصبش این کارو بکنین:

sudo apt-get install ncurses-dev
با این کار سریعا کتابخانه ی مورد نظر و چندتا dependency دیگه نصب میشه و با خیال راحت و بدون ریستارت میتونین کارتونو شروع کنین.

نوشتن hello world معروف با استفاده از ncurses :
#include <ncurses.h>

int main()
{
initscr();
refresh();
printw("Hello world!!!!\n");
refresh();
getch();
endwin();
return 0;
}
خب الان کدای بالا رو تو یه ادیتور بنویسید و با پسوتد c. ذخیره کنید(البته پسوند اصلا مهم نیست)
مثلا من اسمشو hello.c گذاشتم و حتما باید اینجوری کامپایلش کنم:

gcc -o hello hello.c -lncurses
تو قسمت کامپایل تنها چیزی که مهمه lncurses- هست که به gcc میگه که باید بره یه جای دیگه دنبال کتابخانه ی ncurses بگرده و از مسیر مسیر پیشفرض نمیتونه پیداش کنه.
حالا نوبت اجرا رسیده:

./hello

اما هرکدوم از توابع بالا چه کاری انجام میدن؟
()initscr
این تابع یه بافر مخصوص برای ncurses میسازه و اونو در اختیار ما قرار میده تا برای کارامون به جای صفحه ی ترمینال ازش استفاده کنیم.یعنی درواقع برای ما یه صفحه ای مجازی شبیه ترمینال میسازه و مدیریت کامل اونو در اختیار ما قرار میده تا هرکاری که بخواییم با اون انجام بدیم و بعدا محتویات اونو روی ترمینال نمایش بدیم و نتیجه جوری باشه که انگار با خوده ترمینال کار کردیم.
این بافر روی رم سیستم تعبیه میشه و مث صفحه ای عمل میکنه که ما اونو نمیبینیم اما میتونیم داخلش بنویسیم و حتی کرسر رو به راحتی جابجا کنیم(کاری که بدون این صفحه ی مجازی و ncurses نمیتونیم انجام بدیم).
کسایی که تو ویندوز با c کار کرده باشن حتما کتابخانه ی conio رو میشناسن.اونجا توابعی مث ()getch() , gotoxy و ()clrscr رو دیدن و از کار کردن باهاشون لذت بردن اما متاسفانه این کتابخانه و توابع در standard C وجود ندارن و در واقع اون کتابخانه ی conio محصول بورلتده که برای کاربرا نوشته و در لینوکس چنین چیزی نداریم.

()refresh
این توابع وظیفه ی نوشتن بافر را بر روی ترمینال به عهده دارد.اما چون اینجا فعلا ما چیزی داخل بافر ننویشیم پس نقش ()clrscr رو تو ویندوز ایفا میکنه.اما بعدا برای مقاصد مهم تری هم ازش استفاده میکنیم.
اینجا اگه این خط رو هم ننویسید مشکلی پیش نمیاد اما همیشه قشنگتر اینه که همون اول کار بافر رو بیارید بریزید تو ترمینال تا کارتون استاندارد باشه.

()printw
این تابع بر روی window مجازی ما مینویسه!
پس هر رشته ای که ما بهش بدیم رو میره تو بافر و بر روی پنجره ی مجازی مینویسه.اینجا دقت کنین که ما هنوز چیزی رو تو ترمینال ننویشتیم بلکه رشته ی ما داخل بافر که مستقل از پنجره ی ترمیناله قرار گرفته.

()refresh
حالا وقت اینه که بافر رو بیاریم بریزیم تو پنجره ی ترمینال!
پس با این کار ما رشته مون رو روی صفحه ی ترمینال چاپ میکنیم!

()getch
دیگه فک کنم همه این تابعو میشناسن مخصوصا ویندوز کارا!
این تابع منتظر گرفتن کلیدی از کاربر میشه و درواقع اجرای برنامه رو بلاک میکنه تا خط بعدی اجرا نشه.یعنی تا کابر یه کلید رو از صفحه کلید فشار نده برنامه همونجا میمونه و تکون نمیخوره

()endwin
خب الان دیگه کارمون با اون بافر و پنجره ی مجاز تموم شد چون هرچی لازم بود رو تو پنجره ی ترمینال نویشتیم و الان وقتشه که اون بافر رو از داخل رم سیستم پاک کنیم چون اگه همونجوری بمونه دردسر ساز میشه پس با این تابع گردنشو میزنیم و از رم میندازیمش بیرون.


حرکت کاراکتر بر روی صفحه ی نمایش:

خب الان یه برنامه مینویسیم که یه نوشته ای داخل ترمینال حرکت کنه.
این نوشته با فشار کلید p از صفحه کلید متوقف میشود و کلید r کاراکتر را دوباره به حرکت در می آورد.
کلید q هم برای خروج از برنامه استفاده میشه.
#include <ncurses.h>
#include <unistd.h>


int main()
{
initscr();
refresh();
noecho();

char command;
int i=15 , j=1;

while(1)
{
clear();
move(j , i);
printw("_UBUNTU");
refresh();
usleep(150000);
i++;
if(i>60)
{
j++;
i = 15;
}
nodelay(stdscr,1);
command = (char)getch();
if(command == 'q')
break;
else
if( command == 'p')
{
move(7,34);
printw("Pausing . . . \n");

printw("\n\nPress r to Resume or q to quite:");
while(1)
{
command = (char)getch();
if(command == 'q')
{
clear();
move(9,34);
printw("Quiting . . . ");
refresh();
usleep(1070000);
endwin();
return 0;
}
if(command == 'r')
break;
}
}
}
clear();
move(9,34);
printw("Quiting . . . ");
refresh();
usleep(1070000);
endwin();
return 0;
}
الان با نام ubuntu.c ذخیره ش کنین و اینجوری کامپایل و اجراش کنین:

gcc ubuntu ubuntu.c -lncurses

./ubuntu
و اما معرفی توابع جدید:
()noecho
همونجور که از اسم تابع معلومه وظیفه ی مخفی کردن کاراکترهایی که کاربر برای توقف و یا ادامه و خروج برنامه تایپ میکند را بر عهده دارد.یعنی با این کار دیگر هیچ کدام از ورودی های کاربر که از طریق صفحه کلید وارد میشه بر روی صفحه نمایش داده نمیشه
این تابع یکی از توابه ncurses هست

()clear
این تابع همزاد لینوکسی ()clrscr در بورلنده و ویندوزه که انصافا هم چیز خوب و قشنگیه
این تابعم باز مال ncurses عه

(move(i,j
اینم همزاد تابع ()gotoxy در ویندوزه که کرسر رو به مکان خاصی با مختصات i , j در صفحه میبره.
فقط توجه کنید که i مختصات ارتفاع و j مختصات ظول صفحه ست!
بدیهیه که این تابعم داخل ncurses قرار داره

()nodelay
همونطور که در قسمت قبل گفتم تابع ()getch باعث بلاک شدن برنامه میشه یعنی تا زمانی که که کاربر کاراکتری رو وارد نکنه اجرای برنامه به خط بعدی نمیره.
اما در نقشه ی برنامه ی ما همچین چیزی نباید اتفاق بیوفته.باید برنامه اجرا شود و هرگاه که کاربر کلیدی را فشار داد آنگاه کلید دریافت شده و تصمیم اتخاذ شود.
برای اینکه تابع ()getch برنامه را بلاک نکند از این تابع داخل ncurses استفاده میکنیم.
این تابع دو ورودی میگیرد که یکی صفحه ی مجازی کنونیه که داریم باهاش کار میکنیم و دیگری یه مقداره بولینه که اگه 1 باشه به معنی اینه که تابع بیاد و از بلاک شدن برنامه جلوگیری کنه

()usleep
برای کنترل سرعت حرکت متن از این تابع استفاده میکنیم.
این تابع که درون کتابخانه ی unistd قرار دارد کل برنامه را به مدت فلان میلی ثانیه متوقف میکند
پس ورودی تابع برحسب میلی ثانیه ست!


خب الان نوبت میرسه به مقایسه!

تقریبا همه ی اون کارایی که با ncurses انجام میشه رو میتونیم با سختی چند صدبرابر با سایر کتابخانه های غیر تخصصی هم انجام بدیم.
برای مثال این برنامه که اینجا گذاشتم همون کاره حرکت کاراکتر در صفحه رو انجام میده اما چون بدون ncurses نوشتم مجبور بودم خیلی از توابع رو خودم پیاده سازی بکنم:
#include <stdio.h>
#include <stdlib.h>


int lasti=0,lastj=0;

void goback(int x,int y )
{
printf("\033[%dA",x);
printf("\033[%dD",y);
}

void gotoxy(int x,int y )
{
extern int lasti, lastj;

goback(lasti, lastj);
int i;

for(i=0;i<x;i++)
printf("\n");
for(i=0;i<y;i++)
printf(" ");
lasti=x;
lastj=y;
}

int main(int argc , char* argv[])
{
printf( "\e[2J\e[H" );
usleep(100900);
int i = 1;
int j = 20;
// gotoxy(0,0);
// printf("A");
usleep(100900);


int b = 200;

while(b)
{
gotoxy(i,j-6);
printf(" _");
usleep(100900);
gotoxy(i,j);
printf(" BEHNAM");
usleep(100900);
j++;
if(j==60)
{
gotoxy(i,j--);
printf(" ");
j++;
i++;
j = 20;
gotoxy(i,j);
printf(" BEHNAM");
j++;

}

b--;
}

printf("\n\n");
return 0;

}

میبینید که چقد پیچیده و مشکل شد؟
تازشم اینکه روند و کیفیت این دو برنامه اصلا شبیه همدیگه نیست و این مثل یه تقلب چینی از یه جنس آلمانیه!!!!

بهنامک‍‍
پنج شنبه 02 آبان 1392, 08:27 صبح
ترکیب pthread و ncurses در بازی خرگوش و لاکپشت Hare & Turtle:

در این برنامه ترکیب این دو کتابخانه ی بسیار پرکاربرد لینوکس رو باهم میبینیم.
خرگوش و لاکپشت باهم مسابقه ی دو میذارن اما چون خرگوش قصه ی ما خیلی مغروره همین که چند قدم از لاکپشت جلو میوفته میگیره میخوابه چون فک میکنه که عمرا لاکپشت بهش برسه اما بعضی وقتا وسط خوابش چشاشو باز میکنه و وقتی لاکپشتو نزدیک خودش میبینه دوباره یه پرش میزنه و دوباره میگیره میخوابه.
حالا بشینین این مسابقه ی جذابو باهم ببینین.
ببینید هر بار کدومشون برنده میشه!!!!
// Behnam Mohammad Karimi

#include <pthread.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/types.h>

#include <stdlib.h> //exit

#ifndef pthread_attr_default /* For native POSIX threads */
#define pthread_attr_default NULL
#endif


struct location {

int hx;
int hy;
int tx;
int ty;
int finish;

};

struct location loc;

void *show(void *ptr)
{
clear();
move(loc.hy,loc.hx);
printw("H");

move(loc.ty,loc.tx);
printw("T");

move(9,19);
printw("------------------------------------------");
move(14,19);
printw("------------------------------------------");
move(10,19);
printw("|");
move(13,19);
printw("|");
move(11,19);
printw("|");
move(12,19);
printw("|");

move(10,60);
printw("|");
move(13,60);
printw("|");
move(11,60);
printw("|");
move(12,60);
printw("|");

refresh();
if(loc.finish == 0)
pthread_exit(0);

}

void *moveH(void *ptr)
{
if(loc.hx == loc.tx )
loc.hx += (rand()%4 + 1);

pthread_exit(0);
}

void *moveT(void *ptr)
{
loc.tx += 1;

pthread_exit(0);
}

void *judge(void *ptr)
{
if(loc.hx > 60 )
{
loc.finish = 0;
clear();
move(10,35);
printw("Hare win!!!");
refresh();
}

if(loc.tx > 60)
{
loc.finish = 0;
clear();
move(10,35);
printw("Turtle win!!!");
refresh();
}
pthread_exit(0);

}

int main()
{
initscr();

loc.hx = 20;
loc.hy = 11;
loc.tx = 20;
loc.ty = 12;
loc.finish = 1;

char *message = "null";

clear();
move(loc.ty,loc.tx);
printw("T");

move(loc.hy,loc.hx);
printw("H");

move(9,19);
printw("------------------------------------------");
move(14,19);
printw("------------------------------------------");
move(10,19);
printw("|");
move(13,19);
printw("|");
move(11,19);
printw("|");
move(12,19);
printw("|");

move(11,60);
printw("|");
move(12,60);
printw("|");
move(10,60);
printw("|");
move(13,60);
printw("|");

refresh();

sleep(1);

pthread_t thread1, thread2 , thread3 , thread4;

while(loc.finish)
{
pthread_create( &thread1, pthread_attr_default,
(void *) moveH, (void *) message);
sleep(1);

pthread_create( &thread2, pthread_attr_default,
(void *) moveT, (void *) message);
// sleep(1);

pthread_create( &thread4, pthread_attr_default,
(void *) show, (void *) message);

pthread_create( &thread3, pthread_attr_default,
(void *) judge, (void *) message);
pthread_join(thread3, NULL);


}
sleep(2);
getch();
endwin();
exit (0);

}

کامپایل:

gcc -o name prog.c -lncurses -pthread

اجرا :

./name

http://upload7.ir/images/55951875868898503070_thumb.png (http://upload7.ir/viewer.php?file=55951875868898503070.png)

کی میتونه این برنامه رو بهتر کنه؟