code:
#if 0
# /*
# make -f debug.h [target]
# targets below

check:
		gcc -Wall -Wextra -DFULLDEBUG -DDEBUG_INCLUDESRC -std=c9x -fsyntax-only -Werror debug.h

#(dump save flags)
flags:
		@echo -ne '-DENABLEDEBUG -DDEBUG_INCLUDESRC'

test: debug.h
		gcc -DENABLEDEBUG -DDEBUG_INCLUDESRC -DTEST_DEBUG -o testdebug -x c debug.h

testfile: debug.h
		gcc -DENABLEDEBUG -DDEBUG_INCLUDESRC -DTEST_DEBUG -DTEST_DEBUG_FILE -o testdebug -x c debug.h

all: debug.h.txt html

debug.h.txt: debug.h.3

README.rst: debug.h
		$(file > README.rst,$(README))

manpage: debug.h.3

debug.h.3: README.rst
		rst2man README.rst > debug.h.3

html: README.rst
	pandoc -f rst -t html -s README.rst > debug.html

define README =

=========
 debug.h
=========

debugging functions, colored, file logging

SYNOPSIS
========

 #define DEBUG

 #define FULLDEBUG

 #define DEBUG_FILELEVEL [0..5]

 #define DEBUG_INCLUDESRC

 #include "debug.h"

**setdebugtarget(debugtarget,filename)**
	set the debug target to one of:

	- STDOUT
	- STDERR
	- TOFILE (filename)

**dbg[1..5]("message [fmt]",arg1,arg2,..)**:
  debug, when [1..5] < debug_filelevel

**dbgif[1..5]( if , "message [fmt]",arg1,arg2,..)**:
  debug, when "if" evaluates to true

**dbgf[1..5]("message [fmt]",arg1,arg2,..)**:
	debug with additional info: sourcefile, line

**warn("message [fmt]",arg1,arg2,..)**:
  Dump a warning, independent of debug switches

**warnif( if, "message [fmt]",arg1,arg2,..)**:
  Dump a warning, when "if" is true, independent of debug switches

**error("message [fmt]",arg1,arg2,..)**:
  Dump an error, independent of debug switches

**errorif( if, "message [fmt]",arg1,arg2,..)**:
  Dump an error, when "if" is true, independent of debug switches

**fatal("message [fmt]",arg1,arg2,..)**:
  Dump an error, independent of debug switches, and quit

OVERVIEW
========

Include debug.h into all sources, where you need debug macros.
In one (and only one) sourcefile define DEBUG_INCLUDESRC,
before the include.

debug (and dbg) macros can be enabled and disabled by #define DEBUG
globally.

The debugtarget can be set by the function setdebugtarget at runtime.
possible targets: STDERR, STDOUT, TOFILE

error and warning macros are not affected by the DEBUG switch.

dbg(..) calls will dump the message,
		when debug_filelevel is not set, or set to 1..5

dbg[x] will log only, when debug_filelevel is set to any value > x

DESCRIPTION
===========

Hopefully uploading this to github prevents me of doing this for the X'th time,
again and again. (this file might date back to ~2003,2004.
And I was about to do this work today, again.)

The intention of this file is to have several debug macros, dbg, and dbg[2..5]
dbg messages will always be logged, except DEBUG is not defined or
debug_filelevel is set to 0
dbg2 and dbg messages will be logged, when debug is set to a value >= 2
dbg3, dbg2 and dbg messages will be logged, when debug is set to a value >= 3
.... until dbg5

When FULLDEBUG is defined, the filename and line is also dumped.

So spread dbg instructions wildly around, and simply disable them when you
are going to do a release. When bugs arise, you are able to ask for a recompile
with DEBUG enabled, and for the resulting log file.
This also means sort of transparency.
When releasing with the source code,
the debug functions give much insight into the development process.
(I do not believe, revealing, where and why you did have to look for bugs
would show bad skills. Whoever has the skills to understand the debug
messages and debug functions spreading patterns, will most possibly
also be able to read your code. If not, it doesn't matter anyways)

My workflow is mostly related with debugging: separate a problem into as many different parts as possible,
and implement them part for part.
After every implementation of a part, check for the wished functionality, via debugging functions,
also with unexpected input.
Sometimes this can involve changing only a single source line,
and check after that.
This might seem like a slowly approach.
But it isn't, in my experience.
When fully in flow, I put out up to thousands of (tested) source lines
per day. The big advantage of this approach might also be the continuing
success feelings. Which keeps me flowing, in turn. Anyways.

Include this header into all sources, where you need debug macros.
In one (and only one) sourcefile define DEBUG_INCLUDESRC,
before the include.

debug (and dbg) macros can be enabled and disabled by #define DEBUG
globally.

The debugtarget can be set by the function setdebugtarget at runtime.
possible targets: STDERR, STDOUT, TOFILE

error and warning macros are not affected by the DEBUG switch.

The debuglevel (-1..5) can be set differently for every source file,
either at runtime via dbg_filelevel = level,
and/or once by '#define DEBUG_FILEVEL', before the includement of debug.h
It's set to 5 per default, meaning, all dbg[1..5] calls will debug output.

LICENSE
=======

BSD License

AUTHOR
======

Michael (misc) Myer misc.myer at zoho.com

endef
ifdef UNDEF
*/
#endif

#ifndef debug_h
#define debug_h

#ifdef __cplusplus
extern "C" {
#endif

#ifndef bool
#define bool int
#define true 1
#define false 0
#endif

#if ( ENABLEDEBUG == NOCOLOR )
#define AC_NORM
#define AC_BLACK
#define AC_RED
#define AC_GREEN
#define AC_BROWN
#define AC_BLUE
#define AC_MAGENTA
#define AC_MARINE
#define AC_LGREY
#define AC_WHITE

#define AC_GREY
#define AC_LRED
#define AC_LGREEN
#define AC_YELLOW
#define AC_LBLUE
#define AC_LMAGENTA
#define AC_LMARINE
#define AC_LWHITE
#else
// ansicolor escape sequences
#define AC_NORM      "\033[0m"
#define AC_BOLD      "\033[1m"
#define AC_FAINT     "\033[2m"
#define AC_CURSIV    "\033[3m"
#define AC_UNDERLINE "\033[4m"
#define AC_BLINK     "\033[5m"
#define AC_INVERSE   "\033[7m"
#define AC_BLACK     "\033[0;30m"
#define AC_RED       "\033[0;31m"
#define AC_GREEN     "\033[0;32m"
#define AC_BROWN     "\033[0;33m"
#define AC_BLUE      "\033[0;34m"
#define AC_MAGENTA   "\033[0;35m"
#define AC_MARINE    "\033[0;36m"
#define AC_LGREY     "\033[0;37m"
#define AC_WHITE     "\033[0;38m"

#define AC_GREY      "\033[1;30m"
#define AC_LRED      "\033[1;31m"
#define AC_LGREEN    "\033[1;32m"
#define AC_YELLOW    "\033[1;33m"
#define AC_LBLUE     "\033[1;34m"
#define AC_LMAGENTA  "\033[1;35m"
#define AC_LMARINE   "\033[1;36m"
#define AC_LWHITE    "\033[1;37m"

#ifdef SHORTCOLORNAMES

#define NORM     AC_NORM
#define INVERSE  AC_INVERSE
#define BLINK    AC_BLINK
#define BLACK    AC_BLACK
#define RED      AC_RED
#define GREEN    AC_GREEN
#define BROWN    AC_BROWN
#define BLUE     AC_BLUE
#define MAGENTA  AC_MAGENTA
#define MARINE   AC_MARINE
#define LGREY    AC_LGREY
#define WHITE    AC_WHITE
#define GREY     AC_GREY
#define LRED     AC_LRED
#define LGREEN   AC_LGREEN
#define YELLOW   AC_YELLOW
#define LBLUE    AC_LBLUE
#define LMAGENTA AC_LMAGENTA
#define LMARINE  AC_LMARINE
#define LWHITE   AC_LWHITE

#endif

#ifdef SHRTSHORTCOLORNAMES

#define cN   AC_NORM
#define cBL  AC_BLACK
#define cR   AC_RED
#define cG   AC_GREEN
#define cBR  AC_BROWN
#define cB   AC_BLUE
#define cMG  AC_MAGENTA
#define cM   AC_MARINE
#define cLG  AC_LGREY
#define cW   AC_WHITE
#define cG   AC_GREY
#define cLR  AC_LRED
#define cLGN AC_LGREEN
#define cY   AC_YELLOW
#define cLB  AC_LBLUE
#define cLMG AC_LMAGENTA
#define cLM  AC_LMARINE
#define cLW  AC_LWHITE

#endif

#endif

#ifdef FULLDEBUG
#warning FULLDEBUG
#ifdef ENABLEDEBUG
#undef ENABLEDEBUG
#endif
// set to 5 - everything
#define ENABLEDEBUG 5
#else
//#warning NOFU
#endif

		// where the debug should go
		enum _debugtarget { STDERR, STDOUT, TOFILE };

		bool _setdebugtarget( enum _debugtarget t, const char *file );

		// Set a target for debug (defaults to stderr)
		// returns false, if the stream could not be opened
#define setdebugtarget(debugtarget,filename) _setdebugtarget( debugtarget, filename )

		// Error and warning severity
		enum _severity { DEBUG, MINOR, UNUSUAL, WARNING, SEVERE, FATAL};
		//enum _severity { FATAL, SEVERE, WARNING, UNUSUAL, MINOR, DEBUG };
#ifndef DEBUG_FILELEVEL
		// local file debug level (everything by default );
		int dbg_filelevel = DEBUG;
#else
		int dbg_filelevel = DEBUG;
		//static int dbg_filelevel = DEBUG_FILELEVEL;
#endif

		void _warning(enum _severity sev, const char* file, const int line, const char* function, const char* fmt, ... );
		void _error( enum _severity sev, const char* file, const int line, const char* function, const char* fmt, ... );
		void _dbg_full( int level, int filelevel, const char* file, const int line, const char* function, const char* fmt, ... );
		void _dbg( int level, int filelevel, const char* fmt, ... );

#define warning(severity,...) _warning( severity, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__ )
#define warn(...) _warning( WARNING, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__ )
#define warnif(when,...) {if ( when ) warn(__VA_ARGS__);}

#define derror(...) _error( SEVERE, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__ )
#define fatal(...) _error( FATAL, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__ )
#define errorif(when,...) {if ( when ) warning(__VA_ARGS__)}
#define fatalif(when,...) {if ( when ) fatal(__VA_ARGS__)}

#ifdef ENABLEDEBUG
#if ( ENABLEDEBUG>0 )

#define _DBG_FULL_(level,...) _dbg_full( level,  dbg_filelevel,  __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__ )

#define dbgf(...)  _DBG_FULL_( 0,  __VA_ARGS__ )
#define dbgf1(...) _DBG_FULL_( 1,  __VA_ARGS__ )
#define dbgf2(...) _DBG_FULL_( 2,  __VA_ARGS__ )
#define dbgf3(...) _DBG_FULL_( 3,  __VA_ARGS__ )
#define dbgf4(...) _DBG_FULL_( 4,  __VA_ARGS__ )
#define dbgf5(...) _DBG_FULL_( 5,  __VA_ARGS__ )

#define dbgi(arg) _dbg(1, dbg_filelevel, "DBG: L.%d, %s, int %s:   %d", __LINE__, __FILE__, #arg , arg )

#ifdef FULLDEBUG
#define dbg(...)  _DBG_FULL_( 0, __VA_ARGS__ )
#define dbg1(...) _DBG_FULL_( 1, __VA_ARGS__ )
#define dbg2(...) _DBG_FULL_( 2, __VA_ARGS__ )
#define dbg3(...) _DBG_FULL_( 3, __VA_ARGS__ )
#define dbg4(...) _DBG_FULL_( 4, __VA_ARGS__ )
#define dbg5(...) _DBG_FULL_( 5, __VA_ARGS__ )
#define dbgif(when,...) {if ( when ) dbg(__VA_ARGS__)}
#else
#warning FULLDEBUG - else
#define dbg(...)  _dbg( 0, dbg_filelevel, __VA_ARGS__ )
#define dbg1(...) _dbg( 1, dbg_filelevel, __VA_ARGS__ )
#define dbg2(...) _dbg( 2, dbg_filelevel, __VA_ARGS__ )
#define dbg3(...) _dbg( 3, dbg_filelevel, __VA_ARGS__ )
#define dbg4(...) _dbg( 4, dbg_filelevel, __VA_ARGS__ )
#define dbg5(...) _dbg( 5, dbg_filelevel, __VA_ARGS__ )
#define dbgif(when,...) {if ( when ) dbg(__VA_ARGS__)}
#endif //FULLDEBUG

#else //ENABLEDEBUG not defined
//#warning ENABLEDEBUG not defined
#define dbg(...) {}
#define dbgif(...) {}
#define dbg1(...) {}
#define dbg2(...) {}
#define dbg3(...) {}
#define dbg4(...) {}
#define dbg5(...) {}
#define dbgf(...) {}
#define dbgf1(...) {}
#define dbgf2(...) {}
#define dbgf3(...) {}
#define dbgf4(...) {}
#define dbgf5(...) {}

#define dbgi(_int) {}

#endif //enabledebug>0
#endif //ENABLEDEBUG

#ifdef __cplusplus
}
#endif

#ifdef DEBUG_INCLUDESRC

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

// Implementation

static FILE* target = 0;

static void init(){
		if ( !target )
				target = stderr;
}

bool _setdebugtarget( enum _debugtarget t , const char * file)
{
		switch(t){
				case STDOUT: target = stdout;
										 break;

				case TOFILE: target = fopen(file,"w");
										 if ( ferror( target ) ){
												 target = stderr;
												 warn("Couldn't open debug target: %s", file);
												 return(false);
										 }
										 break;
				default:
										 target = stderr;
										 break;

		}

		return(true);
}

void _warning( enum _severity sev, const char* file, const int line, const char* function, const char*fmt, ... ){
		init();
		fprintf(target, AC_BROWN "WARNING:" AC_NORM " %s  line %d, in %s\n   ", file, line, function);
		va_list ap;
		va_start(ap,fmt);
		vfprintf(target, fmt, ap );
		fprintf(target,"\n");
		va_end(ap);
		fflush(target);
		if (sev==FATAL){
				exit(1);
		}
}
void _error( enum _severity sev, const char* file, const int line, const char* function, const char*fmt, ... ){
		init();
		fprintf(target, AC_RED "ERROR:"AC_NORM" %s  line %d, in %s    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n", file, line, function);
		va_list ap;
		va_start(ap,fmt);
		vfprintf(target, fmt, ap );
		fprintf(target,"\n");
		va_end(ap);
		fflush(target);
		if (sev==FATAL){
				fprintf(target, AC_LRED "FATAL"AC_NORM"\n");
				if ( !( target == stderr || target == stdout ) ) // TOFILE
						fprintf( stderr, AC_LRED "FATAL error in %s line %d\n"AC_NORM"Quit\n", file, line );
				fflush(target);
				fflush( STDERR );
				exit(1);
		}
}
void _dbg_full( int level, int filelevel, const char* file, const int line, const char* function, const char*fmt, ... ){
		if ( level < filelevel )
				return;
		init();
		fprintf(target, "   %s   %d, in %s\n", file, line, function );
		va_list ap;
		va_start(ap,fmt);
		vfprintf(target, fmt,  ap );
		fprintf(target,"\n");
		va_end(ap);
		fflush(target);
}
void _dbg( int level, int filelevel, const char*fmt, ... ){
		if ( level < filelevel )
				return;
		init();
		va_list ap;
		va_start(ap,fmt);
		vfprintf(target, fmt,  ap );
		fprintf(target,"\n");
		va_end(ap);
		fflush(target);
}

#endif //DEBUG_INCLUDESRC

#endif //IFNDEF debug_h

#ifdef TEST_DEBUG

// testing, and also serves as example

int main(){
#ifdef TEST_DEBUG_FILE
		setdebugtarget( TOFILE, "test.log" );
#endif

		int a = 3;
		dbg( "Checking debug functions." );
		dbg5( "dbg5" );
		dbg2( "dbg2, a value: %d", 42 );

		dbg(""); // Empty line

		dbg_filelevel = 2;
		dbg3(" Shouldn't show up" );

		dbg("this is higher than the debuglevel");
		dbg2("level 2, too");

		dbg5("this not."); // not

		dbgf2(" A \"full\" debug message by dbgf2");

		dbg(""); // Empty line
		warnif( a>0, "a gt 0 ");
		warnif( a>3, "a gt 3 ");

		dbg(""); // Empty line
		error( "Raising an error" );

		fatal( "Good bye" ); // Quits

		dbg("This shouldn't show up");

		return(0);
}

#endif //testdebug

#if 0
endif
#endif


code:
The fonts LiberationMono_Bold and LiberationMono_BoldItalic,
to be optionally embedded into the binary.

The files are renamed to slterm_font.ttf and slterm_font_italic.ttf,
regardless of their weight, I prefer bold fonts at the terminal.
"Bold" is shown as bright color instead.

This can be changed in config.h

I suggest to install those Liberation fonts when installing slterm,
if not already present.

Anyways, to have a working font especially with the statically linked binary,
they can be embedded. (+400kB)

The empty ttf files are needed anyways for the build process.

To change the embedded fonts, copy the ttf font files into this directory,
overwriting the existing ttf files,
update the size of the uncompressed ttf files in embed_fonts.h,
(manually or execute updatesizes.sh)
and rerun make. (set EMBEDFONT := 1 in config.make, or `make static_embedfont`)

repo_name: misc/slterm

path: src/embed/README

license: mit


code:
#pragma once

// Exact size of the decompressed fonts in bytes.
// Need to be updated when the fonts are changed
unsigned int slterm_font_ttf_len = 303656;
unsigned int slterm_font_italic_ttf_len = 279668;
unsigned int slterm_font_bold_ttf_len = 0;
unsigned int slterm_font_bold_italic_ttf_len = 0;

extern unsigned int slterm_font_ttfz_len;
extern unsigned char slterm_font_ttfz[];

extern unsigned int slterm_font_italic_ttfz_len;
extern unsigned char slterm_font_italic_ttfz[];

extern unsigned int slterm_font_bold_ttfz_len;
extern unsigned char slterm_font_bold_ttfz[];

extern unsigned int slterm_font_bold_italic_ttfz_len;
extern unsigned char slterm_font_bold_italic_ttfz[];


code:
#!/bin/sh

for i in *ttf
do
	echo $i
	sed -Ei "s/("$i"_len = ).*/\1"`stat $i -c %s`";/" embed_font.h
done


code:
// font loading and handling
//
//

#include "includes.h"
#include "fonts.h"
#include "xwindow.h"
#include "config.h"

#if EMBEDFONT == 1
	 #define SINFL_IMPLEMENTATION
  	 #include "sinfl.h"
#endif

//char *usedfont = NULL;
double usedfontsize = 0;
double defaultfontsize = 0;

void print_fontmetriks(){
	printf("font metriks: width: %d height: %d usedfontsize: %f fontspacing: %d\n",
			dc.font.width, dc.font.height, usedfontsize, fontspacing );
}

// callbacks
void set_fontwidth( const Arg *a ){
	if ( fontwidth <= 0 )
		fontwidth = dc.font.width;
	fontwidth += a->i;
	Arg larg;
	larg.f = usedfontsize;
	zoomabs(&larg);
}

// callbacks
void set_fontheight( const Arg *a ){
	if ( fontheight <= 0 )
		fontheight = dc.font.height;
	fontheight += a->i;
	Arg larg;
	larg.f = usedfontsize;
	zoomabs(&larg);
}

void zoom(const Arg *arg) {
	Arg larg;

	larg.f = usedfontsize + arg->f;
	zoomabs(&larg);
}

void zoomabs(const Arg *arg) {
	xunloadfonts();
	xloadfonts(arg->f);
	cresize(0, 0);
	redraw();
	xhints();
	print_fontmetriks();
}

void zoomreset(const Arg *arg) {
	fontwidth = 0;
	fontheight = 0;
	Arg larg;

	if (defaultfontsize > 0) {
		larg.f = defaultfontsize;
		zoomabs(&larg);
	}
}

void fill_font_asciitable(Font *f){
	// fill ascii table
	for ( int a = 0; a<=127; a++ ){
		int glyphidx = XftCharIndex(xwin.dpy, f->match, a);
		//printf("%3d -> %3d\n",a,glyphidx );
		f->asciitable[a] = glyphidx;
	}
	for ( int a = 128; a<=255; a++ ){
		// this uses memory, but the performance gain is neglectibel.
		// surprisingly.
		//int glyphidx = XftCharIndex(xwin.dpy, f->match, charmap_convert(a,0));
		//printf("%3d -> %3d\n",a,glyphidx );
		//if ( ! glyphidx ){
			/*
			Glyph g = { .u=a };
			XftGlyphFontSpec fs;
			xmakeglyphfontspecs( &fs, &g, 1, 0, 0 );
			printf("frclen: %d   glyph: %d\n", frclen, fs.glyph );
			*/
			// spare that, load other fonts later, if needed
			// also for other attributes. (if added)
		//}
		f->asciitable[a] = 0;
		//f->asciitable[a] = glyphidx;

	}
}

int xloadfont(Font *f, FcPattern *pattern, int pixelsize,  const char* fontfile){
	//#define xloadfont(_font,_pattern,...) __xloadfont(_font,_pattern,__VA_OPT__(__VA_ARGS__,) 0 )
	//#define __xloadfont(_font,_pattern,_file,...) _xloadfont(_font,_pattern,_file)
	FcPattern *configured;
	FcPattern *match;
	FcResult result;
	XGlyphInfo extents;
	int wantattr, haveattr;

	if (pixelsize) {
		FcPatternDel(pattern, FC_PIXEL_SIZE);
		FcPatternDel(pattern, FC_SIZE);
		FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)pixelsize);
	}

	/*
	 * Manually configure instead of calling XftMatchFont
	 * so that we can use the configured pattern for
	 * "missing glyph" lookups.
	 */
	configured = FcPatternDuplicate(pattern);
	if (!configured)
		return 1;

	FcConfigSubstitute(NULL, configured, FcMatchPattern);
	XftDefaultSubstitute(xwin.dpy, xwin.scr, configured);

	match = FcFontMatch(NULL, configured, &result);
	if (!match && !fontfile ) {
		FcPatternDestroy(configured);
		return 1;
	}

	if ( fontfile && *fontfile ){
		FcPatternDel( match, FC_FILE );
		FcPatternAddString(match, FC_FILE, (const FcChar8 *) fontfile);
	}

//#ifdef DEBUG
#if 0
	FcPatternPrint( match );
	char *s = FcNameUnparse( match );
	printf( "match name: %s\n", s );
	free(s);
#endif

	f->match = XftFontOpenPattern(xwin.dpy, match);
	if (!f->match) {
		FcPatternDestroy(configured);
		FcPatternDestroy(match);
		return 1;
	}

	if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
				XftResultMatch)) {
		/*
		 * Check if xft was unable to find a font with the appropriate
		 * slant but gave us one anyway. Try to mitigate.
		 */
		if ((XftPatternGetInteger(f->match->pattern, "slant", 0, &haveattr) !=
					XftResultMatch) ||
				haveattr < wantattr) {
			f->badslant = 1;
			fputs("***** font slant does not match\n", stderr);
		}
	}

	if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
				XftResultMatch)) {
		if ((XftPatternGetInteger(f->match->pattern, "weight", 0, &haveattr) !=
					XftResultMatch) ||
				haveattr != wantattr) {
			f->badweight = 1;
			fputs("***** font weight does not match\n", stderr);
		}
	}

	if ( fontwidth ){
		f->width = fontwidth;
	} else {

#ifdef UTF8
			XftTextExtentsUtf8(xwin.dpy, f->match, (const FcChar8 *)ascii_printable,
							strlen(ascii_printable), &extents);
	f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
	//f->width = DIVCEIL(extents.xOff,190 );

#else
			// might be unneeded, fonts are rendered as monospace
			// don't know. something is happening. It works.
			//
	char printable[255];
	int p = 0;
	for ( int a = 32; a<127; a++)
		printable[p++] = a;
	for ( int a = 128; a<256; a++)
		printable[p++] = charmap_convert(a,0); //
	printable[p] = 0;

	XftTextExtents8(xwin.dpy, f->match, (const FcChar8 *)printable,
	//XftTextExtentsUtf8(xwin.dpy, f->match, (const FcChar8 *)printable,
			(127-32)+127, &extents);
			//sizeof(printable), &extents);
	//f->width=8;
	f->width = DIVCEIL(extents.xOff, (127-32)+127 );
	//printf("w:%d, height: %d\n",f->width, f->height);
	// pixelsize 13: w 8, h 15
	//f->width = DIVCEIL(extents.xOff, 96);

#endif
	}

	f->set = NULL;
	f->pattern = configured;

	f->ascent = f->match->ascent;
	f->descent = f->match->descent;
	f->lbearing = 0;
	f->rbearing = f->match->max_advance_width;

	if ( fontheight )
		f->height = fontheight;
	else
		f->height = f->ascent + f->descent;

	f->width += fontspacing;
	if ( f->width < 1 )
		f->width=1;

	fill_font_asciitable(f);

	//printf("font: %d %d %d %d %d\n",f->width,f->height,f->ascent,f->descent, f->rbearing );

	return 0;
}

FcPattern *get_fcpattern( const char* fontstr ){
	if (fontstr[0] == '-')
		return( XftXlfdParse(fontstr, False, False) );
	return( FcNameParse((FcChar8 *)fontstr) );
}

// load the configured fonts, with (optional) fontsize
void xloadfonts(double fontsize) {
	FcPattern *pattern;
	double fontval;

 	// get fontstr, from commandline or config.h
	char* fontstr = (opt_font == NULL) ? regular_font : opt_font;

	pattern = get_fcpattern(fontstr);

	if (!pattern)
		die("can't open font %s\n", fontstr);

	// determine the fontsize
	if (fontsize > 1) {
		FcPatternDel(pattern, FC_PIXEL_SIZE);
		FcPatternDel(pattern, FC_SIZE);
		FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
		usedfontsize = fontsize;
	} else {
		if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) ==
				FcResultMatch) {
			usedfontsize = fontval;
		} else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) ==
				FcResultMatch) {
			usedfontsize = -1;
		} else {
			/*
			 * Default font size, if none given. This is to
			 * have a known usedfontsize value.
			 */
			FcPatternAddDouble(pattern, FC_PIXEL_SIZE, default_font_pixelsize);
			usedfontsize = default_font_pixelsize;
		}
		defaultfontsize = usedfontsize;
	}

	// names of temporary font files
	char fname[4][32] = { {0},{0},{0},{0} };

#if EMBEDFONT == 1
  	 #warning embedding fonts
  	 #include "embed/embed_font.h"

  	 int fd[4] = { 0,0,0,0 };
  	  uchar *embfont[4] = {
  	 	slterm_font_ttfz,
  	 	slterm_font_bold_ttfz,
  	 	slterm_font_italic_ttfz,
  	 	slterm_font_bold_italic_ttfz };
  	  unsigned int embfontlenz[4] = {
  	 	slterm_font_ttfz_len,
  	 	slterm_font_bold_ttfz_len,
  	 	slterm_font_italic_ttfz_len,
  	 	slterm_font_bold_italic_ttfz_len };
  	  unsigned int embfontlen[4] = {
  	 	slterm_font_ttf_len,
  	 	slterm_font_bold_ttf_len,
  	 	slterm_font_italic_ttf_len,
  	 	slterm_font_bold_italic_ttf_len };

	  size_t bufsize = 128;
  	 for ( int i = 0; i<4 ; i++ )
		 if ( embfontlen[i]+128 > bufsize )
		 	bufsize = embfontlen[i] + 128;

	 {
		struct {
			unsigned char buf[bufsize];
		 	unsigned long canary;
		} s;
		s.canary = 0xa23df1223;

		 // prevent compiler "optimizations" by using (portable) assembly,
		 // demonstrating the power of void
		 // a warning: volatile is useless. the keyword just doesn't work. (sometimes..)
		 asm( "" :"+m"(s.canary) );

		 for ( int i = 0; i<4 ; i++ ){
			 if ( embfontlenz[i]){
#ifdef DEBUG
				 printf("Decompress Font: %d -> %d\n",embfontlenz[i],embfontlen[i]);
#endif
				 strcpy( fname[i], "/tmp/slterm_XXXXXX.ttf" );
				 fd[i] = mkstemps( fname[i], 4 );
				 if ( fd[i] <= 0 ){
					 fprintf(stderr,"Cannot create temporary file\n");
					 fd[i] = 0;
					 *fname[i] = 0;
				 } else {
					 uint len = sinflate( s.buf, bufsize, embfont[i], embfontlenz[i] );
					 if ( len != embfontlen[i] ){
						 fprintf(stderr, "Error decompressing embedded font #%d\n",i);
						 close( fd[i] );
						 unlink(fname[i]);
						 *fname[i] = 0;
					 } else {
						 write( fd[i], s.buf, embfontlen[i] );
						 fsync( fd[i] );
					 }
				 }
			 }
		 }
		 asm( "" :"+m"(s.canary) );
		 if ( s.canary != 0xa23df1223 ){
			 die("buffer overflow");
		 }
	 }

#endif

	// load regular font
	if (xloadfont(&dc.font, pattern, 0, ( opt_regular_font? 0:fname[0]) ))
		die("x can't open font %s\n", fontstr);

	if (usedfontsize < 0) { // determine the used fontsize as pixelsize of the regular font
		FcPatternGetDouble(dc.font.match->pattern, FC_PIXEL_SIZE, 0, &fontval);
		usedfontsize = fontval;
		if (fontsize == 0)
			defaultfontsize = fontval;
	}

	/* Setting character width and height. */
	twin.cw = dc.font.width;
	//twin.cw = ceilf(dc.font.width * cwscale);
	twin.ch = dc.font.height;
	//twin.ch = ceilf(dc.font.height * chscale);

	if ( borderperc != 0 )
		borderpx = (borderperc* twin.cw)/100;
		//borderpx = ceilf(((float)borderperc / 100) * twin.cw);

	if ( useboldfont ){
		if ( bold_font ){
			FcPatternDestroy(pattern);
			pattern = get_fcpattern(bold_font);
		} else {
			FcPatternDel(pattern, FC_WEIGHT);
			FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
		}
		if (xloadfont(&dc.bfont, pattern, usedfontsize,  opt_bold_font? 0:fname[1] ) )
			die("can't open font %s\n", fontstr);
		FcPatternDel(pattern, FC_WEIGHT);
	}

	if ( useitalicfont ){
		if ( italic_font ){
			FcPatternDestroy(pattern);
			pattern = get_fcpattern(italic_font);
		} else {
			FcPatternDel(pattern, FC_SLANT);
			FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
		}
		if (xloadfont(&dc.ifont, pattern, usedfontsize,  opt_italic_font? 0:fname[2] ))
			die("can't open font %s\n", fontstr);
	}

	if ( usebolditalicfont ){
		if ( bolditalic_font ){
			FcPatternDestroy(pattern);
			pattern = get_fcpattern(bolditalic_font);
		} else {
			FcPatternDel(pattern, FC_WEIGHT);
			FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
		}
		if (xloadfont(&dc.ibfont, pattern, usedfontsize,  opt_bolditalic_font? 0:fname[3]))
			die("can't open font %s\n", fontstr);
	}

	FcPatternDestroy(pattern);

	#if EMBEDFONT == 1
		// remove fonts from tmp, if written
		for ( int i = 0; i<4; i++ ){
			if (fd[i])
				close (fd[i]);
			if ( *fname[i] )
				unlink( fname[i] );
		}
	#endif

}

void xunloadfont(Font *f) {
	XftFontClose(xwin.dpy, f->match);
	FcPatternDestroy(f->pattern);
	if (f->set)
		FcFontSetDestroy(f->set);
}

void xunloadfonts(void) {
	/* Free the loaded fonts in the font cache.  */
	while (frclen > 0)
		XftFontClose(xwin.dpy, frc[--frclen].font);

	xunloadfont(&dc.font);
	if ( useboldfont )
		xunloadfont(&dc.bfont);
	if ( useitalicfont )
		xunloadfont(&dc.ifont);
	if ( usebolditalicfont )
		xunloadfont(&dc.ibfont);
}

// use a table for the glyph translation
// call makeglyphfontspecs only, when the char hasn't been found.
// better: load all ascii chars, in all weights. dont branch anymore.
// use a font table. -> 255*4 { glyphnumber,fontnumber }
// need to lookup, how many bits the glyphnumber can have. 16 could be enough.
// and this should most possibly branch, for chars < 127.
// the table is loaded into the cacheline else.
// it's a inner loop here.
// I'm wondering,whether 7bit ascii would be useful.
int noutf8_xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len,
		int x, int y) {
#ifndef UTF8
	// use table: 0..255, 0..3 char,weight -> glyph, font
	uint winx = twin.hborderpx + x * twin.cw, winy = twin.vborderpx + y * twin.ch,
	xp, yp;
	uchar mode, prevmode = UCHAR_MAX;
	Font *font = &dc.font;
	uint runewidth = twin.cw;
	FT_UInt glyphidx;

	int i, numspecs = 0;

	for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
		/* Fetch rune and mode for current glyph. */
		mode = glyphs[i].mode;

		/* Determine font for glyph if different from previous glyph. */
		if (prevmode != mode) {
			prevmode = mode;
			font = &dc.font;
			runewidth = twin.cw;// * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
			if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD) && usebolditalicfont ) {
				font = &dc.ibfont;
			} else if (mode & ATTR_ITALIC && useitalicfont ) {
				font = &dc.ifont;
			} else if (mode & ATTR_BOLD && useboldfont) {
				font = &dc.bfont;
			}
		}
		glyphidx = font->asciitable[ glyphs[i].u ];

		if (!glyphidx){
			//printf("Not found\n");
				xmakeglyphfontspecs( specs+numspecs, glyphs+i, 1, x+i , y );

		} else {
			//if ( rune>0x80 )
			//printf("rune: %x  idx: %x\n",rune,glyphidx);
			specs[numspecs].font = font->match;
			specs[numspecs].glyph = glyphidx;
			specs[numspecs].x = (short)xp;
			specs[numspecs].y = (short)yp;
		}
		xp += runewidth;
		numspecs++;
	}
	return( numspecs );
#else
	return( xmakeglyphfontspecs( specs, glyphs, len, x, y ) );
#endif
}

int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len,
		int x, int y) {
	float winx = twin.hborderpx + x * twin.cw, winy = twin.vborderpx + y * twin.ch,
	xp, yp;
	ushort mode, prevmode = USHRT_MAX;
	Font *font = &dc.font;
	int frcflags = FRC_NORMAL;
	float runewidth = twin.cw;
	//Rune rune;
	uint rune;
	FT_UInt glyphidx;
	FcResult fcres;
	FcPattern *fcpattern, *fontpattern;
	FcFontSet *fcsets[] = {NULL};
	FcCharSet *fccharset;
	int i, f, numspecs = 0;

	for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
		/* Fetch rune and mode for current glyph. */
		mode = glyphs[i].mode;

#ifdef UTF8
		/* Skip dummy wide-character spacing. */
		if (mode == ATTR_WDUMMY)
			continue;
#endif

		/* Determine font for glyph if different from previous glyph. */
		if (prevmode != mode) {
			prevmode = mode;
			font = &dc.font;
			frcflags = FRC_NORMAL;
#ifdef UTF8
			runewidth = twin.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
#else
			runewidth = twin.cw;// * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
#endif
			if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD) && usebolditalicfont ) {
				font = &dc.ibfont;
				frcflags = FRC_ITALICBOLD;
			} else if (mode & ATTR_ITALIC && useitalicfont ) {
				font = &dc.ifont;
				frcflags = FRC_ITALIC;
			} else if (mode & ATTR_BOLD && useboldfont) {
				font = &dc.bfont;
				frcflags = FRC_BOLD;
			}
			yp = winy + font->ascent;
		}
		//glyphidx = font->asciitable[ glyphs[i].u ];
		//if ( rune>=0x80 )
		//printf("rune: %x  \n",rune,glyphidx);
		/* Lookup character index with default font. */
		//if ( !glyphidx ){
			rune = charmap_convert( glyphs[i].u,glyphs[i].mode );
			// todo: cache that. ( besser auch die charmap )
			glyphidx = XftCharIndex(xwin.dpy, font->match, rune);
		//}
		if (glyphidx) {
			//if ( rune>0x80 )
			//printf("rune: %x  idx: %x\n",rune,glyphidx);
			specs[numspecs].font = font->match;
			specs[numspecs].glyph = glyphidx;
			specs[numspecs].x = (short)xp;
			specs[numspecs].y = (short)yp;
			xp += runewidth;
			numspecs++;
			continue;
		} else {
			// todo: load chars when loading the font, spare that
			// the table is limited to ascii 255
		}

		/* Fallback on font cache, search the font cache for match. */
		// TODO: reverse that. "cache" fonts in a table indexed by ascii
		for (f = 0; f < frclen; f++) {
			glyphidx = XftCharIndex(xwin.dpy, frc[f].font, rune);
			/* Everything correct. */
			if (glyphidx && frc[f].flags == frcflags){
				//font->asciitable[rune] = glyphidx;
				break;
			}
			/* We got a default font for a not found glyph. */
			if (!glyphidx && frc[f].flags == frcflags && frc[f].unicodep == rune) {
				//font->asciitable[rune] = glyphidx;
				break;
			}
		}

		/* Nothing was found. Use fontconfig to find matching font. */
		if (f >= frclen) {
			// not in cache
			fprintf(stderr,"rune missing in font, unicode: %x  ascii: %x\n",rune,glyphs[i].u);

			if (!font->set)
				font->set = FcFontSort(0, font->pattern, 1, 0, &fcres);
			fcsets[0] = font->set;

			/*
			 * Nothing was found in the cache. Now use
			 * some dozen of Fontconfig calls to get the
			 * font for one single character.
			 *
			 * Xft and fontconfig are design failures.
			 */
			fcpattern = FcPatternDuplicate(font->pattern);
			fccharset = FcCharSetCreate();

			FcCharSetAddChar(fccharset, rune);
			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
			//FcPatternAddBool(fcpattern, FC_SCALABLE, 1);

			FcConfigSubstitute(0, fcpattern, FcMatchPattern);
			FcDefaultSubstitute(fcpattern);

		//FcPatternDel(fcpattern, FC_FAMILY);
			//FcPatternDel(fcpattern, FC_WEIGHT);

			fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres);
			//fontpattern = FcFontMatch(NULL, fcpattern, &fcres);

			/* Allocate memory for the new cache entry. */
			if (frclen >= frccap) {
				frccap += 16;
				frc = xrealloc(frc, frccap * sizeof(Fontcache));
			}

			frc[frclen].font = XftFontOpenPattern(xwin.dpy, fontpattern);
			if (!frc[frclen].font)
				die("XftFontOpenPattern failed seeking fallback font: %s\n",
						strerror(errno));
			frc[frclen].flags = frcflags;
			frc[frclen].unicodep = rune;

			glyphidx = XftCharIndex(xwin.dpy, frc[frclen].font, rune);

			fprintf(stderr,"found idx: %d\nfrclen: %d\n",glyphidx,frclen);
			// misc. ok. memeory leak. frclen, if runes aren't found.
			// todo: write a rune buffer for codepoints and fonts.

			if ( glyphidx ){
				//FcPatternPrint( fcpattern );

				//frc[frclen].font.asciitable[rune] = glyphidx;
			}

			f = frclen;
			frclen++;

			FcPatternDestroy(fcpattern);
			FcCharSetDestroy(fccharset);
		}

		specs[numspecs].font = frc[f].font;
		specs[numspecs].glyph = glyphidx;
		specs[numspecs].x = (short)xp;
		specs[numspecs].y = (short)yp;
		xp += runewidth;
		numspecs++;
	}

	return numspecs;
}


code:
#ifndef fonts_h
#define fonts_h

#include <X11/Xft/Xft.h>

#include "xevent.h"
#include "term.h"

typedef XftGlyphFontSpec GlyphFontSpec;

//extern char *usedfont;

/* Font structure */
#define Font Font_
typedef struct {
#ifndef UTF8
		int asciitable[256]; // cache codepoints
#endif
		int height;
		int width;
		int ascent;
		int descent;
		int badslant;
		int badweight;
		short lbearing;
		short rbearing;
		XftFont *match;
		FcFontSet *set;
		FcPattern *pattern;
} Font;

// font cache. growing array. no ring. as stated below.
// in theory, this shouldn't be global, the cache depends
// on the used font.
enum { FRC_NORMAL, FRC_ITALIC, FRC_BOLD, FRC_ITALICBOLD };

typedef struct {
		XftFont *font;
		int flags;
		Rune unicodep;
} Fontcache;

/* Fontcache is an array now. A new font will be appended to the array. */
Fontcache *frc = NULL;
int frclen = 0;
int frccap = 0;

// callbacks
void set_fontwidth( const Arg *a );
void set_fontheight( const Arg *a );
void zoom(const Arg *);
void zoomabs(const Arg *);
void zoomreset(const Arg *);

int noutf8_xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);

int xloadfont(Font *, FcPattern *, int pixelsize, const char* filename);

void xloadfonts(double);
void xunloadfont(Font *);
void xunloadfonts(void);

void fill_font_asciitable(Font *f);

#endif


code:
// globals.

// need to check, in which files they are used.

// pid of executed shell
pid_t shellpid;

Term *term=0;
Term *p_term=0;
Term *p_help=0;
Term *p_help_storedterm=0;
Term *p_alt=0;

// term.c
int enterlessmode;

XWindow xwin;
TermWindow twin;

// in selection.c
//Selection sel;
//XSelection xsel;

// dummy functions, to remove dependencies on unused libraries

// overwrite the (only) dependency of fontconfig to lbintl, and therefore iconv.
// The internationalization is used solely for the font info name.
// adding nearly 1MB to the memory usage
const char *libintl_dgettext( const char* domain, const char* text){
	return(text);
}

#ifndef WITHBZIP2
// bzip2 dummies
// only needed to load bzip2 compressed fonts. (seems so..)
int BZ2_bzDecompressEnd(void*v, int a,int b){ return 0; };
int BZ2_bzDecompress( void*v ){ return 0; };
int BZ2_bzDecompressInit( void*v, int a,int b ){ die("bzip2 not linked. remove dummie functions, and relink\n"); return 0; };
#endif

void XdmcpWrap (
		unsigned char *input,
		unsigned char *wrapper,
		unsigned char *output,
		int        bytes)
{};


code:
#pragma once

// Globals, used across several source files,
// global typedefs (type aliases), macros.
// There might be globals left within other source files,
// todo: keep them together here.

// parsed commandline options
char *argv0;
char *opt_class = NULL;
char **opt_cmd = NULL;
char *opt_embed = NULL;
char *opt_font = NULL;
char *opt_io = NULL;
char *opt_line = NULL;
char *opt_name = NULL;
char *opt_title = NULL;
char opt_xresources;

char opt_regular_font = 0;
char opt_italic_font = 0;
char opt_bold_font = 0;
char opt_bolditalic_font = 0;

int ispagebased = 0; // counter, tries to keep track, whether the running program
							// has it's own screen buffer. (e.g. vim)
							// doesnt work well.

// number of saved scroll marks, set with enter.
// needs to be a power of 2.
// memory usage is RETMARKCOUNT*4 bytes,
// and the array is scanned linear when browsing to the marks.
#define RETMARKCOUNT 512

typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned short ushort;

// callback argument
typedef union {
  int i;
  unsigned int ui;
  float f;
  const void *v;
  const char *s;
} Arg;

#define ARGP(_assign) &(Arg){ ._assign }
#define ARGPi(_value) &(Arg){ .i= _value }

#include "fonts.h"

// ascii needs the whole 256 char table,
// therefore unsigned chars
#ifdef UTF8

// silence quirky cpp warnings ("quirky". .. misc24. Hopefully fixed that.)
#define utfchar char
#define UTF_SIZ 4
#define UTF_INVALID 0xFFFD

#define IF_UTF8(...) __VA_ARGS__
#define IFNOT_UTF8(...)

#else

//#define utfchar char
#define utfchar unsigned char
#define UTF_INVALID 0xff
#define UTF_SIZ 1

#define IF_UTF8(...)
#define IFNOT_UTF8(...) __VA_ARGS__

#endif

#if (__SIZEOF_POINTER__==8)
#define POINTER unsigned long
#else
#if (__SIZEOF_POINTER__==4)
#define POINTER unsigned int
#else
#error
#endif
#endif

// those macros are partially terrorism.
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
// Len in Bytes
#define LEN(a) (sizeof(a) / sizeof(a)[0])
// true, if x between a and b
#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
// division, round upwards.
#define DIVCEIL(n, d) (((n) + ((d)-1)) / (d))
#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
// return x, if between a and b. else a or b.
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)

#define TIMEDIFF(t1, t2)                                                       \
  ((t1.tv_sec - t2.tv_sec) * 1000 + (t1.tv_nsec - t2.tv_nsec) / 1E6)
#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))

//#define SWAPp(a,b) {a = (void*)((POINTER)a ^ (POINTER)b);\
	b = (void*)((POINTER)a ^ (POINTER)b);\
	a = (void*)((POINTER)a ^ (POINTER)b);}

#define SWAPp(_a,_b) { void* _tmp = _a; _a=_b; _b=_tmp; }

#define SWAPint(a,b) {a^=b;b^=a;a^=b;}

extern char* argv0;

typedef XftColor Color;
typedef XftDraw *Draw;

/* Global drawing context */
typedef struct {
	Color *color_array; // Pointer to an array of 256 colors
	size_t color_arraylen;
	Color *colortable; // Colors 0..7 in normal, bold, faint, bold|faint
							 // Pointer to an array
	Color *bgcolors; // Background colors 0..15
	Font font, bfont, ifont, ibfont;
	GC gc;
} DC;

extern DC dc;

/* Display info of the text window */
typedef struct {
	int tw, th; /* tty width and height */
	int w, h;   /* window width and height */
	int hborderpx, vborderpx;
	int ch;     /* char height */
	int cw;     /* char width  */
	uint mode;   /* window state/mode flags */
	int cursor; /* cursor style */
	int cursor_attr[4]; // cursor attributes
} TermWindow;

extern TermWindow twin;

typedef struct {
  Glyph attr; /* current char attributes */ //the rune is set to ' ' (empty)
	// Possibly there should be a difference between space ' ' and empty?
	// evtl render spaces and tabs visually?
  int x;
  int y;
  char state;
} TCursor;

// The xwindow data
typedef struct {
	Display *dpy;
	Colormap cmap;
	Window win;
	Drawable buf;
	GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
	Atom xembed, wmdeletewin, netwmname, netwmpid;
	XIM xim;
	XIC xic;
	Draw draw;
	Visual *vis;
	XSetWindowAttributes attrs;
	int scr;
	int isfixed; /* is fixed geometry? */
	int l, t;    /* left and top offset */
	int gm;      /* geometry mask */
} XWindow;

extern XWindow xwin;

/*  Glyph based representation of the screen and history buffer */
typedef struct {
	// todo: histsize should be smaller for help and alt screens
	//Line hist[HISTSIZE]; /* history buffer */ //
	Line *hist; /* history buffer */ //
	int guard; // canary, debugging hist
	Line *line;                               // visible lines on screen
	int rows;                                  // number of rows visible
	int cols;                                  // number of cols visible
	int colalloc; // allocated cols in the hist buf, can be > cols. won't shrink, only enlarge.
	int histindex;    /* history index */ // points to the top of the terminal, last line (bottom) in hist
	uint histsize; // history size-1, size of the buffer. (the count of written lines is in histindex,
						// albite histindex only indicates the number of written lines in the hist buffer,
						// until the buffer is circled.)
	int scr;   // scroll back. scr is counted for scrolled lines. scr=histsize: scroll at the top
				  // scr=0 : scroll to the bottom

	// needed for defining scroll areas
	// would be possible to abandon the separation of "line"(s) buffer of the visible screen,
	// and the hist buffer. however, if only parts of the screen are scrolled,
	// some pointer copying would be needed. Since it works now,
	// I leave it for now. I'm further abstracting anyways, eventually I'm going
	// to change the line buffer to point at the hist buffer.
	// Maybe. I'm always getting dizzy with the buffer stuff here.
	// Other things are more important, imho. If someone likes to
	// change it, the Macro TLINE and the functions in scroll.c are the locations to begin with.
	int top;                                  /* top    scroll limit */
	int bot;                                  /* bottom scroll limit */
	// top / bot: when only parts of the screen are scrolled (scroll areas set)

	int *dirty;  /* dirtyness of lines */ // points to an array

	TCursor cursor;                                /* cursor */
	int ocx;                                  /* old cursor col */
	int ocy;                                  /* old cursor row */
	int mode;                                 /* terminal mode flags */
	int esc;                                  /* escape state flags */
	char trantbl[4];                          /* charset table translation */
	int charset;                              /* current charset */
	int icharset;                             /* selected charset for sequence */
	int *tabs;

	int scrollmarks[12];
	int retmarks[RETMARKCOUNT];
	int current_retmark; // current retmark. retmarks are stored circular.
	int scrolled_retmark; // to which retmark was scrolled
	//int scroll_retmark; // to which retmark was scrolled
	char circledhist;
} Term;

// would be possible to have several terminals as tabbed or split screens
// I suggest to use i3 or "tabbed", so I'm not going to add that
// the visible terminal
extern Term *term;
// help terminal.
extern Term *p_help;
// alt screen
extern Term *p_alt;

// temporary storages
extern Term *p_help_storedterm;
extern Term *p_term;

// pid of executed shell
extern pid_t shellpid;

/*
Printable characters in ASCII, used to estimate the advance width
of single wide characters.

 */
#ifdef UTF8
static char ascii_printable[]
    = " !\"#$%&'()*+,-./0123456789:;<=>?"
      "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
      "`abcdefghijklmnopqrstuvwxyz{|}~";
#else
// no utf8, but using extended ascii.
// unused. todo: remove that.
// This table was used to calculate the medium advance of fonts without
// fixed width. However, fonts are used as fixed width fonts in each case.
//
/*
static char unused_ascii_printable[]
    = " !\"#$%&'()*+,-./0123456789:;<=>?"
      "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
      "`abcdefghijklmnopqrstuvwxyz{|}~"
			"�������������������/������������"
			"���}�����������������{�����%����"
			"���]�����������"
			"������[�����$���";
			*/
#endif


code:
// the help content.
// It's shown on F1, and compiled into st.
// I guess, it's better to have a help screen always there,
// when to load it (hopefully, when nothing did fuck up).
// Especially, it's maybe 1/100 of the memory usage,
// and furthermore quite possibly shared anyways.
static char* helpcontents = "\
\n\r\
     slterm - slim terminal emulator for X \n\r\
\n\r\
http://github.com/michael105/slterm\n\r\
Version: 0.99.5.7\n\r\
\n\r\
\n\r\
 Help \n\r\
\n\r\
\n\r\
Colors\n\r\
\n\r\
\e[1;30mecho -e \"\\e[A;Nm\" text ..\n\r\
N: fg color 30 - 37, and bg color 40 - 47\n\r\
\n\r\
255 colors\n\r\
echo -e \"\\e[A;38;5;Nm\" text ...\n\r\
echo -e \"\\e[0,48,5,Nm text\" for colored background.\n\r\
N: color 0-255\n\r\
A: 0=normal, 1=light, 2=dark\n\r\
\n\r\
\n\r\
\n\r\
 Keybinding reference \n\r\
\n\r\
The keycombinations can be changed in config.h(recompile needed)\n\r\
\n\r\
For other information please have a look into the man page,\n\r\
also accessible with 'slterm -H'\n\r\
\n\r\
\n\r\
 Keystrokes \n\r\
\n\r\
There are 4 different modes in slterm;\n\r\
regular mode, lessmode, selection mode and alt mode(alt screen).\n\r\
slterm is started in the regular mode.\n\r\
When the alt mode is used, e.g. by man or links, \n\r\
scrollmarks and lessmode are disabled.\n\r\
\n\r\
\n\r\
All modes:\n\r\
\n\r\
  Ctrl+Shift + I: Inverse colors\n\r\
  (Anymod)+F1:    Show this help\n\r\
\n\r\
\n\r\
Set font width/size:\n\r\
\n\r\
  Ctrl+Shift + Insert/Delete:   Enlarge/Shrink width\n\r\
  Ctrl+Shift + Home/End:        Enlarge/Shrink height\n\r\
  Ctrl+Shift + PageUp/PageDown: Zoom in / out\n\r\
  Ctrl+Shift + Backspace:       Reset font display\n\r\
\n\r\
\n\r\
Select Codepage:\n\r\
\n\r\
  Ctrl+Win + 0 CP1250 \n\r\
             1 CP1251\n\r\
             2 CP1252 (this is mostly ANSI, and the 1. page of Unicode)\n\r\
             3 CP1253\n\r\
             4 CP437  (Old IBM codetable, borders and tables)\n\r\
             5 CP850  (DOS Standard table)\n\r\
             6 CP4002 (Custom table, mix of 1252 and 437)\n\r\
\n\r\
\n\r\
Regular mode:\n\r\
\n\r\
Scrolling:\n\r\
\n\r\
  Shift + Up/Down/PageUp/Pagedown: Scroll up/down\n\r\
  Shift + Home/End:  Scroll to top/bottom\n\r\
  Shift + Backspace: Scroll to the location of the last command (shell)\n\r\
                     and enter lessmode\n\r\
  Shift+Enter:       Execute command, enter lessmode if more than\n\r\
                     one screen is displayed by the command.\n\r\
\n\r\
\n\r\
Clipboard:\n\r\
\n\r\
  Shift + Insert / Ctrl+Shift + y:  Paste\n\r\
  Ctrl+Shift + c / mouse selection: Copy \n\r\
\n\r\
\n\r\
Scrollmarks:\n\r\
\n\r\
  Ctrl+Alt + [0..9]: Set Scrollmark 0 - 9\n\r\
  Ctrl + [0..9]:     Scroll to mark 0 - 9\n\r\
  Shift + Backspace: Scroll to the last entered command (in shell)\n\r\
\n\r\
\n\r\
\n\r\
Lessmode:\n\r\
\n\r\
  Alt+Shift + Down/PageDown/Up/PageUp/l, ScrollLock:  Enter lessmode. \n\r\
  Scroll around with cursor keys, Home, End.\n\r\
  Backspace/Tab left go to the location of the previous command in shell,\n\r\
  Tab scrolls down to the next entered command location.\n\r\
  Exit with q/ESC\n\r\
\n\r\
  Shift+Backspace/Tab left: Scroll to the location of the last entered command,\n\r\
    enter lessmode\n\r\
\n\r\
  Tab: Scroll downwards to the next entered command line\n\r\
\n\r\
  Ctrl+Alt + [0..9]: Set Scrollmark 0 - 9\n\r\
      Ctrl + [0..9]: Goto Scrollmark 0 - 9\n\r\
  Lessmode:  [0..9]: Goto Retmark 1 - 10\n\r\
\n\r\
\n\r\
\n\r\
Selection Mode:\n\r\
\n\r\
  Ctrl+Shift + S, Alt + S: Enter selection mode\n\r\
\n\r\
  There are 3 submodes in selection mode:\n\r\
    - move mode : to set the start of the selection;\n\r\
    - select mode : to activate and set the end of the selection;\n\r\
    - input mode : to enter the search criteria.\n\r\
	\n\r\
\n\r\
  Shortcuts for move and select modes :\n\r\
 \n\r\
 h, j, k, l:    move cursor left/down/up/right (also with arrow keys)\n\r\
 !, _, *:       move cursor to the middle of the line/column/screen\n\r\
 Backspace,Home move cursor to the beginning of line\n\r\
 $,End          move cursor to end of line\n\r\
 PgUp, PgDown:  move cursor to the beginning/end of the column\n\r\
 g, G:          move cursor to the top/bottom left corner of the screen\n\r\
\n\r\
 y:             In move mode, select the current line.\n\r\
                In select mode, copy the selection to the clipboard, quit selection\n\r\
					 (yy means yank the current line)\n\r\
 /, ?:          activate input mode and search up/down\n\r\
 n, N:          repeat last search, up/down\n\r\
\n\r\
 s:             toggle move/selection mode\n\r\
 v/V:           toggle move/selection mode, select regular/rectangular type\n\r\
 t:             toggle regular/rectangular selection type\n\r\
\n\r\
 p:             quit keyboard_select, copy and paste selection\n\r\
 Return:        quit keyboard_select, copy selection to clipboard\n\r\
 Escape:        quit keyboard_select\n\r\
 \n\r\
 With h,j,k,l (also with arrow keys), you can use a quantifier.\n\r\
 Enter a number before hitting the appropriate key.\n\r\
 \n\r\
\n\r\
  Shortcuts for input mode :\n\r\
 \n\r\
 Return:       Return to the previous mode\n\r\
 \n\r\
 \n\r\
\n\r\
 Shortcut list, without selection mode bindings \n\r\
\n\r\
\n\r\
Mode\t Modifiers\t\t Key\t\t Function\t Info\n\r\
\n\r\
\n\r\
All	 All                	 Break      	 sendbreak 	\n\r\
All	 All                	 Print      	 printsel 	\n\r\
All	 All                	 Scroll_Lock 	 lessmode_toggle 	\n\r\
All	 Alt                	 s          	 keyboard_select 	\n\r\
All	 Alt+Shift          	 Down       	 lessmode_toggle 	\n\r\
All	 Alt+Shift          	 Home       	 lessmode_toggle 	\n\r\
All	 Alt+Shift          	 L          	 lessmode_toggle 	\n\r\
All	 Alt+Shift          	 Page_Down  	 lessmode_toggle 	\n\r\
All	 Alt+Shift          	 Page_Up    	 lessmode_toggle 	\n\r\
All	 Alt+Shift          	 Up         	 lessmode_toggle 	\n\r\
All	 Control            	 0          	 scrollmark 	\n\r\
All	 Control            	 1          	 scrollmark 	\n\r\
All	 Control            	 2          	 scrollmark 	\n\r\
All	 Control            	 3          	 scrollmark 	\n\r\
All	 Control            	 4          	 scrollmark 	\n\r\
All	 Control            	 5          	 scrollmark 	\n\r\
All	 Control            	 6          	 scrollmark 	\n\r\
All	 Control            	 7          	 scrollmark 	\n\r\
All	 Control            	 8          	 scrollmark 	\n\r\
All	 Control            	 9          	 scrollmark 	\n\r\
All	 Control            	 F1         	 showhelp 	\n\r\
All	 Control            	 Print      	 toggleprinter 	\n\r\
All	 Control+Alt        	 0          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 1          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 2          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 3          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 4          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 5          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 6          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 7          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 8          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 9          	 set_scrollmark 	\n\r\
All	 Control+Alt        	 Return     	 enterscroll 	\n\r\
All	 Control+Shift      	 C          	 clipcopy 	\n\r\
All	 Control+Shift      	 I          	 inverse_screen 	\n\r\
All	 Control+Shift      	 Num_Lock   	 numlock 	\n\r\
All	 Control+Shift      	 S          	 keyboard_select 	\n\r\
All	 Control+Shift      	 V          	 clippaste 	\n\r\
All	 Control+Shift      	 Y          	 selpaste 	\n\r\
All	 Control+Win        	 0          	 set_charmap 	\n\r\
All	 Control+Win        	 1          	 set_charmap 	\n\r\
All	 Control+Win        	 2          	 set_charmap 	\n\r\
All	 Control+Win        	 3          	 set_charmap 	\n\r\
All	 Control+Win        	 4          	 set_charmap 	\n\r\
All	 Control+Win        	 5          	 set_charmap 	\n\r\
All	 Control+Win        	 6          	 set_charmap 	\n\r\
All	 Control+Win        	 7          	 set_charmap 	\n\r\
All	 Control+Win        	 8          	 set_charmap 	\n\r\
All	 Control+Win        	 9          	 set_charmap 	\n\r\
All	 Shift              	 BackSpace  	 retmark 	\n\r\
All	 Shift              	 Down       	 kscrolldown 	\n\r\
All	 Shift              	 End        	 scrolltobottom 	\n\r\
All	 Shift              	 Home       	 scrolltotop 	\n\r\
All	 Shift              	 ISO_Left_Tab 	 retmark 	\n\r\
All	 Shift              	 Insert     	 selpaste 	\n\r\
All	 Shift              	 Page_Down  	 kscrolldown 	\n\r\
All	 Shift              	 Page_Up    	 kscrollup 	\n\r\
All	 Shift              	 Print      	 printscreen 	\n\r\
All	 Shift              	 Return     	 enterscroll 	\n\r\
All	 Shift              	 Up         	 kscrollup 	\n\r\
All	 Shift+Control      	 BackSpace  	 zoomreset 	\n\r\
All	 Shift+Control      	 Delete     	 set_fontwidth 	\n\r\
All	 Shift+Control      	 End        	 set_fontheight 	\n\r\
All	 Shift+Control      	 Home       	 set_fontheight 	\n\r\
All	 Shift+Control      	 Insert     	 set_fontwidth 	\n\r\
All	 Shift+Control      	 Page_Down  	 zoom 	\n\r\
All	 Shift+Control      	 Page_Up    	 zoom 	\n\r\
Help	 All                	 ALL_KEYS   	 dummy 	\n\r\
Help	 All                	 Down       	 kscrolldown 	\n\r\
Help	 All                	 End        	 scrolltobottom 	\n\r\
Help	 All                	 Escape     	 quithelp 	\n\r\
Help	 All                	 Home       	 scrolltotop 	\n\r\
Help	 All                	 Page_Down  	 kscrolldown 	\n\r\
Help	 All                	 Page_Up    	 kscrollup 	\n\r\
Help	 All                	 Up         	 kscrollup 	\n\r\
Help	 All                	 q          	 quithelp 	\n\r\
Help	 All                	 space      	 kscrolldown 	\n\r\
Less	 All                	 0          	 retmark 	\n\r\
Less	 All                	 1          	 retmark 	\n\r\
Less	 All                	 2          	 retmark 	\n\r\
Less	 All                	 3          	 retmark 	\n\r\
Less	 All                	 4          	 retmark 	\n\r\
Less	 All                	 5          	 retmark 	\n\r\
Less	 All                	 6          	 retmark 	\n\r\
Less	 All                	 7          	 retmark 	\n\r\
Less	 All                	 8          	 retmark 	\n\r\
Less	 All                	 9          	 retmark 	\n\r\
Less	 All                	 BackSpace  	 retmark 	\n\r\
Less	 All                	 Down       	 kscrolldown 	\n\r\
Less	 All                	 End        	 scrolltobottom 	\n\r\
Less	 All                	 Escape     	 lessmode_toggle 	\n\r\
Less	 All                	 Home       	 scrolltotop 	\n\r\
Less	 All                	 Page_Down  	 kscrolldown 	\n\r\
Less	 All                	 Page_Up    	 kscrollup 	\n\r\
Less	 All                	 Tab        	 retmark 	\n\r\
Less	 All                	 Up         	 kscrollup 	\n\r\
Less	 All                	 q          	 lessmode_toggle 	\n\r\
Less	 All                	 space      	 kscrolldown 	\n\r\
Less	 Shift              	 Return     	 lessmode_toggle 	\n\r\
MODE_DEFAULT	 Control+Shift|Alt|Win 	 I          	 dump_terminfo 	\n\r\
\n\r\
\n\r\
\n\r\
===============================================================================\n\r\
\n\r\
(2019-2025 miSc, Michael 147, started with the suckless st sources)\n\r\
\n\r\
License: MIT\n\r\
Permission is hereby granted, free of charge, to any person obtaining a copy\n\r\
of this software and associated documentation files (the Software), to deal\n\r\
in the Software without restriction, including without limitation the rights\n\r\
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\r\
copies of the Software, and to permit persons to whom the Software is\n\r\
furnished to do so, subject to the following conditions:\n\r\
\n\r\
The above copyright notice and this permission notice shall be included in all\n\r\
copies or substantial portions of the Software.\n\r\
\n\r\
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\r\
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\r\
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\r\
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\r\
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\r\
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\r\
SOFTWARE.\n\r\
\n\r\
";


code:
// the help content.
// It's shown on F1, and compiled into st.
// I guess, it's better to have a help screen always there,
// when to load it (hopefully, when nothing did fuck up).
// Especially, it's maybe 1/100 of the memory usage,
// and furthermore quite possibly shared anyways.
static char* helpcontents = "\
\n\r\
     slterm - slim terminal emulator for X \n\r\
\n\r\
http://github.com/michael105/slterm\n\r\
Version: {VERSION}\n\r\
\n\r\
\n\r\
 Help \n\r\
\n\r\
\n\r\
Colors\n\r\
\n\r\
\e[1;30mecho -e \"\\e[A;Nm\" text ..\n\r\
N: fg color 30 - 37, and bg color 40 - 47\n\r\
\n\r\
255 colors\n\r\
echo -e \"\\e[A;38;5;Nm\" text ...\n\r\
echo -e \"\\e[0,48,5,Nm text\" for colored background.\n\r\
N: color 0-255\n\r\
A: 0=normal, 1=light, 2=dark\n\r\
\n\r\
\n\r\
\n\r\


code:
$YELLOW Keybinding reference $NORM

The keycombinations can be changed in config.h(recompile needed)

For other information please have a look into the man page,
also accessible with 'slterm -H'

$YELLOW Keystrokes $NORM

${GRAY}There are 4 different modes in slterm;
regular mode, lessmode, selection mode and alt mode(alt screen).
slterm is started in the regular mode.
When the alt mode is used, e.g. by man or links,
scrollmarks and lessmode are disabled.
$N

$UL${LGREEN}All modes:$N

  Ctrl+Shift + I: Inverse colors
  (Anymod)+F1:    Show this help

${BOLD}Set font width/size:$N

  Ctrl+Shift + Insert/Delete:   Enlarge/Shrink width
  Ctrl+Shift + Home/End:        Enlarge/Shrink height
  Ctrl+Shift + PageUp/PageDown: Zoom in / out
  Ctrl+Shift + Backspace:       Reset font display

${BOLD}Select Codepage:$N

  Ctrl+Win + 0 CP1250
             1 CP1251
             2 CP1252 ${GRAY}(this is mostly ANSI, and the 1. page of Unicode)$N
             3 CP1253
             4 CP437  ${GRAY}(Old IBM codetable, borders and tables)$N
             5 CP850  ${GRAY}(DOS Standard table)$N
             6 CP4002 ${GRAY}(Custom table, mix of 1252 and 437)$N

$BOLD${UL}${LGREEN}Regular mode:$N

${BOLD}Scrolling:$N

  Shift + Up/Down/PageUp/Pagedown: Scroll up/down
  Shift + Home/End:  Scroll to top/bottom
  Shift + Backspace: Scroll to the location of the last command (shell)
                     and enter lessmode
  Shift+Enter:       Execute command, enter lessmode if more than
                     one screen is displayed by the command.

${BOLD}Clipboard:$N

  Shift + Insert / Ctrl+Shift + y:  Paste
  Ctrl+Shift + c / mouse selection: Copy

${BOLD}Scrollmarks:$N

  Ctrl+Alt + [0..9]: Set Scrollmark 0 - 9
  Ctrl + [0..9]:     Scroll to mark 0 - 9
  Shift + Backspace: Scroll to the last entered command (in shell)

$BOLD${UL}${LGREEN}Lessmode:$N

  Alt+Shift + Down/PageDown/Up/PageUp/l, ScrollLock:  Enter lessmode.
  Scroll around with cursor keys, Home, End.
  Backspace/Tab left go to the location of the previous command in shell,
  Tab scrolls down to the next entered command location.
  Exit with q/ESC

  Shift+Backspace/Tab left: Scroll to the location of the last entered command,
    enter lessmode

  Tab: Scroll downwards to the next entered command line

  Ctrl+Alt + [0..9]: Set Scrollmark 0 - 9
      Ctrl + [0..9]: Goto Scrollmark 0 - 9
  Lessmode:  [0..9]: Goto Retmark 1 - 10

$BOLD${UL}${LGREEN}Selection Mode:$N

  Ctrl+Shift + S, Alt + S: Enter selection mode

  There are 3 submodes in selection mode:
    - move mode : to set the start of the selection;
    - select mode : to activate and set the end of the selection;
    - input mode : to enter the search criteria.

  Shortcuts for move and select modes :

 h, j, k, l:    move cursor left/down/up/right (also with arrow keys)
 !, _, *:       move cursor to the middle of the line/column/screen
 Backspace,Home move cursor to the beginning of line
 $,End          move cursor to end of line
 PgUp, PgDown:  move cursor to the beginning/end of the column
 g, G:          move cursor to the top/bottom left corner of the screen

 y:             In move mode, select the current line.
                In select mode, copy the selection to the clipboard, quit selection
					 (yy means yank the current line)
 /, ?:          activate input mode and search up/down
 n, N:          repeat last search, up/down

 s:             toggle move/selection mode
 v/V:           toggle move/selection mode, select regular/rectangular type
 t:             toggle regular/rectangular selection type

 p:             quit keyboard_select, copy and paste selection
 Return:        quit keyboard_select, copy selection to clipboard
 Escape:        quit keyboard_select

 With h,j,k,l (also with arrow keys), you can use a quantifier.
 Enter a number before hitting the appropriate key.

  Shortcuts for input mode :

 Return:       Return to the previous mode

${YELLOW} Shortcut list, without selection mode bindings $NORM

Mode\t Modifiers\t\t Key\t\t Function\t Info


code:
$CYAN     slterm - ${MAGENTA}sl${CYAN}im ${MAGENTA}t${CYAN}erminal emulator for X $NORM

(Homepage: http://github.com/michael105/slterm)

$YELLOW Help $NORM

Colors

Colors can be printed at the shell with 'echo -e \"\e[A;Nm\"'

A: one of 0..7, text attributes
N: fg color 30 - 37, and bg color 40 - 47

255 colors are available with: 'echo -e \"\e[A;38;5;Nm\"',
and 'echo -e \"\e[0,48,5,Nm'\" for the background.
A: 0=normal, 1=light, 2=dark


code:
#ifndef includes_h
#define includes_h
// all system includes
#include <X11/X.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/Xatom.h>
#include <X11/Xft/Xft.h>
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/cursorfont.h>

#include <ctype.h>
#include <fcntl.h>
#include <pty.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <stdint.h>
#include <strings.h>

#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <signal.h>
#include <time.h>

#endif


code:
All	 All                	 Break      	 sendbreak
All	 All                	 Print      	 printsel
All	 All                	 Scroll_Lock 	 lessmode_toggle
All	 Alt                	 s          	 keyboard_select
All	 Alt+Shift          	 Down       	 lessmode_toggle
All	 Alt+Shift          	 Home       	 lessmode_toggle
All	 Alt+Shift          	 L          	 lessmode_toggle
All	 Alt+Shift          	 Page_Down  	 lessmode_toggle
All	 Alt+Shift          	 Page_Up    	 lessmode_toggle
All	 Alt+Shift          	 Up         	 lessmode_toggle
All	 Control            	 0          	 scrollmark
All	 Control            	 1          	 scrollmark
All	 Control            	 2          	 scrollmark
All	 Control            	 3          	 scrollmark
All	 Control            	 4          	 scrollmark
All	 Control            	 5          	 scrollmark
All	 Control            	 6          	 scrollmark
All	 Control            	 7          	 scrollmark
All	 Control            	 8          	 scrollmark
All	 Control            	 9          	 scrollmark
All	 Control            	 F1         	 showhelp
All	 Control            	 Print      	 toggleprinter
All	 Control+Alt        	 0          	 set_scrollmark
All	 Control+Alt        	 1          	 set_scrollmark
All	 Control+Alt        	 2          	 set_scrollmark
All	 Control+Alt        	 3          	 set_scrollmark
All	 Control+Alt        	 4          	 set_scrollmark
All	 Control+Alt        	 5          	 set_scrollmark
All	 Control+Alt        	 6          	 set_scrollmark
All	 Control+Alt        	 7          	 set_scrollmark
All	 Control+Alt        	 8          	 set_scrollmark
All	 Control+Alt        	 9          	 set_scrollmark
All	 Control+Alt        	 Return     	 enterscroll
All	 Control+Shift      	 C          	 clipcopy
All	 Control+Shift      	 I          	 inverse_screen
All	 Control+Shift      	 Num_Lock   	 numlock
All	 Control+Shift      	 S          	 keyboard_select
All	 Control+Shift      	 V          	 clippaste
All	 Control+Shift      	 Y          	 selpaste
All	 Control+Win        	 0          	 set_charmap
All	 Control+Win        	 1          	 set_charmap
All	 Control+Win        	 2          	 set_charmap
All	 Control+Win        	 3          	 set_charmap
All	 Control+Win        	 4          	 set_charmap
All	 Control+Win        	 5          	 set_charmap
All	 Control+Win        	 6          	 set_charmap
All	 Control+Win        	 7          	 set_charmap
All	 Control+Win        	 8          	 set_charmap
All	 Control+Win        	 9          	 set_charmap
All	 Shift              	 BackSpace  	 retmark
All	 Shift              	 Down       	 kscrolldown
All	 Shift              	 End        	 scrolltobottom
All	 Shift              	 Home       	 scrolltotop
All	 Shift              	 ISO_Left_Tab 	 retmark
All	 Shift              	 Insert     	 selpaste
All	 Shift              	 Page_Down  	 kscrolldown
All	 Shift              	 Page_Up    	 kscrollup
All	 Shift              	 Print      	 printscreen
All	 Shift              	 Return     	 enterscroll
All	 Shift              	 Up         	 kscrollup
All	 Shift+Control      	 BackSpace  	 zoomreset
All	 Shift+Control      	 Delete     	 set_fontwidth
All	 Shift+Control      	 End        	 set_fontheight
All	 Shift+Control      	 Home       	 set_fontheight
All	 Shift+Control      	 Insert     	 set_fontwidth
All	 Shift+Control      	 Page_Down  	 zoom
All	 Shift+Control      	 Page_Up    	 zoom
Help	 All                	 ALL_KEYS   	 dummy
Help	 All                	 Down       	 kscrolldown
Help	 All                	 End        	 scrolltobottom
Help	 All                	 Escape     	 quithelp
Help	 All                	 Home       	 scrolltotop
Help	 All                	 Page_Down  	 kscrolldown
Help	 All                	 Page_Up    	 kscrollup
Help	 All                	 Up         	 kscrollup
Help	 All                	 q          	 quithelp
Help	 All                	 space      	 kscrolldown
Less	 All                	 0          	 retmark
Less	 All                	 1          	 retmark
Less	 All                	 2          	 retmark
Less	 All                	 3          	 retmark
Less	 All                	 4          	 retmark
Less	 All                	 5          	 retmark
Less	 All                	 6          	 retmark
Less	 All                	 7          	 retmark
Less	 All                	 8          	 retmark
Less	 All                	 9          	 retmark
Less	 All                	 BackSpace  	 retmark
Less	 All                	 Down       	 kscrolldown
Less	 All                	 End        	 scrolltobottom
Less	 All                	 Escape     	 lessmode_toggle
Less	 All                	 Home       	 scrolltotop
Less	 All                	 Page_Down  	 kscrolldown
Less	 All                	 Page_Up    	 kscrollup
Less	 All                	 Tab        	 retmark
Less	 All                	 Up         	 kscrollup
Less	 All                	 q          	 lessmode_toggle
Less	 All                	 space      	 kscrolldown
Less	 Shift              	 Return     	 lessmode_toggle
MODE_DEFAULT	 Control+Shift|Alt|Win 	 I          	 dump_terminfo


code:
// function main
// option parsing, config init.
//
//

#include "xwindow.h"
#include "xevent.h"
#include "includes.h"
#include "scroll.h"
#include "selection.h"
#include "system.h"
#include "compile.h"
#include "config.h"

#include <sys/mman.h>
#include <error.h>

#ifdef XRESOURCES
int resource_load(XrmDatabase db, char *name, enum resource_type rtype,
		void *dst) {
	char **sdst = dst;
	int *idst = dst;
	float *fdst = dst;

	char fullname[256];
	char fullclass[256];
	char *type;
	XrmValue ret;

	snprintf(fullname, sizeof(fullname), "%s.%s", opt_name ? opt_name : "st",
			name);
	snprintf(fullclass, sizeof(fullclass), "%s.%s", opt_class ? opt_class : "St",
			name);
	fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0';

	XrmGetResource(db, fullname, fullclass, &type, &ret);
	if (ret.addr == NULL || strncmp("String", type, 64))
		return 1;

	switch (rtype) {
		case STRING:
			*sdst = ret.addr;
			break;
		case INTEGER:
			*idst = strtoul(ret.addr, NULL, 10);
			break;
		case FLOAT:
			*fdst = strtof(ret.addr, NULL);
			break;
	}
	return 0;
}

void config_init(void) {
	char *resm;
	XrmDatabase db;
	ResourcePref *p;

	XrmInitialize();
	resm = XResourceManagerString(xwin.dpy);
	if (!resm)
		return;

	db = XrmGetStringDatabase(resm);
	for (p = resources; p < resources + LEN(resources); p++)
		resource_load(db, p->name, p->type, p->dst);
}
#endif

void printversion(){
	fprintf( stderr, "slterm version " VERSION "\n" );
}

void printhelp(){
}

void fontusage(){
	fprintf(stderr,
			" slterm [-f fontname] [-fb boldname] [-fi italicname] [-fI bolditalicname]\n"
			"        [-fw fontwidth] [-fh fontheight] [other options]\n"
			"\n   The fontname format is specified in the fontconfig documentation,\n"
			"   http://freedesktop.org/software/fontconfig/fontconfig-user.html\n"
			"   A list of attributes is in doc/fontconfig.txt\n"
			"   Supply 0 to disable bold, italic or bolditalic fonts,\n"
			"   using colors only for the text rendering of the different fonts.\n"
			"\n");
}

void usage(void) {
	printversion();
	fprintf(stderr,"\nusage:\n\n"
			" slterm -H: show help\n"
		  	"        -I: dump terminfo file\n"
			"        -L display license\n\n"
			" slterm [-aiv] [-c class] [-f font] [-g geometry] [-n name] [-o file]\n"
			"        [-T title] [-t title] [-w windowid] [[-e] command [args ...]]\n\n"
			" slterm [-aiv] [-c class] [-f font] [-g geometry] [-n name] [-o file]\n"
			"        [-x] [-v] [-V] [-X] [-T title] [-t title] [-w windowid] -l line\n"
			"        [stty_args ...]\n\n"
			);
	fontusage();

	fprintf(stderr," Original author Aurelien Aptel. 20xx-2019 st, suckless.\n 2020-2025 fork, slterm, misc147 github.com/michael105, MIT license\n\n");

	exit(0);
}

void missingfontname( const char* option ){
	fprintf(stderr, "Missing font name for option %s\nUsage: ",
			option );
	fontusage();
	exit(1);
}

#ifdef shared
// share the whole text segment, including main
int shared_main(int argc, char *argv[]) {
		iofd = 1; //
#else
int main(int argc, char *argv[]) {
#endif
	xwin.l = xwin.t = 0;
	xwin.isfixed = False;
	twin.cursor = cursorshape;
	argv0 = *argv;

#define EARGF(_unneeded)  ({ if ( ! *++argv ){ fprintf(stderr, "missing option for %s\n", argv[-1] ); usage(); }; *argv; })

#define ARGFONT(_type) ({ if ( ! *++argv ) missingfontname( argv[-1] ); \
		if ( **argv == '0' ){ _type##_font = 0; use##_type##font=0; } \
		else { _type##_font = *argv; use##_type##font=1; opt_##_type##_font = 1; } \
		*argv; })

	int useregularfont=1; // dummy

	while ( *++argv && argv[0][0] == '-'  ){
		for ( char *opt = *argv; *opt && *++opt; ){
		//for ( char *opt = *argv+1; *opt; *opt ? opt++:0 ){
			switch ( *opt ){
#ifdef INCLUDETERMINFO
				case 'I': // dump terminfo
					write(STDOUT_FILENO, slterm_terminfo, strlen(slterm_terminfo) );
					exit(0);
#endif
#ifdef INCLUDELICENSE
				case 'L':
					write(STDOUT_FILENO, slterm_license, strlen(slterm_license) );
					exit(0);
#endif
#ifdef INCLUDEMANPAGE
				case 'H':
					write(STDOUT_FILENO, slterm_man, strlen(slterm_man) );
					exit(0);
#endif
				case 'X':
					if ( mlockall(MCL_CURRENT|MCL_FUTURE) ){
						perror("Unable to lock memory pages into memory");
						exit(errno);
					}
					break;
				case 'a':
					allowaltscreen = 0;
					break;
				case 'c':
					opt_class = EARGF(usage());
					break;
				case 'e':
					argv++;
					goto run;
				case 'f':
					switch ( *++opt ){
						case 'w': fontwidth = atoi( EARGF() ); break;
						case 'h': fontheight = atoi( EARGF() ); break;
						case 'R':
						case 'b': ARGFONT(bold); break;
						case 'i': ARGFONT(italic); break;
						case 'I': ARGFONT(bolditalic); break;

						case 'r':
						case 0:
							opt_font = ARGFONT(regular);
							break;
						default:
							usage();
					}
					break;
				case 'g':
					xwin.gm = XParseGeometry(EARGF(usage()), &xwin.l, &xwin.t, &cols, &rows);
					break;
				case 'i':
					xwin.isfixed = 1;
					break;
				case 'o':
					opt_io = EARGF(usage());
					break;
				case 'l':
					opt_line = EARGF(usage());
					break;
				case 'n':
					opt_name = EARGF(usage());
					break;
				case 't':
				case 'T':
					opt_title = EARGF(usage());
					break;
				case 'w':
					opt_embed = EARGF(usage());
					break;
				case 'x':
					opt_xresources = 1;
					break;
				case 'v':
					printversion();
					exit(0);
				case 'V':
					printversion();
# define __Q(_x) #_x
# define _Q(_x) __Q(_x)
					printf( "Git Revision " _GITREVISION_"\n");
					printf( "\nCompiled " __COMPILEDATE__ "\n"
							__UNAME__ "\n"
							"CC: "__CC__" "__CC_VERSION__"\n\n"
							"Compileflags: "
							__OPT_FLAG__ "\n"
							"HISTORY: %d\n"
							"DEBUGLEVEL: "__ENABLEDEBUG__"\n"
							"XRESOURCES: "__XRESOURCES__"\n"
							"XProtocol Version " _Q(X_PROTOCOL) "." _Q(X_PROTOCOL_REVISION) "\n"
							__COMPILECOMMAND__ "\n", ( 1<<HISTSIZEBITS) );
					exit(0);
				case 127: // silence unused var warning
					printf( "%d",useregularfont );
				default:
					fprintf( stderr, "Unknown option: %c\n\n", *opt );
				case 'h':
					usage();
			}
		}
	}

run:
	//if (argc > 0) /* eat all remaining arguments */
	if ( *argv )
		opt_cmd = argv;

	if (!opt_title)
		opt_title = (opt_line || !opt_cmd) ? "slterm" : opt_cmd[0];

	//setlocale(LC_CTYPE, "");
	XSetLocaleModifiers("");

#ifdef XRESOURCES
	if (!(xwin.dpy = XOpenDisplay(NULL)))
		die("Can't open display\n");

	if (opt_xresources)
		config_init();
#endif

	cols = MAX(cols, 1);
	rows = MAX(rows, 1);
	tnew(cols, rows, HISTSIZE);
	xinit(cols, rows);
	xsetenv();
	selinit();

	create_unicode_table(); // unicode to current cp table
	sort_shortcuts();

	run();

	return 0;
}


code:
#ifndef MEM_C
#define MEM_C

// memory related functions

#include "mem.h"

// Set glyphs (non utf8 - one glyph is one int32 )
void memset32( uint32_t* i, uint32_t value, int count ){
	for ( int a=0; a<count; a++ )
		i[a] = value;
	return;

	// marginal gain. (if at all)
	/*
	if ( count & 1 )
		i[count-1] = value;

	uint c =  count>>1;
	uint64_t i64 = value | (ulong)( (ulong)value << 32UL );
	asm volatile ("rep stosq\n" : "+D"(i), "+c"(c): "a"(i64) : "cc", "memory" );
	*/
}

// optimized, only usable for even count values
void memset64( uint32_t* i, uint32_t value, int count ){
	count >>=1;
	uint64_t i64 = value | (ulong)( (ulong)value << 32UL );
	asm volatile ("rep stosq\n" : "+D"(i), "+c"(count): "a"(i64) : "cc", "memory" );
}

ssize_t xwrite(int fd, const char *s, size_t len) {
	size_t aux = len;
	ssize_t r;

	while (len > 0) {
		r = write(fd, s, len);
		if (r < 0) {
			return r;
		}
		len -= r;
		s += r;
	}

	return aux;
}
// opt - power of 8? -> memset32 -> memset64
void *xmalloc(size_t len) {
	void *p;

	if (!(p = malloc(len))) {
		die("malloc: %s\n", strerror(errno));
	}

	return p;
}

void *xcalloc(size_t len) {
	void *p;

	if (!(p = malloc(len))) {
		die("malloc: %s\n", strerror(errno));
	}
	bzero( p, len );

	return p;
}

void *xrealloc(void *p, size_t len) {
	if ((p = realloc(p, len)) == NULL) {
		die("realloc: %s\n", strerror(errno));
	}

	return p;
}

char *xstrdup(char *s) {
	if ((s = strdup(s)) == NULL) {
		die("strdup: %s\n", strerror(errno));
	}

	return s;
}

#endif


code:
#ifndef mem_h
#define mem_h

#include "includes.h"

void memset32( uint32_t* i, uint32_t value, int count );
void memset64( uint32_t* i, uint32_t value, int count );

ssize_t xwrite(int fd, const char *s, size_t len);
void *xmalloc(size_t len);
void *xcalloc(size_t len);
void *xrealloc(void *p, size_t len);
char *xstrdup(char *s);

#endif


code:
/* retmarks.
 stored in term->retmarks[N]
the store is used circular,
term->current_retmark points to the currently unused place,
the last retmark can be accessed with (current_retmark - 1) &( RETMARKCOUNT-1)
The position is given as term->histindex (histindex)
*/

void retmark_scrolledup(){ // scan upwards for the next retmark, update
	for ( int t = term->current_retmark - (term->scrolled_retmark?term->scrolled_retmark:1);
			t!=term->current_retmark;
			t-- ){
		t &= ( RETMARKCOUNT-1 );
		DBG("mark upw: %d   %d\n",t, term->retmarks[t] );
		if ( (term->retmarks[t]==0) || (term->histindex - term->retmarks[t] >= term->scr) ){
			term->scrolled_retmark = (term->current_retmark - t) & ( RETMARKCOUNT-1);
			return;
		}
	}
}

void retmark_scrolleddown(){ // scan downwards for the next retmark, update
	for ( int t = (term->current_retmark - (term->scrolled_retmark?term->scrolled_retmark:1 )) & (RETMARKCOUNT-1); t!=term->current_retmark;
			t = (t+1) & ( RETMARKCOUNT-1 ) ){
		if ( (term->histindex - term->retmarks[t] < term->scr) ){
			term->scrolled_retmark = ( term->current_retmark - t + 1 ) & ( RETMARKCOUNT-1);
			//DBG("mark: %d   %d\n",t, term->retmarks[t] );
			return;
		}
	}
	// at the bottom
	term->scrolled_retmark = 1;
}

void set_retmark() {
	if (term==p_alt) return;
	// check, if (e.g. vi) we are above the last retmark
	if ( (term->retmarks[ (term->current_retmark - 1) & (RETMARKCOUNT-1) ] <
				(term->histindex + term->cursor.y ) )  ||
		( term->circledhist && (term->retmarks[ (term->current_retmark - 1) & (RETMARKCOUNT-1) ] >
			 (term->histindex + term->cursor.y + term->histsize/2 )  ) ) ){
		// second case: circled buffer, first case: try to detect screen based programs
		// e.g. vim
		term->retmarks[ term->current_retmark ] = term->histindex + term->cursor.y;
		term->current_retmark = (term->current_retmark + 1) & (RETMARKCOUNT-1);
		term->scrolled_retmark = 0;
	}
	//DBG("Setretmark: n:%d histindex:%d scr:%d  cursor: %d\n", term->current_retmark, term->histindex, term->scr, term->cursor.y );
}

void retmark(const Arg* a){
	if (term==p_alt) return; // not usable in alt screen
									 //DBG("Retmark: n:%d scrm:%d histindex:%d scr:%d   scroll_mark %d  current_mark %d\n", term->rows, term->retmarks[0],term->histindex, term->scr, term->scroll_retmark, term->current_retmark );

									 // rewrite that. (count curentretmark from 0 to UINT_MAX. Limit bits when
									 // accessing the array. ->scrolled_retmark can be set absolute.

	if ( a->i == -1 ){ // tab right in lessmode -> scrolling down

		// todo: rewrite. also for a circled buffer
		int b = 1;
		// todo: reverse scanning.
		for ( int t = (term->current_retmark +1 ) & (RETMARKCOUNT-1); t!=term->current_retmark;
				t = (t+1) & ( RETMARKCOUNT-1 ) ){
			//if ( (term->retmarks[t] < term->histindex - term->scr) ){
			if ( (term->histindex - term->retmarks[t] < term->scr) ){
				term->scr=(term->histindex - term->retmarks[t]);
				term->scrolled_retmark = ( term->current_retmark - t ) & ( RETMARKCOUNT-1);
				b = 0;
				//DBG("mark: %d   %d\n",t, term->retmarks[t] );
				break;
			}
		}
		if ( b ){
			term->scrolled_retmark = 0;
			scrolltobottom();
		}

		} else if ( a->i >= 1 ){ // number, scroll to retmark number x
			int t = (term->current_retmark - a->i ) & (RETMARKCOUNT-1);
			term->scr=(term->histindex-term->retmarks[t]);
			term->scrolled_retmark = ( term->current_retmark - t ) & ( RETMARKCOUNT-1);

		} else if ( a->i == -2 ){ // = key '0'
			term->scrolled_retmark = 0;
			scrolltobottom();
		} else { // scroll backward / Up
			if ( term->histindex<term->rows){ // at the top
				scrolltotop();
				lessmode_toggle( ARGPi(LESSMODE_ON) );
				return;
			}

#if 0
			// todo: scroll to next retmark
			int t = term->scrolled_retmark;
			if ( t ){
				//t= (t-1) & (RETMARKCOUNT-1);
				t--;
				term->scrolled_retmark = t;
				term->scr=(term->histindex - term->retmarks[(term->current_retmark - t)&(RETMARKCOUNT-1) ]);

			}
#else
			for ( int t = (term->current_retmark -1 ) & (RETMARKCOUNT-1); t!=term->current_retmark;
					t = (t-1) & ( RETMARKCOUNT-1 ) ){
				DBG("mark: %d   %d\n",t, term->retmarks[t] );
				if ( (term->retmarks[t]==0) || (term->histindex - term->retmarks[t] > term->scr) ){
					term->scr=(term->histindex - term->retmarks[t]);
					term->scrolled_retmark = (term->current_retmark - t) & ( RETMARKCOUNT-1);
					break;
				}
			}
#endif

	}
	DBG("scr: %d\n", term->scr );
	if ( term->scr<0 ){
		// TODO: circledhist
		if ( term->circledhist )
			term->scr&=(term->histsize);
		else
			term->scr=0;

		DBG("scr: %d\n", term->scr );
	};
	//DBG("Retmark OUT: n:%d scrm:%d histindex:%d scr:%d   scroll_mark %d  current_mark %d\n", term->rows, term->retmarks[0],term->histindex, term->scr, term->scroll_retmark, term->current_retmark );

	selscroll(0, term->scr);
	tfulldirt();
	//updatestatus();
	lessmode_toggle( ARGPi(LESSMODE_ON) );
}


code:
#include "scroll.h"

void tsetscroll(int t, int b) {
	LIMIT(t, 0, term->rows - 1);
	LIMIT(b, 0, term->rows - 1);
	if (t > b) {
		//		SWAPint( t,b );
		term->top = b;
		term->bot = t;
	} else {
		term->top = t;
		term->bot = b;
	}
}

// scroll downwards
void kscrolldown(const Arg *a) {
	int n = a->i;

	DBG("kscrolldown, n: %d, guard: %x\n",n, term->guard);
	if (n < 0) { // scroll a page
		n = term->rows + n;
	}

	if (n > term->scr) {  // at the bottom ( bottom: scr=0 )
		n = term->scr;
	}
	DBG2("kscrolldown2, n: %d\n",n);

	if (term->scr > 0) {
		term->scr -= n;
		selscroll(0, -n);
		retmark_scrolleddown();

		tfulldirt();
		updatestatus();
	}
}

void scrolltobottom(){
	DBG("scrolltobottom\n"); // xxx
	if ( term->scr ){
		term->scr=0;
		selscroll(0, 0);
		retmark_scrolleddown();
		tfulldirt();
		updatestatus();
	}
}

void scrolltotop(){
	DBG("totop\n");
	term->scr=term->histsize + 1; //HSTSIZE
	if ( (term->circledhist==0) && (term->scr>term->histindex ) )
		term->scr=term->histindex; //
	selscroll(0, term->scr);
	retmark_scrolledup();
	tfulldirt();
	updatestatus();
}

// scroll upwards (Shift+up)_
void kscrollup(const Arg *a) {
	int n = a->i;

	DBG2("kscrollup, n: %d, term->histindex: %d, term->rows: %d scr: %d\n",
			n, term->histindex, term->rows, term->scr);
	if (n < 0) { // scroll a page upwards
					 //n = term->rows + n;
		n = term->rows;
	}
	DBG2("kscrollup2, n: %d\n",n);

	if ( term->scr <= term->histsize - n + 1) { //HSTSIZE
		term->scr += n;

		if ( (term->circledhist==0) && (term->scr>term->histindex ) )
			term->scr=term->histindex; // at the top

		DBG2("kscrollup3, scr: %d\n",term->scr);
		selscroll(0, n);
		retmark_scrolledup();
		tfulldirt();
		updatestatus();
	}
}

void scroll( const Arg *a){
	Arg d;
	switch ( a->i ){ // scrolling and the history make me dizzy. misc.
						  // At some time I'm going to rewrite that
		case SCROLL_BOTTOM:
			scrolltobottom();
			break;
		case SCROLL_TOP:
			scrolltotop();
			break;
		case SCROLL_PAGEUP:
			d.i = -1;
			kscrollup( &d );
			break;
		case SCROLL_PAGEDOWN:
			d.i = -1;
			kscrolldown( &d );
			break;
		default:
			if ( ISSCROLLDOWN(a->i) ){
				kscrolldown( a );
			} else if ( ISSCROLLUP(a->i) ){
				d.i = a->i & SCROLL_LINEMASK;
				kscrollup( &d );
			}
	}
}

void set_scrollmark(const Arg *a) {
	if (term==p_alt) return;
	term->scrollmarks[a->i] = term->histindex - term->scr+1;
	updatestatus();
	//DBG("Setscrollmark: n:%d histindex:%d scr:%d\n", a->i, term->histindex, term->scr );
}

void scrollmark(const Arg *a){
	if (term==p_alt) return;
	//DBG("Scrollmark: n:%d scrm:%d histindex:%d scr:%d\n", a->i, term->scrollmarks[a->i],term->histindex, term->scr );
	//	if ( term->scrollmarks[a->i] ){
	if ( term->scrollmarks[a->i] )
		term->scr=term->histindex-term->scrollmarks[a->i]+1;
	else
		term->scr=term->histindex-term->scrollmarks[a->i];
	selscroll(0, term->scr);
	tfulldirt();
	updatestatus();
	//	}
}

void tscrolldown(int orig, int n, int copyhist) {
	int i;

	DBG("===== tscrolldown, orig:%d n:%d , histindex: %d  scr: %d copyhist: %d term->bot: %d\n",orig,n, term->histindex, term->scr, copyhist, term->bot);
	if ( term->histindex == 0 && IS_SET(MODE_ALTSCREEN) ){ //xxx bug patch. alt screen
																		// else segfaults. reproduce: man man; and scroll with a (now, since this is patched rotfl) unknown combination of commands.
																		//DBG("RETURN\n"); // xxx
																		//return; // ? strange. no segfaults anymore.
	}
	//LIMIT(n, 0, term->bot - orig ); //xxx
	LIMIT(n, 0, term->bot - orig + 1);

	if (copyhist) {
		term->histindex = (term->histindex - 1 ) & (term->histsize);
		SWAPp( term->hist[term->histindex], term->line[term->bot] );
		///DBG("copyhist: term->histindex %d   %p <->  %p  \n", term->histindex, term->hist[term->histindex], term->line[term->bot] );

		if (  term->line[term->bot] == 0 ){
			///DBG("newline .");
			//term->circledhist=1; //?
			term->line[term->bot]  = xmalloc( term->colalloc * sizeof(Glyph));
			memset( term->line[term->bot]  ,0,term->colalloc * sizeof(Glyph));
		}

	}

	tsetdirt(orig, term->bot - n);
	tclearregion(0, term->bot - n + 1, term->cols - 1, term->bot);

	for (i = term->bot; i >= orig + n; i--) {
		SWAPp( term->line[i], term->line[i-n] );
	}

	selscroll(orig, n);
}

// Scroll downwards (And append lines)
// doesn't neccessarily scroll the view.
// Thanks for the names. Afterall, upwards is downwards, its just only the opposite.
void tscrollup(int orig, int n, int copyhist) {
	int i;

	DBG("===== tscrollup, orig:%d n:%d , histindex: %d  scr: %d copyhist: %d \n",orig,n, term->histindex, term->scr, copyhist);
	//LIMIT(n, 0, term->bot - orig ); //xxx
	LIMIT(n, 0, term->bot - orig + 1);

	if (copyhist) {
		DBG("term->histindex: %d\n", term->histindex);
		term->histindex = (term->histindex + 1) & (term->histsize);
		DBG("term->histindex: %d, \n", term->histindex);
		if ( term->histindex == 0 ){
			if ( term->circledhist == 0 ){
				term->circledhist=1;
				DBG("circledhist = 1");
				// dirty bugfix below. didn't find the real problem
				term->hist[0] = xmalloc( term->colalloc * sizeof(Glyph));
				memset(term->hist[0],0,term->colalloc * sizeof(Glyph));
			}
		}

		if ( term->hist[term->histindex] ){
			DBG("SWAP cthist %p, histindex %d, orig %d\n", term->hist[term->histindex], term->histindex, orig);

			if (  term->line[term->bot] == 0 ){
				DBG("newline 2 .");
				//term->circledhist=1; //?
				term->line[term->bot]  = xmalloc( term->colalloc * sizeof(Glyph));
				memset( term->line[term->bot]  ,0,term->colalloc * sizeof(Glyph));
			}
			SWAPp( term->hist[term->histindex], term->line[orig] );

		}	 else {
			DBG("New line, cthist %d, term->histindex: %d, term->col: %d\n", 1, term->histindex, term->cols );
			term->hist[term->histindex] = term->line[orig];
			term->line[orig] = xmalloc( term->colalloc * sizeof(Glyph) );
			memset(term->line[orig],0,term->colalloc * sizeof(Glyph));
		}
		// (candidate for swap or, malloc hist here) done.
		// "compression" might take place here, as well.
		// sort of count*glyph for adjacent equal glyphs.
		// Maybe another text attribute. Then, the next glyph
		// as union int gives the count. Giving for, e.g. an empty line
		// with 200 cols a compression ratio of 200/2 (misc)
	}

	DBG("1\n");
	if (term->scr > 0 && term->scr < term->histsize + 1 ) { //HSTSIZE
		term->scr = MIN(term->scr + n, term->histsize );  //HISTSIZE - 1);
	}

	DBG("1a\n");
	tclearregion(0, orig, term->colalloc - 1, orig + n - 1);
	DBG("2\n");
	tsetdirt(orig + n, term->bot);

	DBG("swap: %d   %d\n",orig,term->bot);
	for (i = orig; i <= term->bot - n; i++) {
		SWAPp(term->line[i],term->line[i+n]);
	}

	selscroll(orig, -n);
	DBG("scrd: %d %d %d %d %d %d\n", orig, n, term->histindex, term->scr, term->scrollmarks[0], term->rows);
	if ( enterlessmode ){ // scroll down until next line.
		if ( term->histindex > term->scrollmarks[0]){
			//DBG("Scroll\n");
			term->scr=(term->histindex)-term->scrollmarks[0];
			selscroll(0, term->scr);
			//tfulldirt();
			lessmode_toggle(ARGP(i=LESSMODE_ON));
			enterlessmode = 0;
			//a.i=0;
			//scrollmark(&a);

			//inputmode |= MODE_LESS;
			//set_notifmode( 2, -1 ); // show message "less"
		}
	}

}

void tnewline(int first_col) {
	int y = term->cursor.y;

	//xxx
	//DBG("tnewline: %d, term->scr: %d  histindex: %d\n",first_col, term->scr,term->histindex );
	if (y == term->bot) {
		tscrollup(term->top, 1, 1);
	} else {
		y++;
	}
	tmoveto(first_col ? 0 : term->cursor.x, y);
	updatestatus();
}

void enterscroll(const Arg *a){
	if (term==p_alt) return;
	//DBG("enterscroll: %d %d %d %d\n",term->histindex,term->rows,term->scr,term->cursor.y);

	term->scrollmarks[0] = term->histindex+ term->rows - ( term->rows - term->cursor.y );
	enterlessmode = term->rows;
	ttywrite((utfchar*)"\n",1,1);
}

void leavescroll(const Arg *a){
	enterlessmode = 0;
	ttywrite((utfchar*)"\n",1,1);
}

// Argument Arg.i is one of LESSMODE_ON, LESSMODE_OFF, LESSMODE_TOGGLE
// can be or'ed with SCROLL (all definitions), then scrolls also
void lessmode_toggle(const Arg *a){
	if (term!=p_term) return;

	switch( a->i & LESSMODEMASK ){
		case LESSMODE_ON:
			inputmode |= MODE_LESS;
			//tfulldirt();
			break;
		case LESSMODE_OFF:
			inputmode &= ~MODE_LESS;
			break;
		case LESSMODE_TOGGLE:
			inputmode ^= MODE_LESS;
			break;
	}

	if ( a->i & ~LESSMODEMASK ){
		//DBG("scroll %d\n",a->i);
		//DBG("scroll %d\n",d.i);
		scroll(ARGP(i = SCROLLMASK & a->i ));
	}

	if ( inputmode & MODE_LESS ){ // enabled
		showstatus(1,"-LESS-");
		updatestatus();
	} else { // disable
		showstatus(0,0);
		//scrolltobottom();
	}
}


code:
#ifndef scroll_h
#define scroll_h

#include "xevent.h"

// SCROLL and LESSMODE are to be or'ed and given to lessmode_toggle
#define LESSMODE(mode) ({ enum { off=(1<<28), toggle=((1<<28)|(1<<29)), on=(1<<29) }; mode; })
#define LESSMODE_OFF (1<<28)
#define LESSMODE_ON (1<<29)
#define LESSMODE_TOGGLE (LESSMODE_OFF|LESSMODE_ON)
#define SCROLL_BOTTOM (1<<26)
#define SCROLL_PAGEDOWN (1<<30)
#define SCROLL_PAGEUP ((1<<30)|(1<<31))
#define SCROLL_TOP (1<<31)
#define SCROLLUP(x) (x|(1<<27))
// scroll down x lines, argument to scroll
#define SCROLLDOWN(x) (x&(~(1<<27)))
#define LESSMODEMASK (3<<28)
#define SCROLLMASK ((3<<30)|((1<<28)-1))
#define SCROLL_LINEMASK ((1<<26)-1)

#define ISSCROLLDOWN(x) ( !(x&(1<<27)) && ( (x&SCROLL_LINEMASK)>0 ))
//#define ISSCROLLDOWN(x) ( x>0 && x<(1<<31) )
//#define ISSCROLLUP(x) ( x<0 && x> -(1UI<<32UI) )
#define ISSCROLLUP(x) ( x&(1<<27) )
//#define ISSCROLLUP(x) ( ( x&(SCROLLMASK&&(~(1<<32))) ) && (x&(1<<32)) )

//extern int scrollmarks[12];
//extern int retmarks[10];

/* Notes for history (todo)
term->scr : scroll
term->histpos: line in history buffer
term->pos : histpos - scroll

line: (TLINE): position of screen 'line' in the buffer

double buffering of screen(?)

zwei optionen: entweder in der history raw terminal input speichern.
schwierig: scrollen, kann sprunghaft werden. oder aufwendig.
(bsp farben: fast unmoeglich)
oder eben den output, wie gehabt.

*/

// callbacks
void kscrolldown(const Arg *);
void kscrollup(const Arg *);
// Argument Arg.i is one of LESSMODE_ON, LESSMODE_OFF, LESSMODE_TOGGLE
// can be or'ed with SCROLL (all definitions), then scrolls also
void lessmode_toggle(const Arg*);

void set_scrollmark(const Arg *a);
void set_retmark();
void retmark(const Arg *a);
void scrollmark(const Arg *a);
void leavescroll(const Arg *a);
void enterscroll(const Arg *a);

// scroll up i lines (-1,..) down (1,..), i must be < (1<<30) (actually > as well )
// or SCROLL(bottom), SCROLL(pagedown), SCROLL(pageup) SCROLL(top)
void scroll(const Arg *a);

void tnewline(int);
void scrolltobottom();
void scrolltotop();

void tsetscroll(int, int);
void tscrollup(int, int, int);
void tscrolldown(int, int, int);

#endif