about summary refs log blame commit diff stats
path: root/src/lcurseslib.c
blob: 441a76c85b628e45027a22eea56c36aaaa814834 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
                    
                   
                   
                   

                
                    



                    
                            
                    





                     
 
                    
                                          






                                      
                                                         
                     

                           
                            

 
                               
                           

                                


                               



                                     


                                                                
 
                
                            


 

                                    
               



           
                                                                        





                                                                    





                                                                             







                                                                                


                                     



           






















                                        
                                                                 



                                  

                            
                           
                            
                                               




                        
                                             

                                
                    
                            
                        
                      






                                                      
                                          









                                                  
                                                      



                                                                     










                                                                    









                                                      






                                                             











                                                 








                                           















                                         





                                             













                                     
                                           





                        




















                                           








                                                  

                                               
                              
                      

                        
                    

                          

                          
                        



              
                                                                                































































                                                      



























































































                                                          



         
                                              
                                        
 



                                     
 
                                                
 
                                        
 
                                    
                           


                                     


                               
 
                         


           
#include <ncurses.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "lua.h"
#include "llimits.h"

#include "lauxlib.h"
#include "lualib.h"

void cleanup_curses (void) {
  if (!isendwin()) {
    wclear(stdscr);
    wrefresh(stdscr);
    endwin();
  }
}


int menu_column = 0;
void draw_string_on_menu (const char* s) {
  mvaddstr(LINES-1, menu_column, " ");
  ++menu_column;
  mvaddstr(LINES-1, menu_column, s);
  menu_column += strlen(s);
  mvaddstr(LINES-1, menu_column, " ");
  ++menu_column;
}
void draw_menu_item (const char* key, const char* name) {
  attroff(A_REVERSE);
  draw_string_on_menu(key);
  attron(A_REVERSE);
  draw_string_on_menu(name);
}

void draw_menu (lua_State *L) {
  attron(A_BOLD|A_REVERSE);
  for (int x = 0; x < COLS; ++x)
    mvaddch(LINES-1, x, ' ');
  menu_column = 2;
  draw_menu_item("^x", "exit");
  draw_menu_item("^e", "edit");

  /* render any app-specific items */
  lua_getglobal(L, "menu");
  int table = lua_gettop(L);
  if (lua_istable(L, -1))
    for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1))
      draw_menu_item(lua_tostring(L, -2), lua_tostring(L, -1));

  lua_pop(L, 1);
  attroff(A_BOLD|A_REVERSE);
}


static int Prefresh (lua_State *L) {
  refresh();
  draw_menu(L);
  return 1;
}


static int argtypeerror (lua_State *L, int narg, const char *expected) {
  const char *got = luaL_typename(L, narg);
  return luaL_argerror(L, narg,
          lua_pushfstring(L, "%s expected, got %s", expected, got));
}


static void checktype (lua_State *L, int narg, int t, const char *expected) {
  if (lua_type(L, narg) != t)
    argtypeerror(L, narg, expected);
}


static lua_Integer checkinteger (lua_State *L, int narg, const char *expected) {
  lua_Integer d = lua_tointeger(L, narg);
  if (d == 0 && !lua_isnumber(L, narg))
    argtypeerror(L, narg, expected);
  return d;
}


static int Pstdscr (lua_State *L) {
  lua_pushstring(L, "curses:stdscr");
  lua_rawget(L, LUA_REGISTRYINDEX);
  return 1;
}


static int Pcolor_pairs (lua_State *L) {
  lua_pushinteger(L, COLOR_PAIRS);
  return 1;
}


static int Pinit_pair (lua_State *L) {
  int pair = checkinteger(L, 1, "int");
  short f = checkinteger(L, 2, "int");
  short b = checkinteger(L, 3, "int");
  init_pair(pair, f, b);
  return 1;
}


static int Pcolor_pair (lua_State *L)
{
  int n = checkinteger(L, 1, "int");
  lua_pushinteger(L, COLOR_PAIR(n));
  return 1;
}


extern void switch_to_editor (lua_State *L, const char *message);
static int Pgetch (lua_State *L) {
  int c = wgetch(stdscr);
  if (c == ERR)
    return 0;
  if (c == 24)  /* ctrl-x */
    exit(0);
  if (c == 5)  /* ctrl-e */
    switch_to_editor(L, "");
  /* handle other standard menu hotkeys here */
  lua_pushinteger(L, c);
  return 1;
}


static const struct luaL_Reg curseslib [] = {
  {"color_pairs", Pcolor_pairs},
  {"color_pair", Pcolor_pair},
  {"getch", Pgetch},
  {"init_pair", Pinit_pair},
  {"refresh", Prefresh},
  {"stdscr", Pstdscr},
  {NULL, NULL}
};


static void curses_newwin (lua_State *L, WINDOW *nw) {
  if (nw) {
    WINDOW **w = lua_newuserdata(L, sizeof(WINDOW*));
    luaL_getmetatable(L, "curses:window");
    lua_setmetatable(L, -2);
    *w = nw;
  }
  else {
    lua_pushliteral(L, "failed to create window");
    lua_error(L);
  }
}


static WINDOW **lc_getwin (lua_State *L, int offset) {
  WINDOW **w = (WINDOW**)luaL_checkudata(L, offset, "curses:window");
  if (w == NULL)
    luaL_argerror(L, offset, "bad curses window");
  return w;
}


static WINDOW *checkwin (lua_State *L, int offset) {
  WINDOW **w = lc_getwin(L, offset);
  if (*w == NULL)
    luaL_argerror(L, offset, "attempt to use closed curses window");
  return *w;
}


static chtype checkch (lua_State *L, int narg) {
  if (lua_isnumber(L, narg))
    return cast(chtype, checkinteger(L, narg, "int"));
  if (lua_isstring(L, narg))
    return *lua_tostring(L, narg);

  return argtypeerror(L, narg, "int or char");
}


static int optint (lua_State *L, int narg, lua_Integer def) {
  if (lua_isnoneornil(L, narg))
    return cast(int, def);
  return cast(int, checkinteger(L, narg, "int or nil"));
}


static int W__tostring (lua_State *L) {
  WINDOW **w = lc_getwin(L, 1);
  char buff[34];
  if (*w == NULL)
    strcpy(buff, "closed");
  else
    sprintf(buff, "%p", lua_touserdata(L, 1));
  lua_pushfstring(L, "curses window (%s)", buff);
  return 1;
}


static int Waddstr (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  const char *str = luaL_checkstring(L, 2);
  int n = optint(L, 3, -1);
  lua_pushboolean(L, waddnstr(w, str, n));
  return 1;
}


static int Wattroff (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  int attrs = checkinteger(L, 2, "int");
  lua_pushboolean(L, wattroff(w, attrs));
  return 1;
}


static int Wattron (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  int attrs = checkinteger(L, 2, "int");
  lua_pushboolean(L, wattron(w, attrs));
  return 1;
}


static int Wclear (lua_State *L) {
  lua_pushboolean(L, wclear(checkwin(L, 1)));
  return 1;
}


static int Wgetyx (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  int y, x;
  getyx(w, y, x);
  lua_pushinteger(L, y);
  lua_pushinteger(L, x);
  return 2;
}


static int Wgetmaxyx (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  int y, x;
  getmaxyx(w, y, x);
  --y;  // set aside space for the menu bar
  lua_pushinteger(L, y);
  lua_pushinteger(L, x);
  return 2;
}


static int Wmvaddch (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  int y = checkinteger(L, 2, "int");
  int x = checkinteger(L, 3, "int");
  chtype ch = checkch(L, 4);
  mvwaddch(w, y, x, ch);
  return 1;
}


static int Wmvaddstr (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  int y = checkinteger(L, 2, "int");
  int x = checkinteger(L, 3, "int");
  const char *str = luaL_checkstring(L, 4);
  int n = optint(L, 5, -1);
  mvwaddnstr(w, y, x, str, n);
  return 1;
}


static int Wnodelay (lua_State *L) {
  WINDOW *w = checkwin(L, 1);
  checktype(L, 2, LUA_TBOOLEAN, "boolean or nil");
  int bf = (int)lua_toboolean(L, 2);
  lua_pushboolean(L, nodelay(w, bf));
  return 1;
}


static const luaL_Reg curses_window_methods[] =
{
  {"__tostring", W__tostring},
  {"addstr", Waddstr},
  {"attroff", Wattroff},
  {"attron", Wattron},
  {"clear", Wclear},
  {"getmaxyx", Wgetmaxyx},
  {"getyx", Wgetyx},
  {"mvaddch", Wmvaddch},
  {"mvaddstr", Wmvaddstr},
  {"nodelay", Wnodelay},
  {NULL, NULL}
};


static void register_curses_constant (lua_State *L, const char* name, int val) {
  lua_pushstring(L, name);
  lua_pushinteger(L, val);
  lua_settable(L, -3);
}


static void register_curses_constants (lua_State *L) {
#define CC(s) register_curses_constant(L, #s, s)
  /* colors */
  CC(COLOR_BLACK);
  CC(COLOR_RED);
  CC(COLOR_GREEN);
  CC(COLOR_YELLOW);
  CC(COLOR_BLUE);
  CC(COLOR_MAGENTA);
  CC(COLOR_CYAN);
  CC(COLOR_WHITE);

  /* alternate character set */
  CC(ACS_BLOCK);
  CC(ACS_BOARD);

  CC(ACS_BTEE);
  CC(ACS_TTEE);
  CC(ACS_LTEE);
  CC(ACS_RTEE);
  CC(ACS_LLCORNER);
  CC(ACS_LRCORNER);
  CC(ACS_URCORNER);
  CC(ACS_ULCORNER);

  CC(ACS_LARROW);
  CC(ACS_RARROW);
  CC(ACS_UARROW);
  CC(ACS_DARROW);

  CC(ACS_HLINE);
  CC(ACS_VLINE);

  CC(ACS_BULLET);
  CC(ACS_CKBOARD);
  CC(ACS_LANTERN);
  CC(ACS_DEGREE);
  CC(ACS_DIAMOND);

  CC(ACS_PLMINUS);
  CC(ACS_PLUS);
  CC(ACS_S1);
  CC(ACS_S9);

  /* attributes */
  CC(A_NORMAL);
  CC(A_STANDOUT);
  CC(A_UNDERLINE);
  CC(A_REVERSE);
  CC(A_BLINK);
  CC(A_DIM);
  CC(A_BOLD);
  CC(A_PROTECT);
  CC(A_INVIS);
  CC(A_ALTCHARSET);
  CC(A_CHARTEXT);
  CC(A_ATTRIBUTES);
  CC(A_COLOR);

  /* key functions */
  CC(KEY_BREAK);
  CC(KEY_DOWN);
  CC(KEY_UP);
  CC(KEY_LEFT);
  CC(KEY_RIGHT);
  CC(KEY_HOME);
  CC(KEY_BACKSPACE);

  CC(KEY_DL);
  CC(KEY_IL);
  CC(KEY_DC);
  CC(KEY_IC);
  CC(KEY_EIC);
  CC(KEY_CLEAR);
  CC(KEY_EOS);
  CC(KEY_EOL);
  CC(KEY_SF);
  CC(KEY_SR);
  CC(KEY_NPAGE);
  CC(KEY_PPAGE);
  CC(KEY_STAB);
  CC(KEY_CTAB);
  CC(KEY_CATAB);
  CC(KEY_ENTER);
  CC(KEY_SRESET);
  CC(KEY_RESET);
  CC(KEY_PRINT);
  CC(KEY_LL);
  CC(KEY_A1);
  CC(KEY_A3);
  CC(KEY_B2);
  CC(KEY_C1);
  CC(KEY_C3);
  CC(KEY_BTAB);
  CC(KEY_BEG);
  CC(KEY_CANCEL);
  CC(KEY_CLOSE);
  CC(KEY_COMMAND);
  CC(KEY_COPY);
  CC(KEY_CREATE);
  CC(KEY_END);
  CC(KEY_EXIT);
  CC(KEY_FIND);
  CC(KEY_HELP);
  CC(KEY_MARK);
  CC(KEY_MESSAGE); /* ncurses extension: CC(KEY_MOUSE); */
  CC(KEY_MOVE);
  CC(KEY_NEXT);
  CC(KEY_OPEN);
  CC(KEY_OPTIONS);
  CC(KEY_PREVIOUS);
  CC(KEY_REDO);
  CC(KEY_REFERENCE);
  CC(KEY_REFRESH);
  CC(KEY_REPLACE);
  CC(KEY_RESIZE);
  CC(KEY_RESTART);
  CC(KEY_RESUME);
  CC(KEY_SAVE);
  CC(KEY_SBEG);
  CC(KEY_SCANCEL);
  CC(KEY_SCOMMAND);
  CC(KEY_SCOPY);
  CC(KEY_SCREATE);
  CC(KEY_SDC);
  CC(KEY_SDL);
  CC(KEY_SELECT);
  CC(KEY_SEND);
  CC(KEY_SEOL);
  CC(KEY_SEXIT);
  CC(KEY_SFIND);
  CC(KEY_SHELP);
  CC(KEY_SHOME);
  CC(KEY_SIC);
  CC(KEY_SLEFT);
  CC(KEY_SMESSAGE);
  CC(KEY_SMOVE);
  CC(KEY_SNEXT);
  CC(KEY_SOPTIONS);
  CC(KEY_SPREVIOUS);
  CC(KEY_SPRINT);
  CC(KEY_SREDO);
  CC(KEY_SREPLACE);
  CC(KEY_SRIGHT);
  CC(KEY_SRSUME);
  CC(KEY_SSAVE);
  CC(KEY_SSUSPEND);
  CC(KEY_SUNDO);
  CC(KEY_SUSPEND);
  CC(KEY_UNDO);
#undef CC
}


LUALIB_API int luaopen_curses (lua_State *L) {
  luaL_newmetatable(L, "curses:window");

  /* metatable.__index = metatable */
  lua_pushstring(L, "__index");
  lua_pushvalue(L, -2);
  lua_settable(L, -3);

  luaL_register(L, NULL, curses_window_methods);

  luaL_register(L, "curses", curseslib);

  /* save main window on registry */
  curses_newwin(L, stdscr);
  lua_pushstring(L, "curses:stdscr");
  lua_pushvalue(L, -2);
  lua_rawset(L, LUA_REGISTRYINDEX);

  lua_pushvalue(L, -2);
  register_curses_constants(L);

  atexit(cleanup_curses);
  return 1;
}