summaryrefslogtreecommitdiff
path: root/st/x.c
diff options
context:
space:
mode:
Diffstat (limited to 'st/x.c')
-rw-r--r--st/x.c600
1 files changed, 376 insertions, 224 deletions
diff --git a/st/x.c b/st/x.c
index 8082ded..d6067d7 100644
--- a/st/x.c
+++ b/st/x.c
@@ -5,6 +5,7 @@
#include <locale.h>
#include <signal.h>
#include <sys/select.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <libgen.h>
@@ -20,7 +21,6 @@ char *argv0;
#include "arg.h"
#include "st.h"
#include "win.h"
-#include "hb.h"
/* types used in config.h */
typedef struct {
@@ -36,6 +36,7 @@ typedef struct {
void (*func)(const Arg *);
const Arg arg;
uint release;
+ int altscrn; /* 0: don't care, -1: not alt screen, 1: alt screen */
} MouseShortcut;
typedef struct {
@@ -47,6 +48,11 @@ typedef struct {
signed char appcursor; /* application cursor */
} Key;
+typedef enum {
+ PixelGeometry,
+ CellGeometry
+} Geometry;
+
/* Xresources preferences */
enum resource_type {
STRING = 0,
@@ -70,17 +76,19 @@ static void clipcopy(const Arg *);
static void clippaste(const Arg *);
static void numlock(const Arg *);
static void selpaste(const Arg *);
-static void changealpha(const Arg *);
static void zoom(const Arg *);
static void zoomabs(const Arg *);
static void zoomreset(const Arg *);
static void ttysend(const Arg *);
+static void chgalpha(const Arg *);
static void cyclefonts(const Arg *);
-
/* config.h for applying patches and the configuration. */
#include "config.h"
+/* size of title stack */
+#define TITLESTACKSIZE 8
+
/* XEMBED messages */
#define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5
@@ -102,7 +110,6 @@ typedef struct {
int hborderpx, vborderpx;
int ch; /* char height */
int cw; /* char width */
- int cyo; /* char y offset */
int mode; /* window state/mode flags */
int cursor; /* cursor style */
} TermWindow;
@@ -114,6 +121,7 @@ typedef struct {
Drawable buf;
GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
+ Atom netwmstate, netwmfullscreen;
struct {
XIM xim;
XIC xic;
@@ -163,7 +171,7 @@ typedef struct {
static inline ushort sixd_to_16bit(int);
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int);
static void xdrawglyph(Glyph, int, int);
static void xclear(int, int, int, int);
static int xgeommasktogravity(int);
@@ -186,7 +194,6 @@ static void xsetenv(void);
static void xseturgency(int);
static int evcol(XEvent *);
static int evrow(XEvent *);
-static float clamp(float, float, float);
static void expose(XEvent *);
static void visibility(XEvent *);
@@ -215,6 +222,7 @@ static void usage(void);
static void (*handler[LASTEvent])(XEvent *) = {
[KeyPress] = kpress,
+ [KeyRelease] = kpress,
[ClientMessage] = cmessage,
[ConfigureNotify] = resize,
[VisibilityNotify] = visibility,
@@ -244,6 +252,8 @@ static DC dc;
static XWindow xw;
static XSelection xsel;
static TermWindow win;
+static int tstki; /* title stack index */
+static char *titlestack[TITLESTACKSIZE]; /* title stack */
/* Font Ring Cache */
enum {
@@ -267,6 +277,11 @@ static char *usedfont = NULL;
static double usedfontsize = 0;
static double defaultfontsize = 0;
+/* declared in config.h */
+extern int disablebold;
+extern int disableitalic;
+extern int disableroman;
+
static char *opt_alpha = NULL;
static char *opt_class = NULL;
static char **opt_cmd = NULL;
@@ -277,11 +292,15 @@ static char *opt_line = NULL;
static char *opt_name = NULL;
static char *opt_title = NULL;
-static int focused = 0;
-
-static int oldbutton = 3; /* button event on startup: 3 = release */
static uint buttons; /* bit field of pressed buttons */
static int cursorblinks = 0;
+static int focused = 0;
+
+static Cursor cursor;
+static XColor xmousefg, xmousebg;
+XWindowAttributes attr;
+XVisualInfo vis;
+
void
clipcopy(const Arg *dummy)
@@ -322,20 +341,6 @@ numlock(const Arg *dummy)
}
void
-changealpha(const Arg *arg)
-{
- if((alpha > 0 && arg->f < 0) || (alpha < 1 && arg->f > 0))
- alpha += arg->f;
- else if (arg->f == 0.0f)
- alpha = alpha_def;
- alpha = clamp(alpha, 0.0, 1.0);
- alphaUnfocus = clamp(alpha-alphaOffset, 0.0, 1.0);
-
- xloadcols();
- redraw();
-}
-
-void
zoom(const Arg *arg)
{
Arg larg;
@@ -359,7 +364,6 @@ void
zoomreset(const Arg *arg)
{
Arg larg;
-
zoomabs(&larg);
}
@@ -396,15 +400,6 @@ evrow(XEvent *e)
return y / win.ch;
}
-float
-clamp(float value, float lower, float upper) {
- if(value < lower)
- return lower;
- if(value > upper)
- return upper;
- return value;
-}
-
void
mousesel(XEvent *e, int done)
{
@@ -425,59 +420,68 @@ mousesel(XEvent *e, int done)
void
mousereport(XEvent *e)
{
- int len, x = evcol(e), y = evrow(e),
- button = e->xbutton.button, state = e->xbutton.state;
+ int len, btn, code;
+ int x = evcol(e), y = evrow(e);
+ int state = e->xbutton.state;
char buf[40];
static int ox, oy;
- /* from urxvt */
- if (e->xbutton.type == MotionNotify) {
+ if (e->type == MotionNotify) {
if (x == ox && y == oy)
return;
if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
return;
- /* MOUSE_MOTION: no reporting if no button is pressed */
- if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
+ /* MODE_MOUSEMOTION: no reporting if no button is pressed */
+ if (IS_SET(MODE_MOUSEMOTION) && buttons == 0)
return;
-
- button = oldbutton + 32;
- ox = x;
- oy = y;
+ /* Set btn to lowest-numbered pressed button, or 12 if no
+ * buttons are pressed. */
+ for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++)
+ ;
+ code = 32;
} else {
- if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
- button = 3;
- } else {
- button -= Button1;
- if (button >= 3)
- button += 64 - 3;
- }
- if (e->xbutton.type == ButtonPress) {
- oldbutton = button;
- ox = x;
- oy = y;
- } else if (e->xbutton.type == ButtonRelease) {
- oldbutton = 3;
+ btn = e->xbutton.button;
+ /* Only buttons 1 through 11 can be encoded */
+ if (btn < 1 || btn > 11)
+ return;
+ if (e->type == ButtonRelease) {
/* MODE_MOUSEX10: no button release reporting */
if (IS_SET(MODE_MOUSEX10))
return;
- if (button == 64 || button == 65)
+ /* Don't send release events for the scroll wheel */
+ if (btn == 4 || btn == 5)
return;
}
+ code = 0;
}
+ ox = x;
+ oy = y;
+
+ /* Encode btn into code. If no button is pressed for a motion event in
+ * MODE_MOUSEMANY, then encode it as a release. */
+ if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12)
+ code += 3;
+ else if (btn >= 8)
+ code += 128 + btn - 8;
+ else if (btn >= 4)
+ code += 64 + btn - 4;
+ else
+ code += btn - 1;
+
if (!IS_SET(MODE_MOUSEX10)) {
- button += ((state & ShiftMask ) ? 4 : 0)
- + ((state & Mod4Mask ) ? 8 : 0)
- + ((state & ControlMask) ? 16 : 0);
+ code += ((state & ShiftMask ) ? 4 : 0)
+ + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */
+ + ((state & ControlMask) ? 16 : 0);
}
if (IS_SET(MODE_MOUSESGR)) {
len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
- button, x+1, y+1,
+ code, x+1, y+1,
e->type == ButtonRelease ? 'm' : 'M');
} else if (x < 223 && y < 223) {
len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
- 32+button, 32+x+1, 32+y+1);
+ 32+code, 32+x+1, 32+y+1);
} else {
return;
}
@@ -504,9 +508,14 @@ mouseaction(XEvent *e, uint release)
/* ignore Button<N>mask for Button<N> - it's set on release */
uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
+ if (release == 0 && e->xbutton.button == Button1) {
+ return followurl(evcol(e), evrow(e));
+ }
+
for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
if (ms->release == release &&
ms->button == e->xbutton.button &&
+ (!ms->altscrn || (ms->altscrn == (tisaltscr() ? 1 : -1))) &&
(match(ms->mod, state) || /* exact or forced */
match(ms->mod, state & ~forcemousemod))) {
ms->func(&(ms->arg));
@@ -738,6 +747,7 @@ setsel(char *str, Time t)
XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
selclear();
+ clipcopy(NULL);
}
void
@@ -815,6 +825,24 @@ xresize(int col, int row)
xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
}
+void
+fullscreen(const Arg *arg)
+{
+ XEvent ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.message_type = xw.netwmstate;
+ ev.xclient.display = xw.dpy;
+ ev.xclient.window = xw.win;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = 2; /* _NET_WM_STATE_TOGGLE */
+ ev.xclient.data.l[1] = xw.netwmfullscreen;
+
+ XSendEvent(xw.dpy, DefaultRootWindow(xw.dpy), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev);
+}
+
ushort
sixd_to_16bit(int x)
{
@@ -848,7 +876,8 @@ xloadcolor(int i, const char *name, Color *ncolor)
void
xloadalpha(void)
{
- float const usedAlpha = focused ? alpha : alphaUnfocus;
+ xloadcolor(focused ?bg :bgUnfocused, NULL, &dc.col[defaultbg]);
+ float const usedAlpha = focused ? alpha : alphaUnfocused;
if (opt_alpha) alpha = strtof(opt_alpha, NULL);
dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * usedAlpha);
dc.col[defaultbg].pixel &= 0x00FFFFFF;
@@ -858,16 +887,15 @@ xloadalpha(void)
void
xloadcols(void)
{
- int i;
static int loaded;
Color *cp;
if (!loaded) {
dc.collen = 1 + (defaultbg = MAX(LEN(colorname), 256));
- dc.col = xmalloc(dc.collen * sizeof(Color));
+ dc.col = xmalloc((dc.collen) * sizeof(Color));
}
- for (i = 0; i+1 < dc.collen; i++)
+ for (int i = 0; i+1 < dc.collen; ++i)
if (!xloadcolor(i, NULL, &dc.col[i])) {
if (colorname[i])
die("could not allocate color '%s'\n", colorname[i]);
@@ -875,9 +903,6 @@ xloadcols(void)
die("could not allocate color %d\n", i);
}
- if (dc.collen) // cannot die, as the color is already loaded.
- xloadcolor(background, NULL, &dc.col[defaultbg]);
-
xloadalpha();
loaded = 1;
}
@@ -885,7 +910,7 @@ xloadcols(void)
int
xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b)
{
- if (!BETWEEN(x, 0, dc.collen))
+ if (!BETWEEN(x, 0, dc.collen - 1))
return 1;
*r = dc.col[x].color.red >> 8;
@@ -900,7 +925,7 @@ xsetcolorname(int x, const char *name)
{
Color ncolor;
- if (!BETWEEN(x, 0, dc.collen))
+ if (!BETWEEN(x, 0, dc.collen - 1))
return 1;
if (!xloadcolor(x, name, &ncolor))
@@ -924,6 +949,12 @@ xclear(int x1, int y1, int x2, int y2)
}
void
+xclearwin(void)
+{
+ xclear(0, 0, win.w, win.h);
+}
+
+void
xhints(void)
{
XClassHint class = {opt_name ? opt_name : "st",
@@ -1098,20 +1129,22 @@ xloadfonts(const char *fontstr, double fontsize)
/* Setting character width and height. */
win.cw = ceilf(dc.font.width * cwscale);
win.ch = ceilf(dc.font.height * chscale);
- win.cyo = ceilf(dc.font.height * (chscale - 1) / 2);
FcPatternDel(pattern, FC_SLANT);
- FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ if (!disableitalic)
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
if (xloadfont(&dc.ifont, pattern))
die("can't open font %s\n", fontstr);
FcPatternDel(pattern, FC_WEIGHT);
- FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+ if (!disablebold)
+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
if (xloadfont(&dc.ibfont, pattern))
die("can't open font %s\n", fontstr);
FcPatternDel(pattern, FC_SLANT);
- FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
+ if (!disableroman)
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
if (xloadfont(&dc.bfont, pattern))
die("can't open font %s\n", fontstr);
@@ -1149,7 +1182,6 @@ xloadsparefonts(void)
double sizeshift, fontval;
int fc;
char **fp;
- int width, height;
if (frclen != 0)
die("can't embed spare fonts. cache isn't empty");
@@ -1166,6 +1198,7 @@ xloadsparefonts(void)
}
for (fp = font2; fp - font2 < fc; ++fp) {
+
if (**fp == '-')
pattern = XftXlfdParse(*fp, False, False);
else
@@ -1225,9 +1258,6 @@ xunloadfont(Font *f)
void
xunloadfonts(void)
{
- /* Clear Harfbuzz font cache. */
- hbunloadfonts();
-
/* Free the loaded fonts in the font cache. */
while (frclen > 0)
XftFontClose(xw.dpy, frc[--frclen].font);
@@ -1293,16 +1323,11 @@ xicdestroy(XIC xim, XPointer client, XPointer call)
}
void
-xinit(int cols, int rows)
+xinit(int w, int h)
{
XGCValues gcvalues;
- Cursor cursor;
Window parent;
pid_t thispid = getpid();
- XColor xmousefg, xmousebg;
- XWindowAttributes attr;
- XVisualInfo vis;
- int width, height;
xw.scr = XDefaultScreen(xw.dpy);
@@ -1321,23 +1346,30 @@ xinit(int cols, int rows)
if (!FcInit())
die("could not init fontconfig.\n");
- usedfont = (opt_font == NULL)? fonts[currentfont] : opt_font;
-
- xloadfonts(usedfont, 0);
+ usedfont = (opt_font == NULL)? fonts[currentfont] : opt_font;
+ xloadfonts(usedfont, 0);
/* spare fonts */
xloadsparefonts();
- /* Backup default alpha value */
- alpha_def = alpha;
+ /* Backup default alpha value */
+ alpha_def = alpha;
/* colors */
xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None);
xloadcols();
/* adjust fixed window geometry */
- win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw;
- win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch;
+ switch (geometry) {
+ case CellGeometry:
+ win.w = 2 * win.hborderpx + 2 * borderpx + w * win.cw;
+ win.h = 2 * win.vborderpx + 2 * borderpx + h * win.ch;
+ break;
+ case PixelGeometry:
+ win.w = w;
+ win.h = h;
+ break;
+ }
if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
if (xw.gm & YNegative)
@@ -1404,6 +1436,9 @@ xinit(int cols, int rows)
XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
PropModeReplace, (uchar *)&thispid, 1);
+ xw.netwmstate = XInternAtom(xw.dpy, "_NET_WM_STATE", False);
+ xw.netwmfullscreen = XInternAtom(xw.dpy, "_NET_WM_STATE_FULLSCREEN", False);
+
win.mode = MODE_NUMLOCK;
resettitle();
xhints();
@@ -1437,13 +1472,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
FcCharSet *fccharset;
int i, f, numspecs = 0;
- for (i = 0, xp = winx, yp = winy + font->ascent + win.cyo; i < len; ++i) {
+ for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
/* Fetch rune and mode for current glyph. */
rune = glyphs[i].u;
mode = glyphs[i].mode;
/* Skip dummy wide-character spacing. */
- if (mode & ATTR_WDUMMY)
+ if (mode == ATTR_WDUMMY)
continue;
/* Determine font for glyph if different from previous glyph. */
@@ -1462,7 +1497,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
font = &dc.bfont;
frcflags = FRC_BOLD;
}
- yp = winy + font->ascent + win.cyo;
+ yp = winy + font->ascent;
}
if (mode & ATTR_BOXDRAW) {
@@ -1475,9 +1510,8 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
if (glyphidx) {
specs[numspecs].font = font->match;
specs[numspecs].glyph = glyphidx;
- specs[numspecs].x = (short)xp + cxoffset;
+ specs[numspecs].x = (short)xp + cxoffset;
specs[numspecs].y = (short)yp + cyoffset;
-
xp += runewidth;
numspecs++;
continue;
@@ -1556,14 +1590,29 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
numspecs++;
}
- /* Harfbuzz transformation for ligatures. */
- hbtransform(specs, glyphs, len, x, y);
-
return numspecs;
}
void
-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
+chgalpha(const Arg *arg)
+{
+ if (arg->f == -1.0f && alpha >= 0.1f)
+ alpha -= 0.1f;
+ else if (arg->f == 1.0f && alpha < 1.0f)
+ alpha += 0.1f;
+ else if (arg->f == 0.0f)
+ alpha = alpha_def;
+ else
+ return;
+
+ dc.col[defaultbg].color.alpha = (unsigned short)(0xFFFF * alpha);
+ /* Required to remove artifacting from borderpx */
+ cresize(0, 0);
+ redraw();
+}
+
+void
+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int dmode)
{
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch,
@@ -1643,9 +1692,14 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
}
if (base.mode & ATTR_REVERSE) {
- temp = fg;
- fg = bg;
- bg = temp;
+ if (bg == fg) {
+ bg = &dc.col[defaultfg];
+ fg = &dc.col[defaultbg];
+ } else {
+ temp = fg;
+ fg = bg;
+ bg = temp;
+ }
}
if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK)
@@ -1654,51 +1708,45 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
if (base.mode & ATTR_INVISIBLE)
fg = bg;
- /* Intelligent cleaning up of the borders. */
- if (x == 0) {
- xclear(0, (y == 0)? 0 : winy, win.hborderpx,
- winy + win.ch +
- ((winy + win.ch >= win.vborderpx + win.th)? win.h : 0));
- }
- if (winx + width >= win.hborderpx + win.tw) {
- xclear(winx + width, (y == 0)? 0 : winy, win.w,
- ((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch)));
- }
- if (y == 0)
- xclear(winx, 0, winx + width, win.vborderpx);
- if (winy + win.ch >= win.vborderpx + win.th)
- xclear(winx, winy + win.ch, winx + width, win.h);
-
- /* Clean up the region we want to draw to. */
- XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
-
- /* Set the clip region because Xft is sometimes dirty. */
- r.x = 0;
- r.y = 0;
- r.height = win.ch;
- r.width = width;
- XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
-
- if (base.mode & ATTR_BOXDRAW) {
- drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len);
- } else {
- /* Render the glyphs. */
- XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
- }
-
- /* Render underline and strikethrough. */
- if (base.mode & ATTR_UNDERLINE) {
- XftDrawRect(xw.draw, fg, winx, winy + win.cyo + dc.font.ascent * chscale + 1,
- width, 1);
- }
-
- if (base.mode & ATTR_STRUCK) {
- XftDrawRect(xw.draw, fg, winx, winy + win.cyo + 2 * dc.font.ascent * chscale / 3,
- width, 1);
- }
-
- /* Reset clip to none. */
- XftDrawSetClip(xw.draw, 0);
+ if (dmode & DRAW_BG) {
+ /* Intelligent cleaning up of the borders. */
+ if (x == 0) {
+ xclear(0, (y == 0)? 0 : winy, win.hborderpx,
+ winy + win.ch +
+ ((winy + win.ch >= win.vborderpx + win.th)? win.h : 0));
+ }
+ if (winx + width >= win.hborderpx + win.tw) {
+ xclear(winx + width, (y == 0)? 0 : winy, win.w,
+ ((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch)));
+ }
+ if (y == 0)
+ xclear(winx, 0, winx + width, win.vborderpx);
+ if (winy + win.ch >= win.vborderpx + win.th)
+ xclear(winx, winy + win.ch, winx + width, win.h);
+
+ /* Clean up the region we want to draw to. */
+ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
+ }
+
+ if (dmode & DRAW_FG) {
+ if (base.mode & ATTR_BOXDRAW) {
+ drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len);
+ } else {
+ /* Render the glyphs. */
+ XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
+ }
+
+ /* Render underline and strikethrough. */
+ if (base.mode & ATTR_UNDERLINE || base.mode & ATTR_URL) {
+ XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
+ width, 1);
+ }
+
+ if (base.mode & ATTR_STRUCK) {
+ XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3,
+ width, 1);
+ }
+ }
}
void
@@ -1708,21 +1756,19 @@ xdrawglyph(Glyph g, int x, int y)
XftGlyphFontSpec spec;
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
+ xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG);
}
void
-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
{
Color drawcol;
+ XRenderColor colbg;
/* remove the old cursor */
if (selected(ox, oy))
og.mode ^= ATTR_REVERSE;
-
- /* Redraw the line where cursor was previously.
- * It will restore the ligatures broken by the cursor. */
- xdrawline(line, 0, oy, len);
+ xdrawglyph(og, ox, oy);
if (IS_SET(MODE_HIDE))
return;
@@ -1746,22 +1792,31 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
if (selected(cx, cy)) {
g.fg = defaultfg;
g.bg = defaultrcs;
+ } else if (!(og.mode & ATTR_REVERSE)) {
+ unsigned long col = g.bg;
+ g.bg = g.fg;
+ g.fg = col;
+ }
+
+ if (IS_TRUECOL(g.bg)) {
+ colbg.alpha = 0xffff;
+ colbg.red = TRUERED(g.bg);
+ colbg.green = TRUEGREEN(g.bg);
+ colbg.blue = TRUEBLUE(g.bg);
+ XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol);
} else {
- g.fg = defaultbg;
- g.bg = defaultcs;
+ drawcol = dc.col[g.bg];
}
- drawcol = dc.col[g.bg];
}
/* draw the new one */
if (IS_SET(MODE_FOCUSED)) {
switch (win.cursor) {
- default:
+ default:
case 0: /* blinking block */
case 1: /* blinking block (default) */
if (IS_SET(MODE_BLINK))
break;
-
/* FALLTHROUGH */
case 2: /* steady block */
xdrawglyph(g, cx, cy);
@@ -1831,6 +1886,9 @@ xseticontitle(char *p)
XTextProperty prop;
DEFAULT(p, opt_title);
+ if (p[0] == '\0')
+ p = opt_title;
+
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
return;
@@ -1840,10 +1898,33 @@ xseticontitle(char *p)
}
void
-xsettitle(char *p)
+xfreetitlestack(void)
{
- XTextProperty prop;
- DEFAULT(p, opt_title);
+ for (int i = 0; i < LEN(titlestack); i++) {
+ free(titlestack[i]);
+ titlestack[i] = NULL;
+ }
+}
+
+void
+xsettitle(char *p, int pop)
+{
+ XTextProperty prop;
+
+ free(titlestack[tstki]);
+ if (pop) {
+ titlestack[tstki] = NULL;
+ tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE;
+ p = titlestack[tstki] ? titlestack[tstki] : opt_title;
+ } else if (p) {
+ titlestack[tstki] = xstrdup(p);
+ } else {
+ titlestack[tstki] = NULL;
+ p = opt_title;
+ }
+
+ if (p[0] == '\0')
+ p = opt_title;
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
@@ -1853,41 +1934,60 @@ xsettitle(char *p)
XFree(prop.value);
}
+void
+xpushtitle(void)
+{
+ int tstkin = (tstki + 1) % TITLESTACKSIZE;
+
+ free(titlestack[tstkin]);
+ titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL;
+ tstki = tstkin;
+}
+
int
xstartdraw(void)
{
+ if (IS_SET(MODE_VISIBLE))
+ XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0);
return IS_SET(MODE_VISIBLE);
}
void
xdrawline(Line line, int x1, int y1, int x2)
{
- int i, x, ox, numspecs;
+ int i, x, ox, numspecs, numspecs_cached;
Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
- for (x = x1; x < x2 && i < numspecs; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
- if (i > 0 && ATTRCMP(base, new)) {
- xdrawglyphfontspecs(specs, base, i, ox, y1);
- specs += i;
- numspecs -= i;
- i = 0;
- }
- if (i == 0) {
- ox = x;
- base = new;
+ XftGlyphFontSpec *specs;
+
+ numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1);
+
+ /* Draw line in 2 passes: background and foreground. This way wide glyphs
+ won't get truncated (#223) */
+ for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) {
+ specs = xw.specbuf;
+ numspecs = numspecs_cached;
+ i = ox = 0;
+ for (x = x1; x < x2 && i < numspecs; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+ if (i > 0 && ATTRCMP(base, new)) {
+ xdrawglyphfontspecs(specs, base, i, ox, y1, dmode);
+ specs += i;
+ numspecs -= i;
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
}
- i++;
+ if (i > 0)
+ xdrawglyphfontspecs(specs, base, i, ox, y1, dmode);
}
- if (i > 0)
- xdrawglyphfontspecs(specs, base, i, ox, y1);
}
void
@@ -1944,6 +2044,12 @@ xsetmode(int set, unsigned int flags)
{
int mode = win.mode;
MODBIT(win.mode, set, flags);
+ if (flags & MODE_MOUSE) {
+ if (win.mode & MODE_MOUSE)
+ XUndefineCursor(xw.dpy, xw.win);
+ else
+ XDefineCursor(xw.dpy, xw.win, cursor);
+ }
if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE))
redraw();
}
@@ -1996,7 +2102,7 @@ focus(XEvent *ev)
ttywrite("\033[I", 3, 0);
if (!focused) {
focused = 1;
- xloadcols();
+ xloadalpha();
tfulldirt();
}
} else {
@@ -2007,7 +2113,7 @@ focus(XEvent *ev)
ttywrite("\033[O", 3, 0);
if (focused) {
focused = 0;
- xloadcols();
+ xloadalpha();
tfulldirt();
}
}
@@ -2060,11 +2166,9 @@ void
kpress(XEvent *ev)
{
XKeyEvent *e = &ev->xkey;
- KeySym ksym;
- char *buf = NULL, *customkey;
- int len = 0;
- int buf_size = 64;
- int critical = - 1;
+ KeySym ksym = NoSymbol;
+ char buf[64], *customkey;
+ int len;
Rune c;
Status status;
Shortcut *bp;
@@ -2072,44 +2176,51 @@ kpress(XEvent *ev)
if (IS_SET(MODE_KBDLOCK))
return;
-reallocbuf:
- if (critical > 0)
- goto cleanup;
- if (buf)
- free(buf);
-
- buf = xmalloc((buf_size) * sizeof(char));
- critical += 1;
-
if (xw.ime.xic) {
- len = XmbLookupString(xw.ime.xic, e, buf, buf_size, &ksym, &status);
- if (status == XBufferOverflow) {
- buf_size = len;
- goto reallocbuf;
- }
+ len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
+ if (status == XBufferOverflow)
+ return;
} else {
- // Not sure how to fix this and if it is fixable
- // but at least it does write something into the buffer
- // so it is not as critical
- len = XLookupString(e, buf, buf_size, &ksym, NULL);
+ len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
+ }
+
+ /* KeyRelease not relevant to shortcuts */
+ if (ev->type == KeyRelease)
+ return;
+
+ if ( IS_SET(MODE_KBDSELECT) ) {
+ if ( match(XK_NO_MOD, e->state) ||
+ (XK_Shift_L | XK_Shift_R) & e->state )
+ win.mode ^= trt_kbdselect(ksym, buf, len);
+ return;
}
+
/* 1. shortcuts */
for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
if (ksym == bp->keysym && match(bp->mod, e->state)) {
+ if (bp -> func != autocomplete)
+ autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
bp->func(&(bp->arg));
- goto cleanup;
+ return;
}
}
+ if (!(
+ len == 0 &&
+ e -> state & ~ignoremod // ACMPL_ISSUE: I'm not sure that this is the right way
+ | ALTCTRL == ALTCTRL
+ ))
+ autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
+
/* 2. custom keys from config.h */
if ((customkey = kmap(ksym, e->state))) {
ttywrite(customkey, strlen(customkey), 1);
- goto cleanup;
+ return;
}
/* 3. composed string from input method */
if (len == 0)
- goto cleanup;
+ return;
if (len == 1 && e->state & Mod1Mask) {
if (IS_SET(MODE_8BIT)) {
if (*buf < 0177) {
@@ -2122,11 +2233,7 @@ reallocbuf:
len = 2;
}
}
- if (len <= buf_size)
- ttywrite(buf, len, 1);
-cleanup:
- if (buf)
- free(buf);
+ ttywrite(buf, len, 1);
}
void
@@ -2237,7 +2344,6 @@ run(void)
win.mode ^= MODE_BLINK;
}
lastblink = now;
-
drawing = 1;
}
timeout = (maxlatency - TIMEDIFF(now, trigger)) \
@@ -2322,16 +2428,24 @@ config_init(void)
void
usage(void)
{
- die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
+ die("usage: %s [-aiv] [-b borderpx] [-c class] [-f font] [-g geometry]"
" [-n name] [-o file]\n"
" [-T title] [-t title] [-w windowid]"
" [[-e] command [args ...]]\n"
- " %s [-aiv] [-c class] [-f font] [-g geometry]"
+ " %s [-aiv] [-b borderpx] [-c class] [-f font] [-g geometry]"
" [-n name] [-o file]\n"
" [-T title] [-t title] [-w windowid] -l line"
" [stty_args ...]\n", argv0, argv0);
}
+void toggle_winmode(int flag) {
+ win.mode ^= flag;
+}
+
+void keyboard_select(const Arg *dummy) {
+ win.mode ^= trt_kbdselect(-1, NULL, 0);
+}
+
int
main(int argc, char *argv[])
{
@@ -2343,6 +2457,9 @@ main(int argc, char *argv[])
case 'a':
allowaltscreen = 0;
break;
+ case 'b':
+ borderpx = atoi(EARGF(usage()));
+ break;
case 'A':
opt_alpha = EARGF(usage());
break;
@@ -2359,6 +2476,12 @@ main(int argc, char *argv[])
case 'g':
xw.gm = XParseGeometry(EARGF(usage()),
&xw.l, &xw.t, &cols, &rows);
+ geometry = CellGeometry;
+ break;
+ case 'G':
+ xw.gm = XParseGeometry(EARGF(usage()),
+ &xw.l, &xw.t, &width, &height);
+ geometry = PixelGeometry;
break;
case 'i':
xw.isfixed = 1;
@@ -2400,12 +2523,20 @@ run:
die("Can't open display\n");
config_init();
+ switch (geometry) {
+ case CellGeometry:
+ xinit(cols, rows);
+ break;
+ case PixelGeometry:
+ xinit(width, height);
+ cols = (win.w - 2 * borderpx) / win.cw;
+ rows = (win.h - 2 * borderpx) / win.ch;
+ break;
+ }
cols = MAX(cols, 1);
rows = MAX(rows, 1);
defaultbg = MAX(LEN(colorname), 256);
- alphaUnfocus = alpha-alphaOffset;
tnew(cols, rows);
- xinit(cols, rows);
xsetenv();
selinit();
run();
@@ -2413,3 +2544,24 @@ run:
return 0;
}
+void
+opencopied(const Arg *arg)
+{
+ char * const clip = xsel.clipboard;
+ pid_t chpid;
+
+ if(!clip) {
+ fprintf(stderr, "Warning: nothing copied to clipboard\n");
+ return;
+ }
+
+ if ((chpid = fork()) == 0) {
+ if (fork() == 0)
+ execlp(arg->v, arg->v, clip, NULL);
+ exit(1);
+ }
+ if (chpid > 0)
+ waitpid(chpid, NULL, WNOHANG); // Non-blocking wait
+ return;
+}
+