// Copyright (C) 2013-2015 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/curses/curses_terminal.hh" #include "nv/core/time.hh" #include "nv/lib/curses.hh" using namespace nv; curses_terminal::curses_terminal( dimension size ) : terminal( size ) { load_curses_library(); m_screen = initscr(); raw(); cbreak(); noecho(); nodelay ( static_cast( m_screen ), true ); intrflush( static_cast( m_screen ), false ); keypad ( static_cast( m_screen ), true ); start_color(); resize_term( size.y, size.x ); PDC_save_key_modifiers( true ); for ( short i = 0; i < 64; ++i ) init_pair( i+1, i % 8, i / 8 ); clear(); refresh(); curs_set(1); m_update_needed = false; } void curses_terminal::update() { if (m_update_needed) { refresh(); m_update_needed = false; } } void curses_terminal::print( position p, term_color fgcolor, term_color bgcolor, char ch ) { uint32 fcolor = fgcolor.get_color(); uint32 bcolor = ( bgcolor.get_color() % 8 ) * 8; m_update_needed = true; chtype attr = static_cast( ( fcolor > 7 ? fcolor - 7 + bcolor : fcolor + bcolor + 1 ) << 24 ); if ( fcolor > 7 ) attr = attr | 0x00800000ul; attrset(attr); mvaddch( p.y-1, p.x-1, chtype(ch) ); ::move( m_cursor.y-1, m_cursor.x-1 ); } void curses_terminal::clear( rectangle r, term_color fgcolor, term_color bgcolor ) { uint32 fcolor = fgcolor.get_color(); uint32 bcolor = ( bgcolor.get_color() % 8 ) * 8; chtype attr = static_cast( ( fcolor > 7 ? fcolor - 7 + bcolor : fcolor + bcolor + 1 ) << 24 ); if ( fcolor > 7 ) attr = attr | 0x00800000ul; attrset( attr ); for ( int x = r.ul.x-1; x <= r.lr.x-1; ++x ) for ( int y = r.ul.y-1; y <= r.lr.y-1; ++y ) mvaddch( y, x, ' ' ); } void curses_terminal::clear() { m_update_needed = true; ::clear(); ::move( m_cursor.y-1, m_cursor.x-1 ); } bool curses_terminal::poll( io_event& kevent ) { // Sleep so we don't get 100% processor usage sleep(10); // Get value from curses int result = wgetch( static_cast( m_screen ) ); // If value is err, return none event if ( result == -1 ) { return false; } // Zero the fields kevent.type = EV_KEY; kevent.key.pressed = true; kevent.key.ascii = 0; kevent.key.code = nv::KEY_NONE; kevent.key.control = false; kevent.key.shift = false; // if result is a typable char place it into the structure if ( result >= 32 && result < 128 ) { kevent.key.ascii = static_cast(result); } // Check the control and shift states // TODO: obviously there is an ERROR here :P kevent.key.control = (PDC_get_key_modifiers() & 2) != 0; kevent.key.shift = (PDC_get_key_modifiers() & 2) != 0; // if result is a typable char from the directly transformable ranges if ( (result >= nv::KEY_A && result <= nv::KEY_Z) || (result >= nv::KEY_0 && result <= nv::KEY_9) || result == ' ' ) { kevent.key.code = static_cast(result); } // if result is a typable char from the a..z range if ( result >= nv::KEY_A + 32 && result <= nv::KEY_Z + 32 ) { kevent.key.code = static_cast(result - 32); } // if result is a typable char from the 0..9 range if ( result >= nv::KEY_0 && result <= nv::KEY_9 ) { kevent.key.code = static_cast(result); } // other recognized codes switch ( result ) { case 8 : kevent.key.code = nv::KEY_BACK; break; case 9 : kevent.key.code = nv::KEY_TAB; break; case 13 : kevent.key.code = nv::KEY_ENTER; break; case 27 : kevent.key.code = nv::KEY_ESCAPE; break; case 0x103 : kevent.key.code = nv::KEY_UP; break; case 0x102 : kevent.key.code = nv::KEY_DOWN; break; case 0x104 : kevent.key.code = nv::KEY_LEFT; break; case 0x105 : kevent.key.code = nv::KEY_RIGHT; break; case 0x153 : kevent.key.code = nv::KEY_PGUP; break; case 0x152 : kevent.key.code = nv::KEY_PGDOWN; break; case 0x106 : kevent.key.code = nv::KEY_HOME; break; case 0x166 : kevent.key.code = nv::KEY_END; break; } // If key was understood by nv, then it's valid, otherwise ignored return kevent.key.ascii != 0 || kevent.key.code != 0; } void curses_terminal::set_cursor( position p ) { terminal::set_cursor( p ); ::move( m_cursor.y-1, m_cursor.x-1 ); } void curses_terminal::show_cursor() { curs_set(1); } void curses_terminal::hide_cursor() { curs_set(0); } curses_terminal::~curses_terminal() { endwin(); }