#include #include #include #include #include #include #include #include #include "lcd.h" #include "../../shared/str.h" #include "glk.h" #include "glkproto.h" static GLKDisplay * PortFD ; /* Initialize pseudo-CGRAM to empty */ static unsigned char CGRAM[8] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; ////////////////////////////////////////////////////////////////////////// //////////////////// Matrix Orbital Graphical Driver ///////////////////// ////////////////////////////////////////////////////////////////////////// lcd_logical_driver *glk; static unsigned char * screen_contents = NULL ; int fontselected = 0 ; int gpo_count = 0 ; // TODO: Get lcd.framebuf to properly work as whatever driver is running... //////////////////////////////////////////////////////////// // init() should set up any device-specific stuff, and // point all the function pointers. int glk_init(struct lcd_logical_driver *driver, char *args) { char * argv[64]; int argc; char * device = "/dev/lcd" ; int contrast = 140; speed_t speed = B19200 ; int i ; int width ; int height ; // printf("glk_init()\n"); glk = driver; argc = get_args( argv, args, 64 ); for( i = 0 ; i < argc; ++i ) { if( 0 == strcmp( argv[i], "-d") || 0 == strcmp( argv[i], "--device" ) ) { if( i+1 >= argc ) { fprintf( stderr, "glk: %s requires an argument\n", argv[i]); return( -1 ); }; device = argv[i+1] ; ++i ; } else if( 0 == strcmp( argv[i], "-c" ) || 0 == strcmp( argv[i], "--contrast" ) ) { int tmp ; if( i+1 >= argc ) { fprintf( stderr, "glk: %s requires an argument\n", argv[i]); return( -1 ); }; tmp = atoi( argv[i+1] ); if( tmp < 0 || tmp > 255 ) { fprintf( stderr, "glk: %s argument must be between 0 and 255\n", argv[i]); } else { contrast = tmp ; }; ++i ; } else if( 0 == strcmp( argv[i], "-s" ) || 0 == strcmp( argv[i], "--speed" ) ) { int tmp ; if( i+1 >= argc ) { fprintf( stderr, "glk: %s requires an argument\n", argv[i]); return( -1 ); }; tmp = atoi( argv[i+1] ); if( tmp == 9600 ) { speed = B9600 ; } else if( tmp == 19200 ) { speed = B19200 ; } else if( tmp == 38400 ) { speed = B38400 ; } else { fprintf( stderr, "glk: %s argument (%s) not a valid speed.\n", argv[i], argv[i+1] ); }; ++i ; } else if( 0 == strcmp( argv[i], "-h" ) || 0 == strcmp( argv[i], "--help" ) ) { printf( "LCDproc Matrix-Orbital GLK Graphical LCD driver\n" "\n" "-d, --device select the serial device to use [/dev/lcd]\n" "-c, --contrast set the initial contrast value [140]\n" "-s, --speed set the serial port speed [19200]\n" "-h, --help display this help text\n" ); return( -1 ); } else { fprintf( stderr, "glk: Invalid parameter: %s\n", argv[i] ); }; }; PortFD = glkopen( device, speed ); if( PortFD == NULL ) { return -1 ; }; // Query the module for a device type glkputl( PortFD, GLKCommand, 0x37, EOF ); i = glkget( PortFD ); if( i < 0 ) { fprintf( stderr, "glk: GLK did not respond to READ MODULE TYPE.\n" ); return -1 ; } else { switch( i ) { case 0x10 : // GLC12232 width = 20 ; height = 4 ; break ; case 0x11 : // GLC12864 width = 20 ; height = 8 ; break ; case 0x12 : // GLC128128 width = 20 ; height = 16 ; break ; case 0x13 : // GLC24064 width = 40 ; height = 8 ; gpo_count = 1 ; break ; case 0x14 : // GLK12864-25 width = 20 ; height = 8 ; break ; case 0x15 : // GLK24064-25 width = 40 ; height = 8 ; gpo_count = 1 ; break ; case 0x21 : // GLK128128-25 width = 20 ; height = 16 ; break ; case 0x22 : // GLK12232-25 width = 20 ; height = 4 ; gpo_count = 2 ; break ; case 0x23 : // GLK12232-25SM width = 20 ; height = 4 ; gpo_count = 2 ; break ; default : fprintf( stderr, "glk: Unrecognized module type: 0x%02x\n", i ); return( -1 ); }; }; driver->wid = width ; driver->hgt = height ; // You must use driver->framebuf here, but may use lcd.framebuf later. if(!driver->framebuf) { driver->framebuf = malloc(driver->wid * driver->hgt); }; screen_contents = malloc( driver->wid * driver->hgt ); if(driver->framebuf == NULL || screen_contents == NULL ) { fprintf( stderr, "glk: Unable to allocate memory for screen buffers\n" ); glk_close(); return -1; } memset(driver->framebuf, ' ', driver->wid*driver->hgt); // glk_clear(); // glkputl( PortFD, GLKCommand, 0x58, EOF ); // No font selected fontselected = -1 ; // Enable flow control glkflow( PortFD, 40, 2 ); // Set read character timeout to 0 glktimeout( PortFD, 0 ); // Enable auto-transmit of up/down key events // This allows us to generate REPEAT keys distinct from // normal keys using timeouts. (see glk_getkey) glkputl( PortFD, GLKCommand, 0x7e, 1, GLKCommand, 0x41, EOF ); // Set contrast glk_contrast( contrast ); driver->cellwid = 5; driver->cellhgt = 8; driver->clear = glk_clear; driver->string = glk_string; driver->chr = glk_chr; driver->vbar = glk_vbar; driver->init_vbar = glk_init_vbar; driver->hbar = glk_hbar; driver->init_hbar = glk_init_hbar; driver->num = glk_num ; driver->init_num = glk_init_num ; driver->init = glk_init; driver->close = glk_close; driver->flush = glk_flush; driver->flush_box = glk_flush_box; driver->contrast = glk_contrast; driver->backlight = glk_backlight; driver->output = glk_output; driver->set_char = glk_set_char; driver->icon = glk_icon; driver->draw_frame = glk_draw_frame; driver->getkey = glk_getkey; return 200; // 200 is arbitrary. (must be 1 or more) } // Below here, you may use either lcd.framebuf or driver->framebuf.. // lcd.framebuf will be set to the appropriate buffer before calling // your driver. void glk_close() { if(lcd.framebuf != NULL) free(lcd.framebuf); lcd.framebuf = NULL; glkclose( PortFD ) ; } ///////////////////////////////////////////////////////////////// // Clears the LCD screen // #define CLEARCOUNT (1000000) static int clearcount = 0 ; void glk_clear_forced() { // puts( "REALLY CLEARING the display" ); clearcount = CLEARCOUNT ; glkputl( PortFD, GLKCommand, 0x58, EOF ); memset(screen_contents, ' ', lcd.wid*lcd.hgt); } void glk_clear() { // puts( "glk_clear( )" ); memset(lcd.framebuf, ' ', lcd.wid*lcd.hgt); if( --clearcount < 0 ) { glk_clear_forced( ); }; } ////////////////////////////////////////////////////////////////// // Flushes all output to the lcd... // void glk_flush() { // puts( "glk_flush( )" ); lcd.draw_frame(lcd.framebuf); } ///////////////////////////////////////////////////////////////// // Prints a string on the lcd display, at position (x,y). The // upper-left is (1,1), and the lower right should be (20,4). // void glk_string(int x, int y, char string[]) { char * p ; // printf( "glk_string( %d, %d, \"%s\" )\n", x, y, string ); if( x > lcd.wid || y > lcd.hgt ) { return ; }; for( p = string ; *p && x <= lcd.wid ; ++x, ++p ) { glk_chr( x, y, *p ); }; } ///////////////////////////////////////////////////////////////// // Prints a character on the lcd display, at position (x,y). The // upper-left is (1,1), and the lower right should be (20,4). // void glk_chr(int x, int y, char c) { int myc = (unsigned char) c ; x -= 1; // Convert 1-based coords to 0-based... y -= 1; if( fontselected != 2 ) { // puts( "Switching to font 2" ); /* Select font 2 */ glkputl( PortFD, GLKCommand, 0x31, 2, EOF ); fontselected = 2 ; /* Set font metrics */ glkputl( PortFD, GLKCommand, 0x32, 1, 0, 1, 1, 32, EOF ); /* Clear the screen */ glk_clear_forced( ); }; if( myc >= 0 && myc <= 15 ) { /* CGRAM */ // if( myc != 0 ) { // printf( "CGRAM changing %d => %d\n", myc, CGRAM[myc&7] ); // }; myc = CGRAM[myc&7] ; } else if( myc == 255 || myc == -1 ) { /* Solid block */ myc = 133 ; } else if( (myc > 15 && myc < 32) || myc > 143 ) { fprintf( stderr, "Attempt to write %d to (%d,%d)\n", myc, x, y ); myc = 133 ; }; lcd.framebuf[(y*lcd.wid) + x] = myc; } ////////////////////////////////////////////////////////////////////// // Sets the contrast of the display. Value is 0-255, where 140 is // what I consider "just right". // int glk_contrast(int contrast) { static int saved_contrast = 140 ; if( contrast > 0 && contrast < 256 ) { saved_contrast = contrast ; // printf("Contrast: %i\n", contrast); glkputl( PortFD, GLKCommand, 0x50, contrast, EOF ); }; return( saved_contrast ); } ////////////////////////////////////////////////////////////////////// // Turns the lcd backlight on or off... // void glk_backlight(int on) { /* if(on) { // printf("Backlight ON\n"); glkputl( PortFD, GLKCommand, 0x42, 0, EOF ); } else { // printf("Backlight OFF\n"); glkputl( PortFD, GLKCommand, 0x46, EOF ); } */ } ////////////////////////////////////////////////////////////////////// // Sets general purpose outputs on or off void glk_output(int on) { if( gpo_count < 2 ) { if( on ) { glkputl( PortFD, GLKCommand, 'W', EOF ); } else { glkputl( PortFD, GLKCommand, 'V', EOF ); }; } else { int i; for( i = 1 ; i <= gpo_count ; ++i, on >>= 1 ) { if( on & 1 ) { glkputl( PortFD, GLKCommand, 'W', i, EOF ); } else { glkputl( PortFD, GLKCommand, 'V', i, EOF ); }; }; }; } ////////////////////////////////////////////////////////////////////// // Tells the driver to get ready for vertical bargraphs. // void glk_init_vbar() { // printf("Vertical bars.\n"); } ////////////////////////////////////////////////////////////////////// // Tells the driver to get ready for horizontal bargraphs. // void glk_init_hbar() { // printf("Horizontal bars.\n"); } ////////////////////////////////////////////////////////////////////// // Tells the driver to get ready for big numbers, if possible. // void glk_init_num() { // printf("Big Numbers.\n"); if( fontselected != 3 ) { /* Select Big Numbers font */ glkputl( PortFD, GLKCommand, 0x31, 3, EOF ); fontselected = 3 ; /* Set font metrics */ glkputl( PortFD, GLKCommand, 0x32, 1, 0, 1, 1, 32, EOF ); /* Clear the screen */ glk_clear_forced( ); }; } ////////////////////////////////////////////////////////////////////// // Draws a big (4-row) number. // void glk_num(int x, int num) { // printf("BigNum(%i, %i)\n", x, num); lcd.framebuf[x-1] = num + '0' ; } ////////////////////////////////////////////////////////////////////// // Changes the font data of character n. // void glk_set_char(int n, char *dat) { printf("Set Character %i\n", n); } ///////////////////////////////////////////////////////////////// // Draws a vertical bar, from the bottom of the screen up. // void glk_vbar(int x, int len) { int y = lcd.hgt ; // printf( "glk_vbar( %d, %d )\n", x, len ); while( len > lcd.cellhgt ) { glk_chr( x, y, 255 ); --y ; len -= lcd.cellhgt ; }; if( y >= 0 ) { int lastc ; switch( len ) { case 0 : return ; break ; /* Don't output a char */ case 1 : lastc = 138 ; break ; /* One bar */ case 2 : lastc = 139 ; break ; case 3 : lastc = 140 ; break ; case 4 : lastc = 141 ; break ; case 5 : lastc = 142 ; break ; case 6 : lastc = 143 ; break ; default: lastc = 133 ; break ; }; glk_chr( x, y, lastc ); }; } ///////////////////////////////////////////////////////////////// // Draws a horizontal bar to the right. // void glk_hbar(int x, int y, int len) { // printf( "glk_hbar( %d, %d, %d )\n", x, y, len ); while( len > lcd.cellwid ) { glk_chr( x, y, 255 ); ++x ; len -= lcd.cellwid ; }; if( x <= lcd.wid ) { int lastc ; switch( len ) { case 0 : lastc = ' ' ; break ; case 1 : lastc = 134 ; break ; /* One bar */ case 2 : lastc = 135 ; break ; case 3 : lastc = 136 ; break ; case 4 : lastc = 137 ; break ; default: lastc = 133 ; break ; }; glk_chr( x, y, lastc ); }; } ///////////////////////////////////////////////////////////////// // Sets character 0 to an icon... // void glk_icon(int which, char dest) { unsigned char old, new ; unsigned char * p ; unsigned char * q ; int count ; // printf("Char %i set to icon %i\n", dest, which); if( dest < 0 || dest > 7 ) { /* Illegal custom character */ return ; }; /* which == 0 => empty heart => 131 * which == 1 => filled heart => 132 * which == 2 => ellipsis => 128 */ switch( which ) { case 0: new = 131 ; break ; case 1: new = 132 ; break ; case 2: new = 128 ; break ; default: return ; /* ERROR */ }; old = CGRAM[(int)dest] ; CGRAM[(int)dest] = new ; p = lcd.framebuf ; q = screen_contents ; /* Replace all old icons with new icon in new frame */ for( count = lcd.wid * lcd.hgt ; count ; --count ) { if( *q == old ) { // printf( "icon %d to %d at %d\n", old, new, q - screen_contents ); *p = new ; }; ++q ; ++p ; }; return ; } ////////////////////////////////////////////////////////////////////// // Send a rectangular area to the display. // // I've just called glk_flush() because there's not much point yet // in flushing less than the entire framebuffer. // void glk_flush_box(int lft, int top, int rgt, int bot) { // printf("glk_flush_box( %d, %d, %d, %d )\n", lft, top, rgt, bot ); glk_flush( ); } ////////////////////////////////////////////////////////////////////// // Draws the framebuffer on the display. // // The commented-out code is from the text driver. // void glk_draw_frame(char *dat) { char * p ; char * q ; int x, y ; int xs ; char * ps = NULL ; // printf( "glk_draw_frame( %p ) lcd.framebuf = %p\n", dat, lcd.framebuf ); p = lcd.framebuf ; q = screen_contents ; for( y = 0 ; y < lcd.hgt ; ++y ) { xs = -1 ; /* XStart not set */ for( x = 0 ; x < lcd.wid ; ++x ) { if( *q == *p && xs >= 0 ) { /* Write accumulated string */ glkputl( PortFD, GLKCommand, 0x79, xs*6+1, y*8, EOF ); glkputa( PortFD, x - xs, ps ); // printf( "draw_frame: Writing at (%d,%d) for %d\n", xs, y, x-xs ); xs = -1 ; } else if( *q != *p && xs < 0 ) { /* Start new string of changes */ ps = p ; xs = x ; }; *q++ = *p++ ; /* Update screen_contents from lcd.framebuf */ }; if( xs >= 0 ) { /* Write accumulated line */ glkputl( PortFD, GLKCommand, 0x79, xs*6+1, y*8, EOF ); glkputa( PortFD, lcd.wid - xs, ps ); // printf( "draw_frame: Writing at (%d,%d) for %d\n", xs, y, lcd.wid-xs ); }; }; /* For y */ return ; } ////////////////////////////////////////////////////////////////////// // Tries to read a character from an input device... // // Return 0 for "nothing available". // char glk_getkey() { int c ; static int key = -1 ; static struct timeval lastkey ; struct timeval now ; // puts( "glk_getkey( )" ); c = glkgetc( PortFD ); if( c >= 'A' && c <= 'Z' ) { /* Key down event */ key = c ; gettimeofday( &lastkey, NULL ); // printf( "KEY %c at %ld.%06ld\n", c, lastkey.tv_sec, lastkey.tv_usec ); } else if( c >= 'a' && c <= 'z' ) { /* Key up event */ // printf( "KEY %c UP\n", c ); key = -1 ; c = 0 ; } else { /* Assume timeout */ c = 0 ; if( key > 0 ) { int msec_diff ; /* A key is down */ gettimeofday( &now, NULL ); msec_diff = (now.tv_sec - lastkey.tv_sec) * 1000 ; msec_diff += (now.tv_usec - lastkey.tv_usec) / 1000 ; // printf( "KEY %c down for %d msec\n", key, msec_diff ); if( msec_diff > 2000 ) { /* Generate repeat event */ c = key | 0x20 ; /* Upper case to lower case */ ++lastkey.tv_sec ; /* HACK HACK. repeat at 1 sec intervals */ // printf( "KEY %c REPEAT\n", c ); }; }; }; /* Remap keys according to what LCDproc expects */ switch( c ) { default : break ; case 'U' : c = 'A' ; break ; case 'Q' : c = 'B' ; break ; case 'P' : c = 'C' ; break ; case 'K' : c = 'D' ; break ; case 'V' : c = 'E' ; break ; case 'L' : c = 'F' ; break ; case 'u' : c = 'N' ; break ; case 'q' : c = 'O' ; break ; case 'p' : c = 'P' ; break ; case 'k' : c = 'Q' ; break ; case 'v' : c = 'R' ; break ; case 'l' : c = 'S' ; break ; }; // if( c ) { // printf( "KEY %c\n", c ); // }; return( c ); }