|
Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
#include <stdio.h>
#include "mktclapp.h"
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
/* for setkbdrepeat: */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
/* Where's the standard definition for this? */
#define min(a,b) ((a) < (b) ? (a) : (b))
/*
* «.defines» (to "defines")
* «.change_char» (to "change_char")
* «.change_nchars» (to "change_nchars")
* «.change_nrows» (to "change_nrows")
* «.duplicate_rows» (to "duplicate_rows")
* «.setfont» (to "setfont")
* «.tobinary» (to "tobinary")
* «.toasciirow» (to "toasciirow")
* «.vcsa2pnmdata» (to "vcsa2pnmdata")
* «.setkbdrepeat» (to "setkbdrepeat")
*
* We are defining the following new Tcl commands:
* change_char nchar chardata nchars nrows data -> newdata
* change_nchars newnchars nchars nrows data -> newdata
* change_nrows newnrows nchars nrows data -> newdata
* duplicate_rows data -> newdata
* setfont channel nchars nrows data
* toasciirow byte c0 c1 width -> chardata
* tobinary rowdata rowdata ... -> chardata
* vcsa2pnmdata rgb0 rgb1 ... rgb15 nchars nrows fontdata vcsadata
* setkbdrepeat value
*
*
* (find-es "emacs" "key_bindings")
* (define-key c-mode-base-map "\ee" nil)
* (find-es "mktclapp")
* (eeman "mktclapp")
* (find-w3 "/usr/doc/mktclapp/mktclapp.html")
*
* (find-node "(libc)Function Index")
* (find-vldifile "tcl8.0-dev.list")
* (find-vldifile "tcl8.2-dev.list")
* (find-vldifile "tcl8.3-dev.list")
* (find-vldifile "tcl8.0-doc.list")
* (find-vldifile "tcl8.2-doc.list")
* (find-vldifile "tcl8.3-doc.list")
* (eeman "Tcl_GetLongFromObj")
* (eeman "Tcl_GetStringFromObj")
* (eeman "Tcl_GetByteArrayFromObj")
* (eeman "Tcl_GetInt")
* (find-tcltag "Tcl_CloseObjCmd")
* (find-tcltag "Tcl_WrongNumArgs")
* (find-tcltag "Tcl_GetChannel")
* (find-tcltag "Channel")
* (eeman "Tcl_GetChannel")
*
* (find-angg "MTA/")
* (eeman "Tcl_SetObjResult")
* (eeman "Tcl_NewStringObj")
* (eeman "Tcl_NewByteArrayObj")
*
*/
/*
* «defines» (to ".defines")
* Some "#define"s to make using MkTclApp easier.
* Important change: in tcl8.2 and later, strings are unibyte by
* default, and we need to use the "ByteArray" functions to access the
* real binary data.
* I haven't changed NewStringObject to NewByteArrayObject yet.
* (eeman "3tcl Tcl_GetStringFromObj")
* (eeman "3tcl Tcl_GetByteArrayFromObj")
*
*/
#define ET_ERRORF(listargs) ({Et_ResultF listargs; return TCL_ERROR;})
#define ET_ERROR(str) ET_ERRORF((interp,str))
#define ET_ERROR1(str,a) ET_ERRORF((interp,str,a))
#define ET_ERROR2(str,a,b) ET_ERRORF((interp,str,a,b))
#define ET_ERROR3(str,a,b,c) ET_ERRORF((interp,str,a,b,c))
#define ET_ERROR4(str,a,b,c,d) ET_ERRORF((interp,str,a,b,c,d))
/* #define OARGV_STRING(n, lenptr) Tcl_GetStringFromObj(objv[n], lenptr) */
#define OARGV_STRING(n, lenptr) Tcl_GetByteArrayFromObj(objv[n], lenptr)
#define OARGV0 OARGV_STRING(0, 0)
#define OARGV_INT(n) ({ \
int _tmpint; \
if (Tcl_GetIntFromObj(interp, objv[n], &_tmpint) != TCL_OK) \
ET_ERROR2("%s: arg %d not an int", OARGV0, n); \
_tmpint; \
})
#define ET_OARGSERROR(argstr) \
ET_ERROR2("wrong # args: should be \"%s %s\"", OARGV0, argstr)
#define ET_ARGSERROR(argstr) \
ET_ERROR2("wrong # args: should be \"%s %s\"", argv[0], argstr)
#define ET_ORETURN(data, len) \
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(data, len))
/*
* A special #define to get the nchars/nrows/data triples.
*
*/
#define get_nchars_nrows_data_len(pnchars, pnrows, pdata) \
({nchars = OARGV_INT(pnchars); \
if (nchars != 256 && nchars != 512) \
ET_ERROR("NCHARS must be 256 or 512"); \
nrows = OARGV_INT(pnrows); \
if (nrows < 1 || nrows > 32) \
ET_ERROR("NROWS must be in the range 1..32"); \
data = OARGV_STRING(pdata, &len); \
if (len != nchars * nrows) \
ET_ERROR2("DATA has len %d, should have been %d", \
len, nchars * nrows); \
})
/*
* «change_char» (to ".change_char")
* change_char NCHAR CHARDATA NCHARS NROWS DATA -> NEWDATA
*
*/
int ET_OBJCOMMAND_change_char(ET_OBJARGS) {
int nchar, charlen, nchars, nrows, len;
char *chardata, *data, newdata[512 * 32];
if(objc != 6)
ET_OARGSERROR("NCHAR CHARDATA NCHARS NROWS DATA");
nchar = OARGV_INT(1);
chardata = OARGV_STRING(2, &charlen);
get_nchars_nrows_data_len(3, 4, 5);
if (nchar < 0 || nchar >= nchars)
ET_ERROR("NCHAR must be in the range 0..NCHARS-1");
if (charlen < 1 || charlen > 32)
ET_ERROR("The lenght of CHARDATA must be in the range 1..32");
memcpy(newdata, data, nchars*nrows);
memset(newdata + nchar*nrows, 0, nrows);
memcpy(newdata + nchar*nrows, chardata, min(charlen, nrows));
ET_ORETURN(newdata, nchars * nrows);
return TCL_OK;
}
/*
* «change_nchars» (to ".change_nchars")
* change_nchars NEWNCHARS NCHARS NROWS DATA -> NEWDATA
*
*/
int ET_OBJCOMMAND_change_nchars(ET_OBJARGS) {
int newnchars, nchars, nrows, len;
char *data, newdata[512 * 32];
if(objc != 5)
ET_OARGSERROR("NEWNCHARS NCHARS NROWS DATA");
newnchars = OARGV_INT(1);
if (newnchars != 256 && newnchars != 512)
ET_ERROR("NEWNCHARS must be 256 or 512");
get_nchars_nrows_data_len(2, 3, 4);
memset(newdata, 0, newnchars * nrows);
memcpy(newdata, data, min(newnchars, nchars) * nrows);
ET_ORETURN(newdata, newnchars * nrows);
return TCL_OK;
}
/*
* «change_nrows» (to ".change_nrows")
* change_nrows NEWNROWS NCHARS NROWS DATA -> NEWDATA
*
*/
int ET_OBJCOMMAND_change_nrows(ET_OBJARGS) {
int newnrows, nchars, nrows, len, i;
char *data, newdata[512 * 32];
if(objc != 5)
ET_OARGSERROR("NEWNROWS NCHARS NROWS DATA");
newnrows = OARGV_INT(1);
if (newnrows < 1 || newnrows > 32)
ET_ERROR("NEWNROWS must be in the range 1..32");
get_nchars_nrows_data_len(2, 3, 4);
memset(newdata, 0, nchars * newnrows);
for(i=0; i<nchars; ++i)
memcpy(newdata + i*newnrows, data + i*nrows, min(newnrows, nrows));
ET_ORETURN(newdata, nchars * newnrows);
return TCL_OK;
}
/*
* «duplicate_rows» (to ".duplicate_rows")
* duplicate_rows DATA -> NEWDATA
*
*/
int ET_OBJCOMMAND_duplicate_rows(ET_OBJARGS) {
int len, i;
char *data, newdata[512 * 32], *p1, *p2;
if(objc != 2)
ET_OARGSERROR("DATA");
data = OARGV_STRING(1, &len);
if (len > 512 * 16)
ET_ERROR("Input data too long (limit 512*16 bytes)");
for(i=0, p1=data, p2=newdata; i<len; ++i) {
*p2++ = *p1;
*p2++ = *p1++;
}
ET_ORETURN(newdata, len * 2);
return TCL_OK;
}
/*
* «setfont» (to ".setfont")
* setfont CHANNEL NCHARS NROWS DATA
*
*/
int ET_OBJCOMMAND_setfont(ET_OBJARGS) {
int f, nchars, nrows, len, i;
char *data, newdata[512 * 32];
struct consolefontdesc cfd;
if (objc != 5)
ET_OARGSERROR("CHANNEL NCHARS NROWS DATA");
if (sscanf(OARGV_STRING(1, 0), "file%d", &f) != 1)
ET_ERROR("FILEDESCR must be a file descriptor");
get_nchars_nrows_data_len(2, 3, 4);
memset(newdata, 0, 512 * 32);
for(i=0; i<nchars; ++i)
memcpy(newdata + i*32, data + i*nrows, nrows);
cfd.charheight = nrows;
cfd.charcount = nchars;
cfd.chardata = newdata;
if (ioctl(f, PIO_FONTX, &cfd))
ET_ERROR1("PIO_FONTX error: %s", strerror(errno));
/* we return nothing. */
return TCL_OK;
}
/*
* «tobinary» (to ".tobinary")
* tobinary ROWDATA ROWDATA ... -> CHARDATA
*
* We only consider the last bit in each char of a ROWDATA.
* Remember that "0" and " " have even ascii codes, "1" and "o" have
* odd ascii codes.
*
*/
int ET_OBJCOMMAND_tobinary(ET_OBJARGS) {
int n, len, i, byte, bit;
char *rowdata, chardata[512*32];
if(objc < 1+1 || objc > 512*32+1)
ET_OARGSERROR("ROWDATA ... (repeated 1 to 512*32 times)");
for(n=0; n<objc-1; ++n) {
rowdata = OARGV_STRING(1+n, &len);
if (len != 8)
ET_ERROR3("The %dth parameter (\"%q\") has lenght %d instead of 8",
n+1, rowdata, len);
for(byte=0, i=0, bit=128; bit>0; ++i, bit>>=1)
byte |= rowdata[i]&1?bit:0;
chardata[n] = byte;
}
ET_ORETURN(chardata, n);
return TCL_OK;
}
/*
* «toasciirow» (to ".toasciirow")
* toasciirow BYTE C0 C1 WIDTH -> CHARDATA
*
* We only consider the last bit in each char of a ROWDATA.
* Remember that "0" and " " have even ascii codes, "1" and "o" have
* odd ascii codes.
*
*/
int ET_OBJCOMMAND_toasciirow(ET_OBJARGS) {
char byte, c0, c1, chardata[10];
int i, bit, width;
if(objc != 4+1)
ET_OARGSERROR("BYTE C0 C1 WIDTH");
byte = *OARGV_STRING(1, 0);
c0 = *OARGV_STRING(2, 0);
c1 = *OARGV_STRING(3, 0);
width = OARGV_INT(4);
if (width < 8 || width > 9)
ET_ERROR("WIDTH must be 8 or 9");
for(i=0, bit=128; i<8; ++i, bit>>=1)
chardata[i] = (byte & bit) ? c1 : c0;
chardata[8] = ((byte&7)==7) ? c1 : c0;
chardata[width] = 0;
ET_ORETURN(chardata, width);
return TCL_OK;
}
/* (+ 2 (* (+ 1 (* 6 8 80)) 8 50)) */
/* (+ 2 (* (+ 1 (* 6 8 132)) 8 50)) */
/* °°ħħÛÛ ²² */
/* (find-fline "~/ICON/vcsa2pnm.icn")
*/
#define ASCS_PER_PIXEL 6
#define PNMBUFFERSIZE ((ASCS_PER_PIXEL*9*132+1)*60*8+2)
#define APPEND(str) (memcpy(p, str, strlen(str)), p+=strlen(str))
/*
* «vcsa2pnmdata» (to ".vcsa2pnmdata")
* vcsa2pnmdata RGB0 RGB1 ... RGB15 NCHARS NROWS FONTDATA VCSADATA
* Used to make screenshots of text screens.
* (find-k22file "drivers/char/vc_screen.c" "4 bytes")
* At this time we just guess it's 80x50.
*
*/
int ET_OBJCOMMAND_vcsa2pnmdata(ET_OBJARGS) {
char pnmbuffer[PNMBUFFERSIZE];
char *p = pnmbuffer;
unsigned char *vcsabuf;
char *colors[16], *fgcolor, *bgcolor;
int i, tmplen, v, pv, h, pbyte, color, pbit;
int nchars, nrows, len;
char *data; /* fontdata */
if(objc != 21)
ET_OARGSERROR("RGB0 RGB1 ... RGB15 NCHARS NROWS FONTDATA VCSADATA");
for(i=0; i<16; ++i) {
colors[i] = OARGV_STRING(1+i, &tmplen);
if(tmplen>ASCS_PER_PIXEL)
ET_ERROR4("color %i ({%s}) has lenght %d; max is %d",
i, colors[i], tmplen, ASCS_PER_PIXEL);
}
get_nchars_nrows_data_len(17, 18, 19);
vcsabuf = OARGV_STRING(20, 0) + 4; /* skip #lines,#cols,x,y */
for(v=0; v<50; ++v) {
for(pv=0; pv<8; ++pv) {
for(h=0; h<80; ++h) {
pbyte = data[vcsabuf[v*80*2 + h*2]*nrows + pv];
color = vcsabuf[v*80*2 + h*2 + 1];
fgcolor = colors[color&0x0F];
bgcolor = colors[(color&0xF0)>>4];
for(pbit=128; pbit!=0; pbit>>=1) {
APPEND(pbyte&pbit?fgcolor:bgcolor);
}
APPEND((pbyte&0x07)==0x07?fgcolor:bgcolor); /* 9th column */
APPEND("\n"); /* after every row of a char a newline */
}
APPEND("\n"); /* after every row an extra newline */
}
APPEND("\n"); /* after every row of chars another newline */
}
*p=0; /* end of string */
Et_ResultF(interp, "%s", pnmbuffer);
return TCL_OK;
}
/*
* «setkbdrepeat» (to ".setkbdrepeat")
* setkbdrepeat VALUE
* This is a quick hack inspired on kbdrate.
* (code-c-d "ul" "/usr/src/util-linux-2.10f/")
* (find-ulfile "kbd/kbdrate.c" "\"/dev/port\"")
* value = ndelay*32 + nrate,
* where ndelay=0 means 250ms and nrate=0 means 30.0 cps
* higher ndelays mean wait more, higher nrates mean slower repeats.
*
*/
int ET_OBJCOMMAND_setkbdrepeat(ET_OBJARGS) {
int value = 0x7f; /* Maximum delay with slowest rate */
int fd;
char data;
int ms;
if(objc != 3)
ET_OARGSERROR("VALUE MS");
value = OARGV_INT(1);
ms = OARGV_INT(2);
if ( (fd = open( "/dev/port", O_RDWR )) < 0) {
ET_ERROR("Cannot open /dev/port. Check the permissions");
}
do {
lseek( fd, 0x64, 0 );
read( fd, &data, 1 );
} while ((data & 2) == 2 ); /* wait */
lseek( fd, 0x60, 0 );
data = 0xf3; /* set typematic rate */
write( fd, &data, 1 );
do {
lseek( fd, 0x64, 0 );
read( fd, &data, 1 );
} while ((data & 2) == 2 ); /* wait */
lseek( fd, 0x60, 0 );
/*
* sleep( 1 );
* (eeman "Tcl_Sleep")
*/
Tcl_Sleep(ms);
write( fd, &value, 1 );
close( fd );
return TCL_OK;
}
/*
* Local Variables:
* coding: no-conversion
* ee-anchor-format: "«%s»"
* ee-charset-indicator: "Ñ"
* End:
*/