-[ 0x0C ]-------------------------------------------------------------------- -[ NCURSES ]----------------------------------------------------------------- -[ by blackngel ]----------------------------------------------------SET-31-- @ @ @@@ blackngel_hack@hotmail.com @@@ IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII III @@@@ @ @@@@@ @@@@@ @ @ @@ @ @@@@@ @@@@@ @ III III @ @ @ @ @ @ @ @ @ @ @ @ @ @ III III @@@@@ @ @@@@@ @ @@@ @ @ @ @ @@@ @@@@ @ III III @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ III III @@@@ @@@@@ @ @ @@@@@ @ @ @ @@ @@@@@ @@@@@ @@@@@ III IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII @@@ @@@ @ @ %%%%%%%%%%%%%%%%%%%%&%%% %%%==================%%% %%% NCURSES 1ª PARTE %%% %%%==================%%% %%%%%%%%%%%%%%%%%%%%%&%% "Bienvenidos al mundo de lo real..." _*#*__*#*__*#*__*#*__*#*_ _*#*_ 00. INDICE _*#*_ _*#*__*#*__*#*__*#*__*#*_ *_#_**_#_**_#_**_#_**_#_**_#_**_#_**_#_* *_#_* 00 - INDICE *_#_* *_#_*______________________________*_#_* *_#_* 01 - PROLOGO *_#_* *_#_*______________________________*_#_* *_#_* 02 - INTRODUCCION *_#_* *_#_*______________________________*_#_* *_#_* 03 - COMPILACION *_#_* *_#_*______________________________*_#_* *_#_* 04 - CONCEPTOS BASICOS *_#_* *_#_*______________________________*_#_* *_#_* 05 - VENTANAS *_#_* *_#_*______________________________*_#_* *_#_* 06 - PANELES *_#_* *_#_*______________________________*_#_* *_#_* 07 - MENUS *_#_* *_#_*______________________________*_#_* *_#_* 08 - PROGRAMA EJEMPLO *_#_* *_#_*______________________________*_#_* *_#_* 09 - DESPEDIDA *_#_* *_#_**_#_**_#_**_#_**_#_**_#_**_#_**_#_* _*#*__*#*__*#*__*#*__*#*_ _*#*_ 01. PROLOGO _*#*_ _*#*__*#*__*#*__*#*__*#*_ Con ganas de trabajar un poco y a cuento de aprender un poco mas, aqui estoy otra vez, con un nuevo articulo que no hara mas feliz al lector pero si a su escritor (deseo equivocarme). Pregunta mas obvia: porque NCURSES? Pues bien, sinceramente, no soy muy amigo de hacer aplicaciones con GUI's (Interfaces Graficas de Usuario), no obstante, cada vez que programo alguna pequeña utilidad a la vieja usanza, se me queda el gusanillo de porque esta no puede ser mas facil de controlar y a la vez mas atractiva al usuario. Desde luego, navegar con las flechas del teclado por un menu es mucho mas comodo que una lista de opciones de la que tienes que escribir el numero de la que deseas escojer. Hace tiempo que escuche hablar de ncurses, pero quizas por las vagas introducciones que he leido en revistas o quizas por mi costumbre de hacer las cosas como en el siglo XV, no me habia parado a investigar mas. Todo ha cambiado, he decidido ponerme las pilas y he encontrado informacion no poco valiosa. Un poco enamorado de ncurses? porque negarlo, cuando programas algo y los resultados son satisfactorios, la recompensa moral es indescriptible. Intentare hacer esto lo mas ameno e inteligible que pueda para el lector, sin dejar a un lado los detalles tecnicos e invitar al programador no solo a programar sino a entender y amar su codigo. Puede que yo no tenga todas las respuestas pero, sin duda alguna, internet es la fuente de informacion mas poderosa, como siempre, el problema es "saber buscar". _*#*__*#*__*#*__*#*__*#*__*#*_ _*#*_ 02. INTRODUCCION _*#*_ _*#*__*#*__*#*__*#*__*#*__*#*_ Que es ncurses? Ncurses es una API que nos proporciona unos servicios con los que podremos crear ventanas, posicionar el cursor, hacer uso de colores y un largo etc pero, lo mas importante, es que tiene unas liberias complementarias conocidas con los nombres de panel, menu y form respectivamente, que nos permitiran controlar las ventanas de un modo mucho mas preciso, nos ayudaran a crear menus y a realizar formularios con los que el usuario se sentira mucho mas comodo. Ncurses es una liberia mejorada de lo que fue en sus tiempos curses. Quien creo ncurses? Pues el encargado de tal tarea fue Pavel Curtis. Hoy en dia, los encargados de mantener el paquete son: - Zeyd Ben-Halim - Eric S. Raymon - Thomas Dickey - Juergen Pfeifer En la version que dispongo yo actualmente de ncurses (5.3-110), estos son los autores que aparecen citados en los fuentes: /**************************************************************************** * Author: Zeyd M. Ben-Halim 1992,1995 * * and: Eric S. Raymond * * and: Thomas E. Dickey 1996-on * ****************************************************************************/ Juergen Pfeifer es el encargado de las librerias "menu" y "form". La primera direccion de correo la he visto cambiada en otro sitio por: _*#*__*#*__*#*__*#*__*#*__*#*_ _*#*_ 03. COMPILACION _*#*_ _*#*__*#*__*#*__*#*__*#*__*#*_ Para poder hacer uso de estas librerias debemos de añadir a nuestros fuentes ciertos includes y agregar alguna opcion a la hora de compilar para que nuestros programas puedan funcionar perfectamente. Principalmente añadiremos a los fuentes las siguientes directivas segun las librerias que vayamos a utilizar: ncurses -> #include panel -> #include menu -> #include form -> #include El sistema posee una liberia estatica y otra dinamica para cada servicio: La primera aumentara el tamaño del programa pero nos asegurara que esta funcione en cualquier ordenador aunque no tenga instalado el paquete citado. La segunda solo añadira lo indispensable a nuestra aplicacion pero para ejecutarse, asume que el ordenador en el que se inicia, cuenta con tales librerias. Esta ultima es la mas utilizada pero, es el programador quien debe decidir cual va ser el ambito de la aplicacion y escoger la opcion mas conveniente. Las librerias son las siguientes: ESTATICA DINAMICA ncurses -> libncurses.a libncursesw.so.X.Y panel -> libpanel.a libpanel.so.X.Y menu -> libmenu.a libmenu.so.X.Y form -> libform.a libform.so.X.Y X e Y -> Version de las librerias. Si queremos hacer el enlace estatico debemos ponder la liberia deseada como un modulo mas de nuestra aplicacion, por ejemplo: gcc programa.c /usr/lib/libncurses.a -o programa Si por el contrario queremos el enlace dinamico el comando seria asi: gcc -lncurses programa.c programa Aqui expongo un ejemplo de makefile en el que utilizo la libreria ncurses de modo estatico y el resto de forma dinamica: <++> LDFLAGS=-lpanel -lmenu -lform all: programa programa: programa.o /usr/lib/libncurses.a clean: rm programa.o programa <--> Cualquier duda para compilar un programa podeis hacermelo saber en mi correo, cuanta mas informacion me deis mas facil sera de solucionar el problema. _*#*__*#*__*#*__*#*__*#*__*#*__*#*_ _*#*_ 04. CONCEPTOS BASICOS _*#*_ _*#*__*#*__*#*__*#*__*#*__*#*__*#*_ Antes de nada me gustaria que conocieseis algunas de las variables mas importantes ya declaradas por la libreria: stdscr -> Puntero a la ventana principal (pantalla completa). curscr -> Puntero a la ventana actual. newscr -> Puntero a una ventana nueva. LINES -> Lineas de la ventana del terminal. COLS -> Columnas de la ventana del terminal. TABSIZE -> Tamaño del tabulador (espacios que contiene). COLORS -> Numero de colores individuales. La primera funcion a utilizar cuando programamos con ncurses es initscr(), cuyo prototipo veremos a continuacion. Su objetivo es determinar el tipo de terminal del que disponemos y de borrar el contenido existente en la pantalla mediante una llamada a la funcion refresh(). Alguien pensaria que iva a decir system("clear"), pero recordar que no estamos trabajando con la biblioteca estandar. Realmente initscr() realiza una llamada a la funcion newterm() que se encarga de la mayor parte del trabajo. Si trabaja sobre multiples terminales puede utilizar newterm() directamente, de todas formas, esta funcion no nos concierno ahora. extern NCURSES_EXPORT(WINDOW *) initscr (void); Como se puede comprobar, si todo ha salido bien, esta funcion devuelve un puntero WINDOW que representa a la pantalla completa. Ahora tendremos a nuestra disposicion una lista de funciones que utilizaremos segun nuestras necesidades y que describire seguidamente. raw() -> Con ella le diremos al programa que recoja el contenido de las teclas segun van siendo pulsadas sin esperar un "Intro", ademas de caracteres, esta funcion reconocera teclas especiales como flechas y combinaciones. keypad() -> Esta es realmente la funcion encargada de detectar la pulsacion de teclas de desplazamiento o de funcion(Fx). Necesita dos argumentos al ser invocada: 1- Puntero WINDOW de la ventana a la que afectara. Utilice la variable "stdscr" para la principal. 2- TRUE o FALSE para activar o desactivar. has_colors() -> Indica si nuestro terminal soporta la capacidad de utilizar colores. El valor devuelto sera 0 si no puede y distinto de 0 si puede. start_color() -> Facil de deducir, si en nuestra aplicacion vas hacer uso de colores, esta se encargara de inicializar un conjunto de ellos para que esten disponibles. init_pair() -> Asocia a un indice (un entero) una pareja de colores, que seran utilizados por la siguiente funcion para establecerlos en una ventana. wcolor_set() -> Establece una pareja de colores (indicada por su indice en el segundo parametro) como predeterminada para una ventana (cuyo puntero WINDOW se facilita como primer parametro). Como tercer parametro utilizaremos NULL. wbkgd() -> Funciona igual que la anterior pero solo requiere dos parametros, el primero es la ventana y el segundo es una directiva llamada COLOR_PAIR() cuyo argumento sera el indice de una pareja de colores. echo() -> Permite que las teclas pulsadas en el teclado se vean en el teclado, su correspondiente es noecho(). nodelay() -> Provoca que el programa no se bloquee si hay una funcion que esta esperando la pulsacion de una tecla. Precisa de los mismos parametros que keypad(). curses_version() -> Informa de la version de esta libreria. endwin() -> Siempre hay que llamar a esta funcion antes de la finalizacion del programa ya que es la encargada de devolver los atributos del terminal a su estado original y que todo siga su transcurso normal. Antes de pasar al siguiente apartado quisiera dar un ejemplo del uso de las funciones init_pair() y wcolor_set() para que no le queden dudas al lector: init_pair(1,COLOR_CYAN,COLOR_BLUE); // 1 es el indice wcolor_set(ventana, 1, NULL); // Tambien vale: wbkgd(ventana,COLOR_PAIR(1)); IMPRIMIR EN PANTALLA -------------------- Las funciones de salida estandar no nos seran nada utiles cuando programamos con ncurses porque en memoria se mantienen unas imagenes que representan el contenido de cada ventana, incluso de la principal, por lo tanto las funciones que utilizaremos para enviar informacion al terminal, escribiran en estas imagenes y luego seran enviadas a la pantalla cuando se utilice la funcion refresh(). Que funciones debo utilizar entonces? wprintw() -> Funciona igual que printf() pero necesita como primer parametro un puntero WINDOW con la ventana en la que se desea escribir. Ej: wprintw(ventana, "Numero %d", 5); printw() -> Lo mismo que la anterior pero no necesita el puntero WINDOW ya que esta solo trabaja sobre "stdscr". Debo mencionar que para actualizar una ventana especifica hay que utilizar la funcion wrefresh() a la que se pasa como primer argumento la ventana deseada. Refresh() como printw() es para "stdscr". Tambien cabe mencionar que tanto refresh() como wrefresh() llaman a la funcion doupdate(), por ello no debemos de escribirla nosotros. Doy por sabido que las siguientes funciones trabajan sobre la ventana indicada como primer parametro: waddch() -> Escribe un caracter en la posicion actual del cursor. mvwaddch() -> Escribe un caracter en la posicion indicada como parametros 2 y 3. waddstr()-> Escribe una cadena en la posicion actual del cursor. mvwaddstr() -> Escribe una cadena en la posicion indicada como parametros 2 y 3. Si a cada una de estas funciones le quitamos la "w" de su nombre, tendremos las funciones complementarias que trabajan sobre "stdscr". El programador no se puede quejar de la mnemotecnica de ncurses. Otras funciones: wmove() -> Mueve el cursor a la posicion deseada. Parametro 2: fila. Parametro 3: columna. wclear() -> Borra el contenido de la ventana. ENTRADA DE DATOS ---------------- Las funciones mas usadas a la hora de obtener datos del teclado son las siguientes: getch() -> Recoge un caracter del teclado y, si el "eco" esta activo, llama a addch para imprimirlo en pantalla. wgetstr -> Recoge una cadena del teclado. wscanw -> Complementaria a wprintw(), recoge una cadena con formato. Seguro que se me quedan otras tantas funciones en el saco pero estas son las mas importantes para empezar. De cualquier modo, teneis los fuentes para ver los demas prototipos. _*#*__*#*__*#*__*#*__*#*_ _*#*_ 05. VENTANAS _*#*_ _*#*__*#*__*#*__*#*__*#*_ Las propiedades de una ventana vienen definidas en "struct _win_st" y no son muy complicadas de comprender. Ha sido definido un tipo de dato llamado WINDOW que utilizaremos para definir nuevas variables para crear ventanas. Aqui muestro parte del codigo: typedef struct _win_st WINDOW; // Definicion de tipo struct _win_st // Estructura de una ventana { NCURSES_SIZE_T _cury, _curx; /* current cursor position */ /* window location and size */ NCURSES_SIZE_T _maxy, _maxx; /* maximums of x and y, NOT window size */ NCURSES_SIZE_T _begy, _begx; /* screen coords of upper-left-hand corner */ short _flags; /* window state flags */ /* attribute tracking */ attr_t _attrs; /* current attribute for non-space character */ chtype _bkgd; /* current background char/attribute pair */ /* option values set by user */ bool _notimeout; /* no time out on function-key entry? */ bool _clear; /* consider all data in the window invalid? */ bool _leaveok; /* OK to not reset cursor on exit? */ bool _scroll; /* OK to scroll this window? */ bool _idlok; /* OK to use insert/delete line? */ bool _idcok; /* OK to use insert/delete char? */ bool _immed; /* window in immed mode? (not yet used) */ bool _sync; /* window in sync mode? */ bool _use_keypad; /* process function keys into KEY_ symbols? */ int _delay; /* 0 = nodelay, <0 = blocking, >0 = delay */ struct ldat *_line; /* the actual line data */ /* global screen state */ NCURSES_SIZE_T _regtop; /* top line of scrolling region */ NCURSES_SIZE_T _regbottom; /* bottom line of scrolling region */ /* these are used only if this is a sub-window */ int _parx; /* x coordinate of this window in parent */ int _pary; /* y coordinate of this window in parent */ WINDOW *_parent; /* pointer to parent if a sub-window */ /* these are used only if this is a pad */ struct pdat { NCURSES_SIZE_T _pad_y, _pad_x; NCURSES_SIZE_T _pad_top, _pad_left; NCURSES_SIZE_T _pad_bottom, _pad_right; } _pad; NCURSES_SIZE_T _yoffset; /* real begy is _begy + _yoffset */ #ifdef _XOPEN_SOURCE_EXTENDED cchar_t _bkgrnd; /* current background char/attribute pair */ #endif }; Los comentarios en ingles faciles de traducir no? Algunos los he dividido en dos lineas por eso del formato de 80 caracteres maximo por linea del articulo. Para crear una ventana llamaremos a la funcion newwin() con los siguientes parametros: 1- Numero de filas 2- Numero de columnas 3 y 4- Coordenadas donde se situara la esquina superior izquierda de la ventana. Esta funcion nos devolvera un puntero WINDOW que utilizaremos a lo largo de la aplicacion para controlar dicha ventana. Ej: WINDOW *miventana; miventa = newwin(7,4,0,0); En este caso la ventana se situara en la esquina superior izquierda de la pantalla. Son muchas las limitaciones que tenemos con ncurses con respecto al control de ventanas pero, para solucionarlo y alegrar el dia al programador, se ha creado la libreria "panel" con la que conseguiremos unos resultados mas que satisfactorios. Pasemos a explicar entonces su funcionamiento. _*#*__*#*__*#*__*#*__*#*_ _*#*_ 06. PANELES _*#*_ _*#*__*#*__*#*__*#*__*#*_ Por decirlo de alguna manera digamos que un panel es el manejador de una ventana, una vez asociado a la misma podemos controlarla y realizar todo tipo de operaciones con ella. Un panel es lo que le da poder a una ventana, es el que la convierte en un objeto dinamico con el que se puede interactuar. Crear un panel es cosa de niños, simplemente llamaremos a la funcion new_panel() con el parametro WINDOW de la ventana a la que queremos asociar el panel. La funcion retorna un puntero del tipo PANEL el cual usaremos apartir de ahora con muchas y muy variadas funciones. Ej: WINDOW *miventana; PANEL *mipanel; miventana = newwin(4,7,0,0); mipanel = new_panel(miventana); Las ultimas dos instrucciones se podrian haber resumido en algo mas simple como : mipanel = new_panel(newwin(4,7,0,0)); Tras realizar la llamada a new_panel(), por defecto, el nuevo panel sera visible y no habra que llamar explicitamente a la funcion show_panel(). Como ya hice anteriormente, asumire que saben que el primer parametro de las siguientes funciones siempre es el puntero PANEL de la ventana a controlar: panel_hidden() -> Nos indica si el panel esta oculto o no. show_panel() -> Hace visible un panel. hide_panel() -> Oculta un panel. move_panel() -> Mueve el panel a las cordenadas indicadas como segundo y tercer parametro. top_panel() -> Superpone el panel sobre el resto. bottom_panel() -> Pone el panel debajo del resto. panel_above() -> Devuelve un puntero tipo PANEL del panel que esta sobre el que hemos indicado como primer argumento. Si este ya era el primero devuelve NULL como valor de retorno. panel_below() -> Opuesta a la anterior, devuelve el que esta debajo. Devuelve NULL si no existe. panel_window() -> Nos devuelve el puntero WINDOW de la ventana asociada al panel. replace_panel() -> Asocia un panel a una ventana proporcionada como segundo argumento. Un panel puede reutilizarse para tantas ventanas como se desee. del_panel() -> Desace la asociacion entre el panel y la ventana eliminandolo. Hay algunas funciones mas que tambien son importantes pero no me gustaria liaros mientras os estais iniciando en esta materia. Si necesitais mas informacion, personalmente la puedo proporcionar o buscar si me escribis a mi correo. Para conseguir que los cambios efectuados con las funciones anteriores tengan un reflejo visible en pantalla, habra que llamar a las funciones update_panels() y doupdate(). En este caso update_panels() no conlleva una llamada a doupdate() por lo tanto la llamaremos desde la aplicacion como otra orden mas. _*#*__*#*__*#*__*#*__*#*_ _*#*_ 07. MENUS _*#*_ _*#*__*#*__*#*__*#*__*#*_ Ha llegado el momento de combinar lo bonito con lo comodo. Con esta libreria conseguiremos hacer listas de opciones navegables con las flechas del teclado y que tambien podran dar paso a otras sublistas (submenus). Lo mas util de todo esto es que conseguiremos que cuando una opcion sea seleccionada y presionemos INTRO u otra tecla que queramos, haga las operaciones que mas deseemos (generalmente llamar a una funcion). La primera estructura que debemos conocer se conoce por el nombre de MENU, no la copiare aqui para no alargar demasiado el articulo. Contiene toda la informacion necesaria para controlar el menu. La segunda estructura mas importante a la hora de conocer la programacion de menus, es conocida como ITEM (en realidad es un tipo de dato definido). Cada puntero de este tipo hara referencia a una opcion del menu. Todas las opciones del menu seran almacenadas en un array (matriz o arreglo) de punteros ITEM. typedef struct tagITEM { TEXT name; /* name of menu item */ TEXT description; /* description of item, optional in display */ struct tagMENU *imenu; /* Pointer to parent menu */ void *userptr; /* Pointer to user defined per item data */ Item_Options opt; /* Item options */ short index; /* Item number if connected to a menu */ short y; /* y and x location of item in menu */ short x; bool value; /* Selection value */ struct tagITEM *left; /* neighbour items */ struct tagITEM *right; struct tagITEM *up; struct tagITEM *down; } ITEM; De nuevo un contenido facil de descifrar si tenemos unos minimos conocimientos de ingles y estamos un poco avispados. Las funciones que mas nos interesan por el momento son: new_item() -> Nos devuelve un puntero ITEM a partir de un nombre y una descripcion que le entregaremos como primer y segundo parametro respectivamente. new_menu() -> Toma como unico parametro la matriz de punteros ITEM que hemos ido rellenado con cada llamada a new_item() y nos devuelve un puntero de tipo MENU con el que controlaremos el mismo. Para que la funcion se ejecute con exito, la matriz debe acabar con un puntero ITEM nulo (NULL). Como crear un menu? Primero declaramos el array de punteros ITEM que sea una unidad mayor que la cantidad de opciones que vaya a tener el menu y, tambien declaramos un puntero tipo MENU con el que se controlara el mismo: MENU *menu; ITEM *ops[6]; Luego debemos crear una matriz normal de tipo char* en la que introduciremos uno a uno los nombres de las opciones que queremos posea el menu: char *opciones[5] = { "Nuevo", "Abrir", "Guardar", "Cerrar", "Salir" }; Lo siguiente es crear los punteros ITEM que seran almacenados en la matriz que hemos creado para tal objetivo. Agregamos tambien el puntero nulo: for(i=0; i < 5; i++) ops[i] = new_item(opciones[i], ""); ops[5] = (ITEM *)NULL; // Realizamos un typecast para que el valor NULL sea tomado tambien como un ITEM mas. Por fin llega el momento de crear el menu: menu = new_menu(ops); Todo correcto pero, si nuestro programa fuera asi, el menu todavia no se visualizaria en pantalla, como siempre, necesitamos un par de llamadas mas: post_menu(menu); // Hace que el menu sea visible, su opuesto unpost_menu() refresh(); // Llamada casi obligatoria despues de todo cambio Bien, todo ha salido como esperabamos. Cabe decir que antes de que la aplicacion termine, el menu debe ser eliminado al igual que todas sus opciones para dejar limpia la memoria. Utilizariamos otras dos funciones de esta forma: free_menu(menu); for(i=0; i < 5; i++) free_item(ops[i]); El tema de la creacion y acceso a submenus sera explicado en la siguiente parte de este articulo. DESPLAZARSE POR EL MENU ----------------------- Un menu en pantalla sobre el que no podamos interactuar no seria de verdadera utilidad, no es cierto? Pues bien, para todos (creo yo) lo mas comodo es utilizar las flechas del teclado y alguna que otra tecla con funcion de navegacion. Antes de empezar debemos de conocer unas constantes que seran las encargadas de la navegacion por la lista de opciones. #define REQ_LEFT_ITEM // Moverse a la opcion de la izquierda #define REQ_RIGHT_ITEM // Moverse a la opcion de la derecha #define REQ_UP_ITEM // Moverse a la opcion de arriba #define REQ_DOWN_ITEM // Moverse a la opcion de abajo #define REQ_FIRST_ITEM // Moverse a la primera opcion #define REQ_LAST_ITEM // Moverse a la ultima opcion #define REQ_NEXT_ITEM // Moverse a la opcion siguiente #define REQ_PREV_ITEM // Moverse a la opcion anterior He omitido varias que de momento no nos seran de utilidad pero ser conscientes de su existencia. Recordad, los fuentes nunca mienten. Todas estas constantes seran utiles a la hora de ejecutar la funcion menu_driver(), que necesita como primer parametro el menu sobre el que va a operar y como segundo una de las constantes anteriores. El segundo parametro tambien puede ser un caracter a partir del cual buscara el patron de coincidencia en el menu pero no me voy a meter mas a dentro para no dificultar la tarea. Los valores de retorno de esta funcion son dos: E_OK -> Todo ha salido bien. E_REQUEST_DENIED -> La opcion requerida no se ha podido realizar. Ahora solo tenemos que hacer que nuestro programa identifique las teclas que son pulsadas y asocie a cada una de ellas una de las constantes anteriores. Las constantes para reconocer las teclas de navegacion son: #define KEY_DOWN // Flecha abajo #define KEY_UP // Flecha arriba #define KEY_LEFT // Flecha izquierda #define KEY_RIGHT // Flecha derecha #define KEY_HOME // Tecla Inicio #define KEY_END // Tecla Fin Hay una tirada de ellas mas pero soy asi de tacaño. Primero prepararemos la funcion que detecta la tecla pulsada y devuelve como valor de retorno la constante apropiada. Acordaros que para que nuestra aplicacion reconozca estas teclas especiales tendremos que llamar primero a keypad() con sus correspondientes argumentos. int identecla(){ int tecla; tecla = getch(); switch(tecla){ case KEY_UP: return REQ_PREV_ITEM; case KEY_DOWN: return REQ_NEXT_ITEM; case KEY_HOME: return REQ_FIRST_ITEM; case KEY_END: return REQ_LAST_ITEM; default: return tecla; } } Y ahora solo queda llamar a la funcion menu_driver() con los parametros necesarios, en nuestro caso: menu_driver(menu, identecla()); Ya puede navegar comodamente por su menu, pero no nos detengamos aqui y sigamos aprendiendo un poco mas. ACTUACION DE LAS OPCIONES ------------------------- Cuando una opcion es seleccionada y presionamos la tecla INTRO es de suponer que una funcion (o trabajo) que nosotros hayamos decido se ejecute. Para este objetivo tenemos a nuestra disposicion un par de funciones interesantes que usadas conjuntamente arrojan una funcionalidad importante. La primera de ellas es llamada current_item() que nos devuelve el puntero ITEM de la opcion que actualmente esta seleccionada. La segunda es item_index() que ofreciendole como unico parametro un puntero del tipo ITEM devuelve como valor de retorno el indice (entero) que ocupa esa opcion en el menu. A partir de este indice cualquiera de nosotros es capaz de asociarle una funcion adecuada. Lo que mas rapido se me viene a la mente es una clausula switch() que tome como parametro item_index(current_item()) y que dependiendo del valor devuelto se ejecute la funcion requerida. De todas formas hay muchas y muy variadas formas de hacer todo esto y seran explicadas en profundidad en la segunda parte del articulo. _*#*__*#*__*#*__*#*__*#*_ _*#*_ _*#*_ _*#*_ 08. PROGRAMA EJEMPLO _*#*_ _*#*__*#*__*#*__*#*__*#*_ _*#*_ _*#*_ Mi objetivo era que el programa ejemplo llegara a tiempo, pero debido a mis estudios en estos momentos mis escasos minutos libres como programador no estan teniendo su espacio. De cualquier manera, el programa va incluir formularios, tematica extensa de la segunda parte de este articulo. Por tal motivo el programa estara a vuestro alcance y disposicion en la segunda parte. El programa que demuestra las aplicaciones de Ncurses pretende ser como un almacen de informacion donde guardar la topologia y caracteristicas tanto de una red como de los ordenadores que la forman. Por el momento queria que el programa solo manejara redes de tipo Ethernet, pero nadie quita (y sera lo mas seguro) que trate con redes wireless y de cualquier otro tipo, ya que ese es el objetivo del programa. De todas formas, os animo a ir escribiendo aplicaciones pequeñitas con Ncurses para coger algo de practica y que saqueis buenos resultados, con la parte final del articulo tendreis bastante mas experiencia y las ideas empezaran a brotar de vuestras propias mentes. Pido disculpas por mis escusas. _*#*__*#*__*#*__*#*__*#*_ _*#*_ 09. DESPEDIDA _*#*_ _*#*__*#*__*#*__*#*__*#*_ Otro hasta luego mas para la coleccion, pero rocordad que nos vemos en la segunda parte de este articulo que espero salgo a la luz lo antes posible. Como siempre, mi idea era la de que los que leeis esto llevarais lo aqui escrito a la practica pero tambien sois vosotros los que debeis elegir con que herramientas luchar. Si, la vida es un combate por la supervivencia, el que tenga mas informacion tendra el poder pero, solo el que sepa utilizarla alcanzara la victoria. Los de ahi arriba tienen el poder pero sin nosotros nunca alcanzaran la victoria, somos la revolucion... Hasta la proxima chic@s. by blackngel *EOF*