Compare commits
	
		
			68 Commits
		
	
	
		
			ed68fe7dce
			...
			9e68fdbcdb
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					9e68fdbcdb | ||
| 
						 | 
					4ef0cbd8b9 | ||
| 
						 | 
					28b4c822c5 | ||
| 
						 | 
					fa253f077f | ||
| 
						 | 
					b27a383a3a | ||
| 
						 | 
					81067c65ea | ||
| 
						 | 
					f74a9df6e1 | ||
| 
						 | 
					818ec746f4 | ||
| 
						 | 
					9ba7ecf7b1 | ||
| 
						 | 
					a2a704492b | ||
| 
						 | 
					0f8b40652b | ||
| 
						 | 
					e6e2c6199f | ||
| 
						 | 
					94b8ec0021 | ||
| 
						 | 
					dec6b530a4 | ||
| 
						 | 
					475a0a36cb | ||
| 
						 | 
					e8392b282c | ||
| 
						 | 
					f8afebdfa0 | ||
| 
						 | 
					bda9c9ffa6 | ||
| 
						 | 
					045a0fab4f | ||
| 
						 | 
					9c30066e73 | ||
| 
						 | 
					8304d4f059 | ||
| 
						 | 
					914fb825df | ||
| 
						 | 
					cde480c693 | ||
| 
						 | 
					8211e36d28 | ||
| 
						 | 
					87545c612e | ||
| 
						 | 
					1d59091065 | ||
| 
						 | 
					d6ea0a1a61 | ||
| 
						 | 
					43a395ae91 | ||
| 
						 | 
					72e3f6c7c0 | ||
| 
						 | 
					33a9a45664 | ||
| 
						 | 
					771bc401f7 | ||
| 
						 | 
					d66bd405c0 | ||
| 
						 | 
					e997303502 | ||
| 
						 | 
					c1145268f6 | ||
| 
						 | 
					0b73612c0d | ||
| 
						 | 
					019449a7e6 | ||
| 
						 | 
					fbae700a3f | ||
| 
						 | 
					e52319cc7d | ||
| 
						 | 
					21e0d6e8b8 | ||
| 
						 | 
					5703aa0390 | ||
| 
						 | 
					28ad288399 | ||
| 
						 | 
					51e19ea11d | ||
| 
						 | 
					26cdfebf31 | ||
| 
						 | 
					cd785755f2 | ||
| 
						 | 
					2cb539142b | ||
| 
						 | 
					99de333951 | ||
| 
						 | 
					895e5b50a8 | ||
| 
						 | 
					384830110b | ||
| 
						 | 
					2e54a21b5a | ||
| 
						 | 
					289c52b7aa | ||
| 
						 | 
					7ceb3d1f72 | ||
| 
						 | 
					ea4d933ed9 | ||
| 
						 | 
					83866428de | ||
| 
						 | 
					1f09f0b0bb | ||
| 
						 | 
					a2c479c4c8 | ||
| 
						 | 
					d2b75db8d7 | ||
| 
						 | 
					b6d280de6d | ||
| 
						 | 
					ba7f4d69af | ||
| 
						 | 
					2b8333f553 | ||
| 
						 | 
					caa1d8fbea | ||
| 
						 | 
					f1546cf9c1 | ||
| 
						 | 
					21367a040f | ||
| 
						 | 
					b650256044 | ||
| 
						 | 
					9acec468fb | ||
| 
						 | 
					927621f6da | ||
| 
						 | 
					add0211522 | ||
| 
						 | 
					d5efd256aa | ||
| 
						 | 
					75b4ba4b4b | 
							
								
								
									
										107
									
								
								FAQ
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								FAQ
									
									
									
									
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
## Why does st not handle utmp entries?
 | 
			
		||||
 | 
			
		||||
Use the excellent tool of [utmp](http://git.suckless.org/utmp/) for this task.
 | 
			
		||||
Use the excellent tool of [utmp](https://git.suckless.org/utmp/) for this task.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever!
 | 
			
		||||
 | 
			
		||||
@@ -8,6 +9,7 @@ It means that st doesn’t have any terminfo entry on your system. Chances are
 | 
			
		||||
you did not `make install`. If you just want to test it without installing it,
 | 
			
		||||
you can manually run `tic -sx st.info`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Nothing works, and nothing is said about an unknown terminal!
 | 
			
		||||
 | 
			
		||||
* Some programs just assume they’re running in xterm i.e. they don’t rely on
 | 
			
		||||
@@ -15,19 +17,21 @@ you can manually run `tic -sx st.info`.
 | 
			
		||||
* Some programs don’t complain about the lacking st description and default to
 | 
			
		||||
  another terminal. In that case see the question about terminfo.
 | 
			
		||||
 | 
			
		||||
## I get some weird glitches/visual bug on _random program_!
 | 
			
		||||
 | 
			
		||||
Try launching it with a different TERM: $ TERM=xterm myapp. toe(1) will give
 | 
			
		||||
you a list of available terminals, but you’ll most likely switch between xterm,
 | 
			
		||||
st or st-256color. The default value for TERM can be changed in config.h
 | 
			
		||||
(TNAME).
 | 
			
		||||
 | 
			
		||||
## How do I scroll back up?
 | 
			
		||||
 | 
			
		||||
Using a terminal multiplexer.
 | 
			
		||||
* Using a terminal multiplexer.
 | 
			
		||||
	* `st -e tmux` using C-b [
 | 
			
		||||
	* `st -e screen` using C-a ESC
 | 
			
		||||
* Using the excellent tool of [scroll](https://git.suckless.org/scroll/).
 | 
			
		||||
* Using the scrollback [patch](https://st.suckless.org/patches/scrollback/).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## I would like to have utmp and/or scroll functionality by default
 | 
			
		||||
 | 
			
		||||
You can add the absolute patch of both programs in your config.h
 | 
			
		||||
file. You only have to modify the value of utmp and scroll variables.
 | 
			
		||||
 | 
			
		||||
* `st -e tmux` using C-b [
 | 
			
		||||
* `st -e screen` using C-a ESC
 | 
			
		||||
 | 
			
		||||
## Why doesn't the Del key work in some programs?
 | 
			
		||||
 | 
			
		||||
@@ -84,12 +88,14 @@ If you are using zsh, then read the zsh FAQ
 | 
			
		||||
 | 
			
		||||
Putting these lines into your .zshrc will fix the problems.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## How can I use meta in 8bit mode?
 | 
			
		||||
 | 
			
		||||
St supports meta in 8bit mode, but the default terminfo entry doesn't
 | 
			
		||||
use this capability. If you want it, you have to use the 'st-meta' value
 | 
			
		||||
in TERM.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## I cannot compile st in OpenBSD
 | 
			
		||||
 | 
			
		||||
OpenBSD lacks librt, despite it being mandatory in POSIX
 | 
			
		||||
@@ -98,13 +104,14 @@ If you want to compile st for OpenBSD you have to remove -lrt from config.mk, an
 | 
			
		||||
st will compile without any loss of functionality, because all the functions are
 | 
			
		||||
included in libc on this platform.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## The Backspace Case
 | 
			
		||||
 | 
			
		||||
St is emulating the Linux way of handling backspace being delete and delete being
 | 
			
		||||
backspace.
 | 
			
		||||
 | 
			
		||||
This is an issue that was discussed in suckless mailing list
 | 
			
		||||
<http://lists.suckless.org/dev/1404/20697.html>. Here is why some old grumpy
 | 
			
		||||
<https://lists.suckless.org/dev/1404/20697.html>. Here is why some old grumpy
 | 
			
		||||
terminal users wants its backspace to be how he feels it:
 | 
			
		||||
 | 
			
		||||
	Well, I am going to comment why I want to change the behaviour
 | 
			
		||||
@@ -159,9 +166,85 @@ terminal users wants its backspace to be how he feels it:
 | 
			
		||||
	[1] http://www.ibb.net/~anne/keyboard.html
 | 
			
		||||
	[2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## But I really want the old grumpy behaviour of my terminal
 | 
			
		||||
 | 
			
		||||
Apply [1].
 | 
			
		||||
 | 
			
		||||
[1] http://st.suckless.org/patches/delkey
 | 
			
		||||
[1] https://st.suckless.org/patches/delkey
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Why do images not work in st using the w3m image hack?
 | 
			
		||||
 | 
			
		||||
w3mimg uses a hack that draws an image on top of the terminal emulator Drawable
 | 
			
		||||
window. The hack relies on the terminal to use a single buffer to draw its
 | 
			
		||||
contents directly.
 | 
			
		||||
 | 
			
		||||
st uses double-buffered drawing so the image is quickly replaced and may show a
 | 
			
		||||
short flicker effect.
 | 
			
		||||
 | 
			
		||||
Below is a patch example to change st double-buffering to a single Drawable
 | 
			
		||||
buffer.
 | 
			
		||||
 | 
			
		||||
diff --git a/x.c b/x.c
 | 
			
		||||
--- a/x.c
 | 
			
		||||
+++ b/x.c
 | 
			
		||||
@@ -732,10 +732,6 @@ xresize(int col, int row)
 | 
			
		||||
 	win.tw = col * win.cw;
 | 
			
		||||
 	win.th = row * win.ch;
 | 
			
		||||
 
 | 
			
		||||
-	XFreePixmap(xw.dpy, xw.buf);
 | 
			
		||||
-	xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
 | 
			
		||||
-			DefaultDepth(xw.dpy, xw.scr));
 | 
			
		||||
-	XftDrawChange(xw.draw, xw.buf);
 | 
			
		||||
 	xclear(0, 0, win.w, win.h);
 | 
			
		||||
 
 | 
			
		||||
 	/* resize to new width */
 | 
			
		||||
@@ -1148,8 +1144,7 @@ xinit(int cols, int rows)
 | 
			
		||||
 	gcvalues.graphics_exposures = False;
 | 
			
		||||
 	dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
 | 
			
		||||
 			&gcvalues);
 | 
			
		||||
-	xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
 | 
			
		||||
-			DefaultDepth(xw.dpy, xw.scr));
 | 
			
		||||
+	xw.buf = xw.win;
 | 
			
		||||
 	XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
 | 
			
		||||
 	XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
 | 
			
		||||
 
 | 
			
		||||
@@ -1632,8 +1627,6 @@ xdrawline(Line line, int x1, int y1, int x2)
 | 
			
		||||
 void
 | 
			
		||||
 xfinishdraw(void)
 | 
			
		||||
 {
 | 
			
		||||
-	XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w,
 | 
			
		||||
-			win.h, 0, 0);
 | 
			
		||||
 	XSetForeground(xw.dpy, dc.gc,
 | 
			
		||||
 			dc.col[IS_SET(MODE_REVERSE)?
 | 
			
		||||
 				defaultfg : defaultbg].pixel);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## BadLength X error in Xft when trying to render emoji
 | 
			
		||||
 | 
			
		||||
Xft makes st crash when rendering color emojis with the following error:
 | 
			
		||||
 | 
			
		||||
"X Error of failed request:  BadLength (poly request too large or internal Xlib length error)"
 | 
			
		||||
  Major opcode of failed request:  139 (RENDER)
 | 
			
		||||
  Minor opcode of failed request:  20 (RenderAddGlyphs)
 | 
			
		||||
  Serial number of failed request: 1595
 | 
			
		||||
  Current serial number in output stream:  1818"
 | 
			
		||||
 | 
			
		||||
This is a known bug in Xft (not st) which happens on some platforms and
 | 
			
		||||
combination of particular fonts and fontconfig settings.
 | 
			
		||||
 | 
			
		||||
See also:
 | 
			
		||||
https://gitlab.freedesktop.org/xorg/lib/libxft/issues/6
 | 
			
		||||
https://bugs.freedesktop.org/show_bug.cgi?id=107534
 | 
			
		||||
https://bugzilla.redhat.com/show_bug.cgi?id=1498269
 | 
			
		||||
 | 
			
		||||
The solution is to remove color emoji fonts or disable this in the fontconfig
 | 
			
		||||
XML configuration.  As an ugly workaround (which may work only on newer
 | 
			
		||||
fontconfig versions (FC_COLOR)), the following code can be used to mask color
 | 
			
		||||
fonts:
 | 
			
		||||
 | 
			
		||||
	FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
 | 
			
		||||
 | 
			
		||||
Please don't bother reporting this bug to st, but notify the upstream Xft
 | 
			
		||||
developers about fixing this bug.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
MIT/X Consortium License
 | 
			
		||||
 | 
			
		||||
© 2014-2018 Hiltjo Posthuma <hiltjo at codemadness dot org>
 | 
			
		||||
© 2014-2020 Hiltjo Posthuma <hiltjo at codemadness dot org>
 | 
			
		||||
© 2018 Devin J. Pohly <djpohly at gmail dot com>
 | 
			
		||||
© 2014-2017 Quentin Rameau <quinq at fifth dot space>
 | 
			
		||||
© 2009-2012 Aurélien APTEL <aurelien dot aptel at gmail dot com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								config.def.h
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								config.def.h
									
									
									
									
									
								
							@@ -11,13 +11,15 @@ static int borderpx = 2;
 | 
			
		||||
/*
 | 
			
		||||
 * What program is execed by st depends of these precedence rules:
 | 
			
		||||
 * 1: program passed with -e
 | 
			
		||||
 * 2: utmp option
 | 
			
		||||
 * 2: scroll and/or utmp
 | 
			
		||||
 * 3: SHELL environment variable
 | 
			
		||||
 * 4: value of shell in /etc/passwd
 | 
			
		||||
 * 5: value of shell in config.h
 | 
			
		||||
 */
 | 
			
		||||
static char *shell = "/bin/sh";
 | 
			
		||||
char *utmp = NULL;
 | 
			
		||||
/* scroll program: to enable use a string like "scroll" */
 | 
			
		||||
char *scroll = NULL;
 | 
			
		||||
char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
 | 
			
		||||
 | 
			
		||||
/* identification sequence returned in DA and DECID */
 | 
			
		||||
@@ -30,9 +32,9 @@ static float chscale = 1.0;
 | 
			
		||||
/*
 | 
			
		||||
 * word delimiter string
 | 
			
		||||
 *
 | 
			
		||||
 * More advanced example: " `'\"()[]{}"
 | 
			
		||||
 * More advanced example: L" `'\"()[]{}"
 | 
			
		||||
 */
 | 
			
		||||
char *worddelimiters = " ";
 | 
			
		||||
wchar_t *worddelimiters = L" ";
 | 
			
		||||
 | 
			
		||||
/* selection timeouts (in milliseconds) */
 | 
			
		||||
static unsigned int doubleclicktimeout = 300;
 | 
			
		||||
@@ -41,9 +43,18 @@ static unsigned int tripleclicktimeout = 600;
 | 
			
		||||
/* alt screens */
 | 
			
		||||
int allowaltscreen = 1;
 | 
			
		||||
 | 
			
		||||
/* frames per second st should at maximum draw to the screen */
 | 
			
		||||
static unsigned int xfps = 120;
 | 
			
		||||
static unsigned int actionfps = 30;
 | 
			
		||||
/* allow certain non-interactive (insecure) window operations such as:
 | 
			
		||||
   setting the clipboard text */
 | 
			
		||||
int allowwindowops = 0;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * draw latency range in ms - from new content/keypress/etc until drawing.
 | 
			
		||||
 * within this range, st draws when content stops arriving (idle). mostly it's
 | 
			
		||||
 * near minlatency, but it waits longer for slow updates to avoid partial draw.
 | 
			
		||||
 * low minlatency will tear/flicker more, as it can "detect" idle too early.
 | 
			
		||||
 */
 | 
			
		||||
static double minlatency = 8;
 | 
			
		||||
static double maxlatency = 33;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * blinking timeout (set to 0 to disable blinking) for the terminal blinking
 | 
			
		||||
@@ -150,14 +161,24 @@ static unsigned int mousebg = 0;
 | 
			
		||||
 */
 | 
			
		||||
static unsigned int defaultattr = 11;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
 | 
			
		||||
 * Note that if you want to use ShiftMask with selmasks, set this to an other
 | 
			
		||||
 * modifier, set to 0 to not use it.
 | 
			
		||||
 */
 | 
			
		||||
static uint forcemousemod = ShiftMask;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Internal mouse shortcuts.
 | 
			
		||||
 * Beware that overloading Button1 will disable the selection.
 | 
			
		||||
 */
 | 
			
		||||
static MouseShortcut mshortcuts[] = {
 | 
			
		||||
	/* button               mask            string */
 | 
			
		||||
	{ Button4,              XK_ANY_MOD,     "\031" },
 | 
			
		||||
	{ Button5,              XK_ANY_MOD,     "\005" },
 | 
			
		||||
	/* mask                 button   function        argument       release */
 | 
			
		||||
	{ XK_ANY_MOD,           Button2, selpaste,       {.i = 0},      1 },
 | 
			
		||||
	{ ShiftMask,            Button4, ttysend,        {.s = "\033[5;2~"} },
 | 
			
		||||
	{ XK_ANY_MOD,           Button4, ttysend,        {.s = "\031"} },
 | 
			
		||||
	{ ShiftMask,            Button5, ttysend,        {.s = "\033[6;2~"} },
 | 
			
		||||
	{ XK_ANY_MOD,           Button5, ttysend,        {.s = "\005"} },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Internal keyboard shortcuts. */
 | 
			
		||||
@@ -195,10 +216,6 @@ static Shortcut shortcuts[] = {
 | 
			
		||||
 * * 0: no value
 | 
			
		||||
 * * > 0: cursor application mode enabled
 | 
			
		||||
 * * < 0: cursor application mode disabled
 | 
			
		||||
 * crlf value
 | 
			
		||||
 * * 0: no value
 | 
			
		||||
 * * > 0: crlf mode is enabled
 | 
			
		||||
 * * < 0: crlf mode is disabled
 | 
			
		||||
 *
 | 
			
		||||
 * Be careful with the order of the definitions because st searches in
 | 
			
		||||
 * this table sequentially, so any XK_ANY_MOD must be in the last
 | 
			
		||||
@@ -217,13 +234,6 @@ static KeySym mappedkeys[] = { -1 };
 | 
			
		||||
 */
 | 
			
		||||
static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Override mouse-select while mask is active (when MODE_MOUSE is set).
 | 
			
		||||
 * Note that if you want to use ShiftMask with selmasks, set this to an other
 | 
			
		||||
 * modifier, set to 0 to not use it.
 | 
			
		||||
 */
 | 
			
		||||
static uint forceselmod = ShiftMask;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is the huge key array which defines all compatibility to the Linux
 | 
			
		||||
 * world. Please decide about changes wisely.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
# st version
 | 
			
		||||
VERSION = 0.8.2
 | 
			
		||||
VERSION = 0.8.4
 | 
			
		||||
 | 
			
		||||
# Customize below to fit your system
 | 
			
		||||
 | 
			
		||||
@@ -28,8 +28,8 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
 | 
			
		||||
# OpenBSD:
 | 
			
		||||
#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
 | 
			
		||||
#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
 | 
			
		||||
#       `pkg-config --libs fontconfig` \
 | 
			
		||||
#       `pkg-config --libs freetype2`
 | 
			
		||||
#       `$(PKG_CONFIG) --libs fontconfig` \
 | 
			
		||||
#       `$(PKG_CONFIG) --libs freetype2`
 | 
			
		||||
 | 
			
		||||
# compiler and linker
 | 
			
		||||
# CC = c99
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								st.1
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								st.1
									
									
									
									
									
								
							@@ -170,7 +170,8 @@ See the LICENSE file for the terms of redistribution.
 | 
			
		||||
.SH SEE ALSO
 | 
			
		||||
.BR tabbed (1),
 | 
			
		||||
.BR utmp (1),
 | 
			
		||||
.BR stty (1)
 | 
			
		||||
.BR stty (1),
 | 
			
		||||
.BR scroll (1)
 | 
			
		||||
.SH BUGS
 | 
			
		||||
See the TODO file in the distribution.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										209
									
								
								st.c
									
									
									
									
									
								
							
							
						
						
									
										209
									
								
								st.c
									
									
									
									
									
								
							@@ -38,10 +38,10 @@
 | 
			
		||||
 | 
			
		||||
/* macros */
 | 
			
		||||
#define IS_SET(flag)		((term.mode & (flag)) != 0)
 | 
			
		||||
#define ISCONTROLC0(c)		(BETWEEN(c, 0, 0x1f) || (c) == '\177')
 | 
			
		||||
#define ISCONTROLC0(c)		(BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
 | 
			
		||||
#define ISCONTROLC1(c)		(BETWEEN(c, 0x80, 0x9f))
 | 
			
		||||
#define ISCONTROL(c)		(ISCONTROLC0(c) || ISCONTROLC1(c))
 | 
			
		||||
#define ISDELIM(u)		(utf8strchr(worddelimiters, u) != NULL)
 | 
			
		||||
#define ISDELIM(u)		(u && wcschr(worddelimiters, u))
 | 
			
		||||
 | 
			
		||||
enum term_mode {
 | 
			
		||||
	MODE_WRAP        = 1 << 0,
 | 
			
		||||
@@ -51,7 +51,6 @@ enum term_mode {
 | 
			
		||||
	MODE_ECHO        = 1 << 4,
 | 
			
		||||
	MODE_PRINT       = 1 << 5,
 | 
			
		||||
	MODE_UTF8        = 1 << 6,
 | 
			
		||||
	MODE_SIXEL       = 1 << 7,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum cursor_movement {
 | 
			
		||||
@@ -78,12 +77,11 @@ enum charset {
 | 
			
		||||
enum escape_state {
 | 
			
		||||
	ESC_START      = 1,
 | 
			
		||||
	ESC_CSI        = 2,
 | 
			
		||||
	ESC_STR        = 4,  /* OSC, PM, APC */
 | 
			
		||||
	ESC_STR        = 4,  /* DCS, OSC, PM, APC */
 | 
			
		||||
	ESC_ALTCHARSET = 8,
 | 
			
		||||
	ESC_STR_END    = 16, /* a final string was encountered */
 | 
			
		||||
	ESC_TEST       = 32, /* Enter in test mode */
 | 
			
		||||
	ESC_UTF8       = 64,
 | 
			
		||||
	ESC_DCS        =128,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -129,13 +127,14 @@ typedef struct {
 | 
			
		||||
	int charset;  /* current charset */
 | 
			
		||||
	int icharset; /* selected charset for sequence */
 | 
			
		||||
	int *tabs;
 | 
			
		||||
	Rune lastc;   /* last printed char outside of sequence, 0 if control */
 | 
			
		||||
} Term;
 | 
			
		||||
 | 
			
		||||
/* CSI Escape sequence structs */
 | 
			
		||||
/* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
 | 
			
		||||
typedef struct {
 | 
			
		||||
	char buf[ESC_BUF_SIZ]; /* raw string */
 | 
			
		||||
	int len;               /* raw string length */
 | 
			
		||||
	size_t len;            /* raw string length */
 | 
			
		||||
	char priv;
 | 
			
		||||
	int arg[ESC_ARG_SIZ];
 | 
			
		||||
	int narg;              /* nb of args */
 | 
			
		||||
@@ -146,8 +145,9 @@ typedef struct {
 | 
			
		||||
/* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */
 | 
			
		||||
typedef struct {
 | 
			
		||||
	char type;             /* ESC type ... */
 | 
			
		||||
	char buf[STR_BUF_SIZ]; /* raw string */
 | 
			
		||||
	int len;               /* raw string length */
 | 
			
		||||
	char *buf;             /* allocated raw string */
 | 
			
		||||
	size_t siz;            /* allocation size */
 | 
			
		||||
	size_t len;            /* raw string length */
 | 
			
		||||
	char *args[STR_ARG_SIZ];
 | 
			
		||||
	int narg;              /* nb of args */
 | 
			
		||||
} STREscape;
 | 
			
		||||
@@ -210,7 +210,6 @@ static void selsnap(int *, int *, int);
 | 
			
		||||
static size_t utf8decode(const char *, Rune *, size_t);
 | 
			
		||||
static Rune utf8decodebyte(char, size_t *);
 | 
			
		||||
static char utf8encodebyte(Rune, size_t);
 | 
			
		||||
static char *utf8strchr(char *, Rune);
 | 
			
		||||
static size_t utf8validate(Rune *, size_t);
 | 
			
		||||
 | 
			
		||||
static char *base64dec(const char *);
 | 
			
		||||
@@ -337,23 +336,6 @@ utf8encodebyte(Rune u, size_t i)
 | 
			
		||||
	return utfbyte[i] | (u & ~utfmask[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
utf8strchr(char *s, Rune u)
 | 
			
		||||
{
 | 
			
		||||
	Rune r;
 | 
			
		||||
	size_t i, j, len;
 | 
			
		||||
 | 
			
		||||
	len = strlen(s);
 | 
			
		||||
	for (i = 0, j = 0; i < len; i += j) {
 | 
			
		||||
		if (!(j = utf8decode(&s[i], &r, len - i)))
 | 
			
		||||
			break;
 | 
			
		||||
		if (r == u)
 | 
			
		||||
			return &(s[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t
 | 
			
		||||
utf8validate(Rune *u, size_t i)
 | 
			
		||||
{
 | 
			
		||||
@@ -383,8 +365,9 @@ static const char base64_digits[] = {
 | 
			
		||||
char
 | 
			
		||||
base64dec_getc(const char **src)
 | 
			
		||||
{
 | 
			
		||||
	while (**src && !isprint(**src)) (*src)++;
 | 
			
		||||
	return *((*src)++);
 | 
			
		||||
	while (**src && !isprint(**src))
 | 
			
		||||
		(*src)++;
 | 
			
		||||
	return **src ? *((*src)++) : '=';  /* emulate padding if string ends */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
@@ -402,6 +385,10 @@ base64dec(const char *src)
 | 
			
		||||
		int c = base64_digits[(unsigned char) base64dec_getc(&src)];
 | 
			
		||||
		int d = base64_digits[(unsigned char) base64dec_getc(&src)];
 | 
			
		||||
 | 
			
		||||
		/* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */
 | 
			
		||||
		if (a == -1 || b == -1)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		*dst++ = (a << 2) | ((b & 0x30) >> 4);
 | 
			
		||||
		if (c == -1)
 | 
			
		||||
			break;
 | 
			
		||||
@@ -476,7 +463,7 @@ selextend(int col, int row, int type, int done)
 | 
			
		||||
	selnormalize();
 | 
			
		||||
	sel.type = type;
 | 
			
		||||
 | 
			
		||||
	if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.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;
 | 
			
		||||
@@ -646,7 +633,8 @@ getsel(void)
 | 
			
		||||
		 * st.
 | 
			
		||||
		 * FIXME: Fix the computer world.
 | 
			
		||||
		 */
 | 
			
		||||
		if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
 | 
			
		||||
		if ((y < sel.ne.y || lastx >= linelen) &&
 | 
			
		||||
		    (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
 | 
			
		||||
			*ptr++ = '\n';
 | 
			
		||||
	}
 | 
			
		||||
	*ptr = 0;
 | 
			
		||||
@@ -677,7 +665,7 @@ die(const char *errstr, ...)
 | 
			
		||||
void
 | 
			
		||||
execsh(char *cmd, char **args)
 | 
			
		||||
{
 | 
			
		||||
	char *sh, *prog;
 | 
			
		||||
	char *sh, *prog, *arg;
 | 
			
		||||
	const struct passwd *pw;
 | 
			
		||||
 | 
			
		||||
	errno = 0;
 | 
			
		||||
@@ -691,13 +679,20 @@ execsh(char *cmd, char **args)
 | 
			
		||||
	if ((sh = getenv("SHELL")) == NULL)
 | 
			
		||||
		sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd;
 | 
			
		||||
 | 
			
		||||
	if (args)
 | 
			
		||||
	if (args) {
 | 
			
		||||
		prog = args[0];
 | 
			
		||||
	else if (utmp)
 | 
			
		||||
		arg = NULL;
 | 
			
		||||
	} else if (scroll) {
 | 
			
		||||
		prog = scroll;
 | 
			
		||||
		arg = utmp ? utmp : sh;
 | 
			
		||||
	} else if (utmp) {
 | 
			
		||||
		prog = utmp;
 | 
			
		||||
	else
 | 
			
		||||
		arg = NULL;
 | 
			
		||||
	} else {
 | 
			
		||||
		prog = sh;
 | 
			
		||||
	DEFAULT(args, ((char *[]) {prog, NULL}));
 | 
			
		||||
		arg = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	DEFAULT(args, ((char *[]) {prog, arg, NULL}));
 | 
			
		||||
 | 
			
		||||
	unsetenv("COLUMNS");
 | 
			
		||||
	unsetenv("LINES");
 | 
			
		||||
@@ -735,7 +730,7 @@ sigchld(int a)
 | 
			
		||||
		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);
 | 
			
		||||
	_exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -828,21 +823,25 @@ ttyread(void)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[BUFSIZ];
 | 
			
		||||
	static int buflen = 0;
 | 
			
		||||
	int written;
 | 
			
		||||
	int ret;
 | 
			
		||||
	int ret, written;
 | 
			
		||||
 | 
			
		||||
	/* append read bytes to unprocessed bytes */
 | 
			
		||||
	if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0)
 | 
			
		||||
	ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
 | 
			
		||||
 | 
			
		||||
	switch (ret) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		exit(0);
 | 
			
		||||
	case -1:
 | 
			
		||||
		die("couldn't read from shell: %s\n", strerror(errno));
 | 
			
		||||
	buflen += ret;
 | 
			
		||||
 | 
			
		||||
	written = twrite(buf, buflen, 0);
 | 
			
		||||
	buflen -= written;
 | 
			
		||||
	/* keep any uncomplete utf8 char for the next call */
 | 
			
		||||
	if (buflen > 0)
 | 
			
		||||
		memmove(buf, buf + written, buflen);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
	default:
 | 
			
		||||
		buflen += ret;
 | 
			
		||||
		written = twrite(buf, buflen, 0);
 | 
			
		||||
		buflen -= written;
 | 
			
		||||
		/* keep any incomplete UTF-8 byte sequence for the next call */
 | 
			
		||||
		if (buflen > 0)
 | 
			
		||||
			memmove(buf, buf + written, buflen);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -1105,27 +1104,17 @@ 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) {
 | 
			
		||||
	if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
 | 
			
		||||
		selclear();
 | 
			
		||||
	} else if (BETWEEN(sel.nb.y, orig, term.bot)) {
 | 
			
		||||
		sel.ob.y += n;
 | 
			
		||||
		sel.oe.y += n;
 | 
			
		||||
		if (sel.ob.y < term.top || sel.ob.y > term.bot ||
 | 
			
		||||
		    sel.oe.y < term.top || sel.oe.y > term.bot) {
 | 
			
		||||
			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.col;
 | 
			
		||||
			}
 | 
			
		||||
			selnormalize();
 | 
			
		||||
		}
 | 
			
		||||
		selnormalize();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1575,6 +1564,7 @@ tsetmode(int priv, int set, int *args, int narg)
 | 
			
		||||
			case 1015: /* urxvt mangled mouse mode; incompatible
 | 
			
		||||
				      and can be mistaken for other control
 | 
			
		||||
				      codes. */
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr,
 | 
			
		||||
					"erresc: unknown private set/reset mode %d\n",
 | 
			
		||||
@@ -1656,6 +1646,12 @@ csihandle(void)
 | 
			
		||||
		if (csiescseq.arg[0] == 0)
 | 
			
		||||
			ttywrite(vtiden, strlen(vtiden), 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case 'b': /* REP -- if last char is printable print it <n> more times */
 | 
			
		||||
		DEFAULT(csiescseq.arg[0], 1);
 | 
			
		||||
		if (term.lastc)
 | 
			
		||||
			while (csiescseq.arg[0]-- > 0)
 | 
			
		||||
				tputc(term.lastc);
 | 
			
		||||
		break;
 | 
			
		||||
	case 'C': /* CUF -- Cursor <n> Forward */
 | 
			
		||||
	case 'a': /* HPR -- Cursor <n> Forward */
 | 
			
		||||
		DEFAULT(csiescseq.arg[0], 1);
 | 
			
		||||
@@ -1779,7 +1775,7 @@ csihandle(void)
 | 
			
		||||
		break;
 | 
			
		||||
	case 'n': /* DSR – Device Status Report (cursor position) */
 | 
			
		||||
		if (csiescseq.arg[0] == 6) {
 | 
			
		||||
			len = snprintf(buf, sizeof(buf),"\033[%i;%iR",
 | 
			
		||||
			len = snprintf(buf, sizeof(buf), "\033[%i;%iR",
 | 
			
		||||
					term.c.y+1, term.c.x+1);
 | 
			
		||||
			ttywrite(buf, len, 0);
 | 
			
		||||
		}
 | 
			
		||||
@@ -1816,7 +1812,7 @@ csihandle(void)
 | 
			
		||||
void
 | 
			
		||||
csidump(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	size_t i;
 | 
			
		||||
	uint c;
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "ESC[");
 | 
			
		||||
@@ -1846,7 +1842,7 @@ csireset(void)
 | 
			
		||||
void
 | 
			
		||||
strhandle(void)
 | 
			
		||||
{
 | 
			
		||||
	char *p = NULL;
 | 
			
		||||
	char *p = NULL, *dec;
 | 
			
		||||
	int j, narg, par;
 | 
			
		||||
 | 
			
		||||
	term.esc &= ~(ESC_STR_END|ESC_STR);
 | 
			
		||||
@@ -1857,15 +1853,21 @@ strhandle(void)
 | 
			
		||||
	case ']': /* OSC -- Operating System Command */
 | 
			
		||||
		switch (par) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			if (narg > 1) {
 | 
			
		||||
				xsettitle(strescseq.args[1]);
 | 
			
		||||
				xseticontitle(strescseq.args[1]);
 | 
			
		||||
			}
 | 
			
		||||
			return;
 | 
			
		||||
		case 1:
 | 
			
		||||
			if (narg > 1)
 | 
			
		||||
				xseticontitle(strescseq.args[1]);
 | 
			
		||||
			return;
 | 
			
		||||
		case 2:
 | 
			
		||||
			if (narg > 1)
 | 
			
		||||
				xsettitle(strescseq.args[1]);
 | 
			
		||||
			return;
 | 
			
		||||
		case 52:
 | 
			
		||||
			if (narg > 2) {
 | 
			
		||||
				char *dec;
 | 
			
		||||
 | 
			
		||||
			if (narg > 2 && allowwindowops) {
 | 
			
		||||
				dec = base64dec(strescseq.args[2]);
 | 
			
		||||
				if (dec) {
 | 
			
		||||
					xsetsel(dec);
 | 
			
		||||
@@ -1883,7 +1885,10 @@ strhandle(void)
 | 
			
		||||
		case 104: /* color reset, here p = NULL */
 | 
			
		||||
			j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
 | 
			
		||||
			if (xsetcolorname(j, p)) {
 | 
			
		||||
				fprintf(stderr, "erresc: invalid color %s\n", p);
 | 
			
		||||
				if (par == 104 && narg <= 1)
 | 
			
		||||
					return; /* color reset without parameter */
 | 
			
		||||
				fprintf(stderr, "erresc: invalid color j=%d, p=%s\n",
 | 
			
		||||
				        j, p ? p : "(null)");
 | 
			
		||||
			} else {
 | 
			
		||||
				/*
 | 
			
		||||
				 * TODO if defaultbg color is changed, borders
 | 
			
		||||
@@ -1898,7 +1903,6 @@ strhandle(void)
 | 
			
		||||
		xsettitle(strescseq.args[0]);
 | 
			
		||||
		return;
 | 
			
		||||
	case 'P': /* DCS -- Device Control String */
 | 
			
		||||
		term.mode |= ESC_DCS;
 | 
			
		||||
	case '_': /* APC -- Application Program Command */
 | 
			
		||||
	case '^': /* PM -- Privacy Message */
 | 
			
		||||
		return;
 | 
			
		||||
@@ -1933,7 +1937,7 @@ strparse(void)
 | 
			
		||||
void
 | 
			
		||||
strdump(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	size_t i;
 | 
			
		||||
	uint c;
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "ESC%c", strescseq.type);
 | 
			
		||||
@@ -1960,7 +1964,10 @@ strdump(void)
 | 
			
		||||
void
 | 
			
		||||
strreset(void)
 | 
			
		||||
{
 | 
			
		||||
	memset(&strescseq, 0, sizeof(strescseq));
 | 
			
		||||
	strescseq = (STREscape){
 | 
			
		||||
		.buf = xrealloc(strescseq.buf, STR_BUF_SIZ),
 | 
			
		||||
		.siz = STR_BUF_SIZ,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -2018,7 +2025,7 @@ tdumpline(int n)
 | 
			
		||||
	bp = &term.line[n][0];
 | 
			
		||||
	end = &bp[MIN(tlinelen(n), term.col) - 1];
 | 
			
		||||
	if (bp != end || bp->u != ' ') {
 | 
			
		||||
		for ( ;bp <= end; ++bp)
 | 
			
		||||
		for ( ; bp <= end; ++bp)
 | 
			
		||||
			tprinter(buf, utf8encode(bp->u, buf));
 | 
			
		||||
	}
 | 
			
		||||
	tprinter("\n", 1);
 | 
			
		||||
@@ -2089,12 +2096,9 @@ tdectest(char c)
 | 
			
		||||
void
 | 
			
		||||
tstrsequence(uchar c)
 | 
			
		||||
{
 | 
			
		||||
	strreset();
 | 
			
		||||
 | 
			
		||||
	switch (c) {
 | 
			
		||||
	case 0x90:   /* DCS -- Device Control String */
 | 
			
		||||
		c = 'P';
 | 
			
		||||
		term.esc |= ESC_DCS;
 | 
			
		||||
		break;
 | 
			
		||||
	case 0x9f:   /* APC -- Application Program Command */
 | 
			
		||||
		c = '_';
 | 
			
		||||
@@ -2106,6 +2110,7 @@ tstrsequence(uchar c)
 | 
			
		||||
		c = ']';
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	strreset();
 | 
			
		||||
	strescseq.type = c;
 | 
			
		||||
	term.esc |= ESC_STR;
 | 
			
		||||
}
 | 
			
		||||
@@ -2148,6 +2153,7 @@ tcontrolcode(uchar ascii)
 | 
			
		||||
		return;
 | 
			
		||||
	case '\032': /* SUB */
 | 
			
		||||
		tsetchar('?', &term.c.attr, term.c.x, term.c.y);
 | 
			
		||||
		/* FALLTHROUGH */
 | 
			
		||||
	case '\030': /* CAN */
 | 
			
		||||
		csireset();
 | 
			
		||||
		break;
 | 
			
		||||
@@ -2302,15 +2308,13 @@ tputc(Rune u)
 | 
			
		||||
	Glyph *gp;
 | 
			
		||||
 | 
			
		||||
	control = ISCONTROL(u);
 | 
			
		||||
	if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
 | 
			
		||||
	if (u < 127 || !IS_SET(MODE_UTF8)) {
 | 
			
		||||
		c[0] = u;
 | 
			
		||||
		width = len = 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		len = utf8encode(u, c);
 | 
			
		||||
		if (!control && (width = wcwidth(u)) == -1) {
 | 
			
		||||
			memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
 | 
			
		||||
		if (!control && (width = wcwidth(u)) == -1)
 | 
			
		||||
			width = 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (IS_SET(MODE_PRINT))
 | 
			
		||||
@@ -2325,24 +2329,12 @@ tputc(Rune u)
 | 
			
		||||
	if (term.esc & ESC_STR) {
 | 
			
		||||
		if (u == '\a' || u == 030 || u == 032 || u == 033 ||
 | 
			
		||||
		   ISCONTROLC1(u)) {
 | 
			
		||||
			term.esc &= ~(ESC_START|ESC_STR|ESC_DCS);
 | 
			
		||||
			if (IS_SET(MODE_SIXEL)) {
 | 
			
		||||
				/* TODO: render sixel */;
 | 
			
		||||
				term.mode &= ~MODE_SIXEL;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			term.esc &= ~(ESC_START|ESC_STR);
 | 
			
		||||
			term.esc |= ESC_STR_END;
 | 
			
		||||
			goto check_control_code;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (IS_SET(MODE_SIXEL)) {
 | 
			
		||||
			/* TODO: implement sixel mode */
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q')
 | 
			
		||||
			term.mode |= MODE_SIXEL;
 | 
			
		||||
 | 
			
		||||
		if (strescseq.len+len >= sizeof(strescseq.buf)-1) {
 | 
			
		||||
		if (strescseq.len+len >= strescseq.siz) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Here is a bug in terminals. If the user never sends
 | 
			
		||||
			 * some code to stop the str or esc command, then st
 | 
			
		||||
@@ -2356,7 +2348,10 @@ tputc(Rune u)
 | 
			
		||||
			 * term.esc = 0;
 | 
			
		||||
			 * strhandle();
 | 
			
		||||
			 */
 | 
			
		||||
			return;
 | 
			
		||||
			if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2)
 | 
			
		||||
				return;
 | 
			
		||||
			strescseq.siz *= 2;
 | 
			
		||||
			strescseq.buf = xrealloc(strescseq.buf, strescseq.siz);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memmove(&strescseq.buf[strescseq.len], c, len);
 | 
			
		||||
@@ -2375,6 +2370,8 @@ check_control_code:
 | 
			
		||||
		/*
 | 
			
		||||
		 * control codes are not shown ever
 | 
			
		||||
		 */
 | 
			
		||||
		if (!term.esc)
 | 
			
		||||
			term.lastc = 0;
 | 
			
		||||
		return;
 | 
			
		||||
	} else if (term.esc & ESC_START) {
 | 
			
		||||
		if (term.esc & ESC_CSI) {
 | 
			
		||||
@@ -2405,7 +2402,7 @@ check_control_code:
 | 
			
		||||
		 */
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
 | 
			
		||||
	if (selected(term.c.x, term.c.y))
 | 
			
		||||
		selclear();
 | 
			
		||||
 | 
			
		||||
	gp = &term.line[term.c.y][term.c.x];
 | 
			
		||||
@@ -2424,6 +2421,7 @@ check_control_code:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tsetchar(u, &term.c.attr, term.c.x, term.c.y);
 | 
			
		||||
	term.lastc = u;
 | 
			
		||||
 | 
			
		||||
	if (width == 2) {
 | 
			
		||||
		gp->mode |= ATTR_WIDE;
 | 
			
		||||
@@ -2447,7 +2445,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
 | 
			
		||||
	int n;
 | 
			
		||||
 | 
			
		||||
	for (n = 0; n < buflen; n += charsize) {
 | 
			
		||||
		if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
 | 
			
		||||
		if (IS_SET(MODE_UTF8)) {
 | 
			
		||||
			/* process a complete utf8 char */
 | 
			
		||||
			charsize = utf8decode(buf + n, &u, buflen - n);
 | 
			
		||||
			if (charsize == 0)
 | 
			
		||||
@@ -2563,6 +2561,7 @@ void
 | 
			
		||||
drawregion(int x1, int y1, int x2, int y2)
 | 
			
		||||
{
 | 
			
		||||
	int y;
 | 
			
		||||
 | 
			
		||||
	for (y = y1; y < y2; y++) {
 | 
			
		||||
		if (!term.dirty[y])
 | 
			
		||||
			continue;
 | 
			
		||||
@@ -2575,7 +2574,7 @@ drawregion(int x1, int y1, int x2, int y2)
 | 
			
		||||
void
 | 
			
		||||
draw(void)
 | 
			
		||||
{
 | 
			
		||||
	int cx = term.c.x;
 | 
			
		||||
	int cx = term.c.x, ocx = term.ocx, ocy = term.ocy;
 | 
			
		||||
 | 
			
		||||
	if (!xstartdraw())
 | 
			
		||||
		return;
 | 
			
		||||
@@ -2591,9 +2590,11 @@ draw(void)
 | 
			
		||||
	drawregion(0, 0, term.col, term.row);
 | 
			
		||||
	xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
 | 
			
		||||
			term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
 | 
			
		||||
	term.ocx = cx, term.ocy = term.c.y;
 | 
			
		||||
	term.ocx = cx;
 | 
			
		||||
	term.ocy = term.c.y;
 | 
			
		||||
	xfinishdraw();
 | 
			
		||||
	xximspot(term.ocx, term.ocy);
 | 
			
		||||
	if (ocx != term.ocx || ocy != term.ocy)
 | 
			
		||||
		xximspot(term.ocx, term.ocy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								st.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								st.h
									
									
									
									
									
								
							@@ -74,6 +74,7 @@ typedef union {
 | 
			
		||||
	uint ui;
 | 
			
		||||
	float f;
 | 
			
		||||
	const void *v;
 | 
			
		||||
	const char *s;
 | 
			
		||||
} Arg;
 | 
			
		||||
 | 
			
		||||
void die(const char *, ...);
 | 
			
		||||
@@ -112,10 +113,12 @@ char *xstrdup(char *);
 | 
			
		||||
 | 
			
		||||
/* config.h globals */
 | 
			
		||||
extern char *utmp;
 | 
			
		||||
extern char *scroll;
 | 
			
		||||
extern char *stty_args;
 | 
			
		||||
extern char *vtiden;
 | 
			
		||||
extern char *worddelimiters;
 | 
			
		||||
extern wchar_t *worddelimiters;
 | 
			
		||||
extern int allowaltscreen;
 | 
			
		||||
extern int allowwindowops;
 | 
			
		||||
extern char *termname;
 | 
			
		||||
extern unsigned int tabspaces;
 | 
			
		||||
extern unsigned int defaultfg;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								st.info
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								st.info
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
st| simpleterm,
 | 
			
		||||
st-mono| simpleterm monocolor,
 | 
			
		||||
	acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
 | 
			
		||||
	am,
 | 
			
		||||
	bce,
 | 
			
		||||
@@ -10,7 +10,7 @@ st| simpleterm,
 | 
			
		||||
	civis=\E[?25l,
 | 
			
		||||
	clear=\E[H\E[2J,
 | 
			
		||||
	cnorm=\E[?12l\E[?25h,
 | 
			
		||||
	colors#8,
 | 
			
		||||
	colors#2,
 | 
			
		||||
	cols#80,
 | 
			
		||||
	cr=^M,
 | 
			
		||||
	csr=\E[%i%p1%d;%p2%dr,
 | 
			
		||||
@@ -158,6 +158,7 @@ st| simpleterm,
 | 
			
		||||
	rc=\E8,
 | 
			
		||||
	rev=\E[7m,
 | 
			
		||||
	ri=\EM,
 | 
			
		||||
	rin=\E[%p1%dT,
 | 
			
		||||
	ritm=\E[23m,
 | 
			
		||||
	rmacs=\E(B,
 | 
			
		||||
	rmcup=\E[?1049l,
 | 
			
		||||
@@ -168,13 +169,8 @@ st| simpleterm,
 | 
			
		||||
	rs1=\Ec,
 | 
			
		||||
	rs2=\E[4l\E>\E[?1034l,
 | 
			
		||||
	sc=\E7,
 | 
			
		||||
	setab=\E[4%p1%dm,
 | 
			
		||||
	setaf=\E[3%p1%dm,
 | 
			
		||||
	setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
 | 
			
		||||
	setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
 | 
			
		||||
	sgr0=\E[0m,
 | 
			
		||||
	sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
 | 
			
		||||
	sitm=\E[3m,
 | 
			
		||||
	sgr0=\E[0m,
 | 
			
		||||
	smacs=\E(0,
 | 
			
		||||
	smcup=\E[?1049h,
 | 
			
		||||
	smir=\E[4h,
 | 
			
		||||
@@ -188,11 +184,22 @@ st| simpleterm,
 | 
			
		||||
# XTerm extensions
 | 
			
		||||
	rmxx=\E[29m,
 | 
			
		||||
	smxx=\E[9m,
 | 
			
		||||
# disabled rep for now: causes some issues with older ncurses versions.
 | 
			
		||||
#	rep=%p1%c\E[%p2%{1}%-%db,
 | 
			
		||||
# tmux extensions, see TERMINFO EXTENSIONS in tmux(1)
 | 
			
		||||
	Se,
 | 
			
		||||
	Ss,
 | 
			
		||||
	Tc,
 | 
			
		||||
	Ms=\E]52;%p1%s;%p2%s\007,
 | 
			
		||||
	Se=\E[2 q,
 | 
			
		||||
	Ss=\E[%p1%d q,
 | 
			
		||||
 | 
			
		||||
st| simpleterm,
 | 
			
		||||
	use=st-mono,
 | 
			
		||||
	colors#8,
 | 
			
		||||
	setab=\E[4%p1%dm,
 | 
			
		||||
	setaf=\E[3%p1%dm,
 | 
			
		||||
	setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
 | 
			
		||||
	setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
 | 
			
		||||
	sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
 | 
			
		||||
 | 
			
		||||
st-256color| simpleterm with 256 colors,
 | 
			
		||||
	use=st,
 | 
			
		||||
@@ -220,3 +227,13 @@ st-meta-256color| simpleterm with meta key and 256 colors,
 | 
			
		||||
	smm=\E[?1034h,
 | 
			
		||||
	rs2=\E[4l\E>\E[?1034h,
 | 
			
		||||
	is2=\E[4l\E>\E[?1034h,
 | 
			
		||||
 | 
			
		||||
st-bs| simpleterm with backspace as backspace,
 | 
			
		||||
	use=st,
 | 
			
		||||
	kbs=\010,
 | 
			
		||||
	kdch1=\177,
 | 
			
		||||
 | 
			
		||||
st-bs-256color| simpleterm with backspace as backspace and 256colors,
 | 
			
		||||
	use=st-256color,
 | 
			
		||||
	kbs=\010,
 | 
			
		||||
	kdch1=\177,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								win.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								win.h
									
									
									
									
									
								
							@@ -30,6 +30,7 @@ void xdrawline(Line, int, int, int);
 | 
			
		||||
void xfinishdraw(void);
 | 
			
		||||
void xloadcols(void);
 | 
			
		||||
int xsetcolorname(int, const char *);
 | 
			
		||||
void xseticontitle(char *);
 | 
			
		||||
void xsettitle(char *);
 | 
			
		||||
int xsetcursor(int);
 | 
			
		||||
void xsetmode(int, unsigned int);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										323
									
								
								x.c
									
									
									
									
									
								
							
							
						
						
									
										323
									
								
								x.c
									
									
									
									
									
								
							@@ -15,7 +15,7 @@
 | 
			
		||||
#include <X11/Xft/Xft.h>
 | 
			
		||||
#include <X11/XKBlib.h>
 | 
			
		||||
 | 
			
		||||
static char *argv0;
 | 
			
		||||
char *argv0;
 | 
			
		||||
#include "arg.h"
 | 
			
		||||
#include "st.h"
 | 
			
		||||
#include "win.h"
 | 
			
		||||
@@ -29,9 +29,11 @@ typedef struct {
 | 
			
		||||
} Shortcut;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	uint b;
 | 
			
		||||
	uint mask;
 | 
			
		||||
	char *s;
 | 
			
		||||
	uint mod;
 | 
			
		||||
	uint button;
 | 
			
		||||
	void (*func)(const Arg *);
 | 
			
		||||
	const Arg arg;
 | 
			
		||||
	uint  release;
 | 
			
		||||
} MouseShortcut;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -56,6 +58,7 @@ static void selpaste(const Arg *);
 | 
			
		||||
static void zoom(const Arg *);
 | 
			
		||||
static void zoomabs(const Arg *);
 | 
			
		||||
static void zoomreset(const Arg *);
 | 
			
		||||
static void ttysend(const Arg *);
 | 
			
		||||
 | 
			
		||||
/* config.h for applying patches and the configuration. */
 | 
			
		||||
#include "config.h"
 | 
			
		||||
@@ -90,9 +93,13 @@ typedef struct {
 | 
			
		||||
	Window win;
 | 
			
		||||
	Drawable buf;
 | 
			
		||||
	GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
 | 
			
		||||
	Atom xembed, wmdeletewin, netwmname, netwmpid;
 | 
			
		||||
	XIM xim;
 | 
			
		||||
	XIC xic;
 | 
			
		||||
	Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
 | 
			
		||||
	struct {
 | 
			
		||||
		XIM xim;
 | 
			
		||||
		XIC xic;
 | 
			
		||||
		XPoint spot;
 | 
			
		||||
		XVaNestedList spotlist;
 | 
			
		||||
	} ime;
 | 
			
		||||
	Draw draw;
 | 
			
		||||
	Visual *vis;
 | 
			
		||||
	XSetWindowAttributes attrs;
 | 
			
		||||
@@ -139,9 +146,10 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
 | 
			
		||||
static void xdrawglyph(Glyph, int, int);
 | 
			
		||||
static void xclear(int, int, int, int);
 | 
			
		||||
static int xgeommasktogravity(int);
 | 
			
		||||
static void ximopen(Display *);
 | 
			
		||||
static int ximopen(Display *);
 | 
			
		||||
static void ximinstantiate(Display *, XPointer, XPointer);
 | 
			
		||||
static void ximdestroy(XIM, XPointer, XPointer);
 | 
			
		||||
static int xicdestroy(XIC, XPointer, XPointer);
 | 
			
		||||
static void xinit(int, int);
 | 
			
		||||
static void cresize(int, int);
 | 
			
		||||
static void xresize(int, int);
 | 
			
		||||
@@ -163,6 +171,8 @@ static void kpress(XEvent *);
 | 
			
		||||
static void cmessage(XEvent *);
 | 
			
		||||
static void resize(XEvent *);
 | 
			
		||||
static void focus(XEvent *);
 | 
			
		||||
static uint buttonmask(uint);
 | 
			
		||||
static int mouseaction(XEvent *, uint);
 | 
			
		||||
static void brelease(XEvent *);
 | 
			
		||||
static void bpress(XEvent *);
 | 
			
		||||
static void bmotion(XEvent *);
 | 
			
		||||
@@ -312,6 +322,12 @@ zoomreset(const Arg *arg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ttysend(const Arg *arg)
 | 
			
		||||
{
 | 
			
		||||
	ttywrite(arg->s, strlen(arg->s), 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
evcol(XEvent *e)
 | 
			
		||||
{
 | 
			
		||||
@@ -332,7 +348,7 @@ void
 | 
			
		||||
mousesel(XEvent *e, int done)
 | 
			
		||||
{
 | 
			
		||||
	int type, seltype = SEL_REGULAR;
 | 
			
		||||
	uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
 | 
			
		||||
	uint state = e->xbutton.state & ~(Button1Mask | forcemousemod);
 | 
			
		||||
 | 
			
		||||
	for (type = 1; type < LEN(selmasks); ++type) {
 | 
			
		||||
		if (match(selmasks[type], state)) {
 | 
			
		||||
@@ -371,7 +387,9 @@ mousereport(XEvent *e)
 | 
			
		||||
			button = 3;
 | 
			
		||||
		} else {
 | 
			
		||||
			button -= Button1;
 | 
			
		||||
			if (button >= 3)
 | 
			
		||||
			if (button >= 7)
 | 
			
		||||
				button += 128 - 7;
 | 
			
		||||
			else if (button >= 3)
 | 
			
		||||
				button += 64 - 3;
 | 
			
		||||
		}
 | 
			
		||||
		if (e->xbutton.type == ButtonPress) {
 | 
			
		||||
@@ -408,25 +426,51 @@ mousereport(XEvent *e)
 | 
			
		||||
	ttywrite(buf, len, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint
 | 
			
		||||
buttonmask(uint button)
 | 
			
		||||
{
 | 
			
		||||
	return button == Button1 ? Button1Mask
 | 
			
		||||
	     : button == Button2 ? Button2Mask
 | 
			
		||||
	     : button == Button3 ? Button3Mask
 | 
			
		||||
	     : button == Button4 ? Button4Mask
 | 
			
		||||
	     : button == Button5 ? Button5Mask
 | 
			
		||||
	     : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
mouseaction(XEvent *e, uint release)
 | 
			
		||||
{
 | 
			
		||||
	MouseShortcut *ms;
 | 
			
		||||
 | 
			
		||||
	/* ignore Button<N>mask for Button<N> - it's set on release */
 | 
			
		||||
	uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
 | 
			
		||||
 | 
			
		||||
	for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
 | 
			
		||||
		if (ms->release == release &&
 | 
			
		||||
		    ms->button == e->xbutton.button &&
 | 
			
		||||
		    (match(ms->mod, state) ||  /* exact or forced */
 | 
			
		||||
		     match(ms->mod, state & ~forcemousemod))) {
 | 
			
		||||
			ms->func(&(ms->arg));
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bpress(XEvent *e)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec now;
 | 
			
		||||
	MouseShortcut *ms;
 | 
			
		||||
	int snap;
 | 
			
		||||
 | 
			
		||||
	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
 | 
			
		||||
	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
 | 
			
		||||
		mousereport(e);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
 | 
			
		||||
		if (e->xbutton.button == ms->b
 | 
			
		||||
				&& match(ms->mask, e->xbutton.state)) {
 | 
			
		||||
			ttywrite(ms->s, strlen(ms->s), 1);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (mouseaction(e, 0))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (e->xbutton.button == Button1) {
 | 
			
		||||
		/*
 | 
			
		||||
@@ -642,21 +686,21 @@ xsetsel(char *str)
 | 
			
		||||
void
 | 
			
		||||
brelease(XEvent *e)
 | 
			
		||||
{
 | 
			
		||||
	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
 | 
			
		||||
	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
 | 
			
		||||
		mousereport(e);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (e->xbutton.button == Button2)
 | 
			
		||||
		selpaste(NULL);
 | 
			
		||||
	else if (e->xbutton.button == Button1)
 | 
			
		||||
	if (mouseaction(e, 1))
 | 
			
		||||
		return;
 | 
			
		||||
	if (e->xbutton.button == Button1)
 | 
			
		||||
		mousesel(e, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bmotion(XEvent *e)
 | 
			
		||||
{
 | 
			
		||||
	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
 | 
			
		||||
	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
 | 
			
		||||
		mousereport(e);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
@@ -999,41 +1043,58 @@ xunloadfonts(void)
 | 
			
		||||
	xunloadfont(&dc.ibfont);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
int
 | 
			
		||||
ximopen(Display *dpy)
 | 
			
		||||
{
 | 
			
		||||
	XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy };
 | 
			
		||||
	XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy };
 | 
			
		||||
	XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy };
 | 
			
		||||
 | 
			
		||||
	if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
 | 
			
		||||
		XSetLocaleModifiers("@im=local");
 | 
			
		||||
		if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
 | 
			
		||||
			XSetLocaleModifiers("@im=");
 | 
			
		||||
			if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL)
 | 
			
		||||
				die("XOpenIM failed. Could not open input device.\n");
 | 
			
		||||
		}
 | 
			
		||||
	xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL);
 | 
			
		||||
	if (xw.ime.xim == NULL)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL))
 | 
			
		||||
		fprintf(stderr, "XSetIMValues: "
 | 
			
		||||
		                "Could not set XNDestroyCallback.\n");
 | 
			
		||||
 | 
			
		||||
	xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot,
 | 
			
		||||
	                                      NULL);
 | 
			
		||||
 | 
			
		||||
	if (xw.ime.xic == NULL) {
 | 
			
		||||
		xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle,
 | 
			
		||||
		                       XIMPreeditNothing | XIMStatusNothing,
 | 
			
		||||
		                       XNClientWindow, xw.win,
 | 
			
		||||
		                       XNDestroyCallback, &icdestroy,
 | 
			
		||||
		                       NULL);
 | 
			
		||||
	}
 | 
			
		||||
	if (XSetIMValues(xw.xim, XNDestroyCallback, &destroy, NULL) != NULL)
 | 
			
		||||
		die("XSetIMValues failed. Could not set input method value.\n");
 | 
			
		||||
	xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
 | 
			
		||||
				XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL);
 | 
			
		||||
	if (xw.xic == NULL)
 | 
			
		||||
		die("XCreateIC failed. Could not obtain input method.\n");
 | 
			
		||||
	if (xw.ime.xic == NULL)
 | 
			
		||||
		fprintf(stderr, "XCreateIC: Could not create input context.\n");
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ximinstantiate(Display *dpy, XPointer client, XPointer call)
 | 
			
		||||
{
 | 
			
		||||
	ximopen(dpy);
 | 
			
		||||
	XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
 | 
			
		||||
					ximinstantiate, NULL);
 | 
			
		||||
	if (ximopen(dpy))
 | 
			
		||||
		XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
 | 
			
		||||
		                                 ximinstantiate, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ximdestroy(XIM xim, XPointer client, XPointer call)
 | 
			
		||||
{
 | 
			
		||||
	xw.xim = NULL;
 | 
			
		||||
	xw.ime.xim = NULL;
 | 
			
		||||
	XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
 | 
			
		||||
					ximinstantiate, NULL);
 | 
			
		||||
	                               ximinstantiate, NULL);
 | 
			
		||||
	XFree(xw.ime.spotlist);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
xicdestroy(XIC xim, XPointer client, XPointer call)
 | 
			
		||||
{
 | 
			
		||||
	xw.ime.xic = NULL;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -1101,7 +1162,10 @@ xinit(int cols, int rows)
 | 
			
		||||
	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 | 
			
		||||
 | 
			
		||||
	/* input methods */
 | 
			
		||||
	ximopen(xw.dpy);
 | 
			
		||||
	if (!ximopen(xw.dpy)) {
 | 
			
		||||
		XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
 | 
			
		||||
	                                       ximinstantiate, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* white cursor, black outline */
 | 
			
		||||
	cursor = XCreateFontCursor(xw.dpy, mouseshape);
 | 
			
		||||
@@ -1124,6 +1188,7 @@ xinit(int cols, int rows)
 | 
			
		||||
	xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
 | 
			
		||||
	xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
 | 
			
		||||
	xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
 | 
			
		||||
	xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False);
 | 
			
		||||
	XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
 | 
			
		||||
 | 
			
		||||
	xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
 | 
			
		||||
@@ -1132,8 +1197,8 @@ xinit(int cols, int rows)
 | 
			
		||||
 | 
			
		||||
	win.mode = MODE_NUMLOCK;
 | 
			
		||||
	resettitle();
 | 
			
		||||
	XMapWindow(xw.dpy, xw.win);
 | 
			
		||||
	xhints();
 | 
			
		||||
	XMapWindow(xw.dpy, xw.win);
 | 
			
		||||
	XSync(xw.dpy, False);
 | 
			
		||||
 | 
			
		||||
	clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1);
 | 
			
		||||
@@ -1464,8 +1529,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
 | 
			
		||||
	/* draw the new one */
 | 
			
		||||
	if (IS_SET(MODE_FOCUSED)) {
 | 
			
		||||
		switch (win.cursor) {
 | 
			
		||||
		case 7: /* st extension: snowman (U+2603) */
 | 
			
		||||
			g.u = 0x2603;
 | 
			
		||||
		case 7: /* st extension */
 | 
			
		||||
			g.u = 0x2603; /* snowman (U+2603) */
 | 
			
		||||
			/* FALLTHROUGH */
 | 
			
		||||
		case 0: /* Blinking Block */
 | 
			
		||||
		case 1: /* Blinking Block (Default) */
 | 
			
		||||
		case 2: /* Steady Block */
 | 
			
		||||
@@ -1516,6 +1582,19 @@ xsetenv(void)
 | 
			
		||||
	setenv("WINDOWID", buf, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
xseticontitle(char *p)
 | 
			
		||||
{
 | 
			
		||||
	XTextProperty prop;
 | 
			
		||||
	DEFAULT(p, opt_title);
 | 
			
		||||
 | 
			
		||||
	Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
 | 
			
		||||
			&prop);
 | 
			
		||||
	XSetWMIconName(xw.dpy, xw.win, &prop);
 | 
			
		||||
	XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname);
 | 
			
		||||
	XFree(prop.value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
xsettitle(char *p)
 | 
			
		||||
{
 | 
			
		||||
@@ -1579,11 +1658,13 @@ xfinishdraw(void)
 | 
			
		||||
void
 | 
			
		||||
xximspot(int x, int y)
 | 
			
		||||
{
 | 
			
		||||
	XPoint spot = { borderpx + x * win.cw, borderpx + (y + 1) * win.ch };
 | 
			
		||||
	XVaNestedList attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
 | 
			
		||||
	if (xw.ime.xic == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	XSetICValues(xw.xic, XNPreeditAttributes, attr, NULL);
 | 
			
		||||
	XFree(attr);
 | 
			
		||||
	xw.ime.spot.x = borderpx + x * win.cw;
 | 
			
		||||
	xw.ime.spot.y = borderpx + (y + 1) * win.ch;
 | 
			
		||||
 | 
			
		||||
	XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -1625,8 +1706,7 @@ xsetmode(int set, unsigned int flags)
 | 
			
		||||
int
 | 
			
		||||
xsetcursor(int cursor)
 | 
			
		||||
{
 | 
			
		||||
	DEFAULT(cursor, 1);
 | 
			
		||||
	if (!BETWEEN(cursor, 0, 6))
 | 
			
		||||
	if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */
 | 
			
		||||
		return 1;
 | 
			
		||||
	win.cursor = cursor;
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -1660,13 +1740,15 @@ focus(XEvent *ev)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (ev->type == FocusIn) {
 | 
			
		||||
		XSetICFocus(xw.xic);
 | 
			
		||||
		if (xw.ime.xic)
 | 
			
		||||
			XSetICFocus(xw.ime.xic);
 | 
			
		||||
		win.mode |= MODE_FOCUSED;
 | 
			
		||||
		xseturgency(0);
 | 
			
		||||
		if (IS_SET(MODE_FOCUS))
 | 
			
		||||
			ttywrite("\033[I", 3, 0);
 | 
			
		||||
	} else {
 | 
			
		||||
		XUnsetICFocus(xw.xic);
 | 
			
		||||
		if (xw.ime.xic)
 | 
			
		||||
			XUnsetICFocus(xw.ime.xic);
 | 
			
		||||
		win.mode &= ~MODE_FOCUSED;
 | 
			
		||||
		if (IS_SET(MODE_FOCUS))
 | 
			
		||||
			ttywrite("\033[O", 3, 0);
 | 
			
		||||
@@ -1721,7 +1803,7 @@ kpress(XEvent *ev)
 | 
			
		||||
{
 | 
			
		||||
	XKeyEvent *e = &ev->xkey;
 | 
			
		||||
	KeySym ksym;
 | 
			
		||||
	char buf[32], *customkey;
 | 
			
		||||
	char buf[64], *customkey;
 | 
			
		||||
	int len;
 | 
			
		||||
	Rune c;
 | 
			
		||||
	Status status;
 | 
			
		||||
@@ -1730,7 +1812,10 @@ kpress(XEvent *ev)
 | 
			
		||||
	if (IS_SET(MODE_KBDLOCK))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
 | 
			
		||||
	if (xw.ime.xic)
 | 
			
		||||
		len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
 | 
			
		||||
	else
 | 
			
		||||
		len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
 | 
			
		||||
	/* 1. shortcuts */
 | 
			
		||||
	for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
 | 
			
		||||
		if (ksym == bp->keysym && match(bp->mod, e->state)) {
 | 
			
		||||
@@ -1798,10 +1883,9 @@ run(void)
 | 
			
		||||
	XEvent ev;
 | 
			
		||||
	int w = win.w, h = win.h;
 | 
			
		||||
	fd_set rfd;
 | 
			
		||||
	int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
 | 
			
		||||
	int ttyfd;
 | 
			
		||||
	struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
 | 
			
		||||
	long deltatime;
 | 
			
		||||
	int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing;
 | 
			
		||||
	struct timespec seltv, *tv, now, lastblink, trigger;
 | 
			
		||||
	double timeout;
 | 
			
		||||
 | 
			
		||||
	/* Waiting for window mapping */
 | 
			
		||||
	do {
 | 
			
		||||
@@ -1822,82 +1906,77 @@ run(void)
 | 
			
		||||
	ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
 | 
			
		||||
	cresize(w, h);
 | 
			
		||||
 | 
			
		||||
	clock_gettime(CLOCK_MONOTONIC, &last);
 | 
			
		||||
	lastblink = last;
 | 
			
		||||
 | 
			
		||||
	for (xev = actionfps;;) {
 | 
			
		||||
	for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) {
 | 
			
		||||
		FD_ZERO(&rfd);
 | 
			
		||||
		FD_SET(ttyfd, &rfd);
 | 
			
		||||
		FD_SET(xfd, &rfd);
 | 
			
		||||
 | 
			
		||||
		if (XPending(xw.dpy))
 | 
			
		||||
			timeout = 0;  /* existing events might not set xfd */
 | 
			
		||||
 | 
			
		||||
		seltv.tv_sec = timeout / 1E3;
 | 
			
		||||
		seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec);
 | 
			
		||||
		tv = timeout >= 0 ? &seltv : NULL;
 | 
			
		||||
 | 
			
		||||
		if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
 | 
			
		||||
			if (errno == EINTR)
 | 
			
		||||
				continue;
 | 
			
		||||
			die("select failed: %s\n", strerror(errno));
 | 
			
		||||
		}
 | 
			
		||||
		if (FD_ISSET(ttyfd, &rfd)) {
 | 
			
		||||
			ttyread();
 | 
			
		||||
			if (blinktimeout) {
 | 
			
		||||
				blinkset = tattrset(ATTR_BLINK);
 | 
			
		||||
				if (!blinkset)
 | 
			
		||||
					MODBIT(win.mode, 0, MODE_BLINK);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (FD_ISSET(xfd, &rfd))
 | 
			
		||||
			xev = actionfps;
 | 
			
		||||
 | 
			
		||||
		clock_gettime(CLOCK_MONOTONIC, &now);
 | 
			
		||||
		drawtimeout.tv_sec = 0;
 | 
			
		||||
		drawtimeout.tv_nsec =  (1000 * 1E6)/ xfps;
 | 
			
		||||
		tv = &drawtimeout;
 | 
			
		||||
 | 
			
		||||
		dodraw = 0;
 | 
			
		||||
		if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
 | 
			
		||||
			tsetdirtattr(ATTR_BLINK);
 | 
			
		||||
			win.mode ^= MODE_BLINK;
 | 
			
		||||
			lastblink = now;
 | 
			
		||||
			dodraw = 1;
 | 
			
		||||
		}
 | 
			
		||||
		deltatime = TIMEDIFF(now, last);
 | 
			
		||||
		if (deltatime > 1000 / (xev ? xfps : actionfps)) {
 | 
			
		||||
			dodraw = 1;
 | 
			
		||||
			last = now;
 | 
			
		||||
		if (FD_ISSET(ttyfd, &rfd))
 | 
			
		||||
			ttyread();
 | 
			
		||||
 | 
			
		||||
		xev = 0;
 | 
			
		||||
		while (XPending(xw.dpy)) {
 | 
			
		||||
			xev = 1;
 | 
			
		||||
			XNextEvent(xw.dpy, &ev);
 | 
			
		||||
			if (XFilterEvent(&ev, None))
 | 
			
		||||
				continue;
 | 
			
		||||
			if (handler[ev.type])
 | 
			
		||||
				(handler[ev.type])(&ev);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (dodraw) {
 | 
			
		||||
			while (XPending(xw.dpy)) {
 | 
			
		||||
				XNextEvent(xw.dpy, &ev);
 | 
			
		||||
				if (XFilterEvent(&ev, None))
 | 
			
		||||
					continue;
 | 
			
		||||
				if (handler[ev.type])
 | 
			
		||||
					(handler[ev.type])(&ev);
 | 
			
		||||
		/*
 | 
			
		||||
		 * To reduce flicker and tearing, when new content or event
 | 
			
		||||
		 * triggers drawing, we first wait a bit to ensure we got
 | 
			
		||||
		 * everything, and if nothing new arrives - we draw.
 | 
			
		||||
		 * We start with trying to wait minlatency ms. If more content
 | 
			
		||||
		 * arrives sooner, we retry with shorter and shorter periods,
 | 
			
		||||
		 * and eventually draw even without idle after maxlatency ms.
 | 
			
		||||
		 * Typically this results in low latency while interacting,
 | 
			
		||||
		 * maximum latency intervals during `cat huge.txt`, and perfect
 | 
			
		||||
		 * sync with periodic updates from animations/key-repeats/etc.
 | 
			
		||||
		 */
 | 
			
		||||
		if (FD_ISSET(ttyfd, &rfd) || xev) {
 | 
			
		||||
			if (!drawing) {
 | 
			
		||||
				trigger = now;
 | 
			
		||||
				drawing = 1;
 | 
			
		||||
			}
 | 
			
		||||
			timeout = (maxlatency - TIMEDIFF(now, trigger)) \
 | 
			
		||||
			          / maxlatency * minlatency;
 | 
			
		||||
			if (timeout > 0)
 | 
			
		||||
				continue;  /* we have time, try to find idle */
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			draw();
 | 
			
		||||
			XFlush(xw.dpy);
 | 
			
		||||
 | 
			
		||||
			if (xev && !FD_ISSET(xfd, &rfd))
 | 
			
		||||
				xev--;
 | 
			
		||||
			if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
 | 
			
		||||
				if (blinkset) {
 | 
			
		||||
					if (TIMEDIFF(now, lastblink) \
 | 
			
		||||
							> blinktimeout) {
 | 
			
		||||
						drawtimeout.tv_nsec = 1000;
 | 
			
		||||
					} else {
 | 
			
		||||
						drawtimeout.tv_nsec = (1E6 * \
 | 
			
		||||
							(blinktimeout - \
 | 
			
		||||
							TIMEDIFF(now,
 | 
			
		||||
								lastblink)));
 | 
			
		||||
					}
 | 
			
		||||
					drawtimeout.tv_sec = \
 | 
			
		||||
					    drawtimeout.tv_nsec / 1E9;
 | 
			
		||||
					drawtimeout.tv_nsec %= (long)1E9;
 | 
			
		||||
				} else {
 | 
			
		||||
					tv = NULL;
 | 
			
		||||
				}
 | 
			
		||||
		/* idle detected or maxlatency exhausted -> draw */
 | 
			
		||||
		timeout = -1;
 | 
			
		||||
		if (blinktimeout && tattrset(ATTR_BLINK)) {
 | 
			
		||||
			timeout = blinktimeout - TIMEDIFF(now, lastblink);
 | 
			
		||||
			if (timeout <= 0) {
 | 
			
		||||
				if (-timeout > blinktimeout) /* start visible */
 | 
			
		||||
					win.mode |= MODE_BLINK;
 | 
			
		||||
				win.mode ^= MODE_BLINK;
 | 
			
		||||
				tsetdirtattr(ATTR_BLINK);
 | 
			
		||||
				lastblink = now;
 | 
			
		||||
				timeout = blinktimeout;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		draw();
 | 
			
		||||
		XFlush(xw.dpy);
 | 
			
		||||
		drawing = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1919,7 +1998,7 @@ main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	xw.l = xw.t = 0;
 | 
			
		||||
	xw.isfixed = False;
 | 
			
		||||
	win.cursor = cursorshape;
 | 
			
		||||
	xsetcursor(cursorshape);
 | 
			
		||||
 | 
			
		||||
	ARGBEGIN {
 | 
			
		||||
	case 'a':
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user