#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h> /* Mauspfeil-Konstanten */
#include <X11/keysym.h> /* Tastatur-Konstanten */
#include <signal.h> /* wegen "Timer-Message" */
#include <X11/Xatom.h> /* wegen XA_CUT_BUFFER0 */
#include <errno.h>
#include "gifsave.h"
/* Dieser Quelltext benötigt gcc's Verschachtelte Funktionen! */
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
#define motorola /* use byte swaps */
#endif
/********************************/
/* Internationale Stringtabelle */
/********************************/
static const char* const sApp[]={
"Teletext",
"Videotext"};
static const char* const sDisplayAbort[]={
"VTX: Fatal: Cannot open display `%s'!\n",
"VTX Abbruch: Display '%s' kann nicht geöffnet werden!\n"};
static const char* const sHelp[]={
"Teletext viever for X window system X11 by haftmann#software\n\
Usage: vtx [options] filename[.vt]\n\
Available options:\n\
-d displayame\tOutput display (i.e. \"-d oasis.etc:0\")\n\
-l{d|f}\tChoose language {German|(French not installed)}\n\
-p xxx/yy\toutput page/subpage as plain text to stdout without starting X\n\
-m xxx\tUse this display flags (see README)\n\
-f xx\tUse this font number (see README)\n\
-n\tSuppress all flashing characters and rolling subpages\n\
-s xx\tUse this search mode (see README)\n\
-v xxx\tVolume of XBell (-100..0..100)\n\
-h\tThis help\n",
"Videotext-Betrachter für X Window System X11 von haftmann#software\n\
Anwendung: vtx [optionen] dateiname[.vt]\n\
Mögliche Optionen:\n\
-d displayname\tAusgabe-Display (z.B. \"-d toaster.csn:0\")\n\
-l{e|d}\tSprache auswählen {englisch|deutsch} sonst automatisch\n\
-p xxx/yy\tAusgabe des Inhalts von Seite xxx Unterseite yy als Text; ohne X\n\
-m xxx\tDiese Anzeige-Flags verwenden (s. LIESMICH)\n\
-f xx\tDiese Zeichensatz-Nummer verwenden (s. LIESMICH)\n\
-n\tKeinerlei blinkende Anzeige und rollende Unterseiten\n\
-s xx\tDiesen Suchmodus benutzen (s. LIESMICH)\n\
-v xxx\tLautstärke von XBell (-100..0..100) bei falschen Tasten\n\
-h\tDiese Hilfe\n"};
static const char* const sGotoPage[]={
"Go to page number:",
"Gehe zu Seite Nummer:"};
static const char* const sNoPage[]={
"Page %hX doesn't exist!",
"Seite %hX existiert nicht!"};
static const char* const sGotoSubp[]={
"Go to subpage number:",
"Gehe zu Unterseite Nummer:"};
static const char* const sNoSubp[]={
"Subpage %04hX doesn't exist!",
"Unterseite %04hX existiert nicht!"};
static const char* const sOnlyOneSubp[]={
"This page has only one subpage!",
"Diese Seite hat nur eine Unterseite!"};
static const char* const sNoBack[]={
"\"Back\" impossible!",
"Rückschritt nicht möglich!"};
static const char* const sNoMatch[]={
"No (more) matching pages!",
"Keine (weiteren) Übereinstimmungen!"};
static const char* const sOpenFile[]={
"Filename:",
"Dateiname:"};
static const char* const sChooseFont[]={
"Choose font:",
"Zeichensatz:"};
static const char* const sFileError[]={
"Error reading file %1.10s, code %hd!",
"\"%1.10s\"-Dateiladefehler, Code %hd"};
static const char* const sWriteError[]={
"Error writing file %1.10s, code %hd!",
"Kann nicht speichern \"%1.6s\", Code %hd"};
static const char* const sSuchString[]={
"Seach pattern:",
"Suchkette:"};
static const char* const sPageInfo[]={
"CBits=%hXh, PageFont=%s",
"CBits=%04hXh, Zeichens.=%s"};
static const char* const sFontName[][16]={
"auto detect", "*German", "*English", "*Scandinavian",
"Polish", "Czech/Slovak", "*Italian", "*French",
"*Spanish", "Icelandic", "Hungarian", "Turkish",
" reserved", " reserved", "Cyrillic", "Arabic",
"automatisch", "*deutsch", "*englisch", "*skandinavisch",
"polnisch", "tschechoslowakisch", "*italienisch", "*französisch",
"*spanisch", "isländisch", "ungarisch", "türkisch",
" reserviert", " reserviert", "kyrillisch", "arabisch"};
/*^--------------^---Trick-Leerzeichen! Bitte stehen lassen! */
static const char* const sHelpPage[]={"\
\0\0\0\0\0\0\0\7 Help page \
\4\x1d\7 *** navigate *** \
\6o F4 \7open a file... \
\6g Home \7page 100 \
\6j PgDn + CurDn \7next page \
\6k PgUp - CurUp \7previous page \
\6l CurRight Tabulator\7next subpage \
\6h CurLeft \7previous subpage \
\6 Spacebar \7next subpage or pg\
\6q ^C \7quit VTX \
\6y F1 \7this help \
\6/ F2 \7find... \
\6n F3 \7find next \
\6p Left mouse button \7go to page xxx \
\6s \7go to subpage xxxx\
\6b BkSp RightButton \7go back \
\6f \7go forward \
\4\x1d\7 *** render options *** \
\2Q\7toggle QUIZ (on remote control) \
\2D\7toggle displaying DoubleHeight \
\2M\7toggle mixed mode \
\2I\7toggle invers rendering \
\2W\7toggle black/white or color display \
\2B\7toggle debug display (control codes) \
\2F\7choose font... (then use cursor keys)\
","\
\0\0\0\0\0\x40\0\7 Hilfeseite \
\4\x1d\7 *** Navigation *** \
\6o F4 \7Datei |ffnen... \
\6g Pos1 \7Seite 100 \
\6j BildAb + PfeilAb \7N{chste Seite \
\6k BildAuf - PfeilAuf\7Vorherige Seite \
\6l PfeilRechts Tab. \7N{chste Unterseite\
\6h PfeilLinks \7Vorherige Unters. \
\6 Leertaste \7N{chste Unters.+S.\
\6q F10 ^C \7VTX beenden \
\6y F1 \7Diese Hilfe \
\6/ F2 \7Suchen... \
\6n F3 \7Weitersuchen \
\6p Linke Maustaste \7Gehe zu Seite xxx \
\6s \7Gehe zu Unterseite\
\6b Rechte Maustaste \7Gehe zur}ck \
\6f \7Gehe vorw{rts \
\4\x1d\7 *** Anzeige-Optionen *** \
\2Q\7Quiz-Taste ein/aus \
\2D\7Anzeige von doppelt hohen Zeichen e/a\
\2M\7Mix-Betrieb ein/aus \
\2I\7Invers-Darstellung \
\2W\7Schwarz/Wei~- oder farbige Darstellg.\
\2B\7Debug-Anzeige (Steuercodes) \
\2F\7Font ausw{hlen (benutze Cursortasten)\
"};
static const char* const sPagedefault[]={"\
\0\0\0\0\0\0\0\7 Welcome Page \5h_s \00700:00:00\
pppppppppppppppppppppppppppppppppppp \
\
|||||||||||||||||| \
\
\
######## \
\
\
\
\
\7 \
\7 Teletext viewer for X window system \
\7 by haftmann_software \
\7 \
\
\
\
\
\
\
\
////////// \
\
\6 Press F1 for help or F4 to open a file\
","\
\0\0\0\0\0\x40\0\7 Begr}~ungsseite \5h#s \00700:00:00\
pppppppppppppppppppppppppppppppppppp \
\
|||||||||||||||||| \
\
\
######## \
\
\
\
\
\7 \
\7 Videotext-Betrachter f}r X-Windows \
\7 von haftmann#software \
\7 \
\
\
\
\
\
\
\
////////// \
\
\6 F1 f}r Hilfe oder F4 zum \\ffnen \
"};
static int country; /* Default: englisch */
static int volume;
/******************/
/* Display öffnen */
/******************/
static Display *display; /* Zeiger auf Display-Struktur */
static Screen *screen;
static Colormap cmap;
static void InitDisplay(char *DName){
display=XOpenDisplay(DName);
if (display==NULL){
fprintf(stderr,sDisplayAbort[country],XDisplayName(DName));
exit(1);
}
screen=DefaultScreenOfDisplay(display);
cmap=DefaultColormap(display,DefaultScreen(display));
}
/**********************************************/
/* Hauptfenster erstellen, Ressourcen belegen */
/**********************************************/
#define FONTW 8
#define FONTH 10
#define GetLines(x) ( ((x) & f1)? 1: (((x) & f25)? 25 : 24) )
#define GetCols(x) ( ((x) & f41)? 41 : 40 )
static Window window;
static GC MainGC,XorGC;
static XContext Linklist;
#include "vticon.inc"
static Pixmap icon;
static Cursor idc_arrow,idc_hand,idc_wait;
static dword Farbe[9]; /* globale Pixelwerte */
tRGBTriple RGBWerte[16]; /* Farbwerte für Abspeicherung als GIF */
struct{
Pixmap hbm;
bool valid;
} Pixmaps[2]={None,false,None,false};
static short BMIdx=-1; /* Keine Bitmap */
static void SetWindowTitle(char *FileName){
char CatBuf[256];
strcpy(CatBuf,sApp[country]); /* Neuen Fenstertitel aufbauen */
if (FileName){ /* Nur wenn einer vorhanden dann anhängen */
strcat(CatBuf,": ");
strcat(CatBuf,FileName);
}
XSetStandardProperties(display,window,CatBuf,CatBuf,icon,NULL,0,NULL);
}
static void GetColors(bool invert){
XColor TempColor,ExactColor;
static const char* const Farbname[9]={
"black","red","green","yellow","blue","magenta","cyan","white","gray"};
static const char* const InvFarbe[9]={
"white","red","green4","yellow4","blue","magenta4","cyan4","black","gray50"};
int i;
const char *s;
for (i=0; i<9; i++){
s=invert? InvFarbe[i] : Farbname[i];
if (!XAllocNamedColor(display,cmap,s,&TempColor,&ExactColor))
fprintf(stderr,"VTX Warning: Error allocating color '%s'.\n",s);
RGBWerte[i].R=ExactColor.red >>8; /* Farben für den GIF-Packer ablegen */
RGBWerte[i].G=ExactColor.green >>8;
RGBWerte[i].B=ExactColor.blue >>8;
Farbe[i]=TempColor.pixel; /* Pixelwert für BitBlt-Operationen ablegen */
}
}
static void CreateMainWindow(){
XSizeHints xsh;
XSetWindowAttributes xswa;
XGCValues xgcv;
/* Fenster erzeugen */
/* printf("Hier vorbei: Fenster erstellen\n");*/
xsh.flags=PSize|PMaxSize|PMinSize;
xsh.min_width=xsh.max_width=xsh.width=41*FONTW;
xsh.min_height=xsh.max_height=xsh.height=25*FONTH;
xswa.cursor=idc_arrow=XCreateFontCursor(display,XC_top_left_arrow);
xswa.border_pixel = BlackPixelOfScreen(screen);
xswa.background_pixmap=None;
xswa.event_mask = ButtonPressMask|ButtonReleaseMask|Button1MotionMask|
KeyPressMask|ExposureMask;
window=XCreateWindow(display,RootWindowOfScreen(screen),
0,0,xsh.width,xsh.height,1,
CopyFromParent, CopyFromParent, CopyFromParent,
CWBorderPixel|CWCursor|CWBackPixmap|CWEventMask,&xswa);
/* Icon erzeugen und Fenster-Name geben */
icon=XCreateBitmapFromData(display,RootWindowOfScreen(screen),
vticon, vticon_w, vticon_h);
XSetStandardProperties(display,window,sApp[country],sApp[country],
icon,NULL,0,&xsh);
XMapWindow(display,window);
/* Fenster-GC dauerhaft holen */
xgcv.subwindow_mode=IncludeInferiors;
xgcv.graphics_exposures=false;
xgcv.foreground=WhitePixelOfScreen(screen);
xgcv.background=BlackPixelOfScreen(screen);
MainGC=XCreateGC(display,RootWindowOfScreen(screen),
GCForeground|GCBackground|GCSubwindowMode|GCGraphicsExposures,&xgcv);
/* XorGC dauerhaft belegen */
xgcv.function=GXxor;
xgcv.foreground^=BlackPixelOfScreen(screen);
XorGC=XCreateGC(display,RootWindowOfScreen(screen),
GCFunction|GCForeground|GCBackground|GCSubwindowMode|
GCGraphicsExposures,&xgcv);
/* Weitere Ressourcen belegen */
idc_hand=XCreateFontCursor(display,XC_hand2);
idc_wait=XCreateFontCursor(display,XC_watch);
Linklist=XUniqueContext();
Pixmaps[0].hbm=XCreatePixmap(display,window,
41*FONTW,25*FONTH,DefaultDepthOfScreen(screen));
Pixmaps[1].hbm=XCreatePixmap(display,RootWindowOfScreen(screen),
41*FONTW,25*FONTH,DefaultDepthOfScreen(screen));
GetColors(false);
/* printf("WeissPixel=%d, SchwarzPixel=%d.\n",
WhitePixelOfScreen(screen),
BlackPixelOfScreen(screen));*/
}
/*****************************/
/* Videotext-Font "hochladen"*/
/*****************************/
static Pixmap vtfont1=None,vtfont2=None; /* Die beiden Handles auf die Fonts */
#define ZEICHEN (512+128)
#include "vtfont.inc" /* Die binären Daten */
static void FontUpload(){
byte *Puffer,*Zeiger2;
const byte *Zeiger1;
int i;
if (vtfont1!=None) {
XFreePixmap(display,vtfont1); vtfont1=None;
}
if (vtfont2!=None) {
XFreePixmap(display,vtfont2); vtfont2=None;
}
vtfont1=XCreateBitmapFromData(display,window,vtfont,FONTW,ZEICHEN*FONTH);
/* Doppelt hohe Zeichen generieren */
Zeiger1=vtfont;
Zeiger2=Puffer=(byte *) calloc(FONTH*2,ZEICHEN);
for (i=0; i<ZEICHEN*FONTH; i++){
*Zeiger2++ = *Zeiger1;
*Zeiger2++ = *Zeiger1++;
}
vtfont2=XCreateBitmapFromData(display,window,Puffer,FONTW,ZEICHEN*FONTH*2);
free(Puffer);
}
/* Bedeutung der C-Bits
C4 - Löschen einer Seite
C5 - Schlagzeilen
C6 - Untertitel
C7 - Unterdrücken der Kopfzeile
C8 - Texterneuerung
C9 - Unterbrochene Sequenz
C10 - Unterdrückung der Darstellung
C11 - Serielle Magazinfolge
C12 .. C14
000 - Englisch(0)
001 - Deutsch(4)
010 - Schwedisch/Finnisch(2)
011 - Italienisch(6)
100 - Belgisch/Französisch(1)
101 - Portugisisch/Spanisch(5)
110 - dynamische Zeichenzuordnung(3)
111 - reserviert(7)
Ersetzkette 40 60 7B 23 24 ** ** ** 7C 7D 7E 5B 5C 5D 5E 5F */
/****************************/
/* Videotext-Seite zeichnen */
/****************************/
#define bit 1 <<
#define fQuiz (bit 0)
#define fBlink (bit 1)
#define fDblH (bit 2)
#define fMix (bit 3)
#define fInvers (bit 4)
#define fBunt (bit 5)
#define fDebug (bit 6)
#define fRoll (bit 7)
#define fDblSize (bit 8)
#define f41 (bit 9)
#define f25 (bit 10)
#define f1 (bit 11)
#define fBBar (bit 12)
#define fNoX (bit 13)
#define fGraf (bit 8)
#define fHold (bit 9)
#define fSep (bit 10)
#define fShift (bit 11)
#define fLatin (bit 12)
typedef union{
dword L;
struct{
word Page,Subp;
} W;
} tIdx;
/*#define IdxL(x) (*(long *)&(x))*/
typedef byte VTLine[40];
typedef union{
dword L;
struct{
word Page,Subp;
word CBits;
byte errors;
} W;
} tHeader;
typedef struct{
VTLine L[25]; /* 25*40=1000 Bytes Text */
byte Page10,Page1,Subp1000,Subp100,Subp10,Subp1,Page100,C1,C2,PBLF;
/* struct tHSFTrail{ /* haftmann#software und FifiSoft Schwanz */
byte Acqisitor; /* Wer eingelesen hat */
byte MaxSubp; /* nur aus der Angabe xx/yy auf der Seite entnommen */
dword MsDosTime; /* Einlesezeitpunkt, zeitzonenabhängig */
word CRC; /* Prüfsumme über diese Seite für Fehlerkorrektur */
word Mirror; /* Adresse einer Spiegelseite während des Einlesens */
long UnixTime; /* Einlesezeitpunkt, Universal Time */
/* } E; /* 14 Bytes freie Zusatz-Info (wie oben definiert) */
} tTrail;
typedef union{
byte B[1024]; /* 1 Kilobyte */
VTLine L[25]; /* 25 Zeilen mit je 40 Zeichen */
tTrail T; /* 25 Zeilen und der Trailer */
tHeader H; /* 7 Bytes kompaktierte SAA-Info */
char TopLeft[7]; /* Zum Überschreiben des Headers, Nicht nullterminiert! */
} tPage;
static tHeader Header;
tPage Page; /* eine passende Variable definieren */
static const char Ansitab[]={
' ','!','\"','#','$','%','&','\'','(',')','*','+',',','-','.','/',
'0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
0xa7,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
'P','Q','R','S','T','U','V','W','X','Y','Z',0xc4,0xd6,0xdc,'^','_',
0xb0,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w','x','y','z',0xe4,0xf6,0xfc,0xdf,' ',
'@','-',0xbc,0xa3,'$',' ',' ',' ',0xa6,0xbe,0xad,0xab,0xbd,0xbb,'^','#',
0xc9,0xe9,0xe4,'#',0xa4,' ',' ',' ',0xf6,0xe5,0xfc,0xc4,0xd6,0xc5,0xdc,'_',
0x20,0x20,0x20,0x20,0x20,0x20,0x20,'l',0x20,0x20,'S','L',0x20,0x20,0x20,0x20,
'c',0xe9,0xe1,'#',0xf9,0x20,0x20,0x20,0xe8,0xfa,'s','t','z',0xff,0xed,'r',
0xe9,0xf9,0xe0,0xa3,'$',0xe3,0xf5,0xb7,0xf2,0xe8,0xec,0xb0,0xe7,0xbb,'^','#',
0xe0,0xe8,0xe2,0xe9,0xef,0xc3,0xd5,0xc7,0xf4,0xfb,0xe7,0xeb,0xea,0xf9,0xee,'#',
0xa1,0xbf,0xfc,0xe7,'$',0xaa,0xba,0xd1,0xf1,0xe8,0xe0,0xe1,0xe9,0xed,0xf3,0xfa,
0xc1,0xc0,0xc8,0xcd,0xcf,0xd3,0xd2,0xda,0xe6,0xc6,'d','D',0xf8,0xd8,0xfe,0xee};
static void DrawBWUtf8Char(byte col,byte line,char code){
const char *Such;
static char utf8lead; /* for simplicity, expect only two-byte UTF-8 */
if ((code&0xE0)==0xC0 && !utf8lead) { /* UTF-8 lead */
utf8lead=code;
return; /* swallow code, expect nect "character" */
}
if (utf8lead) {
if ((code&0xC0)==0x80) code=utf8lead<<6|code&0x3F;
utf8lead=0;
}
Such=strchr(Ansitab,code); /* Ersatz gefunden? */
if (Such) code=(char)(Such-Ansitab)+0x20; /* umwandeln in VT-Font-Zeichen (?) */
XCopyPlane(display,vtfont1,window,MainGC,
0,(code+128)*FONTH,FONTW,FONTH,
(short)col*FONTW,(short)line*FONTH,1);
}
static const byte LandMap[8]={2,7,3,0,1,8,6,0};
static int Translate(byte c,word FlagReg,word Font){
const char *ChChar;
int code;
static const char ErsetzKette[16]={
0x40, 0x60, 0x7B, 0x23, 0x24, 0x40, 0x40, 0x40,
0x7C, 0x7D, 0x7E, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F};
code=(short)c; /* Standardfall */
if (FlagReg & (fBlink|fQuiz)){
return 32; /* Leerzeichen ausgeben */
}
if (FlagReg & fGraf){
if ((code>=32) && (code<64)) code=(int)c-128-32;
if ((code>=96) && (code<128)) code=(int)c-128-96+32;
/* Negative Codes für Grafiksymbole */
if (FlagReg & fSep) code+=64; /* Separierte Grafik */
return code;
}
if ((Font>=2) && (Font<14)){
/* 12 = reserve I, 13 = reserve II */
ChChar=(byte *)memchr(ErsetzKette,c,16); /* in Ersetzkette suchen */
if (ChChar){ /* Gefunden ? */
code=((Font+6) <<4) + (ChChar-ErsetzKette);
/* printf("Zeichen-Ersetzung! Zeichen-Nummer %u, neuer Code %d\n",
ChChar-ErsetzKette,code);*/
return code;
/* Code im Bereich 80..13F */
}
return code;
}
if ((Font>=14) && (Font<16) && !(FlagReg & fLatin)){
return (Font-14)*96+0x140+(c-32);
/* Kyrillisch, arabisch im Bereich 140..1FF */
}
return code;
}
static void DrawVT(Pixmap drw, word Flags, word Font){
GC gc; /* GC für Vorder- und Hintergrundfarbe */
XGCValues xgcv;
word FlagReg;
byte lines,line,col;
auto byte vfarbe=7,vfarbc=7,hfarbe=0,hfarbc=0; /* Farbbytes und Caches */
auto bool Mixc=false;
bool LineContainsDblH;
byte OldByte,CurByte;
/* Verschachtelte Hilfsfunktionen */
void WriteChar(int code){
/* Folgender Code vermeidet unnötige XSet{Fore|Back}ground's */
if (Flags & fBunt){ /* Nur wenn farbig! */
if (FlagReg & fMix){ /* fMix gesetzt? */
if (!Mixc){ /* fMix verändert? */
Mixc=true;
XSetBackground(display,gc,Farbe[8]); /* Transparente Farbe */
/* printf("Hier vorbei [8]\n");*/
}
}else{ /* fMix gelöscht? */
if ((hfarbe != hfarbc) || (Mixc)){ /* Neue HFarbe oder Mix war gesetzt? */
Mixc=false;
XSetBackground(display,gc,Farbe[hfarbc=hfarbe]); /* Farbe setzen */
}
}
if (vfarbe != vfarbc) /* Vordergrund geändert? */
XSetForeground(display,gc,Farbe[vfarbc=vfarbe]); /* Farbe setzen */
}
/* Jetzt das Zeichen ausgeben */
if (FlagReg & fDblH){
XCopyPlane(display,vtfont2,drw,gc,
0,(code+128)*FONTH*2,FONTW,FONTH*2,
col*FONTW,line*FONTH,1);
}else{
XCopyPlane(display,vtfont1,drw,gc,
0,(code+128)*FONTH,FONTW,FONTH,
col*FONTW,line*FONTH,1);
if (LineContainsDblH)
XCopyPlane(display,vtfont1,drw,gc,
0,(32+128)*FONTH,FONTW,FONTH,
col*FONTW,(line+1)*FONTH,1);
}
} /* Prozedur WriteChar */
void WriteCharTranslate(byte c){
WriteChar(Translate(c,FlagReg,Font)); /* Jetzt ausgeben */
}
bool CanDblH(){ /* Doppelte Zeichenhöhe erlaubt? */
return(line>0 && line<23 && Flags&fDblH);
}
void SetMix(){
if (Flags&fMix || Header.W.CBits&0x60)
FlagReg|= fMix; /* InBox aus, also Mix ein */
}
/* Gerätekontexte erstellen */
/* hgc=XCreateGC(display,drw,0,NULL);*/
xgcv.subwindow_mode=IncludeInferiors;
xgcv.graphics_exposures=false;
xgcv.foreground=Farbe[vfarbe];
xgcv.background=Farbe[hfarbe];
gc=XCreateGC(display,drw,
GCForeground|GCBackground|GCSubwindowMode|GCGraphicsExposures,&xgcv);
/* Land-Variable aufbereiten */
if (Font==0)
Font=LandMap[(Header.W.CBits >> 12) & 7];
/* Wenn default(Auto-detect) dann aus den C-Bits*/
lines=GetLines(Flags);
/* mit Schleifen loslegen */
LineContainsDblH=false;
for (line=0; line<lines; line++){
if (LineContainsDblH){ /* hier: vorhergehende Linie! */
LineContainsDblH=false; /* nichts weiter tun, dann nächste Zeile */
}else{
LineContainsDblH=CanDblH() && memchr(Page.L[line],13,40);
/* Stellen; 13 ist Code für DblH */
FlagReg=0;
SetMix(); /* auf Null oder Mixbetrieb setzen */
hfarbe=0; /* Hintergrundfarbe */
vfarbe=7;
OldByte=32; /* auch das vorhergehende Byte zu Space machen */
for(col=0; col<40; col++){
CurByte=Page.L[line][col]; /* aus der Seite holen */
/* Sofort wirksame Schalter auswerten *//* Ein Zeichen ausgeben */
switch (CurByte) {
case 28:
hfarbe=0;
break;
case 29:
hfarbe=vfarbe;
break;
}
if ((CurByte<0x20) && ((Flags & fDebug)==0)){
if ((FlagReg & fHold) && (FlagReg & fGraf) &&
/* HoldGrafix und GrafikFlag */
((OldByte&0xbf)>=0x20) && ((OldByte&0xbf)<0x40)){
/* Zeichen mit Codes 0x20..0x3f sowie 0x60..0x7f */
WriteCharTranslate(OldByte);
}else{
WriteCharTranslate(32); /* Leerzeichen */
/* if ((FlagReg & fHold) && (FlagReg & fGraf))
printf("Haltegrafik+Grafikflag, aber OldByte=%hX und geANDed=%hX\n",
OldByte,OldByte&0xbf);*/
}; /* Auf jeden Fall OldByte behalten! */
}else{
WriteCharTranslate(CurByte);
OldByte=CurByte;
}
/* Nachher wirksame Schalter */
if (CurByte<0x20)
FlagReg&= ~fQuiz; /* Secret aus */
switch (CurByte){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
FlagReg&= ~fGraf; /* Grafikflag aus */
vfarbe=CurByte;
break;
case 8:
if (!(Flags & fBlink)) /* wenn "blinkende Zeichen AUS" */
FlagReg|= fBlink; /* Blinken ein */
break;
case 9:
FlagReg&= ~fBlink; /* Blinken aus */
break;
case 10:
SetMix();
break;
case 11:
FlagReg&= ~fMix; /* InBox ein */
break;
case 12:
FlagReg&= ~fDblH; /* DoubleHeigth aus */
break;
case 13:
if (CanDblH()) /* wenn nicht verboten */
FlagReg|= fDblH; /* DoubleHeigth ein */
break;
case 14:
FlagReg&= ~fShift; /* Shift aus (später verwendet) */
break;
case 15:
FlagReg|= fShift; /* Shift ein (später verwendet) */
break;
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
FlagReg|= fGraf; /* Grafikflag ein */
vfarbe=(CurByte & 7);
break;
case 24:
if (!(Flags & fQuiz)) /* wenn "Quiz-Zeichen AUS" */
FlagReg|= fQuiz; /* Secret ein */
break;
case 25:
FlagReg&= ~fSep; /* separated aus */
break;
case 26:
FlagReg|= fSep; /* separated ein */
break;
case 27:
FlagReg^= fLatin; /* Sprachumschaltung Lateinisch */
break;
case 30:
FlagReg|= fHold; /* holdgrafix ein */
break;
case 31:
FlagReg&= ~fHold; /* holdgrafix aus */
break;
}
} /* 1 Zeile */
if (Flags & f41){
hfarbe=0; /* schwarz */
WriteCharTranslate(32); /* noch ein Space dran */
}
} /* if-else */
} /* for */
/* Kontexte auflösen */
XFreeGC(display,gc);
}
static void InvokeExpose(){
XClearArea(display,window,0,0,0,0,true);
}
static void Page2Asc(char *dest, word Flags, word Font,
short left, short top, short right, short bottom){
word FlagReg;
byte lines,line,col;
bool LineContainsDblH;
byte CurByte;
/* Verschachtelte Hilfsfunktionen */
void WriteChar(int code){
/* Jetzt das Zeichen ausgeben */
if ((col>=left) && (col<=right) &&
(line>=top) && (line<=bottom)){
if ((code>=32) && (code<256)){
char c=Ansitab[code-32]; /* nach ANSI gewandeltes Zeichen */
if (c&0x80) {
*dest++=(byte)c>>6|0xC0; /* mache UTF-8 daraus */
*dest++=c&0x3F|0x80;
}else *dest++=c;
}else{
*dest++=' '; /* sonst Leerzeichen (puh, UTF-8 kann doch mehr!!) */
}
if (col==right){
*dest++='\n'; /* Newline dranhängen */
}
}
} /* Prozedur WriteChar */
void WriteCharTranslate(byte c){
WriteChar(Translate(c,FlagReg,Font)); /* Jetzt ausgeben */
}
bool CanDblH(){ /* Doppelte Zeichenhöhe erlaubt? */
return(line>0 && line<23 && Flags&fDblH);
}
/* Land-Variable aufbereiten */
if (!Font) Font=LandMap[(Header.W.CBits >> 12) & 7];
/* Wenn default(Auto-detect) dann aus den C-Bits*/
lines=GetLines(Flags);
/* mit Schleifen loslegen */
LineContainsDblH=false;
for (line=0; line<lines; line++){
if (LineContainsDblH){ /* hier: vorhergehende Linie! */
LineContainsDblH=false; /* nichts weiter tun, dann nächste Zeile */
}else{
FlagReg=0;
for(col=0; col<40; col++){
CurByte=Page.L[line][col]; /* aus der Seite holen */
/* Ein Zeichen ausgeben */
if (CurByte<0x20){
WriteCharTranslate(32); /* Leerzeichen */
}else{
WriteCharTranslate(CurByte);
}
/* Nachher wirksame Schalter */
if (CurByte<0x20)
FlagReg&= ~fQuiz; /* Secret aus */
switch (CurByte){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
FlagReg&= ~fGraf; /* Grafikflag aus */
break;
case 12:
FlagReg&= ~fDblH; /* DoubleHeigth aus */
break;
case 13:
if (CanDblH()){ /* wenn nicht verboten */
FlagReg|= fDblH; /* DoubleHeigth ein */
LineContainsDblH=true;
}
break;
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
FlagReg|= fGraf; /* Grafikflag ein */
break;
case 24:
if (!(Flags & fQuiz)) /* wenn "Quiz-Zeichen AUS" */
FlagReg|= fQuiz; /* Secret ein */
break;
case 27:
FlagReg^= fLatin; /* Sprachumschaltung Lateinisch */
break;
}
} /* 1 Zeile */
if (Flags & f41){
WriteCharTranslate(32); /* noch ein Space dran */
}
} /* if-else */
} /* for */
*dest++='\0'; /* Abschluß-Null dranbammeln */
}
/* Such-Definitionen */
#define BACKWARD (bit 0)
#define NOCASESENS (bit 1)
#define NOHIGHCHARS (bit 2)
#define USEREGEXP (bit 3)
static void PrepareString(char *Str, word How){
/* Umkodierungstabelle für "hohe ASCIIs" */
static const char AnsiUmcodTab[64]={
'A','A','A','A','A','A','A','C','E','E','E','E','I','I','I','I',
'D','N','O','O','O','O','O',0xd7,'O','U','U','U','U','Y','P','s',
'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i',
'd','n','o','o','o','o','o',0xf7,'o','u','u','u','u','y','p','y'};
if (Str){ /* Wenn nicht NULL */
for(;*Str;Str++){ /* String bis zum \0-Zeichen parsen */
if ((How & NOHIGHCHARS) && ((byte)*Str >0xc0))
*Str=AnsiUmcodTab[*Str-0xc0];
if (How & NOCASESENS)
*Str=toupper(*Str);
}
}
}
/****************************/
/* Datei browsen nach Index */
/****************************/
#ifdef motorola
static void byteswap(short *i){
byte t;
t=((byte *)i)[0];
((byte *)i)[0]=((byte *)i)[1];
((byte *)i)[1]=t;
}
static void longswap(long *i){
byte t;
t=((byte *)i)[0];
((byte *)i)[0]=((byte *)i)[3];
((byte *)i)[3]=t;
t=((byte *)i)[1];
((byte *)i)[1]=((byte *)i)[2];
((byte *)i)[2]=t;
}
#endif
/* #define INDIZES 2048 /* Maximale Dateigröße in Kilobytes */
#ifdef motorola
#define MAGIC 0xd6d4e8e6 /* ('VThf'^0x80808080) */
#define MAGIC_HS 0xd6d4e8f3 /* ('VThs'^0x80808080) */
#else
#define MAGIC 0xe6e8d4d6 /* 'VThf' mit gesetzten Bit7 rückwärts */
#define MAGIC_HS 0xf3e8d4d6 /* 'VThs' mit gesetzten Bit7 rückwärts */
#endif
#define INDEXPP 249 /* Indexeinträge pro führender Index-Seite */
#define INDEXPP_HS 255 /* im h#s-Format passen mehr Einträge 'rein */
static tIdx *Index=NULL; /* der Index über die Datei */
static word IndexCnt=0;
static FILE *VTFile=NULL; /* Aktueller Datei-Pointer */
static char FName[256]; /* aktueller Dateiname (undefiniert) */
static void CloseFile(){
if (VTFile){
fclose(VTFile);
VTFile=NULL;
}
}
static int LoadFile(char *FileName){ /* Tatsächlicher Dateiname wird rückgeschrieben! */
word i;
long hdr;
char CatBuf[256];
tIdx *NewIndex;
FILE *VTTemp; /* solange bis Datei OK ist */
void ReadNewIndex(word indexpp){
/* Routine zum Einlesen eines New-Format-VT-Files */
/* Die Kennung sei bereits vorher gecheckt! (schneller) */
word i,i1;
i=(IndexCnt+indexpp) / (indexpp+1); /* Reservierte Seiten am Beginn */
for (i1=0; i1<i; i1++)
Index[i1].L=MAGIC; /* Erste Indexeinträge sperren */
for (; i<IndexCnt; i+=i1){
fread(&Page,sizeof(tPage),1,VTFile);
/* if (Page.B[0]<0x80)
printf("Warnung: VT-Datei-Index inkonsistent oder zu kurz (evtl. Bug)!\n");*/
i1=IndexCnt-i;
if (i1>indexpp) i1=indexpp;
memcpy(&Index[i],&Page.B[4],sizeof(tIdx)*i1); /* Indizes kopieren */
}
}
if (!FileName) return 9; /* NULL übergeben? - Fehler! */
strcpy(CatBuf,FileName); /* die Katze verwurschteln
(oder kann jemand "strcpy" richtig aussprechen:-)*/
VTTemp=fopen(CatBuf,"rb");
if (!VTTemp){ /* Bei Fehler: Versuche es mit Suffix ".vt" */
strcat(CatBuf,".vt"); /* die Katze aus dem Sack lassen */
VTTemp=fopen(CatBuf,"rb"); /* Zweiter Versuch */
if (!VTTemp){
return errno; /* Jetzt weiß "LoadFile()" nicht mehr weiter */
}
}
fread(&hdr,4,1,VTTemp); /* Kennung lesen */
fseek(VTTemp,0L,SEEK_END); /* Größe feststellen */
i=ftell(VTTemp) / 1024; /* Erforderliche Größe */
if (i==0) return 11; /* Null ist falsch (Ungültiges Format) */
rewind(VTTemp);
NewIndex=(tIdx *)realloc(Index,i*sizeof(tIdx));
/* Dieser Heapbefehl trifft den Nagel auf dem Kopf */
if (!NewIndex){ /* Kein (neuer) Speicher? */
return 8; /* alten Zustand beibehalten! */
}
Index=NewIndex; /* Neuer Index-Zeiger */
IndexCnt=i; /* in globale Variable schreiben */
CloseFile(); /* Jetzt erst alte Datei zumachen */
VTFile=VTTemp; /* neuen Zeiger zuweisen */
strcpy(FName,CatBuf); /* neuer Name - alles roger */
/* printf("'%s' geoeffnet, Laenge: %hu Seiten, Datei-Typ: ",FileName,IndexCnt); */
switch (hdr){
case MAGIC: /* das Haftmann-Fiedler-Format */
/* printf("VThf\n"); */
ReadNewIndex(INDEXPP);
break;
case MAGIC_HS: /* das etwas speichersparsamere Haftmann-F. */
/* printf("VThs\n"); */
ReadNewIndex(INDEXPP_HS);
break;
case 0x20202020: /* Uralt-Format (immer noch von Fiedel benutzt!) */
/* printf("uralt\n"); */
for (i=0; i<IndexCnt; i++){
fread(&Page,1024,1,VTFile);
Index[i].W.Page=
((Page.T.Page100 & 7) <<8)|
((Page.T.Page10 & 0xf) <<4)|
(Page.T.Page1 & 0xf);
Index[i].W.Subp=
((Page.T.Subp1000 & 3) <<12)|
((Page.T.Subp100 & 0xf) <<8)|
((Page.T.Subp10 & 7) <<4)|
(Page.T.Subp1 & 0xf);
}
break;
default: /* Reine Seitennummer: "File of TPage" */
/* printf("traditionell\n"); */
for (i=0; i<IndexCnt; i++){
fseek(VTFile,(long)i*1024,SEEK_SET);
fread(&Index[i],sizeof(tIdx),1,VTFile);
}
}
#ifdef motorola
if (hdr!=0x20202020){ /* im Falle "Uralt" stimmt der Index schon! */
for (i=0; i<IndexCnt; i++){
byteswap(&Index[i].W.Page);
byteswap(&Index[i].W.Subp); /* Gesamten Index "behandeln" */
}
}
#endif
return 0; /* fehlerfrei */
}
/*****************************/
/* Seite auswählen und laden */
/*****************************/
/* Suchmodi, wenn angegebene Seite nicht gefunden: */
#define EQUALPAGE 1
/* Seite und Unterseite muß stimmen */
#define NEXTPAGE 2
/* Gehe zur nächsten Seite, minimale Unterseite */
#define PREVPAGE 3
/* Gehe zur vorherigen Seite, minimale Unterseite */
#define NEXTSUB 4
/* Gehe zur nächsten Unterseite mit Wrap-Around, Seite muß stimmen */
#define PREVSUB 5
/* Gehe zur vorherigen Unterseite mit Wrap-Around, Seite muß stimmen */
#define NEXTSUBPAGE 6
/* Gehe zur nächsten Unterseite, wenn nicht dann zur nächsten Seite */
#define PREVSUBPAGE 7
/* Gehe zur vorherigen Unterseite, wenn nicht dann zur vorherigen Seite */
#define ADDNEXTPAGE 10
/* wie NEXTPAGE, jedoch nicht die angegebene Seite nehmen */
#define SUBPREVPAGE 11
/* wie PREVPAGE, jedoch nicht die angegebene Seite nehmen */
#define ADDNEXTSUB 12
/* wie NEXTSUB, jedoch nicht die angegebene Unterseite nehmen */
#define SUBPREVSUB 13
/* wie PREVSUB, jedoch nicht die angegebene Unterseite nehmen */
#define ADDNEXTSUBPAGE 14
/* wie NEXTSUBPAGE, jedoch nicht die angegebene Seite nehmen */
#define SUBPREVSUBPAGE 15
/* wie PREVSUBPAGE, jedoch nicht die angegebene Seite nehmen */
/* Videotext-Seite in "gewöhnliches Format" wandeln */
static word UsualPage(word Page){
Page&=0x7FF; /* Obere Bits löschen */
if (Page<0x100) return Page+0x800; /* 000..0FF --> 800..8FF */
return Page; /* alles andere so lassen */
}
/* Videotext-Index in String wandeln */
static char *Idx2Str(tIdx Index){ /* Nicht reentrant! */
static char Puffer[10];
sprintf(Puffer,"%03hX/%02hX",
UsualPage(Index.W.Page),Index.W.Subp & 0x3f7f);
return Puffer;
}
/* Seite auswählen, liefert Index-Nummer oder FFFF bei Fehler */
static word SelectPage(word DP,word DS,byte SearchMode){
word i,idx; /* Indizes */
#define CP ((Index[i].W.Page) & 0x7ff)
#define CS ((Index[i].W.Subp) & 0x3f7f)
dword BB,OO; /* BB=Bester Abstand, OO=Abstand */
auto word f01=0; /* Entscheidet, ob GLEICHE Seite gefunden wird */
BB=OO=0xffffffff;
idx=0xffff; /* ungültigen Indexeintrag erzeugen */
DP&=0x7FF;
DS&=0x3F7F;
/*printf("SelectPage: %hx/%hx Suchmodus %hd, ",
UsualPage(DP),DS,SearchMode);*/
if (SearchMode & 8){
SearchMode&=~8;
f01++;
}
/* if (!Index){
printf("SelectPage aufgerufen mit Index=NULL!\n");
} */
for (i=0; i<IndexCnt; i++){
if (CP < 0x1000){ /* Nur mit gültigen Einträgen arbeiten */
/* Ziel ist es, BP bzw. BS zu minimieren und bei Erfolg wird "idx" gesetzt */
switch (SearchMode){
case NEXTPAGE:
OO=(((dword)CP-DP-f01)<<16)+(CS-0);
/* Vorwärts, DesiredSub verwerfen */
break;
case PREVPAGE:
OO=(((dword)DP-CP-f01)<<16)+(CS-0);
/* Rückwärts & Vorwärts, DesiredSub verwerfen */
break;
case NEXTSUB:
if (CP==DP) /* Gleiche Seite? */
OO=(dword)CS-DS-f01; /* Vorwärts */
break;
case PREVSUB:
if (CP==DP) /* Gleiche Seite? */
OO=(dword)DS-CS-f01; /* Rückwärts */
break;
case NEXTSUBPAGE:
OO=(((dword)CP<<16)+CS)-(((dword)DP<<16)+DS)-f01;
/* Vorwärts, Abstand als dword */
break;
case PREVSUBPAGE:
OO=(((dword)DP<<16)+DS)-(((dword)CP<<16)+CS)-f01;
/* Rückwärts, Abstand als dword */
break;
case EQUALPAGE:
if ((DP==CP) && (DS==CS))
OO=0; /* Nur wenn gleich den Index setzen */
break;
} /* switch */
if (OO<BB){ /* "Bessere" Seite? */
BB=OO; /* merken */
idx=i; /* Heißen Index merken */
}
} /* if */
} /* for */
/*printf("Ergebnis %s Index %hu Abst.%X\n",(idx==0xffff)?"Nicht gefunden":
Idx2Str(Index[idx]),idx,BB);*/
return idx;
}
/* Seite.Zeile[24] im Speicher mit "Buttonleiste" versehen */
static void PatchPage(word Flags){
word PageOf(word Page, byte Mode){
Page=Index[SelectPage(Page,0,Mode)].W.Page; /* überschreibend */
return UsualPage(Page);
}
sprintf((char *)&Page.L[24],
"\4\x1d\7(B) \7\x1d\4%03hX %03hX << >> %03hX %03hX \4\x1d\7(F) ",
PageOf(Header.W.Page&0x700,NEXTPAGE), /* zum Hunderter */
PageOf(Header.W.Page, SUBPREVPAGE), /* Seite Ab */
PageOf(Header.W.Page, ADDNEXTPAGE), /* Seite Auf */
PageOf((Header.W.Page+0x100)& 0x700,NEXTPAGE)); /* zum nächsten Hunderter */
}
/* Word- und Long-Angaben korrekt ausrichten und Headerbytes extrahieren */
/* Header auf magenta Spaces setzen */
static void SetupPage(){
#ifdef motorola
byteswap(&Page.H.W.Page);
byteswap(&Page.H.W.Subp);
byteswap(&Page.H.W.CBits);
longswap(&Page.T.MsDosTime); /* Einlesezeitpunkt, zeitzonenabhängig */
byteswap(&Page.T.CRC); /* Prüfsumme über diese Seite für Fehlerkorrektur */
byteswap(&Page.T.Mirror); /* Adresse einer Spiegelseite während des Einlesens */
longswap(&Page.T.UnixTime); /* Einlesezeitpunkt, Universal Time */
#endif
memcpy(&Header,&Page.H,sizeof(tHeader)); /* Header auslagern */
Page.B[0]=5; /* magenta (also lila) */
memset(&Page.B[1],' ',6);
}
/* Seite auswählen und laden */
/* sowie oben und unten "dekorieren" */
/* Ist Index=NULL dann wird die Begrüßungsseite geladen */
/* sowie das Ergebnis ebenfalls auf "Not found" 0xffff gesetzt */
static word SelectLoadPage(tIdx *Desire,byte SearchMode,word Flags){
word i; /* Lade-Index */
if (!Index){
memcpy(&Page,sPagedefault[country],1024);
SetupPage(); /* kopieren und Header extrahieren */
return 0xffff; /* "Fehler" melden */
}
/* char CatBuf[8];*/
if (SearchMode==0) /* Defaultwert? */
SearchMode=NEXTSUB; /* Default-Suchmodus */
i=SelectPage(Desire->W.Page,Desire->W.Subp,SearchMode);
if (i!=0xffff){ /* Suche erfolgreich */
fseek(VTFile,(long)i*1024,SEEK_SET);
if (fread(&Page,sizeof(Page),1,VTFile)!=1){
/* printf("Fehler beim Lesen der VT-Datei an Position %hu Kilobytes",i); */
}else{
SetupPage();
Desire->L=Header.L; /* Ergebnis rückschreiben */
memcpy(&Page.B[1],Idx2Str(*Desire),6); /* links oben hinpinseln */
if (Flags & fBBar)
PatchPage(Flags); /* Button-Bar anfügen */
/* if (Desire->L!=Index[i].L){
printf("Warnung: VT-Datei inkonsistent oder Programm-Bug!\n");
}*/
}
}else{
/* printf("Seite %s im Suchmodus %hd nicht gefunden\n",
Idx2Str(*Desire),SearchMode); */
}
return i;
}
/*********************************************************/
/* History, ein Objekt? zur Verwaltung der History-Liste */
/*********************************************************/
#define HISTSIZE 100
/* merkt sich maximal 100 Einträge */
#define History_CanFwd (HistPtr<HistLen)
#define History_CanBack (HistPtr>=2)
#define History_FwdIdx (Hist[HistPtr])
#define History_BackIdx (Hist[HistPtr-2])
static tIdx Hist[HISTSIZE]; /* Das Gedächtnis */
static word HistPtr,HistLen; /* Zwei Arrayzeiger */
static void History_Init(){ /* Ein Objekt emulieren? */
HistPtr=HistLen=0;
}
static void History_Append(tIdx *That){/* geht immer, notfalls wird das Array verschoben */
if ((HistPtr>0) &&
(Hist[HistPtr-1].L==That->L)){
/* printf("Nichtstun\n"); */
return; /* Nichtstun */
}
if ((History_CanBack) && /* Zurück denkbar? */
(Hist[HistPtr-2].L==That->L)){/* Dort liege diese Seite bereits? */
/* printf("Zurueck\n"); */
--HistPtr;
}else{
if (HistPtr>=HISTSIZE){ /* Überlauf? */
memmove(&Hist[0],&Hist[1],sizeof(tIdx)*(--HistPtr));
/* Jeden Index vorschieben */
/* printf("Pufferschieben\n"); */
}
if ((HistPtr<HistLen) &&
(Hist[HistPtr].L==That->L)){
HistPtr++; /* Vorwärtsschritt zu gleicher Seite */
/* printf("Vorwärts\n"); */
}else{
Hist[HistPtr++]=*That;
HistLen=HistPtr; /* Länge kürzen oder verlängern */
/* printf("Neue Seite\n");*/
}
}
}
word MenuFlags, MenuFont; /* Haupt-Einstell-Organe */
tIdx CurIdx; /* Momentaner Index */
static int OpenVTFile(char *FileName){
int Returncode;
Returncode=LoadFile(FileName); /* Öffnen, parsen */
if (!Returncode){ /* Erfolgreich? */
History_Init(); /* Geschichte totlegen */
CurIdx.W.Page=0x100;
CurIdx.W.Subp=0; /* Definierte Startwerte */
SetWindowTitle(FName); /* Fenstertitel anpassen */
SelectLoadPage(&CurIdx,NEXTSUBPAGE,MenuFlags);
}
return Returncode;
}
/***************************************************************/
/* "unsichtbare" Unterfenster erstellen und mit Link verbinden */
/***************************************************************/
/* Basisroutine, Rechteckangaben in Zeicheneinheiten */
static void CreateInputWindow(byte x,byte y,byte b,byte h, tIdx *Indexp){
Window sw;
tIdx *hilfidx;
XSetWindowAttributes xswa;
/* printf("CreateInputWindow(%hd,%hd,%hd,%hd; %s)\n",
x,y,b,h,Idx2Str(*Indexp));*/
xswa.cursor=idc_hand;
sw=XCreateWindow(display,window,
(short)x*FONTW,(short)y*FONTH,(word)b*FONTW,(word)h*FONTH,0,
0, InputOnly, CopyFromParent, CWCursor, &xswa);
XSaveContext(display,sw,Linklist,(caddr_t) Indexp);
/* XFindContext(display,sw,Linklist,(caddr_t *) &hilfidx);
if (hilfidx!=Indexp)
printf("Nicht korrekt gespeichert!\n");*/
/* XMapWindow(display,sw);*/
}
/*************************/
/* Link-Fenster erzeugen */
/*************************/
/* Spezialbehandlungsroutine für "forward" und "back"-Knöpfe */
static void MkLinkBF(word Flags){
if (History_CanBack){
CreateInputWindow(3,24,3,1,&History_BackIdx);
/* printf("Back=%s\n",Idx2Str(History_BackIdx));*/
}
if (History_CanFwd){
CreateInputWindow(35,24,3,1,&History_FwdIdx);
/* printf("Fwd=%s\n",Idx2Str(History_FwdIdx));*/
}
}
/* eine Rechteckliste erzeugen */
/* anhand 3stelliger Zahlen und ">"..">>>", "<".."<<<" */
static void MkLinkList(word Flags){
word FlagReg;
byte lines,line,col;
bool LineContainsDblH;
byte CurByte;
byte Ziffern,Winkel;
char cWinkel; /* WELCHER Winkel */
word Seite;
/* Verschachtelte Hilfsfunktionen */
bool CanDblH(){ /* Doppelte Zeichenhöhe erlaubt? */
return((line) && (line!=lines-1) && (Flags & fDblH));
}
void TestZiffern(){
word i;
if ((Ziffern==3) && (Seite>=0x100) && (Seite<=0x899) &&
((i=SelectPage(Seite&0x7FF,0,NEXTSUB))!=0xffff) &&
(Index[i].L!=CurIdx.L)){ /* nicht zufällig dieselbe Seite? */
CreateInputWindow(col-Ziffern,line,
Ziffern,(FlagReg & fDblH)? 2 : 1,
&Index[i]);
}
Ziffern=0; /* Zähler erneut scharfmachen */
Seite=0;
}
void TestWinkel(){
word i;
/* printf("TestWinkel\n");*/
if ((Winkel>=1) && (Winkel<=3) && /* Hier: 1, 2 oder 3 Winkel zulassen */
((i=SelectPage(Header.W.Page,Header.W.Subp,
(cWinkel=='>')? ADDNEXTSUBPAGE : SUBPREVSUBPAGE))!=0xffff) &&
(Index[i].L!=CurIdx.L)){ /* nicht zufällig dieselbe Seite? */
CreateInputWindow(col-Winkel,line,
Winkel,(FlagReg & fDblH)? 2 : 1,
&Index[i]);
}
Winkel=0; /* Zähler erneut scharfmachen */
cWinkel='\0';
}
/* printf("MkLinkList\n");*/
lines=GetLines(Flags);
/* mit Schleifen loslegen */
LineContainsDblH=false;
for (line=0; line<lines; line++){
if (LineContainsDblH){ /* hier: vorhergehende Linie! */
LineContainsDblH=false; /* nichts weiter tun, dann nächste Zeile */
}else{
FlagReg=0; Ziffern=Winkel=0; Seite=0; cWinkel='\0';
for (col=0; col<40; col++){ /* Neue Zeile, neues Glück */
CurByte=Page.L[line][col]; /* aus der Seite holen */
if (CurByte <0x20)
FlagReg&=~fQuiz;
switch (CurByte){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
FlagReg&= ~fGraf; /* Grafikflag aus */
break;
case 12:
FlagReg&= ~fDblH; /* DoubleHeigth aus */
break;
case 13:
if (CanDblH()) /* wenn nicht verboten */
FlagReg|= fDblH; /* DoubleHeigth ein */
LineContainsDblH=true;
break;
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
FlagReg|= fGraf; /* Grafikflag ein */
break;
case 24:
if (!(Flags & fQuiz)) /* wenn "Quiz-Zeichen AUS" */
FlagReg|= fQuiz; /* Secret ein */
break;
case 27:
FlagReg^= fLatin; /* Sprachumschaltung Lateinisch */
break;
} /* case */
if (FlagReg & (fQuiz|fGraf))
CurByte=0x20;
if ((CurByte>=0x30) && (CurByte<=0x39)){ /* Ziffer? */
Ziffern++; /* Eine Ziffer mehr */
Seite=(Seite << 4)+(CurByte-0x30); /* Seitennummer zusammenschieben */
}else TestZiffern();
switch ((char)CurByte){
case '>':
if (cWinkel=='>'){
Winkel++;
}else{
cWinkel='>';
Winkel=1;
}
break;
case '<':
if (cWinkel=='<'){
Winkel++;
}else{
cWinkel='<';
Winkel=1;
}
break;
default:
TestWinkel();
}
} /* for col */
TestZiffern();
TestWinkel();
} /* else */
} /* for line */
}
/******************/
/* GIF-Foto-Schuß */
/******************/
static bool SchiessEinFoto(FILE *f, Pixmap hbm,
word width, word height, char *comment){
XImage *image;
byte GetPixelValue(short x, short y){
long pixval;
byte b;
/* if (x==0) printf("%hd ",y); /* Scan-Zeie */
pixval=XGetPixel(image,x,y); /* Pixel-Wert erhalten */
for (b=0; b<9; b++){ /* Index rückgewinnen durch Suchen */
if (Farbe[b]==pixval) break; /* Gefunden; b=Index */
}
return b; /* ausgeben */
}
/* printf("Start GetImage \n",image); */
image=XGetImage(display,hbm,0,0,width,height,AllPlanes,ZPixmap);
/* printf("Image besorgt, hat Adresse %p.\n",image); */
WriteGIF89(f,width,height,4,RGBWerte,8,comment,GetPixelValue);
/* printf("Fertig.\n",image); */
XDestroyImage(image);
}
/**************************/
/* Benutzer-Schnittstelle */
/**************************/
static XPoint P1,P2,Pn; /* Punkt, wo Maustaste gedrückt bzw. losgelassen */
static bool moved; /* True=Markierung auf Bildschirm vorhanden */
static byte autoredrawlock=0; /* <>0=Kein Blinken und keine rollenden Unterseiten */
static bool AppDone=false;
static bool HelpActive=false;
static tIdx Suchstart;
static char Suchkette[40];
static word SuchArt=6; /* vorwärts/rückwärts, mit RegExp oder ohne usw. */
static word KindOfInput; /* Eingabezeilen-Flag */
static char ClipBuff[42*25+1];
static char FindBuff[41*24+1];
/* Suchen bzw. Weitersuchen */
static bool Suche(SuchArt){ /* liefert false wenn nichts gefunden */
XDefineCursor(display,window,idc_wait); /* Sanduhr (wäre das unter MS-Windows) */
XFlush(display);
do{
SelectLoadPage(&CurIdx,
(SuchArt & BACKWARD)? SUBPREVSUBPAGE : ADDNEXTSUBPAGE, MenuFlags);
if (CurIdx.L==Suchstart.L){
XDefineCursor(display,window,idc_arrow); /* normaler Mauspfeil */
return false;
}
Page2Asc(FindBuff,MenuFlags,MenuFont,0,0,39,23);
PrepareString(FindBuff,SuchArt);
}while (!strstr(FindBuff,Suchkette)); /* bis ein "Matsch" gefunden wurde */
XDefineCursor(display,window,idc_arrow); /* normaler Mauspfeil */
return true;
}
/**************************/
/* grafische Eingabezeile */
/**************************/
static char Topline[50]; /* Reserve für zu lange Strings lassen */
static byte StartInput,Cursorpos;
static void DrawInputLine(){
byte i;
for (i=0; i<40; i++){
DrawBWUtf8Char(i,0,Topline[i]); /* darstellen der Zeile */
}
}
static void BeginInput(const char *What, const char *Item){
autoredrawlock++; /* Fenster "statisch" machen */
if (What) strncpy(Topline,What,38);
StartInput=strlen(Topline);
Topline[StartInput++]=' '; /* Leerzeichen nach 1. String */
Cursorpos=StartInput;
if (Item){ /* Eingabezeilen-Vorgabe vorhanden? */
strncpy(&Topline[Cursorpos],Item,38-Cursorpos);
Cursorpos+=strlen(Item);
if (Cursorpos>38) Cursorpos=38; /* begrenzen */
}
memset(&Topline[Cursorpos],' ',sizeof(Topline)-Cursorpos);
Topline[Cursorpos]='\x7f'; /* Cursor-Symbol */
DrawInputLine();
}
static void InputChar(char What){ /* 1 Zeichen eingeben, Sonderfall ist '\b' */
if (What=='\b'){
if (Cursorpos==StartInput){
XBell(display,volume);
}else{
DrawBWUtf8Char(Cursorpos,0,Topline[Cursorpos]=' ');
Cursorpos--;
DrawBWUtf8Char(Cursorpos,0,Topline[Cursorpos]='\x7f');
}
}else{
if (Cursorpos==39){
XBell(display,volume);
}else{ /* Ansi-Zeichen abspeichern */
DrawBWUtf8Char(Cursorpos,0,Topline[Cursorpos]=What);
Cursorpos++;
DrawBWUtf8Char(Cursorpos,0,Topline[Cursorpos]='\x7f');
}
}
}
#define GetInputChars() (Cursorpos-StartInput)
static char *EndInput(bool NeedRedraw){/* Zeichenkette zurückgeben */
DrawBWUtf8Char(Cursorpos,0,' '); /* Cursor verschwinden lassen */
autoredrawlock--;
if (NeedRedraw) InvokeExpose();/* Bildschirm restaurieren */
Topline[Cursorpos]='\0'; /* Null-terminieren */
return &Topline[StartInput]; /* Zeiger auf Stringanfang */
}
static void UpdateBitmaps(bool); /* solche Prototypen sollten wieder weg */
/*********************************/
/* Maus-Ereignisse und Markieren */
/*********************************/
static void XorRect(Window window){
auto short x=P1.x, y=P1.y;
auto word w=P2.x-P1.x, h=P2.y-P1.y;
/* printf("XorRect(%hd,%hd,%hd,%hd)\n",P1.x,P1.y,P2.x,P2.y);*/
/* Sicherstellen, daß Breite und Höhe positiv */
if (P1.x>P2.x){
w=P1.x-P2.x;
x=P2.x;
}
if (P1.y>P2.y){
h=P1.y-P2.y;
y=P2.y;
}
/* printf("Umgerechnet(%hd,%hd,%hd,%hd)\n",x,y,x+w,y+h);*/
XFillRectangle(display,window,XorGC,x*FONTW,y*FONTH,(w+1)*FONTW,(h+1)*FONTH);
}
static void XorPixmapRect(short bmidx){
if (Pixmaps[bmidx].valid) XorRect(Pixmaps[bmidx].hbm);
}
static void RemoveAnySelection(){
if (moved){
XorRect(window); /* Evtl. noch vorhandenes Rechteck entfernen */
XorPixmapRect(0);
XorPixmapRect(1);
moved=false; /* Cache-Pixmaps nicht vergessen! */
}
}
static void WMLButtonDown(XEvent *event){
/* printf("WMLButtonDown\n");*/
RemoveAnySelection();
P1.x=P2.x=event->xbutton.x/FONTW;
P1.y=P2.y=event->xbutton.y/FONTH;
autoredrawlock++; /* Blinken u.ä. sperren */
}
static void WMMouseMove(XEvent *event){
/* printf("WMMouseMove\n"); */
Pn.x=event->xmotion.x/FONTW; /* Neue Koordinaten */
Pn.y=event->xmotion.y/FONTH;
if (Pn.x<0) Pn.x=0; /* gegen Fenstergrenzen begrenzen */
if (Pn.x>=41) Pn.x=41-1;
if (Pn.y<0) Pn.y=0;
if (Pn.y>=25) Pn.y=25-1;
if ((P2.x!=Pn.x) || /* Änderung der Position von Belang? */
(P2.y!=Pn.y)){ /* (Flackern minimieren) */
if (moved) XorRect(window);
P2=Pn; /* Neuer 2. Punkt */
moved=true; /* Berührt - geführt: fortan Rechteck */
XorRect(window);
}
}
static void WMLButtonUp(XEvent *event){
auto short left=P1.x,top=P1.y,right=P2.x,bottom=P2.y;
tIdx *IdxPtr; /* zur Hilfe ein Zeiger auf einen Index */
XEvent hevent;
/* printf("WMLButtonUp, moved=%s\n",
moved?"true":"false"); */
autoredrawlock--; /* Blinken u.ä. wieder freigeben */
if (moved){ /* Rechtecke nun auch in Cachepixmaps nachführen */
XorPixmapRect(0);
XorPixmapRect(1); /* Cache-Pixmaps nachführen, wenn gültig */
/* "Schnelles Clipboard" (wie sonst in X üblich */
if (P1.x>P2.x){ /* erst wieder die Koordinaten sortieren */
left=P2.x;
right=P1.x;
}
if (P1.y>P2.y){
top=P2.y;
bottom=P1.y;
}
Page2Asc(ClipBuff,MenuFlags,MenuFont,left,top,right,bottom);
/* printf("left=%hd, top=%hd, right=%hd, bottom=%hd\n",
left,top,right,bottom);*/
ClipBuff[strlen(ClipBuff)-1]='\0'; /* Letztes '\n' abschneiden */
/* printf("%s<END>\n",ClipBuff);*/
XSetSelectionOwner(display,XA_PRIMARY,window,event->xbutton.time);
if (XGetSelectionOwner(display,XA_PRIMARY)==window){
/* printf("Selektion erfolgreich gekapert!\n"); */
XStoreBytes(display,ClipBuff,strlen(ClipBuff));
}else{
XBell(display,volume);
RemoveAnySelection();
}
/*
XChangeProperty(display, RootWindowOfScreen(screen),
XA_CUT_BUFFER0,XA_STRING,8,PropModeReplace,ClipBuff,strlen(ClipBuff));
XChangeProperty(display, RootWindowOfScreen(screen),
hevent.type=PropertyNotify;
hevent.xproperty.time=event->xbutton.time;
hevent.xproperty.display=display;
hevent.xproperty.window=RootWindowOfScreen(screen);
hevent.xproperty.atom=XA_CUT_BUFFER0;
hevent.xproperty.state=PropertyNewValue;
if (XSendEvent(display,RootWindowOfScreen(screen),true,0xffffff,&hevent))
printf("Hier vorbei(XSendEvent)\n");
XFlush(display);*/
}else{ /* zur neuen Seite hüpfen? */
if (XFindContext(display,event->xbutton.subwindow,Linklist,
(caddr_t *)&IdxPtr)==0){
/* printf("subwindow im Kontext gefunden\n");*/
if (SelectLoadPage(IdxPtr,EQUALPAGE,MenuFlags)!=0xffff){
CurIdx=*IdxPtr; /* aktualisieren */
UpdateBitmaps(true);
}else{
/* printf("Programm-Bug: Seite %s nicht (mehr) da!\n",Idx2Str(*IdxPtr));*/
}
}else{
/* printf("subwindow im Kontext NICHT gefunden\n");*/
}
}
}
static void WMRButtonDown(XEvent *event){
/*
if (moved){
/* ergänzen: Rechteck ins Clipboard bringen *
XorRect(window); /* Evtl. noch vorhandenes Rechteck entfernen * /
XorPixmapRect(0); /* Cache-Pixmaps Rechteck entfernen * /
XorPixmapRect(1);
moved=false;
}else*/ if (History_CanBack){
CurIdx=History_BackIdx;
SelectLoadPage(&CurIdx,EQUALPAGE,MenuFlags);
UpdateBitmaps(true);
}
}
static void WMRButtonUp(XEvent *event){
}
/*******************************/
/* Tastatur-Ereignisse (ASCII) */
/*******************************/
/* Unterprogramm zur Dateinamenseingabe (nur Backspace zum Editieren) */
static bool HandleNameInput(char ch){
switch (ch){
case '\x1b':
EndInput(true); /* Eingabe ergebnislos beenden */
KindOfInput=0;
break;
case '\n':
case '\r':
KindOfInput=0; /* EndInput muß vom Programm gerufen werden! */
return true; /* (ist zugegebenermaßen etwas unsauber) */
default:
InputChar(ch);
}
return false;
}
void WMChar(char ch){
#define iPage 1
#define iSubp 2
#define iFind 3
#define iExit 4
#define iMsg 5
#define iOpen 6
#define iFont 7
#define iText 8
#define iGif 9
tIdx HilfIdx;
char FileName[40], *sp; /* Stringzeiger für Hilfszwecke */
word CurFont;
int Returncode;
FILE *f; /* Ausgabe-Datei */
if (HelpActive){
if (SelectLoadPage(&CurIdx,EQUALPAGE,MenuFlags)==0xffff){
UpdateBitmaps(false);
}else{
UpdateBitmaps(true);
}
HelpActive=false;
}
/* Zahl 1..8 eingegeben und kein Input aktiv und Index vorhanden? */
if ((ch>='1') && (ch<='8') && !KindOfInput && Index){
BeginInput(sGotoPage[country],NULL);
KindOfInput=iPage; /* flugs Eingabezeile eröffnen */
}
switch (KindOfInput){
/*case iExit:
break;*/
/****************** Speichern-Eingabezeile (Text) *****************/
case iText:
if (HandleNameInput(ch)){ /* wenn mit ENTER beendet */
strcpy(FileName,EndInput(true)); /* beschaffen */
sp=(char *)malloc(41*24+1); /* Kurzzeit-Speicher beschaffen */
if (sp){
Page2Asc(sp,MenuFlags,MenuFont,0,0,39,23); /* Langen String draus machen */
if (strlen(FileName)){
f=fopen(FileName,"a"); /* anhängen an evtl. vorhandene Datei */
}else{
f=stdout; /* Standardausgabe */
}
if (f){ /* Gültiger Zeiger? */
fprintf(f,"%s",sp); /* String schreiben */
if (f!=stdout)
fclose(f); /* zumachen */
}else{
sprintf(Topline,sWriteError[country],FileName,(word)errno);
BeginInput(NULL,NULL);
KindOfInput=iMsg;
}
free(sp); /* Angeforderten Speicher freigeben */
}
}
break;
/****************** Speichern-Eingabezeile (Bild) *****************/
case iGif:
if (HandleNameInput(ch)){ /* wenn mit ENTER beendet */
strcpy(FileName,EndInput(true)); /* beschaffen */
if (strlen(FileName)){
f=fopen(FileName,"wb");
if (f){
sp=(char *)malloc(41*24+1); /* Kurzzeit-Speicher beschaffen */
if (sp){
XDefineCursor(display,window,idc_wait);
/* "Sanduhr" anzeigen (Hände bleiben bei den Subwindows) */
/* sonst müßte hier noch ein UnmapSubwindows() hin */
XFlush(display);
Page2Asc(sp,MenuFlags,MenuFont,0,0,39,23); /* Pascal-Strings draus machen */
SchiessEinFoto(f,Pixmaps[0].hbm,41*FONTW,24*FONTH,sp);
XDefineCursor(display,window,idc_arrow);
free(sp);
}
fclose(f); /* Scließen */
}else{
sprintf(Topline,sWriteError[country],FileName,(word)errno);
BeginInput(NULL,NULL);
KindOfInput=iMsg;
}
}
}
break;
/****************** Such-Eingabezeile *****************/
case iFind:
if (HandleNameInput(ch)){ /* wenn mit ENTER beendet */
strcpy(Suchkette,EndInput(false)); /* "herbekommen" */
if (strlen(Suchkette)){
PrepareString(Suchkette,SuchArt); /* zu Großbuchstaben usw. */
if (SuchArt & USEREGEXP){
/* hier Regexp-Übersetzung einbauen (später) */
}
if (Suche(SuchArt)){
UpdateBitmaps(true);
}else{
BeginInput(sNoMatch[country],NULL);
KindOfInput=iMsg;
}
}
}
break;
/****************** Öffnen-Eingabezeile *****************/
case iOpen:
if (HandleNameInput(ch)){ /* wenn mit ENTER beendet */
strcpy(FileName,EndInput(false));
if ((strlen(FileName)==0) || (Returncode=OpenVTFile(FileName))){
sprintf(Topline,sFileError[country],FileName,(word)Returncode);
BeginInput(NULL,NULL);
KindOfInput=iMsg;
}else{ /* erfolgreich geladen */
UpdateBitmaps(true);
}
}
break;
/****************** Fontwahl-Eingabezeile *****************/
case iFont:
switch (ch){
case '\x1b':
case 'q':
case '\3':
EndInput(true); /* Eingabe ergebnislos beenden */
KindOfInput=0;
break;
case ' ':
case 'j':
case 'l':
case '\t':
case '+':
CurFont=(CurFont+2)%16; /* 2 vorwärts */
case 'h':
case 'k':
case '\b':
case '-':
CurFont=(CurFont+15)%16; /* und 1 zurück */
EndInput(false); /* kein Redraw nötig! */
BeginInput(sChooseFont[country],sFontName[country][CurFont]);
break;
case '\n':
case '\r':
MenuFont=CurFont;
EndInput(false);
KindOfInput=0;
UpdateBitmaps(Index!=NULL);/* wenn Index=NULL dann nicht "laden" */
break;
default:
XBell(display,volume);
}
break;
/****************** Seiten-Eingabezeile *****************/
case iPage: /* Es müssen exakt 3 Ziffern eingegeben werden */
if ((isxdigit(ch)) || (ch=='\b')){
InputChar(ch);
if (GetInputChars()==3){
sscanf(EndInput(false),"%hx",&HilfIdx.W.Page);
KindOfInput=0;
HilfIdx.W.Subp=0;
if (SelectLoadPage(&HilfIdx,NEXTSUB,MenuFlags)!=0xffff){
CurIdx=HilfIdx;
UpdateBitmaps(true);
}else{
sprintf(Topline,sNoPage[country],UsualPage(HilfIdx.W.Page));
BeginInput(NULL,NULL);
KindOfInput=iMsg;
}
}
}else{
EndInput(true);
KindOfInput=0;
}
break;
/****************** Unterseiten-Eingabezeile *****************/
case iSubp: /* es können max. 4 Ziffern eingegeben werden */
if ((isxdigit(ch)) || (ch=='\b')){
InputChar(ch);
}
if ((isxdigit(ch) && (GetInputChars()==4)) ||
(((ch=='\n') || (ch=='\r')) && (GetInputChars()>0))){
sscanf(EndInput(false),"%hx",&HilfIdx.W.Subp);
KindOfInput=0;
HilfIdx.W.Page=CurIdx.W.Page;
if (SelectLoadPage(&HilfIdx,EQUALPAGE,MenuFlags)!=0xffff){
CurIdx=HilfIdx;
UpdateBitmaps(true);
}else{
sprintf(Topline,sNoSubp[country],HilfIdx.W.Subp);
BeginInput(NULL,NULL);
KindOfInput=iMsg;
}
}else if (!((isxdigit(ch)) || (ch=='\b'))){
EndInput(true);
KindOfInput=0;
}
break;
/****************** Hinweis-Zeile *****************/
case iMsg:
/* printf("Hier iMsg!\n");*/
EndInput(true); /* Meldungszeile verschwinden lassen */
KindOfInput=0;
break;
/****************** normale Tastenauswertung *****************/
default:
switch (ch){
case '\x1b':
break; /* Nicht piepsen */
case ' ': /* Leertaste nextsubpage */
if (Index){
SelectLoadPage(&CurIdx,ADDNEXTSUBPAGE,MenuFlags);
UpdateBitmaps(true);
}
break;
case '/': /* Suchen */
if (Index){
Suchstart=CurIdx; /* alte Suchkette vorgeben */
BeginInput(sSuchString[country],Suchkette);
KindOfInput=iFind;
}
break;
case 'g': /* Homepage */
if (Index){
CurIdx.W.Page=0x100; CurIdx.W.Subp=0;
SelectLoadPage(&CurIdx,NEXTSUBPAGE,MenuFlags);
UpdateBitmaps(true);
break;
}
case 'h': /* prevsub */
if (Index){
if (SelectLoadPage(&CurIdx,SUBPREVSUB,MenuFlags)!=0xffff){
UpdateBitmaps(true);
}else{
BeginInput(sOnlyOneSubp[country],NULL);
KindOfInput=iMsg;
}
}
break;
case 'i': /* Infos über aktuelle Seite */
CurFont=LandMap[(Header.W.CBits >> 12) & 7];
if (CurFont==0) CurFont=13; /* "reserviert" statt "automatisch" */
sprintf(Topline,sPageInfo[country],
Header.W.CBits,&sFontName[country][CurFont][1]);
BeginInput(NULL,NULL);
KindOfInput=iMsg;
break;
case 'j':
case 'd': /* nextpage */
case '+':
if (Index){
SelectLoadPage(&CurIdx,ADDNEXTPAGE,MenuFlags);
UpdateBitmaps(true);
break;
}
case 'k':
case 'u': /* prevpage */
case '-':
if (Index){
SelectLoadPage(&CurIdx,SUBPREVPAGE,MenuFlags);
UpdateBitmaps(true);
break;
}
case 'l': /* nextsub */
case '\t': /* nextsub */
if (Index){
if (SelectLoadPage(&CurIdx,ADDNEXTSUB,MenuFlags)!=0xffff){
UpdateBitmaps(true);
}else{
BeginInput(sOnlyOneSubp[country],NULL);
KindOfInput=iMsg;
}
}
break;
case 'n': /* Weitersuchen */
if (strlen(Suchkette)){
if (Suche(SuchArt)){
UpdateBitmaps(true);
}else{
BeginInput(sNoMatch[country],NULL);
KindOfInput=iMsg;
}
}
break;
case 'N': /* Weitersuchen */
if (strlen(Suchkette)){
if (Suche(SuchArt^BACKWARD)){
UpdateBitmaps(true);
}else{
BeginInput(sNoMatch[country],NULL);
KindOfInput=iMsg;
}
}
break;
case 'o':
strcpy(FileName,FName); /* den alten Pfad schon mal bereiten */
sp=(char *)strrchr(FileName,'/'); /* Pfad-Trenner enthalten? */
if (sp){ /* Ja? */
sp++; /* Zeiger dahinter positionieren */
}else{ /* Nein? */
sp=FileName; /* Zeiger auf Stringanfang */
}
*sp='\0'; /* Abschneiden: Dahinter oder am Anfang */
BeginInput(sOpenFile[country],FileName); /* aktuellen Pfad setzen */
KindOfInput=iOpen;
break;
case 'p': /* Eingabe Seitennummer */
if (Index){
BeginInput(sGotoPage[country],NULL);
KindOfInput=iPage; /* flugs Eingabezeile eröffnen */
}
break;
case 'q':
case '\3': /* ^C bricht auch ab */
AppDone=true; /* Abbruch erzwingen */
break;
case 's': /* subpageinput */
if (Index){
BeginInput(sGotoSubp[country],NULL);
KindOfInput=iSubp;
}
break;
case 't': /* Textdatei schreiben */
strcpy(FileName,FName); /* den alten Dateinamen schon mal bereiten */
sp=(char *)strrchr(FileName,'.'); /* Extension enthalten? */
if (sp){ /* Ja? */
*sp='\0'; /* Da abschneiden */
}
strcat(FileName,Idx2Str(CurIdx)); /* aktuellen Index dranhängen */
*(char *)strrchr(FileName,'/')='S'; /* den (hier) lästigen Slash killen */
strcat(FileName,".txt"); /* Ein bißchen DOS muß sein... */
BeginInput(sOpenFile[country],FileName); /* aktuellen Pfad setzen */
KindOfInput=iText;
break;
case 'y': /* Hilfe */
memcpy(&Page,sHelpPage[country],1024);
SetupPage();
UpdateBitmaps(false);
HelpActive=true; /* nächsten Tastendruck wegfangen */
break;
case 'z': /* Bilddatei schreiben */
strcpy(FileName,FName); /* den alten Dateinamen schon mal bereiten */
sp=(char *)strrchr(FileName,'.'); /* Extension enthalten? (Naja, etwas gewagt) */
if (sp){ /* Ja? */
*sp='\0'; /* Da abschneiden */
}
strcat(FileName,Idx2Str(CurIdx)); /* aktuellen Index dranhängen */
*(char *)strrchr(FileName,'/')='s';/* den (hier) lästigen Slash killen */
strcat(FileName,".gif"); /* Ein bißchen DOS muß sein... */
BeginInput(sOpenFile[country],FileName); /* aktuellen Pfad setzen */
KindOfInput=iGif;
break;
case 'b':
case '\b':
if (History_CanBack){
CurIdx=History_BackIdx;
SelectLoadPage(&CurIdx,EQUALPAGE,MenuFlags);
UpdateBitmaps(true);
}else{
BeginInput(sNoBack[country],NULL);
KindOfInput=iMsg; /* flugs Eingabezeile eröffnen */
}
break;
case 'D':
MenuFlags^=fDblH;
if (memchr(&Page,13,24*40)) /* DblH-Steuercode enthalten? */
UpdateBitmaps(true); /* Dann sofort auffrischen */
break;
case 'B':
MenuFlags^=fDebug;
UpdateBitmaps(true);
break;
case 'E':
BeginInput("Henrik.Haftmann@E-Technik.TU-Chemnitz.de",NULL);
KindOfInput=iMsg;
break;
case 'F':
CurFont=MenuFont;
BeginInput(sChooseFont[country],sFontName[country][CurFont]);
KindOfInput=iFont;
break;
case 'I':
MenuFlags^=fInvers;
GetColors(MenuFlags&fInvers); /* neue Farben besorgen */
UpdateBitmaps(true);
break;
case 'L':
MenuFlags^=fBlink; /* Blinken toggeln */
break;
case 'M':
MenuFlags^=fMix;
if ((Header.W.CBits & 0x60)==0)
UpdateBitmaps(true);
break;
case 'Q':
MenuFlags^=fQuiz; /* Quiz-Flag toggeln */
if (memchr(&Page,24,24*40))
UpdateBitmaps(true); /* Bei Vorhandensein von QUIZ aktualisieren */
break;
case 'R':
MenuFlags^=fRoll;
break;
case 'V':
BeginInput(" h#s VTX teletext for X version 0.91",NULL);
KindOfInput=iMsg;
break;
case 'W':
MenuFlags^=fBunt;
UpdateBitmaps(true);
break;
default:
XBell(display,volume); /* Ein "bellendes" Programm für "liegengebliebene" Ereignisse */
} /* switch(ch) */
} /* switch */
}
/**************************************/
/* Tastatur-Ereignisse (Sondertasten) */
/**************************************/
static void WMKey(KeySym key){ /* Behandlungsroutine für Sondertasten */
switch (key){
case XK_Cancel:
case XK_Break:
case XK_F10:
WMChar('q');
break;
case XK_Home:
WMChar('g');
break;
case XK_End:
WMChar('p');
break;
case XK_Prior:
WMChar('-');
break;
case XK_Next:
WMChar('+');
break;
case XK_F1:
case XK_Help:
WMChar('y');
break;
case XK_F2:
case XK_Find:
WMChar('/');
break;
case XK_F3:
WMChar('n');
break;
case XK_F4:
case XK_Execute:
WMChar('o');
break;
case XK_Left:
WMChar('h');
break;
case XK_Right:
WMChar('l');
break;
case XK_Up:
WMChar('k');
break;
case XK_Down:
WMChar('j');
break;
case XK_Shift_L:
case XK_Shift_R:
case XK_Control_L:
case XK_Control_R:
case XK_Caps_Lock:
case XK_Shift_Lock:
case XK_Meta_L:
case XK_Meta_R:
case XK_Alt_L:
case XK_Alt_R:
case XK_Super_L:
case XK_Super_R:
case XK_Hyper_L:
case XK_Hyper_R:
break; /* Nicht läuten */
default:
XBell(display,volume); /* Glocken läuten */
}
}
static void DestroyOld(){
/* if (XDeleteContext(display,None,Linklist)) /* ich will ALLE Fenster löschen */
/* printf("Kontext loeschen versagt\n"); /* aber das geht halt nicht! */
XDestroySubwindows(display,window); /* Alle "Eingabefenster" killen */
}
/* void SetTimer(word); /* Forward */
static word TimerCount; /* zählt die Sekunden */
/* Parameter gibt an, ob's vielleicht bloß 'ne Hilfeseite ist */
static void SetupNew(bool TruePage){
Pixmaps[0].valid=Pixmaps[1].valid=false;
BMIdx=0;
moved=false; /* Rechteck verschwindet */
KindOfInput=0; /* Eingabezeile verschwindet */
TimerCount=0; /* "StopBlink" */
if (TruePage){
History_Append(&CurIdx);
MkLinkList(MenuFlags); /* Neue Linkliste erzeugen (dauert nicht lange??) */
MkLinkBF(MenuFlags); /* Neue Linkliste erzeugen (dauert nicht lange??) */
}
}
/******************/
/* Expose-Message */
/******************/
static void UpdateBitmaps(bool TruePage){
DestroyOld();
SetupNew(TruePage);
InvokeExpose();
}
static void UpdateBitmap(short CurIdx){
XDefineCursor(display,window,idc_wait); /* Sanduhr (wäre das unter MS-Windows) */
DrawVT(Pixmaps[CurIdx].hbm,
(CurIdx)? MenuFlags & ~fBlink : MenuFlags | fBlink, MenuFont);
XDefineCursor(display,window,idc_arrow);
Pixmaps[CurIdx].valid=true;
}
static void WMPaint(){
if (BMIdx>=0){ /* Gültiger Wert (gegen verfrühte Exposes) */
if (!Pixmaps[BMIdx].valid){ /* Pixmap noch nicht erstellt? */
UpdateBitmap(BMIdx); /* Na dann aber los! */
if (moved) XorPixmapRect(BMIdx);
}
XCopyArea(display,Pixmaps[BMIdx].hbm,window,MainGC,0,0,41*FONTW,25*FONTH,0,0);
if (KindOfInput) /* Eingabezeile? */
DrawInputLine(); /* Drüberpinseln (Einzelzeichen) */
XMapSubwindows(display,window); /* Jetzt erst Eingabefenster erzeugen */
}else{
/* printf("BMIdx negativ!\n");*/
}
}
/******************/
/* Timer-Messages */
/******************/
static Atom WM_TIMER;
static bool SafeToSendTimerEvent=false,
RequestToSendTimerEvent=false;
static void DefineWMTimer(){
WM_TIMER=XInternAtom(display,"WM_TIMER",false);
}
static void XSendTimerMsg(int dummy){
XEvent event;
event.type=ClientMessage;
event.xclient.display=display;
event.xclient.window=window;
event.xclient.message_type=WM_TIMER;
event.xclient.format=32;
event.xclient.data.l[0]=(long)dummy;
if (SafeToSendTimerEvent){ /* Hauptprogramm tätigt gerade XNextEvent? */
XSendEvent(display,window,True,0,&event); /* XNextEvent() erlösen */
XFlush(display);
}else{
RequestToSendTimerEvent=true; /* Nur vormerken */
}
/* printf("SendTimerEvent\n"); */
}
static void SetTimer(word seconds){
signal(SIGALRM,XSendTimerMsg);
alarm(seconds);
}
/* Reaktionsprogramm auf Timer-Message */
/* (Möglicherweise ginge alles auch ohne die Erfindung einer
extra Timermessage, d.h. WMTimer würde direkt vom Alarm-Handler aufgerufen.
Aber so ist's mehr zu MS-Windows kompatibel, und außerdem lernte ich
etwas über den Nutzen der Atome) */
static void WMTimer(XEvent *event){
short BMMerk; /* zum Entscheiden, ob Expose generiert werden soll */
if (!autoredrawlock){ /* Timer nicht gesperrt? */
autoredrawlock++; /* Reentranz-Hahn zudrehen (sicherheitshalber) */
TimerCount++; /* Neuen "Sekundenwert" erzeugen */
BMMerk=BMIdx;
BMIdx=0; /* Grundsätzlich Pixmap #0 nehmen */
if ((MenuFlags & fBlink) && (TimerCount % 3==2) && /* Rest=2 */
memchr(&Page,9,24*40)){ /* Blink-Zeichen enthalten? */
BMIdx=1; /* Nur bei Rest=2 Pixmap #1 nehmen */
}
if ((MenuFlags & fRoll) && (TimerCount % 3==0)){
/* Seitenwechsel fällig? */
if ((Header.W.CBits & (bit 11)) || (TimerCount % 24==0)){
/* Schneller oder langsamer Wechsel? */
if (SelectLoadPage(&CurIdx,ADDNEXTSUB,MenuFlags)!=0xFFFF){
UpdateBitmaps(false); /* Hier: Nicht in History eintragen */
MkLinkList(MenuFlags); /* Neue Linkliste erzeugen (dauert nicht lange??) */
MkLinkBF(MenuFlags); /* Neue Linkliste erzeugen (dauert nicht lange??) */
BMMerk=BMIdx; /* Gleichsetzen, da InvokeExpose */
} /* von UpdateBitmaps() ausgeführt wurde */
}
}
if (BMMerk!=BMIdx) /* Verschieden? */
InvokeExpose(); /* Expose generieren */
autoredrawlock--;
}
SetTimer(1); /* Hier erst neu programmieren */
}
/*****************/
/* Hauptprogramm */
/*****************/
int main(int argc, char **argv){
XEvent event;
char CatBuf[16];
XWMHints xwmh;
char *DName=NULL; /* Display-Name */
char *iFName=NULL; /* Kommandozeilen-Dateiname */
char switchchar1, switchchar2;
int i;
KeySym key;
XComposeStatus cs;
/* ':' und '=' im Kommando übergehen; wenn '\0' nächsten String nehmen */
char *AdjustPP(char *sp){
if (*sp=='\0'){
i++;
if (i==argc){
fprintf(stderr,"Missing argument!\n");
exit(1);
}else{
return argv[i];
}
}else if ((*sp==':') || (*sp=='=')){
return ++sp;
}else{
return sp;
}
}
/* Dateiname der Variablen iFName zuweisen; aber NUR EINMAL */
void AssignFName(char *Name){
if (iFName){ /* Wenn bereits ein Name angegeben wurde */
fprintf(stderr,"VTX accepts only one filename; ignore %s\n",Name);
}else{
iFName=Name;
}
}
MenuFlags=fBunt|f41|f25|fDblH|fBlink|fBBar;
MenuFont=0;
CurIdx.W.Page=0x100;
CurIdx.W.Subp=0;
/* printf("sizeof(tPage)=%hd, sizeof(tHeader)=%hd, \
sizeof(byte)=%hd, sizeof(word)=%hd, \
sizeof(long)=%hd, sizeof(tIdx)=%hd, sizeof(Ansitab)=%hx\n",
sizeof(tPage),sizeof(tHeader),
/* sizeof(tSAATrail),sizeof(tHSFTrail),
sizeof(byte),sizeof(word),
sizeof(long),sizeof(tIdx),sizeof(Ansitab));
/* scanf("c",NULL);*/
const char*lang=getenv("LANG");
if (lang && !strncmp(lang,"de",2)) country = 1;
/* Parameter parsen */
for (i=1; i<argc; i++){
if (argv[i][0]=='-'){
switch (switchchar1=argv[i][1]){
case 'D':
case 'd':
DName=AdjustPP(&argv[i][2]);
break;
case 'H':
case 'h':
case '?':
puts(sHelp[country]);
exit(0);
break;
case 'L':
case 'l':
switch (switchchar2=(AdjustPP(&argv[i][2])[0])){
case 'D':
case 'd':
country=1;
break;
case 'E':
case 'e':
country=0;
break;
default:
fprintf(stderr,"VTX: Unknown country %c!\n",switchchar2);
exit(1);
}
break;
case 'P':
case 'p':
sscanf(AdjustPP(&argv[i][2]),"%hx/%hx",&CurIdx.W.Page,&CurIdx.W.Subp);
MenuFlags|=fNoX;
break;
case 'F':
case 'f':
sscanf(AdjustPP(&argv[i][2]),"%hd",&MenuFont);
break;
case 'M':
case 'm':
sscanf(AdjustPP(&argv[i][2]),"%hd",&MenuFlags);
break;
case 'N':
case 'n':
autoredrawlock++; /* So das Blinken unterbinden */
break;
case 'S':
case 's':
sscanf(AdjustPP(&argv[i][2]),"%hd",&SuchArt);
break;
case 'V':
case 'v':
sscanf(AdjustPP(&argv[i][2]),"%d",&volume);
break;
case '-':
AssignFName(&argv[i][1]);
break;
default:
fprintf(stderr,"VTX: Unknown option -%c - for help type \"vtx -h\"!\n",
switchchar1);
exit(1);
}
}else{
AssignFName(argv[i]);
}
} /* for */
if (MenuFlags & fNoX){
if (!iFName){
fprintf(stderr,"You MUST specify a file here!\n");
return -2;
}
if (i=LoadFile(iFName)){
fprintf(stderr,"Error reading file %s, code %d\n",iFName,i);
return i;
}else{
SelectLoadPage(&CurIdx,NEXTSUBPAGE,MenuFlags & ~fBBar);
Page2Asc(FindBuff,MenuFlags,MenuFont,0,0,39,23);
printf("%s",FindBuff);
return 0;
}
}
InitDisplay(DName);
if (DefaultDepthOfScreen(screen)<3){
MenuFlags&=~fBunt; /* Bei armseligen Displays auf Schwarzweiß schalten */
}
/* printf("Display geoeffnet\n"); */
/* XSynchronize(display,true);*/
CreateMainWindow();
/* printf("Hauptfenster erstellt\n"); */
/* GetColors();*/
/* printf("Farben geholt\n");*/
FontUpload();
/* printf("Font geladen\n");*/
if (iFName){
i=OpenVTFile(iFName); /* Datei mit Trara öffnen, Returncode aufheben */
if (i){
SelectLoadPage(&CurIdx,NEXTSUBPAGE,MenuFlags);
/* Startseite laden (lassen) */
SetupNew(false);
sprintf(Topline,sFileError[country],iFName,i);
BeginInput(NULL,NULL);
KindOfInput=iMsg;
}else{
SetupNew(true);
}
}else{
SelectLoadPage(&CurIdx,NEXTSUBPAGE,MenuFlags);
/* Startseite laden (lassen) */
SetupNew(false);
}
DefineWMTimer(); /* Timer-Atom vereinbaren */
if (!autoredrawlock)
SetTimer(1); /* Timer starten wenn erlaubt */
/* Hauptereignisschleife */
while (!AppDone){
XFlush(display); /* Warteschlange leeren */
if (RequestToSendTimerEvent){ /* Anhängiges Timer-Event vorlassen */
event.type=ClientMessage;
event.xclient.message_type=WM_TIMER;
RequestToSendTimerEvent=false;
}else{
SafeToSendTimerEvent=true; /* Reentranz zulassen */
XNextEvent(display,&event);
SafeToSendTimerEvent=false; /* Reentranz sperren */
}
/* printf("Hier vorbei (XNextEvent)!\n");*/
switch (event.type){
case MappingNotify:
/* printf("Mapping-Message\n");*/
XRefreshKeyboardMapping((XMappingEvent *)&event); /* sollte halt so sein */
break;
case Expose:
/* printf("1 Expose\n"); */
/* if (event.xany.window != window)
printf("Da stimmt was nicht!"); */
if (event.xexpose.count==0){
/* printf("Start Expose\n"); */
WMPaint();
/* printf("Ende Expose\n"); */
}
break;
case ButtonPress:
switch (event.xbutton.button){
case 1: /* Linke Maustaste */
WMLButtonDown(&event);
break;
default: /* Alle anderen */
WMRButtonDown(&event);
}
break;
case MotionNotify:
WMMouseMove(&event);
break;
case ButtonRelease:
switch (event.xbutton.button){
case 1: /* Linke Maustaste */
WMLButtonUp(&event);
break;
default: /* Alle anderen */
WMRButtonUp(&event);
}
break;
case KeyPress:
CatBuf[XLookupString((XKeyEvent *)&event,CatBuf,sizeof(CatBuf),&key,&cs)]='\0';
/* printf("Tastatur-Ereignis! String=\"%s\" Code %hX, ",CatBuf,CatBuf[0]);
printf("KeySym=0x%04X\n",key);*/
if (strlen(CatBuf)==1){
WMChar(CatBuf[0]);
}else{
WMKey(key);
}
break;
case ClientMessage:
/* printf("Timer-Event?\n"); */
if (event.xclient.message_type==WM_TIMER){
/* printf("Das Timer-Event! Hurra!\n"); */
WMTimer(&event); /* Windows-like aufrufen */
}
break;
case NoExpose:
/* printf("Huch! Ein NoExpose Event.\n");*/
break;
case SelectionRequest:
/* printf("SelectionRequest!\n"); */
/* if (event.xselectionrequest.target==XA_STRING)
printf("Hervorragend: als String!\n");
if (event.xselectionrequest.owner==window)
printf("Hervorragend: Owner=Window!\n");
if (event.xselectionrequest.selection==XA_PRIMARY)
printf("Hervorragend: selection=XA_PRIMARY!\n");
if (event.xselectionrequest.property==XA_CUT_BUFFER0){
printf("Hervorragend: property=CUT_BUFFER0!\n");
}else{
printf("Nicht Hervorragend: property=%d!\n",
event.xselectionrequest.property);
}
if (event.xselectionrequest.requestor==RootWindowOfScreen(screen)){
printf("Hervorragend: requestor=CRootWindow!\n");
}else{
printf("Nicht Hervorragend: requestor=%d!%d\n",
event.xselectionrequest.requestor,window);
} */
/* XChangeProperty(display,
event.xselection.requestor,
event.xselectionrequest.property,
event.xselectionrequest.target,
8,PropModeReplace,ClipBuff,strlen(ClipBuff));*/
XChangeProperty(display, RootWindowOfScreen(screen),
XA_CUT_BUFFER0,XA_STRING,8,PropModeReplace,ClipBuff,strlen(ClipBuff));
XChangeProperty(display, event.xselection.requestor,
XA_CUT_BUFFER0,XA_STRING,8,PropModeReplace,ClipBuff,strlen(ClipBuff));
event.type=SelectionNotify;
event.xselection.requestor=event.xselectionrequest.requestor;
event.xselection.selection=XA_PRIMARY;
event.xselection.target =XA_STRING;
event.xselection.property =XA_CUT_BUFFER0;
event.xselection.time =event.xselectionrequest.time;
XSendEvent(display,event.xselection.requestor,
true,0xffffffL,&event);
/* event.xselection.requestor=RootWindowOfScreen(screen);
XSendEvent(display,RootWindowOfScreen(screen),
true,0xffffffL,&event);
event.xselection.requestor=window;
XSendEvent(display,RootWindowOfScreen(screen),
true,0xffffffL,&event);
if (XSendEvent(display,event.xselection.requestor,
true,0xffffffL,&event))
printf("Erfolgreich gesendet!\n");*/
break;
case SelectionClear:
if (event.xselectionclear.selection==XA_PRIMARY){
RemoveAnySelection();
}
break;
default:;
/* printf("Unbekanntes Event %d\n",event.type); */
} /* switch */
} /* while */
CloseFile();
return 0;
} /* main */
Detected encoding: UTF-8 | 0
|