code:
// selection.c
//
// selection mode, and mouse selection

#include "xwindow.h"

#include "term.h"

#include <X11/X.h>
#include <X11/keysym.h>

#include "utf8.h"
#include "selection.h"
#include "termdraw.h"
#include "mem.h"

#include "debug.h"

Selection sel;

static int sel_savedcursor;

void keyboard_select(const Arg *dummy) {
	twin.mode ^= trt_kbdselect(-1, NULL, 0);
	sel_savedcursor = xgetcursor();
	xsetcursor(11); // empty block
}

void selstart(int col, int row, int snap) {
	selclear();
	sel.mode = SEL_EMPTY;
	sel.type = SEL_REGULAR;
	sel.alt = IS_SET(MODE_ALTSCREEN);
	sel.snap = snap;
	sel.oe.x = sel.ob.x = col;
	sel.oe.y = sel.ob.y = row;
	selnormalize();

	if (sel.snap != 0) {
		sel.mode = SEL_READY;
	}
	xsetcursor(2); // block
	tsetdirt(sel.nb.y, sel.ne.y);
}

void mousesel(XEvent *e, int done) {
	int type, seltype = SEL_REGULAR;
	uint state = e->xbutton.state & ~(Button1Mask | forcemousemod);

	for (type = 1; type < LEN(selmasks); ++type) {
		if (match(selmasks[type], state)) {
			seltype = type;
			break;
		}
	}
	selextend(evcol(e), evrow(e), seltype, done);
	if (done)
		setsel(getsel(), e->xbutton.time);
}

void selextend(int col, int row, int type, int done) {
	int oldey, oldex, oldsby, oldsey, oldtype;

	if (sel.mode == SEL_IDLE) {
		return;
	}
	if (done && sel.mode == SEL_EMPTY) {
		selclear();
		return;
	}

	oldey = sel.oe.y;
	oldex = sel.oe.x;
	oldsby = sel.nb.y;
	oldsey = sel.ne.y;
	oldtype = sel.type;

	sel.oe.x = col;
	sel.oe.y = row;
	selnormalize();
	sel.type = type;

	if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type ||
			sel.mode == SEL_EMPTY) {
		tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
	}

	sel.mode = done ? SEL_IDLE : SEL_READY;
}

void selclear(void) {
	if (sel.ob.x == -1) {
		return;
	}
	sel.mode = SEL_IDLE;
	sel.ob.x = -1;
	tsetdirt(sel.nb.y, sel.ne.y);
}

void select_or_drawcursor(int selectsearch_mode, int type) {
	int done = 0;

	if (selectsearch_mode & 1) {
		selextend(term->cursor.x, term->cursor.y, type, done);
		xsetsel(getsel());
	} else { //
		xdrawcursor(term->cursor.x, term->cursor.y, TLINE(term->cursor.y)[term->cursor.x], term->ocx,
				term->ocy, TLINE(term->ocy)[term->ocx]);
	}
}

void selinit(void) {
	sel.mode = SEL_IDLE;
	sel.snap = 0;
	sel.ob.x = -1;
}

void selnormalize(void) {
	int i;

	if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
		sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
		sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
	} else {
		sel.nb.x = MIN(sel.ob.x, sel.oe.x);
		sel.ne.x = MAX(sel.ob.x, sel.oe.x);
	}
	sel.nb.y = MIN(sel.ob.y, sel.oe.y);
	sel.ne.y = MAX(sel.ob.y, sel.oe.y);

	selsnap(&sel.nb.x, &sel.nb.y, -1);
	selsnap(&sel.ne.x, &sel.ne.y, +1);

	/* expand selection over line breaks */
	if (sel.type == SEL_RECTANGULAR) {
		return;
	}
	i = tlinelen(sel.nb.y);
	if (i < sel.nb.x) {
		sel.nb.x = i;
	}
	if (tlinelen(sel.ne.y) <= sel.ne.x) {
		sel.ne.x = term->cols - 1;
	}
}

int selected(int x, int y) {
	if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
			sel.alt != IS_SET(MODE_ALTSCREEN)) {
		return 0;
	}

	if (sel.type == SEL_RECTANGULAR) {
		return BETWEEN(y, sel.nb.y, sel.ne.y) && BETWEEN(x, sel.nb.x, sel.ne.x);
	}

	return BETWEEN(y, sel.nb.y, sel.ne.y) && (y != sel.nb.y || x >= sel.nb.x) &&
		(y != sel.ne.y || x <= sel.ne.x);
}

void search(int selectsearch_mode, Rune *target, int ptarget, int incr,
		int type, TCursor *cu) {
	Rune *r;
	int i, bound = (term->cols * cu->y + cu->x) * (incr > 0) + incr;

	for (i = term->cols * term->cursor.y + term->cursor.x + incr; i != bound; i += incr) {
		for (r = target; r - target < ptarget; r++) {
			if (*r ==
					term->line[(i + r - target) / term->cols][(i + r - target) % term->cols]
					.u) {
				if (r - target == ptarget - 1) {
					break;
				}
			} else {
				r = NULL;
				break;
			}
		}
		if (r != NULL) {
			break;
		}
	}

	if (i != bound) {
		term->cursor.y = i / term->cols, term->cursor.x = i % term->cols;
		select_or_drawcursor(selectsearch_mode, type);
	}
}

void selsnap(int *x, int *y, int direction) {
	int newx, newy, xt, yt;
	int delim, prevdelim;
	Glyph *gp, *prevgp;

	switch (sel.snap) {
		case SNAP_WORD:
			/*
			 * Snap around if the word wraps around at the end or
			 * beginning of a line.
			 */
			prevgp = &TLINE(*y)[*x];
			prevdelim = ISDELIM(prevgp->u);
			for (;;) {
				newx = *x + direction;
				newy = *y;
				if (!BETWEEN(newx, 0, term->cols - 1)) {
					newy += direction;
					newx = (newx + term->cols ) % term->cols;
					if (!BETWEEN(newy, 0, term->rows - 1)) {
						break;
					}

					if (direction > 0) {
						yt = *y, xt = *x;
					} else {
						yt = newy, xt = newx;
					}
					if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) {
						break;
					}
				}

				if (newx >= tlinelen(newy)) {
					break;
				}

				gp = &TLINE(newy)[newx];
				delim = ISDELIM(gp->u);
#ifdef UTF8
				if (!(gp->mode & ATTR_WDUMMY) &&
						(delim != prevdelim || (delim && gp->u != prevgp->u))) {
					break;
				}
#else
				if (delim != prevdelim || (delim && gp->u != prevgp->u)) {
					break;
				}
#endif

				*x = newx;
				*y = newy;
				prevgp = gp;
				prevdelim = delim;
			}
			break;
		case SNAP_LINE:
			/*
			 * Snap around if the the previous line or the current one
			 * has set ATTR_WRAP at its end. Then the whole next or
			 * previous line will be selected.
			 */
			*x = (direction < 0) ? 0 : term->cols - 1;
			if (direction < 0) {
				for (; *y > 0; *y += direction) {
					if (!(TLINE(*y - 1)[term->cols - 1].mode & ATTR_WRAP)) {
						break;
					}
				}
			} else if (direction > 0) {
				for (; *y < term->rows - 1; *y += direction) {
					if (!(TLINE(*y)[term->cols - 1].mode & ATTR_WRAP)) {
						break;
					}
				}
			}
			break;
	}
}

char *getsel(void) {
	char *str, *ptr;
	int y, bufsize, lastx, linelen;
	Glyph *gp, *last;

	if (sel.ob.x == -1) {
		return NULL;
	}

	bufsize = (term->cols + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ;
	ptr = str = xmalloc(bufsize);

	/* append every set & selected glyph to the selection */
	for (y = sel.nb.y; y <= sel.ne.y; y++) {
		if ((linelen = tlinelen(y)) == 0) {
			*ptr++ = '\n';
			continue;
		}

		if (sel.type == SEL_RECTANGULAR) {
			gp = &TLINE(y)[sel.nb.x];
			lastx = sel.ne.x;
		} else {
			gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
			lastx = (sel.ne.y == y) ? sel.ne.x : term->cols - 1;
		}
		last = &TLINE(y)[MIN(lastx, linelen - 1)];
		while (last >= gp && last->u == ' ') {
			--last;
		}

		for (; gp <= last; ++gp) {
#ifdef UTF8
			if (gp->mode & ATTR_WDUMMY) {
				continue;
			}
			ptr += utf8encode(gp->u, ptr);
#else
			*ptr = gp->u;
			ptr++;
#endif
		}

		/*
		 * Copy and pasting of line endings is inconsistent
		 * in the inconsistent terminal and GUI world.
		 * The best solution seems like to produce '\n' when
		 * something is copied from st and convert '\n' to
		 * '\r', when something to be pasted is received by
		 * st.
		 * FIXME: Fix the computer world.
		 */
		if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP)) {
			*ptr++ = '\n';
		}
	}
	*ptr = 0;
	return str;
}

void selscroll(int orig, int n) {
	if (sel.ob.x == -1) {
		return;
	}

	if (BETWEEN(sel.ob.y, orig, term->bot) || BETWEEN(sel.oe.y, orig, term->bot)) {
		if ((sel.ob.y += n) > term->bot || (sel.oe.y += n) < term->top) {
			selclear();
			return;
		}
		if (sel.type == SEL_RECTANGULAR) {
			if (sel.ob.y < term->top) {
				sel.ob.y = term->top;
			}
			if (sel.oe.y > term->bot) {
				sel.oe.y = term->bot;
			}
		} else {
			if (sel.ob.y < term->top) {
				sel.ob.y = term->top;
				sel.ob.x = 0;
			}
			if (sel.oe.y > term->bot) {
				sel.oe.y = term->bot;
				sel.oe.x = term->cols;
			}
		}
		selnormalize();
	}
}

int trt_kbdselect(KeySym ksym, char *buf, int len) {
	static TCursor cu;
	static Rune target[64];
	static int type = 1, ptarget, in_use;
	static int sens, quant;
	static char selectsearch_mode;
	int i, bound, *xy;

	if (selectsearch_mode & 2) { //
		dbg("selectsearch_mode & 2\n");
		if (ksym == XK_Return) {
			selectsearch_mode ^= 2;
			set_notifmode(selectsearch_mode, -2);
			if (ksym == XK_Escape) {
				ptarget = 0;
			}
			return 0;
		} else if (ksym == XK_BackSpace) {
			if (!ptarget) {
				return 0;
			}
			term->line[term->bot][ptarget--].u = ' ';
		} else if (len < 1) {
			return 0;
		} else if (ptarget == term->cols || ksym == XK_Escape) {
			return 0;
		} else {
			utf8decode(buf, &target[ptarget++], len);
			term->line[term->bot][ptarget].u = target[ptarget - 1];
		}

		if (ksym != XK_BackSpace) {
			search(selectsearch_mode, &target[0], ptarget, sens, type, &cu);
		}

		term->dirty[term->bot] = 1;
		drawregion(0, term->bot, term->cols, term->bot + 1);
		return 0;
	}

	switch (ksym) {
		case -1:
			in_use = 1;
			cu.x = term->cursor.x, cu.y = term->cursor.y;
			set_notifmode(0, ksym);
			return MODE_KBDSELECT;
		case XK_s:
		case XK_v:
		case XK_V:
			if (selectsearch_mode & 1) {
				selclear();
				xsetcursor(11); // empty block
			} else {
				selstart(term->cursor.x, term->cursor.y, 0);
			}
			set_notifmode(selectsearch_mode ^= 1, ksym);
#if 1
			if ( ksym == XK_V ){
				if ( type ^ 3 ){
					type ^= 3;
					selextend(term->cursor.x, term->cursor.y, type, i = 0); /* 2 fois */
					selextend(term->cursor.x, term->cursor.y, type, i = 0); /* 2 fois */
				}
			}
			if ( ksym == XK_v ){
				if ( !(type ^ 3) ){
					type ^= 3;
					selextend(term->cursor.x, term->cursor.y, type, i = 0); /* 2 fois */
					selextend(term->cursor.x, term->cursor.y, type, i = 0); /* 2 fois */
				}
			}   // well. patched. works.
				 // type is static. and this enum doesn't seem to , whatever.
#endif
			break;
		case XK_t:
			selextend(term->cursor.x, term->cursor.y, type ^= 3, i = 0); /* 2 fois */
			selextend(term->cursor.x, term->cursor.y, type, i = 0);
			break;
		case XK_slash:
		case XK_KP_Divide:
		case XK_question:
			ksym &= XK_question; /* Divide to slash */
			sens = (ksym == XK_slash) ? -1 : 1;
			ptarget = 0;
			set_notifmode(15, ksym);
			selectsearch_mode ^= 2;
			break;
		case XK_y:
			if ( selectsearch_mode == 0 ){
				selstart(0,term->cursor.y,0);
				set_notifmode(selectsearch_mode = 1, ksym);
				selextend(term->cols - 1, term->cursor.y, type, 0);
				xsetsel(getsel());
				break;
			}
			goto L_XK_Return;
		case XK_q:
		case XK_Escape:
			if (!in_use) {
				break;
			}
			selclear();
		case XK_Return:
			L_XK_Return:
			set_notifmode(4, ksym);
			term->cursor.x = cu.x, term->cursor.y = cu.y;
			select_or_drawcursor(selectsearch_mode = 0, type);
			in_use = quant = 0;
			xsetcursor( sel_savedcursor );
			return MODE_KBDSELECT;

		case XK_p:
			set_notifmode(4, ksym);
			char *tmp = getsel();
			term->cursor.x = cu.x, term->cursor.y = cu.y;
			select_or_drawcursor(selectsearch_mode = 0, type);
			in_use = quant = 0;
			xsetcursor( sel_savedcursor );
			ttywrite( (utfchar*)tmp, strlen(tmp), 1 );
			return MODE_KBDSELECT;

		case XK_n:
		case XK_N:
			if (ptarget) {
				search(selectsearch_mode, &target[0], ptarget, (ksym == XK_n) ? -1 : 1,
						type, &cu);
			}
			break;
		case XK_BackSpace:
			term->cursor.x = 0;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		case XK_dollar:
			term->cursor.x = term->cols - 1;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		case XK_g:
			term->cursor.x = 0, term->cursor.y = 0;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		case XK_Home:
			term->cursor.x = 0;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		case XK_G:
			term->cursor.x = cu.x, term->cursor.y = cu.y;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		case XK_End:
			term->cursor.x = term->cols - 1;
			select_or_drawcursor(selectsearch_mode, type);
			break;

		case XK_Page_Up:
		case XK_Page_Down:
			term->cursor.y = (ksym == XK_Prior) ? 0 : cu.y;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		case XK_exclam:
			term->cursor.x = term->cols >> 1;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		case XK_asterisk:
		case XK_KP_Multiply:
			term->cursor.x = term->cols >> 1;
		case XK_underscore:
			term->cursor.y = cu.y >> 1;
			select_or_drawcursor(selectsearch_mode, type);
			break;
		default:
			if (ksym >= XK_0 && ksym <= XK_9) { /* 0-9 keyboard */
				quant = (quant * 10) + (ksym ^ XK_0);
				return 0;
			} else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) { /* 0-9 numpad */
				quant = (quant * 10) + (ksym ^ XK_KP_0);
				return 0;
			} else if (ksym == XK_k || ksym == XK_h) {
				i = ksym & 1;
			} else if (ksym == XK_l || ksym == XK_j) {
				i = ((ksym & 6) | 4) >> 1;
			} else if ((XK_Home & ksym) != XK_Home || (i = (ksym ^ XK_Home) - 1) > 3) {
				break;
			}

			xy = (i & 1) ? &term->cursor.y : &term->cursor.x;
			sens = (i & 2) ? 1 : -1;
			bound = (i >> 1 ^ 1) ? 0 : (i ^ 3) ? term->cols - 1 : term->bot;

			if (quant == 0) {
				quant++;
			}

			if (*xy == bound && ((sens < 0 && bound == 0) || (sens > 0 && bound > 0))) {
				break;
			}

			*xy += quant * sens;
			if (*xy < 0 || (bound > 0 && *xy > bound)) {
				*xy = bound;
			}

			select_or_drawcursor(selectsearch_mode, type);
	}
	quant = 0;
	return 0;
}


code:
#ifndef selection_h
#define selection_h

#include "includes.h"
#include <stddef.h>

typedef struct {
  int mode;
  int type;
  int snap;
  /*
   * Selection variables:
   * nb – normalized coordinates of the beginning of the selection
   * ne – normalized coordinates of the end of the selection
   * ob – original coordinates of the beginning of the selection
   * oe – original coordinates of the end of the selection
   */
  struct {
    int x, y;
  } nb, ne, ob, oe;

  int alt;
} Selection;

enum selection_mode { SEL_IDLE = 0, SEL_EMPTY = 1, SEL_READY = 2 };

enum selection_type { SEL_REGULAR = 1, SEL_RECTANGULAR = 2 };

enum selection_snap { SNAP_WORD = 1, SNAP_LINE = 2 };

// selction

extern Selection sel;

//  callbacks
void keyboard_select(const Arg *);

void mousesel(XEvent *, int);

void selnormalize(void);
void selscroll(int, int);
void selsnap(int *, int *, int);

void setsel(char *, Time);
void xsetsel(char *);

void selclear(void);
void selinit(void);
void selstart(int, int, int);
void selextend(int, int, int, int);
int selected(int, int);
char *getsel(void);

int trt_kbdselect(KeySym, char *, int);

static void tdumpsel(void);

#endif

repo_name: misc/slterm

license: mit


code:
/*
# Small Deflate
`sdefl` is a small bare bone lossless compression library in ANSI C (ISO C90)
which implements the Deflate (RFC 1951) compressed data format specification standard.
It is mainly tuned to get as much speed and compression ratio from as little code
as needed to keep the implementation as concise as possible.

## Features
- Portable single header and source file duo written in ANSI C (ISO C90)
- Dual license with either MIT or public domain
- Small implementation
    - Deflate: 525 LoC
    - Inflate: 500 LoC
- Webassembly:
    - Deflate ~3.7 KB (~2.2KB compressed)
    - Inflate ~3.6 KB (~2.2KB compressed)

## Usage:
This file behaves differently depending on what symbols you define
before including it.

Header-File mode:
If you do not define `SINFL_IMPLEMENTATION` before including this file, it
will operate in header only mode. In this mode it declares all used structs
and the API of the library without including the implementation of the library.

Implementation mode:
If you define `SINFL_IMPLEMENTATION` before including this file, it will
compile the implementation. Make sure that you only include
this file implementation in *one* C or C++ file to prevent collisions.

### Benchmark

| Compressor name         | Compression| Decompress.| Compr. size | Ratio |
| ------------------------| -----------| -----------| ----------- | ----- |
| miniz 1.0 -1            |   122 MB/s |   208 MB/s |    48510028 | 48.51 |
| miniz 1.0 -6            |    27 MB/s |   260 MB/s |    36513697 | 36.51 |
| miniz 1.0 -9            |    23 MB/s |   261 MB/s |    36460101 | 36.46 |
| zlib 1.2.11 -1          |    72 MB/s |   307 MB/s |    42298774 | 42.30 |
| zlib 1.2.11 -6          |    24 MB/s |   313 MB/s |    36548921 | 36.55 |
| zlib 1.2.11 -9          |    20 MB/s |   314 MB/s |    36475792 | 36.48 |
| sdefl 1.0 -0            |   127 MB/s |   355 MB/s |    40004116 | 39.88 |
| sdefl 1.0 -1            |   111 MB/s |   413 MB/s |    38940674 | 38.82 |
| sdefl 1.0 -5            |    45 MB/s |   436 MB/s |    36577183 | 36.46 |
| sdefl 1.0 -7            |    38 MB/s |   432 MB/s |    36523781 | 36.41 |
| libdeflate 1.3 -1       |   147 MB/s |   667 MB/s |    39597378 | 39.60 |
| libdeflate 1.3 -6       |    69 MB/s |   689 MB/s |    36648318 | 36.65 |
| libdeflate 1.3 -9       |    13 MB/s |   672 MB/s |    35197141 | 35.20 |
| libdeflate 1.3 -12      |  8.13 MB/s |   670 MB/s |    35100568 | 35.10 |

### Compression
Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia):

| File    |   Original | `sdefl 0`    | `sdefl 5`  | `sdefl 7`   |
| --------| -----------| -------------| ---------- | ------------|
| dickens | 10.192.446 | 4,260,187    |  3,845,261 |   3,833,657 |
| mozilla | 51.220.480 | 20,774,706   | 19,607,009 |  19,565,867 |
| mr      |  9.970.564 | 3,860,531    |  3,673,460 |   3,665,627 |
| nci     | 33.553.445 | 4,030,283    |  3,094,526 |   3,006,075 |
| ooffice |  6.152.192 | 3,320,063    |  3,186,373 |   3,183,815 |
| osdb    | 10.085.684 | 3,919,646    |  3,649,510 |   3,649,477 |
| reymont |  6.627.202 | 2,263,378    |  1,857,588 |   1,827,237 |
| samba   | 21.606.400 | 6,121,797    |  5,462,670 |   5,450,762 |
| sao     |  7.251.944 | 5,612,421    |  5,485,380 |   5,481,765 |
| webster | 41.458.703 | 13,972,648   | 12,059,432 |  11,991,421 |
| xml     |  5.345.280 | 886,620      |    674,009 |     662,141 |
| x-ray   |  8.474.240 | 6,304,655    |  6,244,779 |   6,244,779 |

## License
```
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2020-2023 Micha Mettke
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
```
*/
#ifndef SINFL_H_INCLUDED
#define SINFL_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif

#define SINFL_PRE_TBL_SIZE 128
#define SINFL_LIT_TBL_SIZE 1334
#define SINFL_OFF_TBL_SIZE 402

struct sinfl {
  const unsigned char *bitptr;
  unsigned long long bitbuf;
  int bitcnt;

  unsigned lits[SINFL_LIT_TBL_SIZE];
  unsigned dsts[SINFL_OFF_TBL_SIZE];
};
extern int sinflate(void *out, int cap, const void *in, int size);
extern int zsinflate(void *out, int cap, const void *in, int size);

#ifdef __cplusplus
}
#endif

#endif /* SINFL_H_INCLUDED */

#ifdef SINFL_IMPLEMENTATION

#include <string.h> /* memcpy, memset */
#include <assert.h> /* assert */

#if defined(__GNUC__) || defined(__clang__)
#define sinfl_likely(x)       __builtin_expect((x),1)
#define sinfl_unlikely(x)     __builtin_expect((x),0)
#else
#define sinfl_likely(x)       (x)
#define sinfl_unlikely(x)     (x)
#endif

#ifndef SINFL_NO_SIMD
#if defined(__x86_64__) || defined(_WIN32) || defined(_WIN64)
  #include <emmintrin.h>
  #define sinfl_char16 __m128i
  #define sinfl_char16_ld(p) _mm_loadu_si128((const __m128i *)(void*)(p))
  #define sinfl_char16_str(d,v)  _mm_storeu_si128((__m128i*)(void*)(d), v)
  #define sinfl_char16_char(c) _mm_set1_epi8(c)
#elif defined(__arm__) || defined(__aarch64__)
  #include <arm_neon.h>
  #define sinfl_char16 uint8x16_t
  #define sinfl_char16_ld(p) vld1q_u8((const unsigned char*)(p))
  #define sinfl_char16_str(d,v) vst1q_u8((unsigned char*)(d), v)
  #define sinfl_char16_char(c) vdupq_n_u8(c)
#else
  #define SINFL_NO_SIMD
#endif
#endif

static int
sinfl_bsr(unsigned n) {
#ifdef _MSC_VER
  _BitScanReverse(&n, n);
  return n;
#elif defined(__GNUC__) || defined(__clang__)
  return 31 - __builtin_clz(n);
#endif
}
static unsigned long long
sinfl_read64(const void *p) {
  unsigned long long n;
  memcpy(&n, p, 8);
  return n;
}
static void
sinfl_copy64(unsigned char **dst, unsigned char **src) {
  unsigned long long n;
  memcpy(&n, *src, 8);
  memcpy(*dst, &n, 8);
  *dst += 8, *src += 8;
}
static unsigned char*
sinfl_write64(unsigned char *dst, unsigned long long w) {
  memcpy(dst, &w, 8);
  return dst + 8;
}
#ifndef SINFL_NO_SIMD
static unsigned char*
sinfl_write128(unsigned char *dst, sinfl_char16 w) {
  sinfl_char16_str(dst, w);
  return dst + 8;
}
static void
sinfl_copy128(unsigned char **dst, unsigned char **src) {
  sinfl_char16 n = sinfl_char16_ld(*src);
  sinfl_char16_str(*dst, n);
  *dst += 16, *src += 16;
}
#endif
static void
sinfl_refill(struct sinfl *s) {
  s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt;
  s->bitptr += (63 - s->bitcnt) >> 3;
  s->bitcnt |= 56; /* bitcount in range [56,63] */
}
static int
sinfl_peek(struct sinfl *s, int cnt) {
  assert(cnt >= 0 && cnt <= 56);
  assert(cnt <= s->bitcnt);
  return s->bitbuf & ((1ull << cnt) - 1);
}
static void
sinfl_eat(struct sinfl *s, int cnt) {
  assert(cnt <= s->bitcnt);
  s->bitbuf >>= cnt;
  s->bitcnt -= cnt;
}
static int
sinfl__get(struct sinfl *s, int cnt) {
  int res = sinfl_peek(s, cnt);
  sinfl_eat(s, cnt);
  return res;
}
static int
sinfl_get(struct sinfl *s, int cnt) {
  sinfl_refill(s);
  return sinfl__get(s, cnt);
}
struct sinfl_gen {
  int len;
  int cnt;
  int word;
  short* sorted;
};
static int
sinfl_build_tbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits,
                const int *cnt) {
  int tbl_end = 0;
  while (!(gen->cnt = cnt[gen->len])) {
    ++gen->len;
  }
  tbl_end = 1 << gen->len;
  while (gen->len <= tbl_bits) {
    do {unsigned bit = 0;
      tbl[gen->word] = (*gen->sorted++ << 16) | gen->len;
      if (gen->word == tbl_end - 1) {
        for (; gen->len < tbl_bits; gen->len++) {
          memcpy(&tbl[tbl_end], tbl, (size_t)tbl_end * sizeof(tbl[0]));
          tbl_end <<= 1;
        }
        return 1;
      }
      bit = 1 << sinfl_bsr((unsigned)(gen->word ^ (tbl_end - 1)));
      gen->word &= bit - 1;
      gen->word |= bit;
    } while (--gen->cnt);
    do {
      if (++gen->len <= tbl_bits) {
        memcpy(&tbl[tbl_end], tbl, (size_t)tbl_end * sizeof(tbl[0]));
        tbl_end <<= 1;
      }
    } while (!(gen->cnt = cnt[gen->len]));
  }
  return 0;
}
static void
sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits,
                   const int *cnt) {
  int sub_bits = 0;
  int sub_start = 0;
  int sub_prefix = -1;
  int tbl_end = 1 << tbl_bits;
  while (1) {
    unsigned entry;
    int bit, stride, i;
    /* start new sub-table */
    if ((gen->word & ((1 << tbl_bits)-1)) != sub_prefix) {
      int used = 0;
      sub_prefix = gen->word & ((1 << tbl_bits)-1);
      sub_start = tbl_end;
      sub_bits = gen->len - tbl_bits;
      used = gen->cnt;
      while (used < (1 << sub_bits)) {
        sub_bits++;
        used = (used << 1) + cnt[tbl_bits + sub_bits];
      }
      tbl_end = sub_start + (1 << sub_bits);
      tbl[sub_prefix] = (sub_start << 16) | 0x10 | (sub_bits & 0xf);
    }
    /* fill sub-table */
    entry = (*gen->sorted << 16) | ((gen->len - tbl_bits) & 0xf);
    gen->sorted++;
    i = sub_start + (gen->word >> tbl_bits);
    stride = 1 << (gen->len - tbl_bits);
    do {
      tbl[i] = entry;
      i += stride;
    } while (i < tbl_end);
    if (gen->word == (1 << gen->len)-1) {
      return;
    }
    bit = 1 << sinfl_bsr(gen->word ^ ((1 << gen->len) - 1));
    gen->word &= bit - 1;
    gen->word |= bit;
    gen->cnt--;
    while (!gen->cnt) {
      gen->cnt = cnt[++gen->len];
    }
  }
}
static void
sinfl_build(unsigned *tbl, unsigned char *lens, int tbl_bits, int maxlen,
            int symcnt) {
  int i, used = 0;
  short sort[288];
  int cnt[16] = {0}, off[16]= {0};
  struct sinfl_gen gen = {0};
  gen.sorted = sort;
  gen.len = 1;

  for (i = 0; i < symcnt; ++i)
    cnt[lens[i]]++;
  off[1] = cnt[0];
  for (i = 1; i < maxlen; ++i) {
    off[i + 1] = off[i] + cnt[i];
    used = (used << 1) + cnt[i];
  }
  used = (used << 1) + cnt[i];
  for (i = 0; i < symcnt; ++i)
    gen.sorted[off[lens[i]]++] = (short)i;
  gen.sorted += off[0];

  if (used < (1 << maxlen)){
    for (i = 0; i < 1 << tbl_bits; ++i)
      tbl[i] = (0 << 16u) | 1;
    return;
  }
  if (!sinfl_build_tbl(&gen, tbl, tbl_bits, cnt)){
    sinfl_build_subtbl(&gen, tbl, tbl_bits, cnt);
  }
}
static int
sinfl_decode(struct sinfl *s, const unsigned *tbl, int bit_len) {
  int idx = sinfl_peek(s, bit_len);
  unsigned key = tbl[idx];
  if (key & 0x10) {
    /* sub-table lookup */
    int len = key & 0x0f;
    sinfl_eat(s, bit_len);
    idx = sinfl_peek(s, len);
    key = tbl[((key >> 16) & 0xffff) + (unsigned)idx];
  }
  sinfl_eat(s, key & 0x0f);
  return (key >> 16) & 0x0fff;
}
static int
sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) {
  static const unsigned char order[] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
  static const short dbase[30+2] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
      257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577};
  static const unsigned char dbits[30+2] = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,
      10,10,11,11,12,12,13,13,0,0};
  static const short lbase[29+2] = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,
      43,51,59,67,83,99,115,131,163,195,227,258,0,0};
  static const unsigned char lbits[29+2] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,
      4,4,4,5,5,5,5,0,0,0};

  const unsigned char *oe = out + cap;
  const unsigned char *e = in + size, *o = out;
  enum sinfl_states {hdr,stored,fixed,dyn,blk};
  enum sinfl_states state = hdr;
  struct sinfl s = {0};
  int last = 0;

  s.bitptr = in;
  while (1) {
    switch (state) {
    case hdr: {
      /* block header */
      int type = 0;
      sinfl_refill(&s);
      last = sinfl__get(&s,1);
      type = sinfl__get(&s,2);

      switch (type) {default: return (int)(out-o);
      case 0x00: state = stored; break;
      case 0x01: state = fixed; break;
      case 0x02: state = dyn; break;}
    } break;
    case stored: {
      /* uncompressed block */
      unsigned len, nlen;
      sinfl__get(&s,s.bitcnt & 7);
      len = (unsigned short)sinfl__get(&s,16);
      nlen = (unsigned short)sinfl__get(&s,16);
      s.bitptr -= s.bitcnt / 8;
      s.bitbuf = s.bitcnt = 0;

      if ((unsigned short)len != (unsigned short)~nlen)
        return (int)(out-o);
      if (len > (e - s.bitptr) || !len)
        return (int)(out-o);

      memcpy(out, s.bitptr, (size_t)len);
      s.bitptr += len, out += len;
      if (last) return (int)(out-o);
      state = hdr;
    } break;
    case fixed: {
      /* fixed huffman codes */
      int n; unsigned char lens[288+32];
      for (n = 0; n <= 143; n++) lens[n] = 8;
      for (n = 144; n <= 255; n++) lens[n] = 9;
      for (n = 256; n <= 279; n++) lens[n] = 7;
      for (n = 280; n <= 287; n++) lens[n] = 8;
      for (n = 0; n < 32; n++) lens[288+n] = 5;

      /* build lit/dist tables */
      sinfl_build(s.lits, lens, 10, 15, 288);
      sinfl_build(s.dsts, lens + 288, 8, 15, 32);
      state = blk;
    } break;
    case dyn: {
      /* dynamic huffman codes */
      int n, i;
      unsigned hlens[SINFL_PRE_TBL_SIZE];
      unsigned char nlens[19] = {0}, lens[288+32];

      sinfl_refill(&s);
      {int nlit = 257 + sinfl__get(&s,5);
      int ndist = 1 + sinfl__get(&s,5);
      int nlen = 4 + sinfl__get(&s,4);
      for (n = 0; n < nlen; n++)
        nlens[order[n]] = (unsigned char)sinfl_get(&s,3);
      sinfl_build(hlens, nlens, 7, 7, 19);

      /* decode code lengths */
      for (n = 0; n < nlit + ndist;) {
        int sym = 0;
        sinfl_refill(&s);
        sym = sinfl_decode(&s, hlens, 7);
        switch (sym) {default: lens[n++] = (unsigned char)sym; break;
        case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break;
        case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break;
        case 18: for (i=11+sinfl_get(&s,7);i;i--,n++) lens[n]=0; break;}
      }
      /* build lit/dist tables */
      sinfl_build(s.lits, lens, 10, 15, nlit);
      sinfl_build(s.dsts, lens + nlit, 8, 15, ndist);
      state = blk;}
    } break;
    case blk: {
      /* decompress block */
      while (1) {
        int sym;
        sinfl_refill(&s);
        sym = sinfl_decode(&s, s.lits, 10);
        if (sym < 256) {
          /* literal */
          if (sinfl_unlikely(out >= oe)) {
            return (int)(out-o);
          }
          *out++ = (unsigned char)sym;
          sym = sinfl_decode(&s, s.lits, 10);
          if (sym < 256) {
            *out++ = (unsigned char)sym;
            continue;
          }
        }
        if (sinfl_unlikely(sym == 256)) {
          /* end of block */
          if (last) return (int)(out-o);
          state = hdr;
          break;
        }
        /* match */
        if (sym >= 286) {
          /* length codes 286 and 287 must not appear in compressed data */
          return (int)(out-o);
        }
        sym -= 257;
        {int len = sinfl__get(&s, lbits[sym]) + lbase[sym];
        int dsym = sinfl_decode(&s, s.dsts, 8);
        int offs = sinfl__get(&s, dbits[dsym]) + dbase[dsym];
        unsigned char *dst = out, *src = out - offs;
        if (sinfl_unlikely(offs > (int)(out-o))) {
          return (int)(out-o);
        }
        out = out + len;

#ifndef SINFL_NO_SIMD
        if (sinfl_likely(oe - out >= 16 * 3)) {
          if (offs >= 16) {
            /* simd copy match */
            sinfl_copy128(&dst, &src);
            sinfl_copy128(&dst, &src);
            do sinfl_copy128(&dst, &src);
            while (dst < out);
          } else if (offs >= 8) {
            /* word copy match */
            sinfl_copy64(&dst, &src);
            sinfl_copy64(&dst, &src);
            do sinfl_copy64(&dst, &src);
            while (dst < out);
          } else if (offs == 1) {
            /* rle match copying */
            sinfl_char16 w = sinfl_char16_char(src[0]);
            dst = sinfl_write128(dst, w);
            dst = sinfl_write128(dst, w);
            do dst = sinfl_write128(dst, w);
            while (dst < out);
          } else {
            /* byte copy match */
            *dst++ = *src++;
            *dst++ = *src++;
            do *dst++ = *src++;
            while (dst < out);
          }
        }
#else
        if (sinfl_likely(oe - out >= 3 * 8 - 3)) {
          if (offs >= 8) {
            /* word copy match */
            sinfl_copy64(&dst, &src);
            sinfl_copy64(&dst, &src);
            do sinfl_copy64(&dst, &src);
            while (dst < out);
          } else if (offs == 1) {
            /* rle match copying */
            unsigned int c = src[0];
            unsigned int hw = (c << 24u) | (c << 16u) | (c << 8u) | (unsigned)c;
            unsigned long long w = (unsigned long long)hw << 32llu | hw;
            dst = sinfl_write64(dst, w);
            dst = sinfl_write64(dst, w);
            do dst = sinfl_write64(dst, w);
            while (dst < out);
          } else {
            /* byte copy match */
            *dst++ = *src++;
            *dst++ = *src++;
            do *dst++ = *src++;
            while (dst < out);
          }
        }
#endif
        else {
          *dst++ = *src++;
          *dst++ = *src++;
          do *dst++ = *src++;
          while (dst < out);
        }}
      }
    } break;}
  }
  return (int)(out-o);
}
extern int
sinflate(void *out, int cap, const void *in, int size) {
  return sinfl_decompress((unsigned char*)out, cap, (const unsigned char*)in, size);
}
static unsigned
sinfl_adler32(unsigned adler32, const unsigned char *in, int in_len) {
  const unsigned ADLER_MOD = 65521;
  unsigned s1 = adler32 & 0xffff;
  unsigned s2 = adler32 >> 16;
  unsigned blk_len, i;

  blk_len = in_len % 5552;
  while (in_len) {
    for (i=0; i + 7 < blk_len; i += 8) {
      s1 += in[0]; s2 += s1;
      s1 += in[1]; s2 += s1;
      s1 += in[2]; s2 += s1;
      s1 += in[3]; s2 += s1;
      s1 += in[4]; s2 += s1;
      s1 += in[5]; s2 += s1;
      s1 += in[6]; s2 += s1;
      s1 += in[7]; s2 += s1;
      in += 8;
    }
    for (; i < blk_len; ++i)
      s1 += *in++, s2 += s1;
    s1 %= ADLER_MOD; s2 %= ADLER_MOD;
    in_len -= blk_len;
    blk_len = 5552;
  } return (unsigned)(s2 << 16) + (unsigned)s1;
}
extern int
zsinflate(void *out, int cap, const void *mem, int size) {
  const unsigned char *in = (const unsigned char*)mem;
  if (size >= 6) {
    const unsigned char *eob = in + size - 4;
    int n = sinfl_decompress((unsigned char*)out, cap, in + 2u, size);
    unsigned a = sinfl_adler32(1u, (unsigned char*)out, n);
    unsigned h = eob[0] << 24 | eob[1] << 16 | eob[2] << 8 | eob[3] << 0;
    return a == h ? n : -1;
  } else {
    return -1;
  }
}
#endif


code:
.\" Man page generated from reStructuredText.
.
.TH SLTERM  "" "" ""
.SH NAME
slterm \-
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.sp
(sl)im (term)inal
.SH SYNOPSIS
.sp
\fBslterm\fP \fB\-I\fP \fB\-H\fP \fB\-L\fP \fB\-h\fP
.sp
\fBslterm\fP [\fB\-aivVx\fP] [\fB\-c\fP \fIclass\fP] [\fB\-f\fP \fIfont\fP] [\fB\-g\fP \fIgeometry\fP]
[\fB\-n\fP \fIname\fP] [\fB\-o\fP \fIiofile\fP] [\fB\-T\fP \fItitle\fP] [\fB\-t\fP \fItitle\fP]
[\fB\-l\fP \fIline\fP] [\fB\-w\fP \fIwindowid\fP] [\fB\-X\fP]
[[\fB\-e\fP] \fIcommand\fP] [\fIarguments\fP\&...]]
.sp
\fBslterm\fP [\fB\-aivVx\fP] [\fB\-c\fP \fIclass\fP] [\fB\-f\fP \fIfont\fP] [\fB\-g\fP \fIgeometry\fP]
[\fB\-n\fP \fIname\fP] [\fB\-o\fP \fIiofile\fP] [\fB\-T\fP \fItitle\fP] [\fB\-t\fP \fItitle\fP]
[\fB\-w\fP \fIwindowid\fP] \-l \fIline\fP [\fIstty_args\fP\&...]
.sp
\fBslterm\fP [\-f regularfont] [\-fb boldfont] [\-fi italicfont] [\-fbi bolditalicfont]
[\-fw fontwidth] [\-fh fontheight] [cmd ...]
.SH DESCRIPTION
.sp
\fBslterm\fP is a virtual terminal emulator for X
.sp
slterm is highly optimized for fast text output and slim memory usage,
while having several addons like switchable codepages or advanced
history browsing.
.sp
In favor of compatibility with terminal programs and performance utf8
support is stripped. Communication with the x clipboard is however
automatically converted from and to utf8.
.SH USAGE
.INDENT 0.0
.TP
.B \fBCtrl+F1\fP
Show a help screen with all shortcuts.
.UNINDENT
.sp
The usage of slterm differs to other terminal emulators by its three different main modes.
.SS normalmode
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
The codepage can be changed via hotkeys. (\(gaCtrl+Shift+0..9\(ga).

Paste from the clipbard with \(gaShift+Insert\(ga.

Set Bookmarks to the current line with \(gaCtrl+Shift+0..9\(ga
Scroll back to a bookmark with \(gaCtrl+0..9\(ga

Retmarks are atomatically stored, and can be browsed to later.
Every time, a command is entered with return, a retmark is stored.
Switch into lessmode, and browse to the location of the
last entered command with \(gaShift+backspace\(ga.

\(gaCtrl+Shift\(ga + \(gaUp\(ga/\(gaDown\(ga/\(gaPageUp\(ga/\(gaPageDown\(ga switch to lessmode.
.ft P
.fi
.UNINDENT
.UNINDENT
.SS lessmode
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
In lessmode it is possible to browse the scrollback buffer via
the standard less like keybindings.

\(gaAlt+Shift Up/Down/PgUp/PdDown\(ga enter lessmode
\(gaShift+Backspace\(ga enters lessmode and browses back to the last line,
a command has been entered.

In lessmode, it is possible to browse between the marks of commands,
comfirmed with \(gaEnter\(ga in the shell via \(gaBackspace\(ga/\(gaTab left\(ga
and \(gaTab\(ga.
\(ga1\(ga to \(ga9\(ga scroll back to the according numbered retmark.

\(gaShift+Enter\(ga in Normal mode enters lessmode, if the output
of a command is more than a screen.

\(gaq\(ga leaves lessmode and switches to normalmode again.
.ft P
.fi
.UNINDENT
.UNINDENT
.SS selectionmode
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
In selection mode it is possible to select lines or areas
(either by lines, or rectangular areas).
enter with \(gaAlt+S\(ga, start selecting with \(gav\(ga, \(gas\(ga or
\(gaV\(ga (rectangular selection) and the cursor keys,
switch between rectangular selection and line selection with \(gat\(ga,
copy to the clipboard with \(gaEnter\(ga.

\(gay\(ga selects the current line, \(gay\(ga with a active selection yanks the
selection to the clipboard and exits selection mode,
\(gayy\(ga (sort of vim binding) selects the current line
and yanks it to the clipboard.
\(gap\(ga copies the selection and pastes it into the terminal.
\(gaq\(ga or \(gaESC\(ga exit selection mode.
.ft P
.fi
.UNINDENT
.UNINDENT
.SS all modes
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
Ctrl+Shift + I: Inverse colors
Ctrl+F1:    Show this help

Set font width/size:
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

Select Codepage: \(gaCtrl+Win\(ga +
0 CP1250
1 CP1251
2 CP1252 this is mostly ANSI, and the 1. page of Unicode
3 CP1253
4 CP437  Old IBM codetable, borders and tables
5 CP850  DOS Standard table
6 CP4002 Custom table, mix of 1252 and 437,
         with German umlauts and box drawing chars
.ft P
.fi
.UNINDENT
.UNINDENT
.SH TERMINAL
.sp
slterm is mostly VT100 compatible, please look in question for the according manuals.
Here only some of the vt100 or slterm additions are described.
.sp
slterm starts either the env variable SHELL, /bin/sh, or a command supplied with
\fIslterm [\-e] command options\fP\&.
.sp
All \fIoptions\fP are forwarded to command without changes.
.sp
The already set environmental variables are kept,
further variables are exported into the environment:
.sp
COLUMNS, LINES, TERMCAP, LOGNAME, USER, SHELL, HOME, TERM.
.sp
Color Ansi escapesequences are set.
.SS Colors
.sp
The basic 8 colors, accessable in the shell as:
$BLACK, $RED, $GREEN, $YELLOW, $BLUE, $MAGENTA, $CYAN, $WHITE.
.sp
\fIecho $RED red text\fP
.sp
They can be combined with prefixes: L(ight), D(ark), LD(light\-dark), BG(background), BGL(ight background).
Example: $LGREEN, D: $DRED, $LD: $LDCYAN, BG: $BGBLUE, BGLCYAN.
.sp
These Colors cannot be combined: ORANGE, ORANGERED, BROWN, BGBROWN, PURPLE, GRAY, MINT, TURQUOISE
.sp
Other text attributes are: $BOLD, $FAINT, $CURSIVE, $UNDERLINE, $BLINK, $REVERSE, $STRIKETHROUGH, $DOUBLEUNDERLINE.
.sp
The text attributes can be combined, with some special combinations:
BLINK and REVERSE blinks by reversing colors.
STRIKETHROUGH and UNDERLINE get a double underline.
.sp
The default foreground and background color and attributes can be reset with $NORMAL.
.sp
255 Colors can be set with:
.INDENT 0.0
.IP \(bu 2
foreground: echo \-e "e[38;5;XXm", XX one of 0 \- 255.
.IP \(bu 2
background: echo \-e "e[48;5;XXm", XX one of 0 \- 255.
.UNINDENT
.SS Cursor
.sp
There are several cursor shapes, set with: echo \-e "e[X q".
X one of 0..12:
.INDENT 0.0
.IP \(bu 2
1,2: block cursor
.IP \(bu 2
3,4: underline
.IP \(bu 2
5,6: vertical bar
.UNINDENT
.SS slterm additions:
.INDENT 0.0
.IP \(bu 2
7:   \(aqX\(aq
.IP \(bu 2
7;Y: Y is the ascii code of the char, used as cursor
.IP \(bu 2
8:   double underline
.IP \(bu 2
9:   empty block
.IP \(bu 2
10:  underline, two lines at the sides
.IP \(bu 2
11:  underline and overline, lines right and left
.IP \(bu 2
12:  overline, lines right and left
.UNINDENT
.SS Bell
.sp
Sending a bell to the terminal (echo \-e "007") sends
the according notification (XBell) to the window manager.
.SH INSTALL
.sp
If obtained from source, edit the files config.make and config.h
to customize slterm. Type \fImake\fP, and \fImake install\fP\&.
.sp
If you downloaded the statically linked binary,
.INDENT 0.0
.IP 1. 3
copy the binary to a suitable place (/usr/local/bin)
.IP 2. 3
install the terminal info file: (for curses) \fIslterm \-I | tic \-sx \-\fP
( the netbsd version of tic, the terminal info compiler,  is supplied as source in
tools/tic, and should be compatible with other curses versions )
.IP 3. 3
If needed, download this man page in its man format (slterm.1)
from github (github.com/michael105/slterm),
copy into the appropiate directory (/usr/local/share/man/man1)
.UNINDENT
.sp
Interestingly, the statically linked binary seems to use even less memory
than the shared binary.
.SS CURSES
.INDENT 0.0
.INDENT 3.5
To be used with curses, the installation of the terminfo database file is needed.
slterm.terminfo is supplied in the sources, within the folder src.
It can be installed with \fItic \-sx slterm.terminfo\fP\&.
Alternatively, the termcap database "linux" is mostly compatible.
Set with \fIexport TERM=linux\fP
.sp
The terminfo database of slterm is also displayed, when slterm was compiled with
EMBEDRESOURCES. Type \fIslterm \-I\fP, to install: \fIslterm \-I | tic \-sx \-\fP\&.
The key combination \fICtrl+Shift+Win+ALT+I\fP will dump the terminal info to
the terminal as well, and can be used, to install the terminal info within
a remote shell. ( type \fItic \-sx \-\fP, hit \fICtrl+Shift+Win+Alt+I\fP, and \fICtrl+D\fP )
.sp
Tic is the terminfo compiler, available from the curses distributions,
the netbsd tic implementation is supplied within tools/tic.
There is a statically linked binary for linux, 64bit of tic at
github.com/michael105/static\-bin
Sources of tic and netbsd curses: github.com/oasislinux/netbsd\-curses/
.UNINDENT
.UNINDENT
.SH OPTIONS
.sp
\-h show short option usage
.sp
\-H Display this manpage as text
.sp
\-L show license
.INDENT 0.0
.TP
.B \-a
disable alternate screens in terminal
.TP
.BI \-c \ class
defines the window class (default $TERM).
.TP
.BI \-f \ font
defines the font to use when slterm is run.
example: slterm \-f \(aqLiberation Mono:Bold:pixelsize=13:antialias=true:autohint=true\(aq
the parameters are described in the fontconfig documentation,
an overview is supplied in doc/fontconfig.txt
.UNINDENT
.INDENT 0.0
.TP
.B \-fb boldfont \-fi italicfont \-fI bolditalicfont
Set bold/italic/bolditalic fonts.
Supply \(aq0\(aq to disable the according font and to display
the text attributes by color changes only
.TP
.B \-fh fontheight
Set the char height in pixel
.TP
.B \-fw fontwidth
Set the char width in pixel
.UNINDENT
.INDENT 0.0
.TP
.BI \-g \ geometry
defines the X11 geometry string. The form is
[=][<cols>{xX}<rows>][{+\-}<xoffset>{+\-}<yoffset>]. See
XParseGeometry (3) for further details.
.TP
.B \-i
will fixate the position given with the \-g option.
.TP
.BI \-n \ name
defines the window instance name (default $TERM).
.TP
.BI \-o \ iofile
writes all the I/O to iofile. This feature is useful when recording
slterm sessions. A value of "\-" means standard output.
.TP
.BI \-T \ title
defines the window title (default \(aqslterm\(aq).
.TP
.BI \-t \ title
defines the window title (default \(aqslterm\(aq).
.TP
.BI \-w \ windowid
embeds slterm within the window identified by windowid
.TP
.BI \-l \ line
use a tty line instead of a pseudo terminal. line should be a
(pseudo\-)serial device (e.g. /dev/ttyS0 on Linux for serial port 0).
When this flag is given remaining arguments are used as flags for
stty(1). By default slterm initializes the serial line to 8 bits, no
parity, 1 stop bit and a 38400 baud rate. The speed is set by
appending it as last argument (e.g. \(aqslterm \-l /dev/ttyS0 115200\(aq).
Arguments before the last one are stty(1) flags. If you want to
set odd parity on 115200 baud use for example \(aqslterm \-l /dev/ttyS0
parenb parodd 115200\(aq. Set the number of bits by using for example
\(aqslterm \-l /dev/ttyS0 cs7 115200\(aq. See stty(1) for more arguments and
cases.
.TP
.B \-v
prints version information, then exits.
.TP
.B \-V
prints version and compile information, then exits
.UNINDENT
.INDENT 0.0
.TP
.B \-e command [ arguments ... ]
slterm executes command instead of the shell. If this is used it must
be the last option on the command line, as in xterm / rxvt. This
option is only intended for compatibility, and all the remaining
arguments are used as a command even without it.
.UNINDENT
.INDENT 0.0
.TP
.B \-x
enable reading of the XResources database for the configuration
slterm must had been compiled with the XRESOURCES flag in config.make set to 1
.TP
.B \-X
lock all memory pages into memory, prevent swapping.
Secrets could be revealed, also years later, if the memory
is swapped to disk. Worse, with flash disks also erasing
the contents will not necessarily erase the written cells.
This option locks all memory pages into ram.
.UNINDENT
.SH AUTHORS
.sp
(2020\-2025) Michael (misc147), www.github.com/michael105
.sp
The code is based on st, the suckless terminal emulator,
fetched from git 1.1.2020, which was originally written by Aurelien Aptel.
.sp
The included patches to st had been provided by:
.sp
Tonton Couillon,
dcat,
Jochen Sprickerhof,
M Farkas\-Dyck,
Ivan Tham,
Ori Bernstein,
Matthias Schoth,
Laslo Hunhold,
Paride Legovini,
Lorenzo Bracco,
Kamil Kleban,
Avi Halachmi,
Jacob Prosser,
Augusto Born de Oliveira,
Kai Hendry,
Laslo Hunhold,
Matthew Parnell,
Doug Whiteley,
Aleksandrs Stier,
Devin J. Pohly,
Sai Praneeth Reddy
.SH LICENSE
.sp
MIT, see the LICENSE file for the terms of redistribution or type slterm \-L
.SH SEE ALSO
.sp
\fBtabbed\fP(1), \fButmp\fP(1), \fBstty\fP(1)
.SH BUGS
.sp
See the README in the distribution.
.\" Generated by docutils manpage writer.
.


code:
========
 slterm
========

(sl)im (term)inal

SYNOPSIS
========

**slterm** **-I** **-H** **-L** **-h**

**slterm** [**-aivVx**] [**-c** *class*] [**-f** *font*] [**-g** *geometry*]
[**-n** *name*] [**-o** *iofile*] [**-T** *title*] [**-t** *title*]
[**-l** *line*] [**-w** *windowid*] [**-X**]
[[**-e**] *command*] [*arguments*...]]

**slterm** [**-aivVx**] [**-c** *class*] [**-f** *font*] [**-g** *geometry*]
[**-n** *name*] [**-o** *iofile*] [**-T** *title*] [**-t** *title*]
[**-w** *windowid*] -l *line* [*stty_args*...]

**slterm** [-f regularfont] [-fb boldfont] [-fi italicfont] [-fbi bolditalicfont]
[-fw fontwidth] [-fh fontheight] [cmd ...]

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

**slterm** is a virtual terminal emulator for X

slterm is highly optimized for fast text output and slim memory usage,
while having several addons like switchable codepages or advanced
history browsing.

In favor of compatibility with terminal programs and performance utf8
support is stripped. Communication with the x clipboard is however
automatically converted from and to utf8.

USAGE
=====

**Ctrl+F1**
  Show a help screen with all shortcuts.

The usage of slterm differs to other terminal emulators by its three different main modes.

normalmode
----------
::

 The codepage can be changed via hotkeys. (`Ctrl+Shift+0..9`).

 Paste from the clipbard with `Shift+Insert`.

 Set Bookmarks to the current line with `Ctrl+Shift+0..9`
 Scroll back to a bookmark with `Ctrl+0..9`

 Retmarks are atomatically stored, and can be browsed to later.
 Every time, a command is entered with return, a retmark is stored.
 Switch into lessmode, and browse to the location of the
 last entered command with `Shift+backspace`.

 `Ctrl+Shift` + `Up`/`Down`/`PageUp`/`PageDown` switch to lessmode.

lessmode
--------
::

 In lessmode it is possible to browse the scrollback buffer via
 the standard less like keybindings.

 `Alt+Shift Up/Down/PgUp/PdDown` enter lessmode
 `Shift+Backspace` enters lessmode and browses back to the last line,
 a command has been entered.

 In lessmode, it is possible to browse between the marks of commands,
 comfirmed with `Enter` in the shell via `Backspace`/`Tab left`
 and `Tab`.
 `1` to `9` scroll back to the according numbered retmark.

 `Shift+Enter` in Normal mode enters lessmode, if the output
 of a command is more than a screen.

 `q` leaves lessmode and switches to normalmode again.

selectionmode
-------------
::

 In selection mode it is possible to select lines or areas
 (either by lines, or rectangular areas).
 enter with `Alt+S`, start selecting with `v`, `s` or
 `V` (rectangular selection) and the cursor keys,
 switch between rectangular selection and line selection with `t`,
 copy to the clipboard with `Enter`.

 `y` selects the current line, `y` with a active selection yanks the
 selection to the clipboard and exits selection mode,
 `yy` (sort of vim binding) selects the current line
 and yanks it to the clipboard.
 `p` copies the selection and pastes it into the terminal.
 `q` or `ESC` exit selection mode.

all modes
---------
::

 Ctrl+Shift + I: Inverse colors
 Ctrl+F1:    Show this help

 Set font width/size:
 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

 Select Codepage: `Ctrl+Win` +
 0 CP1250
 1 CP1251
 2 CP1252 this is mostly ANSI, and the 1. page of Unicode
 3 CP1253
 4 CP437  Old IBM codetable, borders and tables
 5 CP850  DOS Standard table
 6 CP4002 Custom table, mix of 1252 and 437,
          with German umlauts and box drawing chars

TERMINAL
========

slterm is mostly VT100 compatible, please look in question for the according manuals.
Here only some of the vt100 or slterm additions are described.

slterm starts either the env variable SHELL, /bin/sh, or a command supplied with
`slterm [-e] command options`.

All `options` are forwarded to command without changes.

The already set environmental variables are kept,
further variables are exported into the environment:

COLUMNS, LINES, TERMCAP, LOGNAME, USER, SHELL, HOME, TERM.

Color Ansi escapesequences are set.

Colors
------

The basic 8 colors, accessable in the shell as:
$BLACK, $RED, $GREEN, $YELLOW, $BLUE, $MAGENTA, $CYAN, $WHITE.

`echo $RED red text`

They can be combined with prefixes: L(ight), D(ark), LD(light-dark), BG(background), BGL(ight background).
Example: $LGREEN, D: $DRED, $LD: $LDCYAN, BG: $BGBLUE, BGLCYAN.

These Colors cannot be combined: ORANGE, ORANGERED, BROWN, BGBROWN, PURPLE, GRAY, MINT, TURQUOISE

Other text attributes are: $BOLD, $FAINT, $CURSIVE, $UNDERLINE, $BLINK, $REVERSE, $STRIKETHROUGH, $DOUBLEUNDERLINE.

The text attributes can be combined, with some special combinations:
BLINK and REVERSE blinks by reversing colors.
STRIKETHROUGH and UNDERLINE get a double underline.

The default foreground and background color and attributes can be reset with $NORMAL.

255 Colors can be set with:

- foreground: echo -e "\e[38;5;XXm", XX one of 0 - 255.
- background: echo -e "\e[48;5;XXm", XX one of 0 - 255.

Cursor
------

There are several cursor shapes, set with: echo -e "\e[X q".
X one of 0..12:

* 1,2: block cursor
* 3,4: underline
* 5,6: vertical bar

slterm additions:
~~~~~~~~~~~~~~~~~

* 7:   'X'
* 7;Y: Y is the ascii code of the char, used as cursor
* 8:   double underline
* 9:   empty block
* 10:  underline, two lines at the sides
* 11:  underline and overline, lines right and left
* 12:  overline, lines right and left

Bell
----

Sending a bell to the terminal (echo -e "\007") sends
the according notification (XBell) to the window manager.

INSTALL
=======

If obtained from source, edit the files config.make and config.h
to customize slterm. Type `make`, and `make install`.

If you downloaded the statically linked binary,

1. copy the binary to a suitable place (/usr/local/bin)

2. install the terminal info file: (for curses) `slterm -I | tic -sx -`
   ( the netbsd version of tic, the terminal info compiler,  is supplied as source in
   tools/tic, and should be compatible with other curses versions )

3. If needed, download this man page in its man format (slterm.1)
   from github (github.com/michael105/slterm),
   copy into the appropiate directory (/usr/local/share/man/man1)

Interestingly, the statically linked binary seems to use even less memory
than the shared binary.

CURSES
------

   To be used with curses, the installation of the terminfo database file is needed.
   slterm.terminfo is supplied in the sources, within the folder src.
   It can be installed with `tic -sx slterm.terminfo`.
   Alternatively, the termcap database "linux" is mostly compatible.
   Set with `export TERM=linux`

   The terminfo database of slterm is also displayed, when slterm was compiled with
   EMBEDRESOURCES. Type `slterm -I`, to install: `slterm -I | tic -sx -`.
   The key combination `Ctrl+Shift+Win+ALT+I` will dump the terminal info to
   the terminal as well, and can be used, to install the terminal info within
   a remote shell. ( type `tic -sx -`, hit `Ctrl+Shift+Win+Alt+I`, and `Ctrl+D` )

   Tic is the terminfo compiler, available from the curses distributions,
   the netbsd tic implementation is supplied within tools/tic.
   There is a statically linked binary for linux, 64bit of tic at
   github.com/michael105/static-bin
   Sources of tic and netbsd curses: github.com/oasislinux/netbsd-curses/

OPTIONS
=======

-h show short option usage

-H Display this manpage as text

-L show license

-a
   disable alternate screens in terminal

-c class
   defines the window class (default $TERM).

-f font
   defines the font to use when slterm is run.
   example: slterm -f 'Liberation Mono:Bold:pixelsize=13:antialias=true:autohint=true'
   the parameters are described in the fontconfig documentation,
   an overview is supplied in doc/fontconfig.txt

-fb boldfont -fi italicfont -fI bolditalicfont
   Set bold/italic/bolditalic fonts.
   Supply '0' to disable the according font and to display
   the text attributes by color changes only

-fh fontheight
   Set the char height in pixel

-fw fontwidth
   Set the char width in pixel

-g geometry
   defines the X11 geometry string. The form is
   [=][<cols>{xX}<rows>][{+-}<xoffset>{+-}<yoffset>]. See
   XParseGeometry (3) for further details.

-i
   will fixate the position given with the -g option.

-n name
   defines the window instance name (default $TERM).

-o iofile
   writes all the I/O to iofile. This feature is useful when recording
   slterm sessions. A value of "-" means standard output.

-T title
   defines the window title (default 'slterm').

-t title
   defines the window title (default 'slterm').

-w windowid
   embeds slterm within the window identified by windowid

-l line
   use a tty line instead of a pseudo terminal. line should be a
   (pseudo-)serial device (e.g. /dev/ttyS0 on Linux for serial port 0).
   When this flag is given remaining arguments are used as flags for
   stty(1). By default slterm initializes the serial line to 8 bits, no
   parity, 1 stop bit and a 38400 baud rate. The speed is set by
   appending it as last argument (e.g. 'slterm -l /dev/ttyS0 115200').
   Arguments before the last one are stty(1) flags. If you want to
   set odd parity on 115200 baud use for example 'slterm -l /dev/ttyS0
   parenb parodd 115200'. Set the number of bits by using for example
   'slterm -l /dev/ttyS0 cs7 115200'. See stty(1) for more arguments and
   cases.

-v
   prints version information, then exits.

-V
   prints version and compile information, then exits

-e command [ arguments ... ]
   slterm executes command instead of the shell. If this is used it must
   be the last option on the command line, as in xterm / rxvt. This
   option is only intended for compatibility, and all the remaining
   arguments are used as a command even without it.

-x
   enable reading of the XResources database for the configuration
   slterm must had been compiled with the XRESOURCES flag in config.make set to 1

-X
   lock all memory pages into memory, prevent swapping.
   Secrets could be revealed, also years later, if the memory
   is swapped to disk. Worse, with flash disks also erasing
   the contents will not necessarily erase the written cells.
   This option locks all memory pages into ram.

AUTHORS
=======

(2020-2025) Michael (misc147), www.github.com/michael105

The code is based on st, the suckless terminal emulator,
fetched from git 1.1.2020, which was originally written by Aurelien Aptel.

The included patches to st had been provided by:

Tonton Couillon,
dcat,
Jochen Sprickerhof,
M Farkas-Dyck,
Ivan Tham,
Ori Bernstein,
Matthias Schoth,
Laslo Hunhold,
Paride Legovini,
Lorenzo Bracco,
Kamil Kleban,
Avi Halachmi,
Jacob Prosser,
Augusto Born de Oliveira,
Kai Hendry,
Laslo Hunhold,
Matthew Parnell,
Doug Whiteley,
Aleksandrs Stier,
Devin J. Pohly,
Sai Praneeth Reddy

LICENSE
=======

MIT, see the LICENSE file for the terms of redistribution or type slterm -L

SEE ALSO
========

**tabbed**\ (1), **utmp**\ (1), **stty**\ (1)

BUGS
====

See the README in the distribution.


code:
const char* slterm_license = R"(MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
)";


code:
const char* slterm_man = R"(SLTERM()                                                              SLTERM()

NAME
       slterm -

       (sl)im (term)inal

SYNOPSIS
       slterm -I -H -L -h

       slterm  [-aivVx]  [-c  class]  [-f  font]  [-g  geometry] [-n name] [-o
       iofile] [-T title] [-t title] [-l line] [-w windowid] [-X]  [[-e]  com-
       mand] [arguments...]]

       slterm  [-aivVx]  [-c  class]  [-f  font]  [-g  geometry] [-n name] [-o
       iofile] [-T title] [-t title] [-w windowid] -l line [stty_args...]

       slterm [-f regularfont] [-fb boldfont] [-fi italicfont] [-fbi boldital-
       icfont] [-fw fontwidth] [-fh fontheight] [cmd ...]

DESCRIPTION
       slterm is a virtual terminal emulator for X

       slterm  is highly optimized for fast text output and slim memory usage,
       while having several addons like switchable codepages or advanced  his-
       tory browsing.

       In  favor  of compatibility with terminal programs and performance utf8
       support is stripped. Communication with the x clipboard is however  au-
       tomatically converted from and to utf8.

USAGE
       Ctrl+F1
              Show a help screen with all shortcuts.

       The  usage  of  slterm differs to other terminal emulators by its three
       different main modes.

   normalmode
          The codepage can be changed via hotkeys. (`Ctrl+Shift+0..9`).

          Paste from the clipbard with `Shift+Insert`.

          Set Bookmarks to the current line with `Ctrl+Shift+0..9`
          Scroll back to a bookmark with `Ctrl+0..9`

          Retmarks are atomatically stored, and can be browsed to later.
          Every time, a command is entered with return, a retmark is stored.
          Switch into lessmode, and browse to the location of the
          last entered command with `Shift+backspace`.

          `Ctrl+Shift` + `Up`/`Down`/`PageUp`/`PageDown` switch to lessmode.

   lessmode
          In lessmode it is possible to browse the scrollback buffer via
          the standard less like keybindings.

          `Alt+Shift Up/Down/PgUp/PdDown` enter lessmode
          `Shift+Backspace` enters lessmode and browses back to the last line,
          a command has been entered.

          In lessmode, it is possible to browse between the marks of commands,
          comfirmed with `Enter` in the shell via `Backspace`/`Tab left`
          and `Tab`.
          `1` to `9` scroll back to the according numbered retmark.

          `Shift+Enter` in Normal mode enters lessmode, if the output
          of a command is more than a screen.

          `q` leaves lessmode and switches to normalmode again.

   selectionmode
          In selection mode it is possible to select lines or areas
          (either by lines, or rectangular areas).
          enter with `Alt+S`, start selecting with `v`, `s` or
          `V` (rectangular selection) and the cursor keys,
          switch between rectangular selection and line selection with `t`,
          copy to the clipboard with `Enter`.

          `y` selects the current line, `y` with a active selection yanks the
          selection to the clipboard and exits selection mode,
          `yy` (sort of vim binding) selects the current line
          and yanks it to the clipboard.
          `p` copies the selection and pastes it into the terminal.
          `q` or `ESC` exit selection mode.

   all modes
          Ctrl+Shift + I: Inverse colors
          Ctrl+F1:    Show this help

          Set font width/size:
          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

          Select Codepage: `Ctrl+Win` +
          0 CP1250
          1 CP1251
          2 CP1252 this is mostly ANSI, and the 1. page of Unicode
          3 CP1253
          4 CP437  Old IBM codetable, borders and tables
          5 CP850  DOS Standard table
          6 CP4002 Custom table, mix of 1252 and 437,
                   with German umlauts and box drawing chars

TERMINAL
       slterm is mostly VT100 compatible, please look in question for the  ac-
       cording  manuals.   Here only some of the vt100 or slterm additions are
       described.

       slterm starts either the env variable SHELL, /bin/sh, or a command sup-
       plied with slterm [-e] command options.

       All options are forwarded to command without changes.

       The already set environmental variables are kept, further variables are
       exported into the environment:

       COLUMNS, LINES, TERMCAP, LOGNAME, USER, SHELL, HOME, TERM.

       Color Ansi escapesequences are set.

   Colors
       The basic 8 colors, accessable in the shell as: $BLACK,  $RED,  $GREEN,
       $YELLOW, $BLUE, $MAGENTA, $CYAN, $WHITE.

       echo $RED red text

       They  can  be  combined with prefixes: L(ight), D(ark), LD(light-dark),
       BG(background), BGL(ight background).  Example: $LGREEN, D: $DRED, $LD:
       $LDCYAN, BG: $BGBLUE, BGLCYAN.

       These  Colors  cannot  be  combined: ORANGE, ORANGERED, BROWN, BGBROWN,
       PURPLE, GRAY, MINT, TURQUOISE

       Other text attributes are: $BOLD, $FAINT, $CURSIVE, $UNDERLINE, $BLINK,
       $REVERSE, $STRIKETHROUGH, $DOUBLEUNDERLINE.

       The  text  attributes  can be combined, with some special combinations:
       BLINK and REVERSE blinks by reversing colors.  STRIKETHROUGH and UNDER-
       LINE get a double underline.

       The default foreground and background color and attributes can be reset
       with $NORMAL.

       255 Colors can be set with:

       o foreground: echo -e "e[38;5;XXm", XX one of 0 - 255.

       o background: echo -e "e[48;5;XXm", XX one of 0 - 255.

   Cursor
       There are several cursor shapes, set with: echo -e "e[X q".  X  one  of
       0..12:

       o 1,2: block cursor

       o 3,4: underline

       o 5,6: vertical bar

   slterm additions:
       o 7:   'X'

       o 7;Y: Y is the ascii code of the char, used as cursor

       o 8:   double underline

       o 9:   empty block

       o 10:  underline, two lines at the sides

       o 11:  underline and overline, lines right and left

       o 12:  overline, lines right and left

   Bell
       Sending  a bell to the terminal (echo -e "007") sends the according no-
       tification (XBell) to the window manager.

INSTALL
       If obtained from source, edit the files  config.make  and  config.h  to
       customize slterm. Type make, and make install.

       If you downloaded the statically linked binary,

       1. copy the binary to a suitable place (/usr/local/bin)

       2. install the terminal info file: (for curses) slterm -I | tic -sx - (
          the netbsd version of tic, the terminal info compiler,  is  supplied
          as  source  in tools/tic, and should be compatible with other curses
          versions )

       3. If needed, download this man page in its man format (slterm.1)  from
          github  (github.com/michael105/slterm), copy into the appropiate di-
          rectory (/usr/local/share/man/man1)

       Interestingly, the statically linked binary seems to use even less mem-
       ory than the shared binary.

   CURSES
          To  be  used  with curses, the installation of the terminfo database
          file is needed.  slterm.terminfo is supplied in the sources,  within
          the  folder  src.  It can be installed with tic -sx slterm.terminfo.
          Alternatively, the termcap database "linux"  is  mostly  compatible.
          Set with export TERM=linux

          The  terminfo  database of slterm is also displayed, when slterm was
          compiled with EMBEDRESOURCES. Type slterm -I, to install: slterm  -I
          | tic -sx -.  The key combination Ctrl+Shift+Win+ALT+I will dump the
          terminal info to the terminal as well, and can be used,  to  install
          the  terminal  info  within  a  remote  shell. ( type tic -sx -, hit
          Ctrl+Shift+Win+Alt+I, and Ctrl+D )

          Tic is the terminfo compiler, available from  the  curses  distribu-
          tions,  the  netbsd tic implementation is supplied within tools/tic.
          There is a statically linked binary  for  linux,  64bit  of  tic  at
          github.com/michael105/static-bin  Sources  of tic and netbsd curses:
          github.com/oasislinux/netbsd-curses/

OPTIONS
       -h show short option usage

       -H Display this manpage as text

       -L show license

       -a     disable alternate screens in terminal

       -c class
              defines the window class (default $TERM).

       -f font
              defines the font to use when slterm is run.  example: slterm  -f
              'Liberation Mono:Bold:pixelsize=13:antialias=true:autohint=true'
              the parameters are described in the fontconfig documentation, an
              overview is supplied in doc/fontconfig.txt

       -fb boldfont -fi italicfont -fI bolditalicfont
              Set bold/italic/bolditalic fonts.  Supply '0' to disable the ac-
              cording font and to display the text attributes by color changes
              only

       -fh fontheight
              Set the char height in pixel

       -fw fontwidth
              Set the char width in pixel

       -g geometry
              defines    the    X11    geometry    string.    The    form   is
              [=][<cols>{xX}<rows>][{+-}<xoffset>{+-}<yoffset>]. See XParseGe-
              ometry (3) for further details.

       -i     will fixate the position given with the -g option.

       -n name
              defines the window instance name (default $TERM).

       -o iofile
              writes  all  the  I/O  to  iofile.  This  feature is useful when
              recording slterm sessions. A value of "-" means standard output.

       -T title
              defines the window title (default 'slterm').

       -t title
              defines the window title (default 'slterm').

       -w windowid
              embeds slterm within the window identified by windowid

       -l line
              use a tty line instead of a pseudo terminal. line  should  be  a
              (pseudo-)serial device (e.g. /dev/ttyS0 on Linux for serial port
              0).  When this flag is given remaining  arguments  are  used  as
              flags for stty(1). By default slterm initializes the serial line
              to 8 bits, no parity, 1 stop bit and  a  38400  baud  rate.  The
              speed  is  set by appending it as last argument (e.g. 'slterm -l
              /dev/ttyS0 115200').  Arguments before the last one are  stty(1)
              flags.  If you want to set odd parity on 115200 baud use for ex-
              ample 'slterm -l /dev/ttyS0 parenb parodd 115200'. Set the  num-
              ber  of  bits  by  using  for  example 'slterm -l /dev/ttyS0 cs7
              115200'. See stty(1) for more arguments and cases.

       -v     prints version information, then exits.

       -V     prints version and compile information, then exits

       -e command [ arguments ... ]
              slterm executes command instead of the shell. If this is used it
              must be the last option on the command line, as in xterm / rxvt.
              This option is only intended for compatibility, and all the  re-
              maining arguments are used as a command even without it.

       -x     enable  reading of the XResources database for the configuration
              slterm must had been compiled with the XRESOURCES flag  in  con-
              fig.make set to 1

       -X     lock  all  memory  pages into memory, prevent swapping.  Secrets
              could be revealed, also years later, if the memory is swapped to
              disk. Worse, with flash disks also erasing the contents will not
              necessarily erase the written cells.  This option locks all mem-
              ory pages into ram.

AUTHORS
       (2020-2025) Michael (misc147), www.github.com/michael105

       The  code  is based on st, the suckless terminal emulator, fetched from
       git 1.1.2020, which was originally written by Aurelien Aptel.

       The included patches to st had been provided by:

       Tonton Couillon, dcat, Jochen Sprickerhof, M  Farkas-Dyck,  Ivan  Tham,
       Ori Bernstein, Matthias Schoth, Laslo Hunhold, Paride Legovini, Lorenzo
       Bracco, Kamil Kleban, Avi Halachmi,  Jacob  Prosser,  Augusto  Born  de
       Oliveira,  Kai  Hendry,  Laslo Hunhold, Matthew Parnell, Doug Whiteley,
       Aleksandrs Stier, Devin J. Pohly, Sai Praneeth Reddy

LICENSE
       MIT, see the LICENSE file for  the  terms  of  redistribution  or  type
       slterm -L

SEE ALSO
       tabbed(1), utmp(1), stty(1)

BUGS
       See the README in the distribution.

                                                                      SLTERM()
)";


code:
#include "xwindow.h"

int main(int argc, char *argv[]){
		return(shared_main(argc,argv));
}


code:
// Including all sources into a single file
// leaves more possibilities for optimizations to the compiler.
// Did try to split the huge term.c file,
// but there are many globals left.
// beware. it wasn't me.. ;misc 2021

//#define DEBUG_FILELEVEL 5
//#define FULLDEBUG 5
#include "debug.h"

#include "includes.h"
#include "globals.h"
//#include "arg.h"
#include "term.h"
#include "xwindow.h"
#include "xcursor.h"
#include "system.h"
#include "tty.h"
#include "selection.h"
#include "xclipboard.h"
#include "xevent.h"
#include "xmouse.h"
#include "xkeyboard.h"
#include "fonts.h"
#include "statusbar.h"
#include "scroll.h"
#include "xdraw.h"
#include "termdraw.h"
#include "charmaps.h"
#include "colors.h"

// embedded resources
#ifdef INCLUDETERMINFO
#include "slterm_terminfo.h"
#endif
#ifdef INCLUDELICENSE
#include "slterm_license.h"
#endif

#ifdef INCLUDEMANPAGE
#include "slterm_man.h"
#endif

#if 0
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...)
#endif
#define DBG2(...) DBG(__VA_ARGS__)

#include "globals.c"
#include "statusbar.c"
#include "retmarks.c"
//#include "arg.c"
#include "term.c"
#include "controlchars.c"
#include "charmaps.c"
// termdraw needs to go after term.c .?
// otherweise delete and cursor keys do make trouble.
// Not looking for the source now.
#include "termdraw.c"

#include "fonts.c"
#include "mem.c"
#include "base64.c"
#include "selection.c"
#include "xclipboard.c"
#include "colors.c"
#include "xwindow.c"
#include "xcursor.c"
#include "xevent.c"
#include "xmouse.c"
#include "xkeyboard.c"
#include "main.c"
#include "system.c"
#include "tty.c"
#include "utf8.c"
#include "scroll.c"
#include "xdraw.c"


code:
#include "statusbar.h"
#include "term.h"
#include "mem.h"
#include "config.h"

int statusvisible;
Glyph *statusbar = NULL;
char* p_status = NULL;
static int statuswidth = 0;
static int focusdraw = 1;

void drawstatus(){
	term->dirty[term->bot] = 1;
	drawregion(0, term->bot, term->cols, term->bot + 1);
}

void statusbar_focusin(){
	if ( !focusdraw && statusvisible && statusbar ){
		focusdraw = 1;
		for ( Glyph *g = statusbar; g < &statusbar[statuswidth]; g++) {
			g->mode = statusattr;
			g->fg = statusfg;
			g->bg = statusbg;

		}
		drawstatus();
	}
}

void statusbar_focusout(){
	if ( focusdraw && statusvisible && statusbar ){
		focusdraw = 0;
		for ( Glyph *g = statusbar; g < &statusbar[statuswidth]; g++) {
			g->mode = ATTR_REVERSE;
			g->fg = defaultfg,
			g->bg = defaultbg;
		}
		drawstatus();
	}
}

// text entering, textfield
// besser als struct, objektorientiert.
uchar tfbuf[512];
int tfbuflen = 512;

int tfpos = 0; // is not necessarily the pos of the cursor.
int tftextlen = 0;
int tfvisible = 0;

uchar tfinput[512];
int tfinputlen = 512;

// callbacks
//
// xkeyboard -> keypress
// verarbeitet input, auch keycombos
//
// tfevent -> receiver (searchwidget)
// sendet:
// textentered (buf)
// search (buf)
// complete (buf)
//
// setcursor(x) -> relative cursor position
//
// redraw() -> receiver (statusbar)
// ->redraw() beide richtungen. d.h. in statusbarupdate: setwidth(), paint(), getcursor()
//
// ->setwidth(x)
//
// ->view,hide
// ->setfocus()
//
//
// eventfilterlist. (Ctrl+F,Tab,ESC, . hm. evtl zu weit.)
// benoetige ein sinnvolles format fuewr die glyphen.
// vermutlich am besten "doppelt". textstring, und glyphlist fuer farbe und attribute.
//
// auch moeglich: die callbacks von sprintf in minilib fuer colorierung schreiben.
// ->sprintf: marker fuer farbe. zb ein Test in %ROT%Farbe
// hatte ja schonmal ein konzept.
//
// sinnvoll, hier, hab keine zeit: variablen und callbacks als struktur.
//
// Rest koennte ich immer noch aufbauen. bspw sender- verteiler -receiver
// usw.
//
// besser: slots.
// in der strukt slot registerkey[], drin struct: key,callback(key,sender)
// -> structs muessen fuer alle widgets eine selbe grundstruktur am anfang haben.
// -> = "parent" class.
// -> sogar die syntax wird nett: struct _textinput { struct widget; ...
// laesst sich dann auch polymorph gestalten.
// create_widget( typ )... setzt callbacks, initialisiert.
//
// kann natuerlich auch macros machen:
// struct widget; wird zu PARENTCLASS widget;
// "public" scheint unpassend.
// genaugenommen: struct _textinput{ CLASS widget;
// und struct _widget{ CLASS object;
// ...
// nicht mal casten notwendig.
// nur upcasten, von object auf widget, usw.
//
// kann ich natuerlich auch gleich mit ids arbeiten.
// fuer jede instanz, und jede klasse.
//

// for the mode MODE_ENTERSTRING
void statusbar_kpress( KeySym *ks, char *buf ){

}

// updates the statusbar with current line, etc., when visible.
void updatestatus(){

	if ( statusvisible ){

		// currently shown number of cols
		int stwidth = statuswidth;
		if ( term->cols != statuswidth )
			stwidth = term->cols;

		char buf[512];
		memset( buf, ' ', 256 );
		//bzero(buf+256,256);

		//int p = sprintf(buf,"  %s  %5d-%2d %5d %5d %3d%% (%3d%%)   RM:%3d", p_status,
		int p = 0;

		int fstwidth = stwidth;

		stwidth -= strlen(p_status)+3; // try to keep that much free space at the left

		// scrollinfo
		if ( stwidth > 32 + 11 ){
			p = sprintf(buf+256,"%5d-%2d %5d %5d %3d%% (%3d%%)   RM:%3d ",
					term->histindex-term->scr,term->histindex-term->scr+term->rows,
					term->histindex+term->rows, term->histindex+term->rows-(term->histindex-term->scr+term->rows),
					((term->histindex-term->scr)*100)/((term->histindex)?term->histindex:1),
					((term->histindex-term->scr-term->scrollmarks[0]+1)*100)/((term->histindex-term->scrollmarks[0]+1)?term->histindex-term->scrollmarks[0]+1:1),
					term->scrolled_retmark
					);

			if ( stwidth > p+10 ){
				for ( int a=1; a<10; a++ ){
					if ( term->scrollmarks[a] )
						buf[p++] = a+'0';
					else
						buf[p++] = ' ';
				}
				if ( term->scrollmarks[0] )
					buf[p++] = '0';
				else
					buf[p++] = ' ';
			}

		} else if ( stwidth > 20 + 6 ){ //TODO: other values (line number)
			p = sprintf(buf+256,"%5d %5d %3d%%   RM:%3d ",
					term->histindex+term->rows, term->histindex+term->rows-(term->histindex-term->scr+term->rows),
					((term->histindex-term->scr)*100)/((term->histindex)?term->histindex:1),
					term->scrolled_retmark );
		} else {
			p = sprintf(buf+256,"%5d %3d%% ",
					term->histindex+term->rows-(term->histindex-term->scr+term->rows),
					((term->histindex-term->scr)*100)/((term->histindex)?term->histindex:1) );
		}

		//printf("p: %d\n",p);
		buf[p] = 0;

		int bp = 256 - fstwidth + p;
		if ( bp <0 ) bp = 0;
		if ( 256-bp > strlen(p_status) +3 )
			memcpy( buf+bp+3, p_status, strlen(p_status) );

		setstatus(buf+bp);
	}
}

void setstatus(char* status){
	Glyph *deb, *fin;

	if ( term->colalloc != statuswidth ){
		free(statusbar);
		statusbar = xmalloc(term->colalloc * sizeof(Glyph));
		//statusbar = xrealloc(statusbar, term->colalloc * sizeof(Glyph));
		statuswidth = term->colalloc;
	}
	int stwidth = statuswidth;
	if ( term->cols != stwidth )
		stwidth = term->cols;

#ifndef UTF8
	Glyph g = { .fg = statusfg, .bg = statusbg, .mode = statusattr, .u = ' ' };
#endif

	for (deb = statusbar,fin=&statusbar[stwidth]; (deb < fin);	deb++) {
#ifdef UTF8
		deb->mode = statusattr;
		deb->fg = statusfg;
		deb->bg = statusbg;
#else
		deb->intG = g.intG;
#endif
		if ( *status ){
			deb->u = *status;
			status++;
		}
	}

}

void showstatus(int show, char *status){
	if ( p_status ){
		free(p_status);
		p_status = NULL;
	}

	if ( show ){
		p_status = strdup(status);
		if ( !statusvisible ){
			statusvisible = 1;
			Arg a = { .i=0 };
			kscrollup(&a); // (clears the statusbar). I know. But works.
			setstatus(status);
			// paint status
			redraw();
		}

	} else {
		if ( statusvisible ){
			statusvisible = 0;
			// clear status
			term->dirty[term->bot] = 1;
			drawregion(0, term->bot, term->cols, term->bot + 1);
			//term->rows++;
			//tresize(term->cols,term->rows+1);
		}
	}
}

// shows a small message at the bottom right corner
void set_notifmode(int type, KeySym ksym) {
	static char *lib[] = {" MOVE ", "SELECT"," LESS " };
	static Glyph *g, *deb, *fin;
	static int col, bot;
	col = term->cols, bot = term->bot;

	if (ksym == -1) { // show
		free(g);
		g = xmalloc(term->colalloc * sizeof(Glyph));
		memcpy(g, TLINE(bot), term->colalloc * sizeof(Glyph));
		//tresize(term->cols,term->rows-1);
	} else if (ksym == -2) { // hide
		memcpy(TLINE(bot), g, term->colalloc * sizeof(Glyph));
		//tresize(term->cols,term->rows+1);
	}

	if (type < 3) {
		char *z = lib[type];
		for (deb = &TLINE(bot)[col - 6], fin = &TLINE(bot)[col]; deb < fin;
				z++, deb++) {
			deb->mode = ATTR_REVERSE, deb->u = *z, deb->fg = defaultfg,
				deb->bg = defaultbg;
		}
	} else if (type < 5) {
		memcpy(TLINE(bot), g, term->colalloc * sizeof(Glyph));
		//memcpy(TLINE(bot), g, term->colalloc * sizeof(Glyph));
	} else {
		for (deb = &TLINE(bot)[0], fin = &TLINE(bot)[col]; deb < fin;
				deb++) {
			deb->mode = ATTR_REVERSE, deb->u = ' ', deb->fg = defaultfg,
				deb->bg = defaultbg;
		}
		TLINE(bot)[0].u = ksym;
	}

	term->dirty[bot] = 1;
	drawregion(0, bot, col, bot + 1);
}


code:
#ifndef statusbar_h
#define statusbar_h

extern int statusvisible;

void statusbar_kpress( KeySym *ks, char *buf );
void updatestatus();
void setstatus(char* status);
void statusbar_focusin();
void statusbar_focusout();

#endif


code:
// system related. fork, exec, die

#include "includes.h"

void die(const char *errstr, ...) {
		va_list ap;

		va_start(ap, errstr);
		vfprintf(stderr, errstr, ap);
		va_end(ap);
		exit(1);
}

void execsh(char *cmd, char **args) {
		char *sh, *prog;
		const struct passwd *pw;

		errno = 0;
		if ((pw = getpwuid(getuid())) == NULL) {
				if (errno) {
						die("getpwuid: %s\n", strerror(errno));
				} else {
						die("Cannout read uid\n");
				}
		}

		if ((sh = getenv("SHELL")) == NULL) {
				sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd;
		}

		if (args) {
				prog = args[0];
		} else if (utmp) {
				prog = utmp;
		} else {
				prog = sh;
		}
		DEFAULT(args, ((char *[]){prog, NULL}));

		unsetenv("COLUMNS");
		unsetenv("LINES");
		unsetenv("TERMCAP");
		setenv("LOGNAME", pw->pw_name, 1);
		setenv("USER", pw->pw_name, 1);
		setenv("SHELL", sh, 1);
		setenv("HOME", pw->pw_dir, 1);
		setenv("TERM", termname, 1);

		// define env in config.h
		for ( const char **p = (const char**)export_env; *p; p++ ){
			setenv( p[0], p[1], 1 );
		}

		signal(SIGCHLD, SIG_DFL);
		signal(SIGHUP, SIG_DFL);
		signal(SIGINT, SIG_DFL);
		signal(SIGQUIT, SIG_DFL);
		signal(SIGTERM, SIG_DFL);
		signal(SIGALRM, SIG_DFL);

		execvp(prog, args);
		// didn't work.
		// try default, set in config.h.in
		execvp(shell,args);
		_exit(1);
}

void sigchld(int a) {
		int stat;
		pid_t p;

		if ((p = waitpid(shellpid, &stat, WNOHANG)) < 0) {
				die("waiting for pid %hd failed: %s\n", shellpid, strerror(errno));
		}

		if (shellpid != p) {
				return;
		}

		if (WIFEXITED(stat) && WEXITSTATUS(stat)) {
				die("child exited with status %d\n", WEXITSTATUS(stat));
		} else if (WIFSIGNALED(stat)) {
				die("child terminated due to signal %d\n", WTERMSIG(stat));
		}
		exit(0);
}


code:
#ifndef st_system_h
#define st_system_h

void die(const char *errstr, ...);
void execsh(char *, char **);
void sigchld(int a);

#endif


code:
/* See LICENSE for license details. */

#include "term.h"
#include "xwindow.h"
#include "selection.h"
#include "scroll.h"
#include "mem.h"
#include "base64.h"
#include "utf8.h"
#include "statusbar.h"
#include "termdraw.h"

#include "help.h"

#if defined(__linux)
#include <pty.h>
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
#include <util.h>
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include <libutil.h>
#endif

// this is terrorism. misc. How not to use macros.
// Let me guess, "macros are messed up anyways. need to fix C" .....
// Well. something is always messed up.
// And yeah, this should be fixed.
// On the other hand, I do have some fun, and this code isn't professional
// at all. It's been neither my intention, nor the intention of the previous authors (assuming).
#undef IS_SET
// used for winmode, termmode, .. with other macro definitions(!)
#define IS_SET(flag) ((term->mode & (flag)) != 0)

static void tcursor(int);
static void tmoveto(int, int);
static void tmoveato(int, int);
static void treset(void);
static void tswapscreen(void);

static void tdectest(utfchar);
static int32_t tdefcolor(int *, int *, int);

void tty_send_unicode(const Arg *arg){
	utfchar c = (utfchar)arg->i;
	ttywrite(&c, 1, 1);
}

void check_canary(){
	asm( "" : "+m"(term->guard));
	if ( term->guard != 0xf0f0f0f0 )
		die("term->guard modified");
}

// initiate new terminal window and buffers
void tnew(int cols, int rows, uint histsize) {
	dbg2("tnew *******************************************************\n");
	dbg2("cols: %d, rows: %d\n",cols,rows);
	//term = (Term){.c = {.attr = {.fg = defaultfg, .bg = defaultbg}}};
	term = xmalloc(sizeof(Term));
	memset ( term, 0, sizeof(Term) );
	if ( !p_term )
		p_term = term;
	term->cursor = (TCursor){.attr = {.fg = defaultfg, .bg = defaultbg}};

	term->hist = xmalloc( histsize * sizeof(Line*) );
	memset( term->hist, 0, histsize * sizeof(Line*) );

	if ( histsize <65 || ( ((~histsize) & (histsize-1) )+1 != histsize ) )
		die("tnew: history buffer size needs to be a power of 2, and > 64. Got: %d\n",histsize );
	// else hell will break loose.

	term->histsize = histsize-1;

	term->hist[0] = xmalloc( cols * sizeof(Glyph));
	memset(term->hist[0],0, cols * sizeof(Glyph));

	//term->colsalloc = 0;
	//term->scroll_retmark = 1;
	//term->current_retmark = 0;

	term->guard = 0xf0f0f0f0;

	asm( "" : "+m"(term->guard));
	tresize(cols,rows);
	treset();

}

int tlinelen(int y) {
	int i = term->cols;

	if (TLINE(y)[i - 1].mode & ATTR_WRAP) {
		return i;
	}

	while (i > 0 && TLINE(y)[i - 1].u == ' ') {
		--i;
	}

	return i;
}

void tcursor(int mode) {
	static TCursor c[2];
	int alt = IS_SET(MODE_ALTSCREEN);

	if (mode == CURSOR_SAVE) {
		c[alt] = term->cursor;
	} else if (mode == CURSOR_LOAD) {
		term->cursor = c[alt];
		tmoveto(c[alt].x, c[alt].y);
	}
}

void treset(void) {
	uint i;

	term->cursor = (TCursor){{.mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg,.u=' '},
		.x = 0,
		.y = 0,
		.state = CURSOR_DEFAULT};

	memset(term->tabs, 0, term->colalloc * sizeof(*term->tabs));
	for (i = tabspaces; i < term->colalloc; i += tabspaces) {
		term->tabs[i] = 1;
	}
	term->top = 0;
	term->bot = term->rows - 1;
	term->mode = MODE_WRAP | MODE_UTF8; // not UTF8-> MODE_UTF8 eq 0
	memset(term->trantbl, CS_USA, sizeof(term->trantbl));
	term->charset = 0;

	//for (i = 0; i < 2; i++) {
		tmoveto(0, 0);
		tcursor(CURSOR_SAVE);
		tclearregion(0, 0, term->colalloc - 1, term->rows - 1);
		//tswapscreen(); //xxx
	//}
}

int tisaltscr(void) {

	return IS_SET(MODE_ALTSCREEN);
}

// show alt screen (swap)
void tswapscreen(void) {
#if 0
	SWAPp( term->line, term->alt ); //xxx
	term->mode ^= MODE_ALTSCREEN; //xxx
	//printf("swapscreen, mode: %x\n",term->mode);
	tfulldirt();
#else
	// swap the whole term struct
	///printf("swapscreen, mode: %x\n",term->mode);
	if ( p_alt != term ){ // altscr is not visible now
		lessmode_toggle( &(Arg){.i=LESSMODE_OFF} );
		if ( !p_alt ){ // displayed first time
			tnew(term->cols, term->rows, ALTSCREEN_HISTSIZE);
			p_alt = term;
			// fix segfaults (prealloc buffer of alt screen)
			//for ( int i = 1; i<ALTSCREEN_HISTSIZE; i++ ){
			//	term->hist[i] = 0;//xmalloc( term->colalloc * sizeof(Glyph));
				//memset(term->hist[i],0,term->colalloc * sizeof(Glyph));
			//}
			//term->circledhist=1;

		} else { // p_alt != term
			term = p_alt;
			if ( ( p_term->rows != term->rows ) || ( p_term->cols != term->cols )){
				tresize( p_term->cols, p_term->rows );
			}
		}
		term->mode |= MODE_ALTSCREEN;
	} else {
		term = p_term;
		if ( ( p_alt->rows != term->rows ) || ( p_alt->cols != term->cols ))
			tresize( p_alt->cols, p_alt->rows );
	}

	tfulldirt();
#endif
}

void inverse_screen(){
	twin.mode ^= MODE_REVERSE;
	redraw();
}

void quithelp( const Arg *a ){
	showhelp( a );
}

void showhelp(const Arg *a) {
	//printf("showhelp\n");

	if ( p_help != term ){ // help is not visible now
								  //p_term = term;
		if ( !p_help ){ // displayed first time
			p_help_storedterm = term;
			tnew(term->cols, term->rows, HELPSCREEN_HISTSIZE);
			p_help = term;
			twrite( (utfchar*)helpcontents, strlen(helpcontents), 0 );
		} else {
			p_help_storedterm = term;
			term = p_help;
			if ( ( p_term->rows != term->rows ) || ( p_term->cols != term->cols )){
				tresize( p_term->cols, p_term->rows );
				//free(term);	// buggy. There might be some memory leaks.
				// to fix it, I guess it would be best rewriting all this unlucky
				// screen buffer stuff. Separating screen buffer and history clearly
				// isn't a good idea. It works now. Should have rewritten it from the beginning.
				// Would have been less work, than fiddling around with really hard to get lines of code
				// and bugs.
				// Oh. And just now I realize - could be uclibc (I'm working with) or gcc related
				// as well. I did have already other bugs, obviously compiler or standard libc related.
				// But, well. im going outside,
				//  sun is shining.
				//
				//tnew(term->cols, term->rows);
				//p_help = term;
				//twrite( helpcontents, strlen(helpcontents), 0 );
			}
		}
		lessmode_toggle( ARGPi( LESSMODE_ON) );

		help_storedinputmode = inputmode;
		inputmode = inputmode | IMODE_HELP;
		inputmode = inputmode & ~(MODE_LESS);
		//inputmode = inputmode | MODE_LESS | IMODE_HELP;
		term->mode = term->mode | TMODE_HELP;
		//enterscroll(&a);
		showstatus(1,"-HELP- ( q to exit )");

		scrolltotop();

	} else { //help is visible. toggle back to term.
		//inputmode = inputmode & ~(IMODE_HELP);
		inputmode = help_storedinputmode;
		//inputmode = inputmode & ~(MODE_LESS | IMODE_HELP);
		showstatus(0,0);
		term = p_help_storedterm;
		lessmode_toggle( ARGPi( LESSMODE_OFF ) ); // bugs else.
		if ( ( p_help->rows != term->rows ) || ( p_help->cols != term->cols ))
			tresize( p_help->cols, p_help->rows );
	}

	tfulldirt();
}

/* for absolute user moves, when decom is set */
void tmoveato(int x, int y) {
	//printf("tmoveato: %d\n",y);

	// delete retmarks, within the region.
	// needed amongst others for screen based programs
	if ( y==0 ){
		int tend = term->current_retmark;
		for ( int t = (term->current_retmark -1 ) & (RETMARKCOUNT-1);
				(t!=tend) &&
			 	(term->histindex < term->retmarks[t] ) &&
				(term->histindex + term->rows+1 > term->retmarks[t]);
				t = (t-1) & ( RETMARKCOUNT-1 ) ){
			term->current_retmark = t;
			term->retmarks[ term->current_retmark ] = 0;
		}
		//term->scroll_retmark = term->current_retmark;
	}

	tmoveto(x, y + ((term->cursor.state & CURSOR_ORIGIN) ? term->top : 0));
}

void tmoveto(int x, int y) {
	int miny, maxy;

	if (term->cursor.state & CURSOR_ORIGIN) {
		miny = term->top;
		maxy = term->bot;
	} else {
		miny = 0;
		maxy = term->rows - 1;
	}
	term->cursor.state &= ~CURSOR_WRAPNEXT;
	term->cursor.x = LIMIT(x, 0, term->cols - 1);
	term->cursor.y = LIMIT(y, miny, maxy);
}
void tdectest(utfchar c) {
	int x, y;

	if (c == '8') { /* DEC screen alignment test. */
		for (x = 0; x < term->cols; ++x) {
			for (y = 0; y < term->rows; ++y) {
				tsetchar('E', &term->cursor.attr, x, y);
			}
		}
	}
}

void tresize(int col, int row) {
	int i;
	int minrow = MIN(row, term->rows);
	int mincol = MIN(col, term->cols);
	int *bp;
	int enlarge = 0;
	TCursor c;
	int oldwidth = term->colalloc;

	DBG("tresize: %d %d %d %d %d\n", col,row, term->cols, term->rows, term->colalloc );

	if ( col > term->colalloc ){
		term->colalloc = col;
		enlarge = 1;
	}

	if (row < term->rows || col < term->cols) {
		toggle_winmode(trt_kbdselect(XK_Escape, NULL, 0));
	}

	if (col < 1 || row < 1) {
		fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row);
		return;
	}

	/*
	 * slide screen to keep cursor where we expect it -
	 * tscrollup would work here, but we can optimize to
	 * memmove because we're freeing the earlier lines
	 */
	for (i = 0; i <= term->cursor.y - row; i++) {
		free(term->line[i]);
	}
	/* ensure that both src and dst are not NULL */
	if (i > 0) {
		memmove(term->line, term->line + i, row * sizeof(Line));
	}
	for (i += row; i < term->rows; i++) {
		free(term->line[i]);
	}

	/* resize to new height */
	term->line = xrealloc(term->line, row * sizeof(Line));
	term->dirty = xrealloc(term->dirty, row * sizeof(*term->dirty));
	term->tabs = xrealloc(term->tabs, term->colalloc * sizeof(*term->tabs));

	/* int oldline = 0;
	int newline = 0;
	int oldcol = 0;
	int newcol = 0;
//	int newhist = !(term->cthist);
//	int oldhist = term->cthist;
	// delay here. Collect resize events
	if ( term->circledhist  ){
		oldline = (term->histindex+1 > HISTSIZE ) ? 0 : (term->histindex+1);
	}
	dbg2(AC_YELLOW "oldline: %d  term->histindex: %d  term->col: %d col: %d" AC_NORM,oldline,term->histindex, term->cols, col);
	*/
	term->cursor.attr.u = ' ';
#if 0

	if ( oldline != term->histindex ){
		term->hist[newline] = xmalloc( col * sizeof(Glyph));
		memset32( &term->hist[newline][mincol].intG, term->cursor.attr.intG, col-mincol );
	}

	while (oldline!=term->histindex) { // Didn't reach the end of the old history yet
		dbg3( "oldhist: %d term->cols %d newhist %d oldline: %d oldcol: %d newline: %d newcol: %d", oldhist, term->cols,newhist, oldline, oldcol, newline, newcol );
		while( (oldline!=term->histindex) && (oldcol < term->cols ) ){ // && !( ( oldcol>0 ) && (term->hist[oldline][oldcol-1].mode & ATTR_WRAP )) ){
			dbg3( "term->col: %d L2: oldline: %d oldcol: %d newline: %d newcol: %d",term->cols, oldline, oldcol, newline, newcol );
			//dbg3( "intG oldhist: %d - %d\n", term->hist[oldline][oldcol].intG, term->hist[oldline][oldcol].u );
			if ( term->hist[oldline][oldcol].mode & ATTR_WRAP ){
				dbg2("WRAP");
			}
			term->hist[newline][newcol].intG = term->hist[oldline][oldcol].intG;
			//term->hist[newline][newcol].mode
			oldcol++;
			newcol++;
			if ( ( newcol == col) || ( (oldcol>0) && (term->hist[oldline][oldcol-1].mode & ATTR_WRAP )) ){ // end of line
				dbg3("Eol. newcol: %d  oldcol:%d",newcol,oldcol);
				//term->hist[newline][newcol-1].mode |= ATTR_WRAP;
				newline++;
				newline &= ((1<<HISTSIZEBITS)-1);
				newcol=0;
				if ( !term->hist[newline] ){
					term->hist[newline] = xmalloc( col * sizeof(Glyph));
					dbg3(AC_BLUE"malloc: hist %d, line %d, cols: %d"AC_NORM, newhist, newline, col );
				} else {
					dbg3(AC_GREEN"realloc: hist %d, line %d, cols: %d"AC_NORM, newhist, newline, col );
					term->hist[newline] = xrealloc( 	term->hist[newline], col * sizeof(Glyph));
				}
				//dbg3("newline: %d",newline);
				memset32( &term->hist[newline][mincol].intG, term->cursor.attr.intG, col-mincol );
			}
			if ( oldcol == term->cols ){// && !( ( oldcol>0 ) && (term->hist[oldline][oldcol-1].mode & ATTR_WRAP )) ){
				dbg3( "YY: newline: %d newcol: %d", newline, newcol );
				//free( term->hist[oldline] );
				oldcol = 0;
				//term->hist[oldline] = 0;
				oldline++;
				oldline &= ((1<<HISTSIZEBITS)-1); // modulo

			}

			}
		}
		term->cthist = newhist;
		dbg2("copied hist. oldhist: %d  term->cthist: %d", oldhist, term->cthist );
#else
		int t = term->histindex;
		if ( term->circledhist  ){
			t = term->histsize+1; //HISTSIZE;
		}

		if ( enlarge )
			for (i = 0; i < t; i++) { //
				term->hist[i] = xrealloc(term->hist[i], term->colalloc * sizeof(Glyph));
#ifndef UTF8
				memset32( &term->hist[i][oldwidth].intG, term->cursor.attr.intG, term->colalloc-oldwidth );
				//memset32( &term->hist[i][mincol].intG, term->cursor.attr.intG, term->colalloc-mincol );
				//for (j = mincol; j < col; j++) {
				//		term->hist[i][j].intG = term->cursor.attr.intG;
				//}
#else
				for (j = mincol; j < term->colalloc; j++) {
					term->hist[i][j] = term->cursor.attr;
					term->hist[i][j].u = ' ';
					//append empty chars, if more cols than before
				}
#endif
			}

#endif
		dbg("cp\n");
		/* resize each row to new width, zero-pad if needed */
		if ( enlarge ){
			for (i = 0; i < minrow; i++) {
				//dbg3("i: %d, %d", i, minrow );
				term->line[i] = xrealloc(term->line[i], term->colalloc * sizeof(Glyph));
				//dbg3("i2\n");
				//term->alt[i] = xrealloc(term->alt[i], term->colalloc * sizeof(Glyph));
				//term->helpscr[i] = xrealloc(term->helpscr[i], term->colalloc * sizeof(Glyph));
			}
		} else { // don't shrink the width.
			for (i = 0; i < minrow; i++) {

				//term->alt[i] = xrealloc(term->alt[i], term->colalloc * sizeof(Glyph));
				//dbg3("i: %d, %d", i, minrow );
				//term->line[i] = xrealloc(term->line[i], col * sizeof(Glyph));
				//dbg3("i2\n");
				//term->alt[i] = xrealloc(term->alt[i], col * sizeof(Glyph));
				//memset(term->alt[i], 0, col);
			}
		}

		dbg3("i4\n");
		/* allocate any new rows */
		for ( i = minrow ; i < row; i++) {
			term->line[i] = xmalloc(term->colalloc * sizeof(Glyph));
			memset(term->line[i], 0, sizeof(Glyph) * term->colalloc);
		}
		/*if ( term->colalloc > col ){
		  if ( minrow < row )
		  term->colalloc = col;
		  } else {
		  if ( col > term->colalloc )
		  term->colalloc = col;
		  }*/

		if ( enlarge )
			if (col > term->cols) {
				bp = term->tabs + term->cols;

				memset(bp, 0, sizeof(*term->tabs) * (term->colalloc - term->cols));
				while (--bp > term->tabs && !*bp) {
					/* nothing */
				}
				for (bp += tabspaces; bp < term->tabs + term->colalloc; bp += tabspaces) {
					*bp = 1;
				}
			}
		/* update terminal size */
		term->cols = col;
		term->rows = row;
		/* reset scrolling region */
		tsetscroll(0, row - 1);
		/* make use of the LIMIT in tmoveto */
		tmoveto(term->cursor.x, term->cursor.y);
		/* Clearing both screens (it makes dirty all lines) */
		c = term->cursor;
		//for (i = 0; i < 2; i++) {
			//if ( mincol < term->colalloc && !enlarge )
			if (mincol < term->colalloc && 0 < minrow && enlarge) {
				tclearregion(mincol, 0, term->colalloc - 1, minrow - 1);
			}
			if (0 < col && minrow < row) {
				tclearregion(0, minrow, term->colalloc - 1, row - 1);
			}
			//tswapscreen(); //xxx
			//tcursor(CURSOR_LOAD);
		//}
		term->cursor = c;

		if ( !enlarge )
			tfulldirt();

}