Files
lcdproc/server/drivers/lcterm.c
T
robijn dc4c049721 - Added some icons.
- Made sure all icon and vbar functions are not the old API functions.
- Removed heartbeat functions from drivers that did standard heartbeat.
2002-05-23 18:03:36 +00:00

763 lines
17 KiB
C

/*lcterm.c*/
/*
This is the LCDproc driver for the "LCTerm" serial LCD terminal
from Helmut Neumark Elektronik, www.neumark.de
Copyright (C) 2002 Michael Schwingen <michael@schwingen.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
This driver is mostly based on the HD44780 and the LCDM001 driver.
(Hopefully I have NOT forgotten any file I have stolen code from.
If so send me an e-mail or add your copyright here!)
TODO: support keyboard input
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "lcd.h"
#include "lcd_lib.h"
#include "lcterm.h"
#include "report.h"
typedef enum
{
CCMODE_STANDARD, /* only char 0 is used for heartbeat */
CCMODE_VBAR,
CCMODE_HBAR,
CCMODE_BIGNUM
} CCMode;
typedef struct
{
CCMode ccmode; /* custom character mode for current display */
CCMode last_ccmode; /* custom character set that is loaded in the display */
unsigned char *framebuf;
unsigned char *last_framebuf;
int width;
int height;
int fd;
}PrivateData;
// Vars for the server core
MODULE_EXPORT char *api_version = API_VERSION;
MODULE_EXPORT int stay_in_foreground = 0;
MODULE_EXPORT int supports_multiple = 0;
MODULE_EXPORT char *symbol_prefix = "lcterm_";
/////////////////////////////////////////////////////////////////
// Opens com port and sets baud correctly...
//
MODULE_EXPORT int
lcterm_init (Driver *drvthis, char *args)
{
char device[200];
int speed=B9600;
struct termios portset;
PrivateData *p;
debug( RPT_INFO, "LCTERM: init(%p,%s)", drvthis, args );
// Alocate and store private data
p = (PrivateData *) calloc( 1, sizeof( PrivateData) );
if( ! p )
return -1;
if( drvthis->store_private_ptr( drvthis, p ) )
return -1;
p->ccmode = p->last_ccmode = CCMODE_STANDARD;
// READ CONFIG FILE:
// which serial device should be used
strncpy(device, drvthis->config_get_string ( drvthis->name , "Device" , 0 , DEFAULT_DEVICE),
sizeof(device));
device[sizeof(device)-1]=0;
report (RPT_INFO,"LCTERM: Using device: %s", device);
{
char *s;
int w,h;
// Get and parse size
s = drvthis->config_get_string( drvthis->name, "size", 0, "16x2" );
fprintf( stderr, "lcterm_init: size: %s\n", s );
if( sscanf( s, "%dx%d", &w, &h) == 2
&& (w > 0) && (w <= LCD_MAX_WIDTH)
&& (h > 0) && (h <= LCD_MAX_HEIGHT))
{
p->width = w;
p->height = h;
}
else
fprintf( stderr, "lcterm_init: Cannot parse size: %s\n", s );
}
fprintf(stderr,"size: %dx%d\n",p->width, p->height);
p->framebuf = malloc (p->width * p->height);
p->last_framebuf = malloc (p->width * p->height);
if (!p->framebuf || !p->last_framebuf) {
report(RPT_ERR, "\nError: unable to create LCTERM framebuffer.\n");
return -1;
}
memset (p->framebuf, ' ', p->width * p->height);
memset (p->last_framebuf, ' ', p->width * p->height);
// Set up io port correctly, and open it...
debug( RPT_DEBUG, "LCTERM: Opening serial device: %s", device);
p->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if (p->fd == -1)
{
switch (errno)
{
case ENOENT: report( RPT_ERR, "LCTERM: lcterm_init() failed: Device file missing: %s\n", device);
break;
case EACCES: report( RPT_ERR, "LCTERM: lcterm_init() failed: Could not open device: %s\n", device);
report( RPT_ERR, "LCTERM: lcterm_init() failed: Make sure you have rw access to %s!\n", device);
break;
default: report( RPT_ERR, "LCTERM: lcterm_init() failed (%s)\n", strerror (errno));
break;
}
return -1;
} else {
report (RPT_INFO, "opened LCTERM display on %s\n", device);
}
tcgetattr(p->fd, &portset);
#ifdef HAVE_CFMAKERAW
/* The easy way: */
cfmakeraw( &portset );
#else
/* The hard way: */
portset.c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON );
portset.c_oflag &= ~OPOST;
portset.c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
portset.c_cflag &= ~( CSIZE | PARENB | CRTSCTS );
portset.c_cflag |= CS8 | CREAD | CLOCAL ;
#endif
cfsetospeed(&portset, speed);
cfsetispeed(&portset, speed);
tcsetattr(p->fd, TCSANOW, &portset);
tcflush(p->fd, TCIOFLUSH);
// clear the display, disable cursor, disable key scanning
write (p->fd, "\x1a\x16\x1bK", 4);
return 0;
}
/////////////////////////////////////////////////////////////////
// Clean-up
//
MODULE_EXPORT void
lcterm_close (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
if(p->framebuf)
free (p->framebuf);
if(p->last_framebuf)
free (p->last_framebuf);
// clear the display, disable key scanning
if (p->fd)
{
write (p->fd, "\x1a\x1bK", 3);
close (p->fd);
}
drvthis->store_private_ptr( drvthis, NULL );
report (RPT_INFO, "LCTERM: closed");
}
/////////////////////////////////////////////////////////////////
// Returns the display width
//
MODULE_EXPORT int
lcterm_width (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
return p->width;
}
/////////////////////////////////////////////////////////////////
// Returns the display height
//
MODULE_EXPORT int
lcterm_height (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
return p->height;
}
/////////////////////////////////////////////////////////////////
// Clears the LCD screen
//
MODULE_EXPORT void
lcterm_clear (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
memset(p->framebuf, ' ', p->width * p->height);
p->ccmode = CCMODE_STANDARD;
}
//////////////////////////////////////////////////////////////////
// Flushes all output to the lcd...
//
MODULE_EXPORT void
lcterm_flush (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
int i,line;
unsigned char *buf, *sp, *dp, c;
if(memcmp(p->framebuf, p->last_framebuf, p->width * p->height) == 0)
return;
buf = alloca(p->width * p->height * 2 + 5); /* worst case: we need to
escape *every* character */
dp = buf;
sp = p->framebuf;
*dp++ = 0x1E; // cursor home
for(line = p->height; line>0; line--)
{
for(i=p->width; i>0; i--)
{
if((c = *sp++) < 0x08) // need to escape used-defined characters
*dp++ = 0x1B;
*dp++ = c;
}
*dp++ = 0x0a;
*dp++ = 0x0d;
}
write(p->fd,buf,dp-buf);
memcpy(p->last_framebuf, p->framebuf, p->width * p->height);
}
/////////////////////////////////////////////////////////////////
// Prints a character on the lcd display, at position (x,y). The
// upper-left is (1,1), and the lower right should be (16,2).
//
MODULE_EXPORT void
lcterm_chr (Driver *drvthis, int x, int y, char ch)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
y--;
x--;
//debug (RPT_DEBUG, "lcterm_chr: x=%d, y=%d, chr=%x", x,y,ch);
if (x >= 0 && x < p->width && y >= 0 && y < p->height)
p->framebuf[ y * p->width + x] = ch;
}
/////////////////////////////////////////////////////////////////
// Prints a string on the lcd display, at position (x,y). The
// upper-left is (1,1), and the lower right should be (16,2).
//
MODULE_EXPORT void
lcterm_string (Driver *drvthis, int x, int y, char *s)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
x --; // Convert 1-based coords to 0-based
y --;
for (; *s && x < p->width; x++)
p->framebuf[y * p->width + x] = *s++;
}
/////////////////////////////////////////////////////////////////
// Sets a custom character from 0-7...
//
// For input, values > 0 mean "on" and values <= 0 are "off".
//
// The input is just an array of characters...
//
MODULE_EXPORT void
lcterm_set_char (Driver *drvthis, int n, char *dat)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
int row, col;
int data;
unsigned char buf[11];
if (n < 0 || n > 7 || !dat)
return;
buf[0] = 0x1F;
buf[1] = 8*n; // CG RAM address */
for (row = 0; row < 8; row++)
{
data = 0;
for (col = 0; col < 5; col++)
{
data <<= 1;
data |= (*dat++ != 0);
}
buf[2+row] = data | 0x80;
}
buf[10] = 0x1E; // Cursor Home - exit CG-RAM mode
write(p->fd,buf,11);
}
/////////////////////////////////////////////////////////////////
// Sets up for vertical bars. Call before lcterm->vbar()
//
static void
lcterm_init_vbar (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
static char vbar_1[] = {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
};
static char vbar_2[] = {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
};
static char vbar_3[] = {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
};
static char vbar_4[] = {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
};
static char vbar_5[] = {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
};
static char vbar_6[] = {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
};
static char vbar_7[] = {
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
};
if( p->last_ccmode == CCMODE_VBAR ) /* Work already done */
return;
if( p->ccmode != CCMODE_STANDARD )
{
/* Not supported (yet) */
report( RPT_WARNING, "lcterm_init_vbar: Cannot combine two modes using user defined characters" );
return;
}
p->ccmode = p->last_ccmode = CCMODE_VBAR;
lcterm_set_char (drvthis, 1, vbar_1);
lcterm_set_char (drvthis, 2, vbar_2);
lcterm_set_char (drvthis, 3, vbar_3);
lcterm_set_char (drvthis, 4, vbar_4);
lcterm_set_char (drvthis, 5, vbar_5);
lcterm_set_char (drvthis, 6, vbar_6);
lcterm_set_char (drvthis, 7, vbar_7);
}
/////////////////////////////////////////////////////////////////
// Inits horizontal bars...
//
static void
lcterm_init_hbar (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
static char hbar_1[] = {
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
};
static char hbar_2[] = {
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
};
static char hbar_3[] = {
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
};
static char hbar_4[] = {
1, 1, 1, 1, 0,
1, 1, 1, 1, 0,
1, 1, 1, 1, 0,
1, 1, 1, 1, 0,
1, 1, 1, 1, 0,
1, 1, 1, 1, 0,
1, 1, 1, 1, 0,
1, 1, 1, 1, 0,
};
if( p->last_ccmode == CCMODE_HBAR ) /* Work already done */
return;
if( p->ccmode != CCMODE_STANDARD )
{
/* Not supported (yet) */
report( RPT_WARNING, "lcterm_init_hbar: Cannot combine two modes using user defined characters" );
return;
}
p->ccmode = p->last_ccmode = CCMODE_HBAR;
lcterm_set_char (drvthis, 1, hbar_1);
lcterm_set_char (drvthis, 2, hbar_2);
lcterm_set_char (drvthis, 3, hbar_3);
lcterm_set_char (drvthis, 4, hbar_4);
}
/////////////////////////////////////////////////////////////////
// Draws a vertical bar, from the bottom of the screen up.
//
MODULE_EXPORT void
lcterm_vbar (Driver *drvthis, int x, int y, int len, int promille, int options)
{
lcterm_init_vbar(drvthis);
lib_vbar_static(drvthis, x, y, len, promille, options, LCD_DEFAULT_CELLHEIGHT, 0);
}
/////////////////////////////////////////////////////////////////
// Draws a horizontal bar to the right.
//
MODULE_EXPORT void
lcterm_hbar(Driver *drvthis, int x, int y, int len, int promille, int options)
{
lcterm_init_hbar(drvthis);
lib_hbar_static(drvthis, x, y, len, promille, options, LCD_DEFAULT_CELLWIDTH, 0);
}
/////////////////////////////////////////////////////////////////
// Sets up for big numbers.
//
MODULE_EXPORT void
lcterm_init_num (Driver *drvthis)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
int i;
static char bignum_ccs[8][5*8] = {{
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0
}, {
0, 0, 0, 1, 1,
0, 0, 0, 1, 1,
0, 0, 0, 1, 1,
0, 0, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
0, 0, 0, 0, 0
}, {
1, 1, 0, 1, 1,
1, 1, 0, 1, 1,
1, 1, 0, 1, 1,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0
}, {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
0, 0, 0, 0, 0
}, {
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 1,
0, 0, 0, 1, 1,
0, 0, 0, 1, 1,
0, 0, 0, 0, 0
}, {
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 0, 1, 1,
1, 1, 0, 1, 1,
1, 1, 0, 1, 1,
0, 0, 0, 0, 0
}, {
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
1, 1, 0, 0, 0,
0, 0, 0, 0, 0
}, {
0, 0, 0, 1, 1,
0, 0, 0, 1, 1,
0, 0, 0, 1, 1,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0
}};
if( p->last_ccmode == CCMODE_BIGNUM ) {
/* Work already done */
return;
}
if( p->ccmode != CCMODE_STANDARD ) {
/* Not supported (yet) */
report( RPT_WARNING, "lcterm_init_num: Cannot combine two modes using user defined characters" );
return;
}
for( i=0; i<8; i++ )
lcterm_set_char (drvthis, i, bignum_ccs[i]);
p->ccmode = p->last_ccmode = CCMODE_BIGNUM;
}
/////////////////////////////////////////////////////////////////
// Writes a big number.
//
MODULE_EXPORT void
lcterm_num (Driver *drvthis, int x, int num)
{
PrivateData *p = (PrivateData *) drvthis->private_data;
static char bignum_map[11][4][3] = {
{ /* 0: */
{1,2,3},
{6,32,6},
{6,32,6},
{7,2,32}
},
{ /* 1: */
{7,6,32},
{32,6,32},
{32,6,32},
{7,2,32},
},
{ /* 2: */
{1,2,3},
{32,5,0},
{1,32,32},
{2,2,0},
},
{ /* 3: */
{1,2,3},
{32,5,0},
{3,32,6},
{7,2,32}
},
{ /* 4: */
{32,3,6},
{1,32,6},
{2,2,6},
{32,32,0}
},
{ /* 5: */
{1,2,0},
{2,2,3},
{3,32,6},
{7,2,32}
},
{ /* 6: */
{1,2,32},
{6,5,32},
{6,32,6},
{7,2,32}
},
{ /* 7: */
{2,2,6},
{32,1,32},
{32,6,32},
{32,0,32}
},
{ /* 8: */
{1,2,3},
{4,5,0},
{6,32,6},
{7,2,32}
},
{ /* 9: */
{1,2,3},
{4,3,6},
{32,1,32},
{7,32,32}
},
{ /* colon: */
{32},
{7},
{7},
{32}
}};
if( num < 0 || num > 10 ) return;
lcterm_init_num(drvthis);
if( p->width >= 16 && p->height >= 4 )
{
int y = 1 + ( p->height - 2 ) / 2;
int x2, y2;
for( x2 = 0; x2 <= 2; x2 ++ )
{
for( y2 = 0; y2 <= 3; y2 ++ )
lcterm_chr( drvthis, x+x2, y+y2, bignum_map[num][y2][x2] );
if( num == 10 )
x2 = 2; /* =break, for colon only */
}
}
else
{
int y = ( p->height - 1 ) / 2 + 1;
if( num == 11 )
lcterm_chr( drvthis, x, y, ':' );
else
lcterm_chr( drvthis, x, y, num + '0' );
}
}
/////////////////////////////////////////////////////////////////
// Sets character 0 to an icon...
//
MODULE_EXPORT int
lcterm_icon (Driver *drvthis, int x, int y, int icon)
{
static char heart_open[] = {
1, 1, 1, 1, 1,
1, 0, 1, 0, 1,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 0, 0, 0, 1,
1, 1, 0, 1, 1,
1, 1, 1, 1, 1 };
static char heart_filled[] = {
1, 1, 1, 1, 1,
1, 0, 1, 0, 1,
0, 1, 0, 1, 0,
0, 1, 1, 1, 0,
0, 1, 1, 1, 0,
1, 0, 1, 0, 1,
1, 1, 0, 1, 1,
1, 1, 1, 1, 1 };
switch( icon )
{
case ICON_BLOCK_FILLED:
lcterm_chr( drvthis, x, y, 255 );
break;
case ICON_HEART_FILLED:
lcterm_set_char( drvthis, 0, heart_filled );
lcterm_chr( drvthis, x, y, 0 );
break;
case ICON_HEART_OPEN:
lcterm_set_char( drvthis, 0, heart_open );
lcterm_chr( drvthis, x, y, 0 );
break;
default:
return -1;
}
return 0;
}