packages feed

hscharm-0.0.1: charm/lib/charm.c

// Copyright (C) YelloSoft

#include "charm.h"
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ANSI Escape Codes
// http://en.wikipedia.org/wiki/ANSI_escape_code

// Terminal information
// http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man4/tty.4.html

// Disabling echo
// http://www.gnu.org/s/hello/manual/libc/getpass.html

int pos_x;
int pos_y;

int get_width(void) {
  struct winsize ws;
  (void) ioctl(0, TIOCGWINSZ, &ws);

  return (int) ws.ws_col;
}

int get_height(void) {
  struct winsize ws;
  (void) ioctl(0, TIOCGWINSZ, &ws);

  return (int) ws.ws_row;
}

void cursor_off(void) {
  printf("\033[?25l");
  (void) fflush(stdout);
}

void cursor_on(void) {
  printf("\033[?25h");
  (void) fflush(stdout);
}

void echo_off(void) {
  struct termios term;
  (void) tcgetattr(fileno(stdout), &term);
  term.c_lflag &= (unsigned long) ~ECHO;
  (void) tcsetattr(fileno(stdout), TCSAFLUSH, &term);
}

void echo_on(void) {
  struct termios term;
  (void) tcgetattr(fileno(stdout), &term);
  term.c_lflag |= ECHO;
  (void) tcsetattr(fileno(stdout), TCSAFLUSH, &term);
}

void raw_on(void) {
  struct termios term;
  (void) tcgetattr(fileno(stdout), &term);
  term.c_lflag &= (unsigned long) ~ICANON;
  (void) tcsetattr(fileno(stdout), TCSAFLUSH, &term);
}

void raw_off(void) {
  struct termios term;
  (void) tcgetattr(fileno(stdout), &term);
  term.c_lflag |= ICANON;
  (void) tcsetattr(fileno(stdout), TCSAFLUSH, &term);
}

void blocking_off(void) {
  struct termios term;
  (void) tcgetattr(fileno(stdout), &term);
  term.c_cc[VMIN] = (cc_t) 0;
  (void) tcsetattr(fileno(stdout), TCSAFLUSH, &term);
}

void blocking_on(void) {
  struct termios term;
  (void) tcgetattr(fileno(stdout), &term);
  term.c_cc[VMIN] = (cc_t) 1;
  (void) tcsetattr(fileno(stdout), TCSAFLUSH, &term);
}

int get_x(void) {
  return pos_x;
}

int get_y(void) {
  return pos_y;
}

// (0, 0) = top left
// (width - 1, 0) = top right
// (0, height - 1) = bottom left
// (width - 1, height - 1) = bottom right
void move_cursor(int x, int y) {
  pos_x = x;
  pos_y = y;
  printf("\033[%d;%dH", y + 1, x + 1);
  (void) fflush(stdout);
}

void blot_char(char c) {
  if (c == '\n') {
    pos_y++;
    move_cursor(0, pos_y);
  } else {
    (void) putchar(c);
    (void) fflush(stdout);
    pos_x++;
  }
}

void blot_string(const char *s) {
  int i;

  for (i = 0; i < (int) strlen(s); i++) {
    blot_char(s[i]);
  }
}

void hcenter_string(const char *s) {
  move_cursor((get_width() - (int) strlen(s)) / 2, pos_y);
  blot_string(s);
}

void vcenter_string(const char *s) {
  move_cursor((get_width() - (int) strlen(s)) / 2 - 1, get_height() / 2 - 1);
  blot_string(s);
}

void clear_screen(void) {
  printf("\033[2J");
  (void) fflush(stdout);
}

void __attribute__((noreturn)) handle_signal(int __attribute__((
      unused)) /*@unused@*/ signal) {
  end_charm();
  exit(0);
}

void start_charm(void) {
  (void) signal(SIGINT, handle_signal);

  cursor_off();
  echo_off();
  raw_on();
  blocking_off();
  move_cursor(0, 0);
  clear_screen();
}

void end_charm(void) {
  move_cursor(0, 0);
  clear_screen();
  blocking_on();
  raw_off();
  echo_on();
  cursor_on();
}

key parse_key(char *buf) {
  switch (buf[0]) {
  case '\x7f':
  case '\b':
    return KEY_BACKSPACE;

  case '\t':
    return KEY_TAB;

  case '\n':
    return KEY_NEWLINE;

  case ' ':
    return KEY_SPACE;

  case '!':
    return KEY_EXCLAMATION;

  case '\"':
    return KEY_DOUBLE_QUOTE;

  case '#':
    return KEY_HASH;

  case '$':
    return KEY_DOLLAR;

  case '%':
    return KEY_PERCENT;

  case '&':
    return KEY_AMPERSAND;

  case '\'':
    return KEY_SINGLE_QUOTE;

  case '(':
    return KEY_LEFT_PAREN;

  case ')':
    return KEY_RIGHT_PAREN;

  case '*':
    return KEY_ASTERISK;

  case '+':
    return KEY_PLUS;

  case ',':
    return KEY_COMMA;

  case '-':
    return KEY_MINUS;

  case '.':
    return KEY_PERIOD;

  case '/':
    return KEY_SLASH;

  case '0':
    return KEY_0;

  case '1':
    return KEY_1;

  case '2':
    return KEY_2;

  case '3':
    return KEY_3;

  case '4':
    return KEY_4;

  case '5':
    return KEY_5;

  case '6':
    return KEY_6;

  case '7':
    return KEY_7;

  case '8':
    return KEY_8;

  case '9':
    return KEY_9;

  case ':':
    return KEY_COLON;

  case ';':
    return KEY_SEMICOLON;

  case '<':
    return KEY_LESS_THAN;

  case '=':
    return KEY_EQUALS;

  case '>':
    return KEY_GREATER_THAN;

  case '?':
    return KEY_QUESTION;

  case '@':
    return KEY_AT;

  case 'A':
    return KEY_CAPITAL_A;

  case 'B':
    return KEY_CAPITAL_B;

  case 'C':
    return KEY_CAPITAL_C;

  case 'D':
    return KEY_CAPITAL_D;

  case 'E':
    return KEY_CAPITAL_E;

  case 'F':
    return KEY_CAPITAL_F;

  case 'G':
    return KEY_CAPITAL_G;

  case 'H':
    return KEY_CAPITAL_H;

  case 'I':
    return KEY_CAPITAL_I;

  case 'J':
    return KEY_CAPITAL_J;

  case 'K':
    return KEY_CAPITAL_K;

  case 'L':
    return KEY_CAPITAL_L;

  case 'M':
    return KEY_CAPITAL_M;

  case 'N':
    return KEY_CAPITAL_N;

  case 'O':
    return KEY_CAPITAL_O;

  case 'P':
    return KEY_CAPITAL_P;

  case 'Q':
    return KEY_CAPITAL_Q;

  case 'R':
    return KEY_CAPITAL_R;

  case 'S':
    return KEY_CAPITAL_S;

  case 'T':
    return KEY_CAPITAL_T;

  case 'U':
    return KEY_CAPITAL_U;

  case 'V':
    return KEY_CAPITAL_V;

  case 'W':
    return KEY_CAPITAL_W;

  case 'X':
    return KEY_CAPITAL_X;

  case 'Y':
    return KEY_CAPITAL_Y;

  case 'Z':
    return KEY_CAPITAL_Z;

  case '[':
    return KEY_LEFT_BRACKET;

  case '\\':
    return KEY_BACKSLASH;

  case ']':
    return KEY_RIGHT_BRACKET;

  case '^':
    return KEY_CARET;

  case '_':
    return KEY_UNDERSCORE;

  case '`':
    return KEY_BACKTICK;

  case 'a':
    return KEY_A;

  case 'b':
    return KEY_B;

  case 'c':
    return KEY_C;

  case 'd':
    return KEY_D;

  case 'e':
    return KEY_E;

  case 'f':
    return KEY_F;

  case 'g':
    return KEY_G;

  case 'h':
    return KEY_H;

  case 'i':
    return KEY_I;

  case 'j':
    return KEY_J;

  case 'k':
    return KEY_K;

  case 'l':
    return KEY_L;

  case 'm':
    return KEY_M;

  case 'n':
    return KEY_N;

  case 'o':
    return KEY_O;

  case 'p':
    return KEY_P;

  case 'q':
    return KEY_Q;

  case 'r':
    return KEY_R;

  case 's':
    return KEY_S;

  case 't':
    return KEY_T;

  case 'u':
    return KEY_U;

  case 'v':
    return KEY_V;

  case 'w':
    return KEY_W;

  case 'x':
    return KEY_X;

  case 'y':
    return KEY_Y;

  case 'z':
    return KEY_Z;

  case '{':
    return KEY_LEFT_BRACE;

  case '|':
    return KEY_PIPE;

  case '}':
    return KEY_RIGHT_BRACE;

  case '~':
    return KEY_TILDE;

  case '\x1b':
    switch (buf[1]) {
    case '\0':
      return KEY_ESCAPE;

    case '[':
      switch (buf[2]) {
      case 'A':
        return KEY_UP;

      case 'B':
        return KEY_DOWN;

      case 'C':
        return KEY_RIGHT;

      case 'D':
        return KEY_LEFT;
      }
    }

  /*@fallthrough@*/
  default:
    return KEY_UNKNOWN;
  }
}

key get_key(void) {
  int i;

  char *buf = (char *) malloc(3 * sizeof(char));

  if (buf == NULL) {
    return KEY_UNKNOWN;
  }

  buf[0] = '\0';
  buf[1] = '\0';
  buf[2] = '\0';

  i = 0;

  char c = '\0';

  ssize_t n = 0;

  // Read at least one character.
  while (n < 1) {
    n = read(fileno(stdin), &c, 1);
  }

  while (i < 3 && n > 0) {
    buf[i++] = c;
    n = read(fileno(stdin), &c, 1);
  }

  key k = parse_key(buf);

  free(buf);

  return k;
}