~kris/suckless

dmenu

097f38a4bd85760e5cf322eb973d2f671673c289 — Kris Yotam 7 months ago ae38979
Migrate to dmenu-flexipatch

Enables preprocessor-based patch management with the following patches:
- alpha: Transparency support
- border: Window border
- caseinsensitive: Case-insensitive matching by default
- center: Centered dmenu window
- ctrl_v_to_paste: Ctrl+V paste support
- fuzzymatch: Fuzzy matching
- highlight: Highlight matched text
- line_height: Configurable line height
- mouse_support: Mouse interaction
- numbers: Show item count
- password: Password input mode
- rejectnomatch: Reject non-matching input
- xresources: Runtime color configuration

Custom settings:
- Font: JetBrainsMono Nerd Font:size=14
- Colors: #2a4055 fg, #9CC2EC bg, #DEC292 selection
- Center enabled by default
- Border width: 2px
A .gitignore => .gitignore +4 -0
@@ 0,0 1,4 @@
*.o
dmenu
stest
config.h

M Makefile => Makefile +4 -1
@@ 14,7 14,10 @@ all: dmenu stest
config.h:
	cp config.def.h $@

$(OBJ): arg.h config.h config.mk drw.h
patches.h:
	cp patches.def.h $@

$(OBJ): arg.h config.h config.mk drw.h patches.h

dmenu: dmenu.o drw.o util.o
	$(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS)

M README.md => README.md +109 -29
@@ 1,44 1,53 @@
# Kris's build of dmenu
# Kris's build of dmenu (flexipatch)

My build of [dmenu](https://tools.suckless.org/dmenu/), the dynamic menu for X.

Based on [BreadOnPenguins' dmenu](https://github.com/BreadOnPenguins/dmenu) with center and xresources patches.
This build is based on [dmenu-flexipatch](https://github.com/bakkeby/dmenu-flexipatch), which uses preprocessor directives to manage patches. This allows easy toggling of features without manual patching.

---

## Features
## How Patches Work (Preprocessor Directives)

Unlike traditional dmenu patching, this build uses C preprocessor directives to include/exclude patches at compile time. To enable or disable a patch, edit `patches.h` and set the value to `1` (enabled) or `0` (disabled):

```c
#define CENTER_PATCH 1      /* enabled */
#define VI_MODE_PATCH 0     /* disabled */
```

- **Center patch**: dmenu appears centered on screen (`-c` flag)
- **Vertical listing**: Show options in a vertical list (`-l` flag)
- **Xresources**: Read colors from `~/.Xresources` (pywal compatible)
- **Alpha**: Transparency support
- **Emoji**: Can view color characters like emoji
- **Password mode**: `-P` flag hides user input
- **Reject non-matching**: `-r` flag rejects input that doesn't match an option
- **Mouse clickable**: dmenu options are mouse clickable
Then rebuild:
```bash
sudo make clean install
```

All patch options are documented in `patches.h`.

---

## Patches
## Features

| Patch | Description |
|-------|-------------|
| [center](https://tools.suckless.org/dmenu/patches/center/) | Center dmenu on screen |
| [xresources](https://tools.suckless.org/dmenu/patches/xresources/) | Read colors and settings from ~/.Xresources |
| [alpha](https://tools.suckless.org/dmenu/patches/alpha/) | Transparency support |
| [password](https://tools.suckless.org/dmenu/patches/password/) | Hide input for password entry |
| mouse support | Click to select options |
| Feature | Description |
|---------|-------------|
| **Centered** | dmenu appears centered on screen (enabled by default) |
| **Fuzzy matching** | Type non-consecutive portions to match |
| **Highlight** | Matched characters are highlighted |
| **Mouse support** | Click to select options |
| **Password mode** | `-P` flag hides input with dots |
| **Transparency** | Alpha support with compositor |
| **Xresources** | Read colors from `~/.Xresources` (pywal compatible) |
| **Numbers** | Shows match count in top right |
| **Border** | Visible border when centered |

---

## Usage

```bash
# Run dmenu centered
dmenu_run -c
# Run dmenu (centered by default)
dmenu_run

# Vertical list centered
echo -e "option1\noption2\noption3" | dmenu -c -l 3
# Vertical list
echo -e "option1\noption2\noption3" | dmenu -l 5

# Password mode
echo -e "option1\noption2" | dmenu -P


@@ 47,33 56,104 @@ echo -e "option1\noption2" | dmenu -P
echo -e "yes\nno" | dmenu -r

# With prompt
dmenu -c -l 5 -p "Select:" < options.txt
dmenu -l 5 -p "Select:" < options.txt

# Case-sensitive (default is insensitive)
dmenu -s
```

---

## Enabled Patches

Configured in `patches.h`:

| Patch | Description |
|-------|-------------|
| alpha | Transparency support |
| border | Visible border around dmenu |
| caseinsensitive | Case-insensitive by default |
| center | Centered on screen |
| ctrl_v_to_paste | Ctrl+V paste support |
| fuzzymatch | Fuzzy matching |
| highlight | Highlight matched characters |
| line_height | Configurable line height |
| mouse_support | Click to select |
| numbers | Show match count |
| password | Hide input with -P |
| rejectnomatch | Reject non-matching input |
| xresources | Read from Xresources |

---

## Installation

```bash
git clone https://github.com/krisyotam/dmenu
cd dmenu
sudo make install
sudo make clean install
```

> Requires a compositor (`xcompmgr`, `picom`, etc.) for transparency.

---

## Credits
## Configuration

### Enabling/Disabling Patches

- [suckless.org](https://tools.suckless.org/dmenu/) - original dmenu
- [BreadOnPenguins](https://github.com/BreadOnPenguins/dmenu) - base build with center + xresources patches
Edit `patches.h` to toggle patches:

```c
#define FUZZYMATCH_PATCH 1   /* Enable fuzzy matching */
#define VI_MODE_PATCH 0      /* Disable vi mode */
```

### Customizing Settings

Edit `config.def.h` for:
- Font settings
- Colors
- Default behavior (centered, etc.)
- Border width

After editing, rebuild:
```bash
rm config.h && sudo make clean install
```

### Xresources

This build reads settings from `~/.Xresources`. Example:

```
dmenu.font: JetBrainsMono Nerd Font:size=14
dmenu.background: #9CC2EC
dmenu.foreground: #2a4055
dmenu.selbackground: #DEC292
dmenu.selforeground: #2a4055
```

Apply with:
```bash
xrdb merge ~/.Xresources
```

---

## Other Suckless Repos
## My Other Suckless Repos

- [dwm](https://github.com/krisyotam/dwm) - dynamic window manager
- [st](https://github.com/krisyotam/st) - simple terminal
- [dwmblocks](https://github.com/krisyotam/dwmblocks) - modular status bar
- [surf](https://github.com/krisyotam/surf) - simple web browser

---

## Credits

- Based on [dmenu-flexipatch](https://github.com/bakkeby/dmenu-flexipatch) by bakkeby
- [suckless.org](https://tools.suckless.org/dmenu/) for the original dmenu

---


M config.def.h => config.def.h +165 -9
@@ 1,26 1,182 @@
/* See LICENSE file for copyright and license details. */
/* Default settings; can be overriden by command line. */

static int topbar = 1;                      /* -b  option; if 0, dmenu appears at bottom     */
static int centered = 1;                    /* -c option; centers dmenu on screen */
static int min_width = 500;                    /* minimum width when centered */
static const float menu_height_ratio = 4.0f;  /* This is the ratio used in the original calculation */
static int topbar = 1;                      /* -b  option; if 0, dmenu appears at bottom */
#if ALPHA_PATCH
static int opacity = 1;                     /* -o  option; if 0, then alpha is disabled */
#endif // ALPHA_PATCH
#if CARET_WIDTH_PATCH
static int caret_width = 2;                 /* -cw option; set default caret width */
#endif // CARET_WIDTH_PATCH
#if FUZZYMATCH_PATCH
static int fuzzy = 1;                       /* -F  option; if 0, dmenu doesn't use fuzzy matching */
#endif // FUZZYMATCH_PATCH
#if INCREMENTAL_PATCH
static int incremental = 0;                 /* -r  option; if 1, outputs text each time a key is pressed */
#endif // INCREMENTAL_PATCH
#if INSTANT_PATCH
static int instant = 0;                     /* -n  option; if 1, selects matching item without the need to press enter */
#endif // INSTANT_PATCH
#if CENTER_PATCH
static int center = 1;                      /* -c  option; if 0, dmenu won't be centered on the screen */
static int min_width = 500;                 /* minimum width when centered */
#endif // CENTER_PATCH
#if BARPADDING_PATCH
static const int vertpad = 10;              /* vertical padding of bar */
static const int sidepad = 10;              /* horizontal padding of bar */
#endif // BARPADDING_PATCH
#if QUIET_PATCH
static int quiet = 0;                       /* -q  option; if 1, dmenu will not show any items if the search string is empty */
#endif // QUIET_PATCH
#if RESTRICT_RETURN_PATCH
static int restrict_return = 0;             /* -1 option; if 1, disables shift-return and ctrl-return */
#endif // RESTRICT_RETURN_PATCH
/* -fn option overrides fonts[0]; default X11 font or font set */
static const char *fonts[] = {
#if PANGO_PATCH
static char *font = "monospace 10";
#else
#if XRESOURCES_PATCH
static char *fonts[] =
#else
static const char *fonts[] =
#endif // XRESOURCES_PATCH
{
	"JetBrainsMono Nerd Font:size=14"
};
#endif // PANGO_PATCH
#if MANAGED_PATCH
static char *prompt            = NULL;      /* -p  option; prompt to the left of input field */
#else
static const char *prompt      = NULL;      /* -p  option; prompt to the left of input field */
static const char *colors[SchemeLast][2] = {
	/*     fg         bg       */
#endif // MANAGED_PATCH
#if DYNAMIC_OPTIONS_PATCH
static const char *dynamic     = NULL;      /* -dy option; dynamic command to run on input change */
#endif // DYNAMIC_OPTIONS_PATCH
#if SYMBOLS_PATCH
static const char *symbol_1 = "<";
static const char *symbol_2 = ">";
#endif // SYMBOLS_PATCH

#if ALPHA_PATCH
static const unsigned int baralpha = 0xd0;
static const unsigned int borderalpha = OPAQUE;
static const unsigned int alphas[][3]      = {
	/*               fg      bg        border     */
	[SchemeNorm] = { OPAQUE, baralpha, borderalpha },
	[SchemeSel]  = { OPAQUE, baralpha, borderalpha },
	#if BORDER_PATCH
	[SchemeBorder] = { OPAQUE, OPAQUE, OPAQUE },
	#endif // BORDER_PATCH
	#if MORECOLOR_PATCH
	[SchemeMid] = { OPAQUE, baralpha, borderalpha },
	#endif // MORECOLOR_PATCH
	#if HIGHLIGHT_PATCH
	[SchemeSelHighlight] = { OPAQUE, baralpha, borderalpha },
	[SchemeNormHighlight] = { OPAQUE, baralpha, borderalpha },
	#endif // HIGHLIGHT_PATCH
	#if HIGHPRIORITY_PATCH
	[SchemeHp] = { OPAQUE, baralpha, borderalpha },
	#endif // HIGHPRIORITY_PATCH
	#if EMOJI_HIGHLIGHT_PATCH
	[SchemeHover] = { OPAQUE, baralpha, borderalpha },
	[SchemeGreen] = { OPAQUE, baralpha, borderalpha },
	[SchemeRed] = { OPAQUE, baralpha, borderalpha },
	[SchemeYellow] = { OPAQUE, baralpha, borderalpha },
	[SchemeBlue] = { OPAQUE, baralpha, borderalpha },
	[SchemePurple] = { OPAQUE, baralpha, borderalpha },
	#endif // EMOJI_HIGHLIGHT_PATCH
	#if VI_MODE_PATCH
	[SchemeCursor] = { OPAQUE, baralpha, borderalpha },
	#endif // VI_MODE_PATCH
	#if CARET_SCHEME_PATCH
	[SchemeCaret] = { OPAQUE, baralpha, borderalpha },
	#endif // CARET_SCHEME_PATCH
};
#endif // ALPHA_PATCH

static
#if !XRESOURCES_PATCH
const
#endif // XRESOURCES_PATCH
char *colors[][2] = {
	/*               fg         bg       */
	[SchemeNorm] = { "#2a4055", "#9CC2EC" },
	[SchemeSel] = { "#2a4055", "#DEC292" },
	[SchemeOut] = { "#2a4055", "#90B0D2" },
	[SchemeSel]  = { "#2a4055", "#DEC292" },
	[SchemeOut]  = { "#2a4055", "#90B0D2" },
	#if BORDER_PATCH
	[SchemeBorder] = { "#2a4055", "#DEC292" },
	#endif // BORDER_PATCH
	#if MORECOLOR_PATCH
	[SchemeMid]  = { "#2a4055", "#B0C0D0" },
	#endif // MORECOLOR_PATCH
	#if HIGHLIGHT_PATCH
	[SchemeSelHighlight]  = { "#1a3045", "#DEC292" },
	[SchemeNormHighlight] = { "#1a3045", "#9CC2EC" },
	#endif // HIGHLIGHT_PATCH
	#if HIGHPRIORITY_PATCH
	[SchemeHp]   = { "#bbbbbb", "#333333" },
	#endif // HIGHPRIORITY_PATCH
	#if EMOJI_HIGHLIGHT_PATCH
	[SchemeHover]  = { "#ffffff", "#353D4B" },
	[SchemeGreen]  = { "#ffffff", "#52E067" },
	[SchemeRed]    = { "#ffffff", "#e05252" },
	[SchemeYellow] = { "#ffffff", "#e0c452" },
	[SchemeBlue]   = { "#ffffff", "#5280e0" },
	[SchemePurple] = { "#ffffff", "#9952e0" },
	#endif // EMOJI_HIGHLIGHT_PATCH
	#if VI_MODE_PATCH
	[SchemeCursor] = { "#222222", "#bbbbbb" },
	#endif // VI_MODE_PATCH
	#if CARET_SCHEME_PATCH
	[SchemeCaret] = { "#eeeeee", "#222222" },
	#endif // CARET_SCHEME_PATCH
};
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines      = 0;
#if GRID_PATCH
/* -g option; if nonzero, dmenu uses a grid comprised of columns and lines */
static unsigned int columns    = 0;
#endif // GRID_PATCH
#if LINE_HEIGHT_PATCH
static unsigned int lineheight = 0;         /* -h option; minimum height of a menu line     */
static unsigned int min_lineheight = 8;
#endif // LINE_HEIGHT_PATCH
#if NAVHISTORY_PATCH
static unsigned int maxhist    = 15;
static int histnodup           = 1;	/* if 0, record repeated histories */
#endif // NAVHISTORY_PATCH

/*
 * Characters not considered part of a word while deleting words
 * for example: " /?\"&[]"
 */
#if PIPEOUT_PATCH
static const char startpipe[] = "#";
#endif // PIPEOUT_PATCH
static const char worddelimiters[] = " ";

#if VI_MODE_PATCH
/*
 * -vi option; if nonzero, vi mode is always enabled and can be
 * accessed with the global_esc keysym + mod mask
 */
static unsigned int vi_mode = 1;
static unsigned int start_mode = 0;			/* mode to use when -vi is passed. 0 = insert mode, 1 = normal mode */
static Key global_esc = { XK_n, Mod1Mask };	/* escape key when vi mode is not enabled explicitly */
static Key quit_keys[] = {
	/* keysym	modifier */
	{ XK_q,		0 }
};
#endif // VI_MODE_PATCH

#if BORDER_PATCH
/* Size of the window border */
static unsigned int border_width = 2;
#endif // BORDER_PATCH

#if PREFIXCOMPLETION_PATCH
/*
 * Use prefix matching by default; can be inverted with the -x flag.
 */
static int use_prefix = 1;
#endif // PREFIXCOMPLETION_PATCH

M config.h => config.h +165 -9
@@ 1,26 1,182 @@
/* See LICENSE file for copyright and license details. */
/* Default settings; can be overriden by command line. */

static int topbar = 1;                      /* -b  option; if 0, dmenu appears at bottom     */
static int centered = 1;                    /* -c option; centers dmenu on screen */
static int min_width = 500;                    /* minimum width when centered */
static const float menu_height_ratio = 4.0f;  /* This is the ratio used in the original calculation */
static int topbar = 1;                      /* -b  option; if 0, dmenu appears at bottom */
#if ALPHA_PATCH
static int opacity = 1;                     /* -o  option; if 0, then alpha is disabled */
#endif // ALPHA_PATCH
#if CARET_WIDTH_PATCH
static int caret_width = 2;                 /* -cw option; set default caret width */
#endif // CARET_WIDTH_PATCH
#if FUZZYMATCH_PATCH
static int fuzzy = 1;                       /* -F  option; if 0, dmenu doesn't use fuzzy matching */
#endif // FUZZYMATCH_PATCH
#if INCREMENTAL_PATCH
static int incremental = 0;                 /* -r  option; if 1, outputs text each time a key is pressed */
#endif // INCREMENTAL_PATCH
#if INSTANT_PATCH
static int instant = 0;                     /* -n  option; if 1, selects matching item without the need to press enter */
#endif // INSTANT_PATCH
#if CENTER_PATCH
static int center = 1;                      /* -c  option; if 0, dmenu won't be centered on the screen */
static int min_width = 500;                 /* minimum width when centered */
#endif // CENTER_PATCH
#if BARPADDING_PATCH
static const int vertpad = 10;              /* vertical padding of bar */
static const int sidepad = 10;              /* horizontal padding of bar */
#endif // BARPADDING_PATCH
#if QUIET_PATCH
static int quiet = 0;                       /* -q  option; if 1, dmenu will not show any items if the search string is empty */
#endif // QUIET_PATCH
#if RESTRICT_RETURN_PATCH
static int restrict_return = 0;             /* -1 option; if 1, disables shift-return and ctrl-return */
#endif // RESTRICT_RETURN_PATCH
/* -fn option overrides fonts[0]; default X11 font or font set */
static const char *fonts[] = {
#if PANGO_PATCH
static char *font = "monospace 10";
#else
#if XRESOURCES_PATCH
static char *fonts[] =
#else
static const char *fonts[] =
#endif // XRESOURCES_PATCH
{
	"JetBrainsMono Nerd Font:size=14"
};
#endif // PANGO_PATCH
#if MANAGED_PATCH
static char *prompt            = NULL;      /* -p  option; prompt to the left of input field */
#else
static const char *prompt      = NULL;      /* -p  option; prompt to the left of input field */
static const char *colors[SchemeLast][2] = {
	/*     fg         bg       */
#endif // MANAGED_PATCH
#if DYNAMIC_OPTIONS_PATCH
static const char *dynamic     = NULL;      /* -dy option; dynamic command to run on input change */
#endif // DYNAMIC_OPTIONS_PATCH
#if SYMBOLS_PATCH
static const char *symbol_1 = "<";
static const char *symbol_2 = ">";
#endif // SYMBOLS_PATCH

#if ALPHA_PATCH
static const unsigned int baralpha = 0xd0;
static const unsigned int borderalpha = OPAQUE;
static const unsigned int alphas[][3]      = {
	/*               fg      bg        border     */
	[SchemeNorm] = { OPAQUE, baralpha, borderalpha },
	[SchemeSel]  = { OPAQUE, baralpha, borderalpha },
	#if BORDER_PATCH
	[SchemeBorder] = { OPAQUE, OPAQUE, OPAQUE },
	#endif // BORDER_PATCH
	#if MORECOLOR_PATCH
	[SchemeMid] = { OPAQUE, baralpha, borderalpha },
	#endif // MORECOLOR_PATCH
	#if HIGHLIGHT_PATCH
	[SchemeSelHighlight] = { OPAQUE, baralpha, borderalpha },
	[SchemeNormHighlight] = { OPAQUE, baralpha, borderalpha },
	#endif // HIGHLIGHT_PATCH
	#if HIGHPRIORITY_PATCH
	[SchemeHp] = { OPAQUE, baralpha, borderalpha },
	#endif // HIGHPRIORITY_PATCH
	#if EMOJI_HIGHLIGHT_PATCH
	[SchemeHover] = { OPAQUE, baralpha, borderalpha },
	[SchemeGreen] = { OPAQUE, baralpha, borderalpha },
	[SchemeRed] = { OPAQUE, baralpha, borderalpha },
	[SchemeYellow] = { OPAQUE, baralpha, borderalpha },
	[SchemeBlue] = { OPAQUE, baralpha, borderalpha },
	[SchemePurple] = { OPAQUE, baralpha, borderalpha },
	#endif // EMOJI_HIGHLIGHT_PATCH
	#if VI_MODE_PATCH
	[SchemeCursor] = { OPAQUE, baralpha, borderalpha },
	#endif // VI_MODE_PATCH
	#if CARET_SCHEME_PATCH
	[SchemeCaret] = { OPAQUE, baralpha, borderalpha },
	#endif // CARET_SCHEME_PATCH
};
#endif // ALPHA_PATCH

static
#if !XRESOURCES_PATCH
const
#endif // XRESOURCES_PATCH
char *colors[][2] = {
	/*               fg         bg       */
	[SchemeNorm] = { "#2a4055", "#9CC2EC" },
	[SchemeSel] = { "#2a4055", "#DEC292" },
	[SchemeOut] = { "#2a4055", "#90B0D2" },
	[SchemeSel]  = { "#2a4055", "#DEC292" },
	[SchemeOut]  = { "#2a4055", "#90B0D2" },
	#if BORDER_PATCH
	[SchemeBorder] = { "#2a4055", "#DEC292" },
	#endif // BORDER_PATCH
	#if MORECOLOR_PATCH
	[SchemeMid]  = { "#2a4055", "#B0C0D0" },
	#endif // MORECOLOR_PATCH
	#if HIGHLIGHT_PATCH
	[SchemeSelHighlight]  = { "#1a3045", "#DEC292" },
	[SchemeNormHighlight] = { "#1a3045", "#9CC2EC" },
	#endif // HIGHLIGHT_PATCH
	#if HIGHPRIORITY_PATCH
	[SchemeHp]   = { "#bbbbbb", "#333333" },
	#endif // HIGHPRIORITY_PATCH
	#if EMOJI_HIGHLIGHT_PATCH
	[SchemeHover]  = { "#ffffff", "#353D4B" },
	[SchemeGreen]  = { "#ffffff", "#52E067" },
	[SchemeRed]    = { "#ffffff", "#e05252" },
	[SchemeYellow] = { "#ffffff", "#e0c452" },
	[SchemeBlue]   = { "#ffffff", "#5280e0" },
	[SchemePurple] = { "#ffffff", "#9952e0" },
	#endif // EMOJI_HIGHLIGHT_PATCH
	#if VI_MODE_PATCH
	[SchemeCursor] = { "#222222", "#bbbbbb" },
	#endif // VI_MODE_PATCH
	#if CARET_SCHEME_PATCH
	[SchemeCaret] = { "#eeeeee", "#222222" },
	#endif // CARET_SCHEME_PATCH
};
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines      = 0;
#if GRID_PATCH
/* -g option; if nonzero, dmenu uses a grid comprised of columns and lines */
static unsigned int columns    = 0;
#endif // GRID_PATCH
#if LINE_HEIGHT_PATCH
static unsigned int lineheight = 0;         /* -h option; minimum height of a menu line     */
static unsigned int min_lineheight = 8;
#endif // LINE_HEIGHT_PATCH
#if NAVHISTORY_PATCH
static unsigned int maxhist    = 15;
static int histnodup           = 1;	/* if 0, record repeated histories */
#endif // NAVHISTORY_PATCH

/*
 * Characters not considered part of a word while deleting words
 * for example: " /?\"&[]"
 */
#if PIPEOUT_PATCH
static const char startpipe[] = "#";
#endif // PIPEOUT_PATCH
static const char worddelimiters[] = " ";

#if VI_MODE_PATCH
/*
 * -vi option; if nonzero, vi mode is always enabled and can be
 * accessed with the global_esc keysym + mod mask
 */
static unsigned int vi_mode = 1;
static unsigned int start_mode = 0;			/* mode to use when -vi is passed. 0 = insert mode, 1 = normal mode */
static Key global_esc = { XK_n, Mod1Mask };	/* escape key when vi mode is not enabled explicitly */
static Key quit_keys[] = {
	/* keysym	modifier */
	{ XK_q,		0 }
};
#endif // VI_MODE_PATCH

#if BORDER_PATCH
/* Size of the window border */
static unsigned int border_width = 2;
#endif // BORDER_PATCH

#if PREFIXCOMPLETION_PATCH
/*
 * Use prefix matching by default; can be inverted with the -x flag.
 */
static int use_prefix = 1;
#endif // PREFIXCOMPLETION_PATCH

M config.mk => config.mk +17 -3
@@ 19,12 19,26 @@ FREETYPEINC = /usr/include/freetype2
#FREETYPEINC = $(X11INC)/freetype2
#MANPREFIX = ${PREFIX}/man

# Uncomment on RHEL for strcasecmp
#EXTRAFLAGS=-D_GNU_SOURCE

# Alpha patch / ALPHA_PATCH
XRENDER = -lXrender

# Uncomment for the bidi patch / BIDI_PATCH
#BIDILIBS = `pkg-config --libs fribidi`
#BIDIINC = `pkg-config --cflags fribidi`

# Uncomment for the pango patch / PANGO_PATCH
#PANGOINC = `pkg-config --cflags xft pango pangoxft`
#PANGOLIB = `pkg-config --libs xft pango pangoxft`

# includes and libs
INCS = -I$(X11INC) -I$(FREETYPEINC)
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
INCS = -I$(X11INC) -I$(FREETYPEINC) $(PANGOINC) $(BIDIINC)
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm $(XRENDER) $(PANGOLIB) $(BIDILIBS)

# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) $(EXTRAFLAGS)
CFLAGS   = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS)
LDFLAGS  = $(LIBS)


M dmenu.1 => dmenu.1 +10 -3
@@ 20,6 20,10 @@ dmenu \- dynamic menu
.IR color ]
.RB [ \-sf
.IR color ]
.RB [ \-ob
.IR color ]
.RB [ \-of
.IR color ]
.RB [ \-w
.IR windowid ]
.P


@@ 40,9 44,6 @@ which lists programs in the user's $PATH and runs the result in their $SHELL.
.B \-b
dmenu appears at the bottom of the screen.
.TP
.B \-c
dmenu appears centered on the screen.
.TP
.B \-f
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
is faster, but will lock up X until stdin reaches end\-of\-file.


@@ 78,6 79,12 @@ defines the selected background color.
.BI \-sf " color"
defines the selected foreground color.
.TP
.BI \-ob " color"
defines the outline background color (for multiple selection).
.TP
.BI \-of " color"
defines the outline foreground color (for multiple selection).
.TP
.B \-v
prints version information to stdout, then exits.
.TP

M dmenu.c => dmenu.c +1703 -133
@@ 15,54 15,175 @@
#include <X11/extensions/Xinerama.h>
#endif
#include <X11/Xft/Xft.h>
#include <X11/Xresource.h>

#include "patches.h"
/* Patch incompatibility overrides */
#if MULTI_SELECTION_PATCH
#undef NON_BLOCKING_STDIN_PATCH
#undef PIPEOUT_PATCH
#undef PRINTINPUTTEXT_PATCH
#endif // MULTI_SELECTION_PATCH

#include "drw.h"
#include "util.h"
#if GRIDNAV_PATCH
#include <stdbool.h>
#endif // GRIDNAV_PATCH

/* macros */
#define INTERSECT(x,y,w,h,r)  (MAX(0, MIN((x)+(w),(r).x_org+(r).width)  - MAX((x),(r).x_org)) \
                             * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
#if PANGO_PATCH
#define TEXTW(X)              (drw_font_getwidth(drw, (X), False) + lrpad)
#define TEXTWM(X)             (drw_font_getwidth(drw, (X), True) + lrpad)
#else
#define TEXTW(X)              (drw_fontset_getwidth(drw, (X)) + lrpad)
#endif // PANGO_PATCH
#if ALPHA_PATCH
#define OPAQUE                0xffU
#define OPACITY               "_NET_WM_WINDOW_OPACITY"
#endif // ALPHA_PATCH

/* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
enum {
	SchemeNorm,
	SchemeSel,
	SchemeOut,
	#if BORDER_PATCH
	SchemeBorder,
	#endif // BORDER_PATCH
	#if MORECOLOR_PATCH
	SchemeMid,
	#endif // MORECOLOR_PATCH
	#if HIGHLIGHT_PATCH
	SchemeNormHighlight,
	SchemeSelHighlight,
	#endif // HIGHLIGHT_PATCH
	#if HIGHPRIORITY_PATCH
	SchemeHp,
	#endif // HIGHPRIORITY_PATCH
	#if EMOJI_HIGHLIGHT_PATCH
	SchemeHover,
	SchemeGreen,
	SchemeYellow,
	SchemeBlue,
	SchemePurple,
	SchemeRed,
	#endif // EMOJI_HIGHLIGHT_PATCH
	#if VI_MODE_PATCH
	SchemeCursor,
	#endif // VI_MODE_PATCH
	#if CARET_SCHEME_PATCH
	SchemeCaret,
	#endif // CARET_SCHEME_PATCH
	SchemeLast,
}; /* color schemes */

struct item {
	char *text;
	unsigned int width;
	#if SEPARATOR_PATCH
	char *text_output;
	#elif TSV_PATCH
	char *stext;
	#endif // SEPARATOR_PATCH | TSV_PATCH
	struct item *left, *right;
	#if NON_BLOCKING_STDIN_PATCH
	struct item *next;
	#endif // NON_BLOCKING_STDIN_PATCH
	#if MULTI_SELECTION_PATCH
	int id; /* for multiselect */
	#else
	int out;
	#endif // MULTI_SELECTION_PATCH
	#if HIGHPRIORITY_PATCH
	int hp;
	#endif // HIGHPRIORITY_PATCH
	#if FUZZYMATCH_PATCH
	double distance;
	#endif // FUZZYMATCH_PATCH
	#if PRINTINDEX_PATCH
	int index;
	#endif // PRINTINDEX_PATCH
};

static char text[BUFSIZ] = "";
#if PIPEOUT_PATCH
static char pipeout[8] = " | dmenu";
#endif // PIPEOUT_PATCH
static char *embed;
#if SEPARATOR_PATCH
static char separator;
static char * (*sepchr)(const char *, int);
static int separator_reverse;
#endif // SEPARATOR_PATCH
static int bh, mw, mh;
#if XYW_PATCH
static int dmx = 0, dmy = 0; /* put dmenu at these x and y offsets */
static unsigned int dmw = 0; /* make dmenu this wide */
#endif // XYW_PATCH
static int inputw = 0, promptw;
#if PASSWORD_PATCH
static int passwd = 0;
#endif // PASSWORD_PATCH
static int lrpad; /* sum of left and right padding */
#if BARPADDING_PATCH
static int vp; /* vertical padding for bar */
static int sp; /* side padding for bar */
#endif // BARPADDING_PATCH
#if REJECTNOMATCH_PATCH
static int reject_no_match = 0;
#endif // REJECTNOMATCH_PATCH
static size_t cursor;
static struct item *items = NULL;
static struct item *matches, *matchend;
static struct item *prev, *curr, *next, *sel;
static int mon = -1, screen;
#if PRINTINDEX_PATCH
static int print_index = 0;
#endif // PRINTINDEX_PATCH
#if MANAGED_PATCH
static int managed = 0;
#endif // MANAGED_PATCH
#if MULTI_SELECTION_PATCH
static int *selid = NULL;
static unsigned int selidsize = 0;
#endif // MULTI_SELECTION_PATCH
#if NO_SORT_PATCH
static unsigned int sortmatches = 1;
#endif // NO_SORT_PATCH
#if PRINTINPUTTEXT_PATCH
static int use_text_input = 0;
#endif // PRINTINPUTTEXT_PATCH
#if PRESELECT_PATCH
static unsigned int preselected = 0;
#endif // PRESELECT_PATCH
#if EMOJI_HIGHLIGHT_PATCH
static int commented = 0;
static int animated = 0;
#endif // EMOJI_HIGHLIGHT_PATCH

static Atom clip, utf8;
#if WMTYPE_PATCH
static Atom type, dock;
#endif // WMTYPE_PATCH
static Display *dpy;
static Window root, parentwin, win;
static XIC xic;

#if ALPHA_PATCH
static int useargb = 0;
static Visual *visual;
static int depth;
static Colormap cmap;
#endif // ALPHA_PATCH

static Drw *drw;
static Clr *scheme[SchemeLast];

/* Temporary arrays to allow overriding xresources values */
static char *colortemp[4];
static char *tempfonts;
#include "patch/include.h"

#include "config.h"

static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;

static unsigned int
textw_clamp(const char *str, unsigned int n)
{


@@ 70,6 191,41 @@ textw_clamp(const char *str, unsigned int n)
	return MIN(w, n);
}

static void appenditem(struct item *item, struct item **list, struct item **last);
static void calcoffsets(void);
static void cleanup(void);
static char * cistrstr(const char *s, const char *sub);
static int drawitem(struct item *item, int x, int y, int w);
static void drawmenu(void);
static void grabfocus(void);
static void grabkeyboard(void);
static void match(void);
static void insert(const char *str, ssize_t n);
static size_t nextrune(int inc);
static void movewordedge(int dir);
static void keypress(XKeyEvent *ev);
static void paste(void);
static void printitem(struct item *item);
static void printtext(char *text);
static void printcurrent(unsigned int state);
#if ALPHA_PATCH
static void xinitvisual(void);
#endif // ALPHA_PATCH
static void readstdin(void);
static void run(void);
static void setup(void);
static void usage(void);

#if CASEINSENSITIVE_PATCH
static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
static char *(*fstrstr)(const char *, const char *) = cistrstr;
#else
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;
#endif // CASEINSENSITIVE_PATCH

#include "patch/include.c"

static void
appenditem(struct item *item, struct item **list, struct item **last)
{


@@ 86,12 242,27 @@ appenditem(struct item *item, struct item **list, struct item **last)
static void
calcoffsets(void)
{
	int i, n;
	int i, n, rpad = 0;

	if (lines > 0)
	if (lines > 0) {
		#if GRID_PATCH
		if (columns)
			n = lines * columns * bh;
		else
			n = lines * bh;
		#else
		n = lines * bh;
	else
		n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
		#endif // GRID_PATCH
	} else {
		#if NUMBERS_PATCH
		rpad = TEXTW(numbers);
		#endif // NUMBERS_PATCH
		#if SYMBOLS_PATCH
		n = mw - (promptw + inputw + TEXTW(symbol_1) + TEXTW(symbol_2) + rpad);
		#else
		n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">") + rpad);
		#endif // SYMBOLS_PATCH
	}
	/* calculate which items will begin the next page and previous page */
	for (i = 0, next = curr; next; next = next->right)
		if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n)


@@ 101,109 272,536 @@ calcoffsets(void)
			break;
}

static int
max_textw(void)
{
	int len = 0;
	for (struct item *item = items; item && item->text; item++)
		len = MAX(item->width, len);
	return len;
}

static void
cleanup(void)
{
	size_t i;

	XUngrabKeyboard(dpy, CurrentTime);
	#if INPUTMETHOD_PATCH
	XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
	#endif // INPUTMETHOD_PATCH
	for (i = 0; i < SchemeLast; i++)
		drw_scm_free(drw, scheme[i], 2);
	for (i = 0; items && items[i].text; ++i)

	#if NAVHISTORY_PATCH
	savehistory();
	cleanhistory();
	restorebackupitems();
	#endif // NAVHISTORY_PATCH

	for (i = 0; items && items[i].text; ++i) {
		#if SEPARATOR_PATCH
		free(separator_reverse ? items[i].text_output : items[i].text);
		#else
		free(items[i].text);
		#endif // SEPARATOR_PATCH
	}
	free(items);

	#if HIGHPRIORITY_PATCH
	for (i = 0; i < hplength; ++i)
		free(hpitems[i]);
	free(hpitems);
	#endif // HIGHPRIORITY_PATCH
	drw_free(drw);
	XSync(dpy, False);
	XCloseDisplay(dpy);
	#if MULTI_SELECTION_PATCH
	free(selid);
	#endif // MULTI_SELECTION_PATCH
}

static char *
cistrstr(const char *h, const char *n)
cistrstr(const char *s, const char *sub)
{
	size_t i;

	if (!n[0])
		return (char *)h;
	size_t len;

	for (; *h; ++h) {
		for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
		            tolower((unsigned char)h[i]); ++i)
			;
		if (n[i] == '\0')
			return (char *)h;
	}
	for (len = strlen(sub); *s; s++)
		if (!strncasecmp(s, sub, len))
			return (char *)s;
	return NULL;
}

static int
drawitem(struct item *item, int x, int y, int w)
{
	int r;
	#if TSV_PATCH && !SEPARATOR_PATCH
	char *text = item->stext;
	#else
	char *text = item->text;
	#endif // TSV_PATCH

	#if EMOJI_HIGHLIGHT_PATCH
	int iscomment = 0;
	if (text[0] == '>') {
		if (text[1] == '>') {
			iscomment = 3;
			switch (text[2]) {
			case 'r':
				drw_setscheme(drw, scheme[SchemeRed]);
				break;
			case 'g':
				drw_setscheme(drw, scheme[SchemeGreen]);
				break;
			case 'y':
				drw_setscheme(drw, scheme[SchemeYellow]);
				break;
			case 'b':
				drw_setscheme(drw, scheme[SchemeBlue]);
				break;
			case 'p':
				drw_setscheme(drw, scheme[SchemePurple]);
				break;
			#if HIGHLIGHT_PATCH
			case 'h':
				drw_setscheme(drw, scheme[SchemeNormHighlight]);
				break;
			#endif // HIGHLIGHT_PATCH
			case 's':
				drw_setscheme(drw, scheme[SchemeSel]);
				break;
			default:
				iscomment = 1;
				drw_setscheme(drw, scheme[SchemeNorm]);
			break;
			}
		} else {
			drw_setscheme(drw, scheme[SchemeNorm]);
			iscomment = 1;
		}
	} else if (text[0] == ':') {
		iscomment = 2;
		if (item == sel) {
			switch (text[1]) {
			case 'r':
				drw_setscheme(drw, scheme[SchemeRed]);
				break;
			case 'g':
				drw_setscheme(drw, scheme[SchemeGreen]);
				break;
			case 'y':
				drw_setscheme(drw, scheme[SchemeYellow]);
				break;
			case 'b':
				drw_setscheme(drw, scheme[SchemeBlue]);
				break;
			case 'p':
				drw_setscheme(drw, scheme[SchemePurple]);
				break;
			#if HIGHLIGHT_PATCH
			case 'h':
				drw_setscheme(drw, scheme[SchemeNormHighlight]);
				break;
			#endif // HIGHLIGHT_PATCH
			case 's':
				drw_setscheme(drw, scheme[SchemeSel]);
				break;
			default:
				drw_setscheme(drw, scheme[SchemeSel]);
				iscomment = 0;
				break;
			}
		} else {
			drw_setscheme(drw, scheme[SchemeNorm]);
		}
	}
	#endif // EMOJI_HIGHLIGHT_PATCH

	#if EMOJI_HIGHLIGHT_PATCH
	int temppadding = 0;
	if (iscomment == 2) {
		if (text[2] == ' ') {
			#if PANGO_PATCH
			temppadding = drw->font->h * 3;
			#else
			temppadding = drw->fonts->h * 3;
			#endif // PANGO_PATCH
			animated = 1;
			char dest[1000];
			strcpy(dest, text);
			dest[6] = '\0';
			drw_text(drw, x, y
				, temppadding
				#if LINE_HEIGHT_PATCH
				, MAX(lineheight, bh)
				#else
				, bh
				#endif // LINE_HEIGHT_PATCH
				, temppadding / 2.6
				, dest + 3
				, 0
				#if PANGO_PATCH
				, True
				#endif // PANGO_PATCH
			);
			iscomment = 6;
			drw_setscheme(drw, sel == item ? scheme[SchemeHover] : scheme[SchemeNorm]);
		}
	}

	char *output;
	if (commented) {
		static char onestr[2];
		onestr[0] = text[0];
		onestr[1] = '\0';
		output = onestr;
	} else {
		output = text;
	}
	#endif // EMOJI_HIGHLIGHT_PATCH

	if (item == sel)
		drw_setscheme(drw, scheme[SchemeSel]);
	#if HIGHPRIORITY_PATCH
	else if (item->hp)
		drw_setscheme(drw, scheme[SchemeHp]);
	#endif // HIGHPRIORITY_PATCH
	#if MORECOLOR_PATCH
	else if (item->left == sel || item->right == sel)
		drw_setscheme(drw, scheme[SchemeMid]);
	#endif // MORECOLOR_PATCH
	#if MULTI_SELECTION_PATCH
	else if (issel(item->id))
	#else
	else if (item->out)
	#endif // MULTI_SELECTION_PATCH
		drw_setscheme(drw, scheme[SchemeOut]);
	else
		drw_setscheme(drw, scheme[SchemeNorm]);

	return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
	r = drw_text(drw
		#if EMOJI_HIGHLIGHT_PATCH
		, x + ((iscomment == 6) ? temppadding : 0)
		#else
		, x
		#endif // EMOJI_HIGHLIGHT_PATCH
		, y
		, w
		, bh
		#if EMOJI_HIGHLIGHT_PATCH
		, commented ? (bh - TEXTW(output) - lrpad) / 2 : lrpad / 2
		#else
		, lrpad / 2
		#endif // EMOJI_HIGHLIGHT_PATCH
		#if EMOJI_HIGHLIGHT_PATCH
		, output + iscomment
		#else
		, text
		#endif // EMOJI_HIGHLIGHT_PATCH
		, 0
		#if PANGO_PATCH
		, True
		#endif // PANGO_PATCH
		);
	#if HIGHLIGHT_PATCH
	#if EMOJI_HIGHLIGHT_PATCH
	drawhighlights(item, output + iscomment, x + ((iscomment == 6) ? temppadding : 0), y, w);
	#else
	drawhighlights(item, x, y, w);
	#endif // EMOJI_HIGHLIGHT_PATCH
	#endif // HIGHLIGHT_PATCH
	return r;
}

static void
drawmenu(void)
{
	#if SCROLL_PATCH
	static int curpos, oldcurlen;
	int curlen, rcurlen;
	#else
	unsigned int curpos;
	#endif // SCROLL_PATCH
	struct item *item;
	int x = 0, y = 0, w;
	int x = 0, y = 0, w, rpad = 0, itw = 0, stw = 0;
	#if LINE_HEIGHT_PATCH && PANGO_PATCH
	int fh = drw->font->h;
	#elif LINE_HEIGHT_PATCH
	int fh = drw->fonts->h;
	#endif // LINE_HEIGHT_PATCH
	#if PASSWORD_PATCH
	char *censort;
	#endif // PASSWORD_PATCH

	drw_setscheme(drw, scheme[SchemeNorm]);
	drw_rect(drw, 0, 0, mw, mh, 1, 1);

	if (prompt && *prompt) {
		#if !PLAIN_PROMPT_PATCH
		drw_setscheme(drw, scheme[SchemeSel]);
		x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
		#endif // PLAIN_PROMPT_PATCH
		x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0
			#if PANGO_PATCH
			, True
			#endif // PANGO_PATCH
		);
	}
	/* draw input field */
	w = (lines > 0 || !matches) ? mw - x : inputw;

	#if SCROLL_PATCH
	w -= lrpad / 2;
	x += lrpad / 2;
	rcurlen = TEXTW(text + cursor) - lrpad;
	curlen = TEXTW(text) - lrpad - rcurlen;
	curpos += curlen - oldcurlen;
	curpos = MIN(w, MAX(0, curpos));
	curpos = MAX(curpos, w - rcurlen);
	curpos = MIN(curpos, curlen);
	oldcurlen = curlen;

	drw_setscheme(drw, scheme[SchemeNorm]);
	drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
	#if PASSWORD_PATCH
	if (passwd) {
		censort = ecalloc(1, sizeof(text));
		memset(censort, '.', strlen(text));
		drw_text_align(drw, x, 0, curpos, bh, censort, cursor, AlignR);
		drw_text_align(drw, x + curpos, 0, w - curpos, bh, censort + cursor, strlen(censort) - cursor, AlignL);
		free(censort);
	} else {
		drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR);
		drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL);
	}
	#else
	drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR);
	drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL);
	#endif // PASSWORD_PATCH

	#if VI_MODE_PATCH
	if (using_vi_mode && text[0] != '\0') {
		drw_setscheme(drw, scheme[SchemeCursor]);
		char vi_char[] = {text[cursor], '\0'};
		drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh, 0, vi_char, 0
			#if PANGO_PATCH
			, False
			#endif // PANGO_PATCH
		);
	} else if (using_vi_mode) {
		drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0);
	} else
	#endif // VI_MODE_PATCH
	#if LINE_HEIGHT_PATCH
	drw_rect(drw, x + curpos - 1, 2 + (bh-fh)/2, 2, fh - 4, 1, 0);
	#else
	drw_rect(drw, x + curpos - 1, 2, 2, bh - 4, 1, 0);
	#endif // LINE_HEIGHT_PATCH
	#else // !SCROLL_PATCH
	drw_setscheme(drw, scheme[SchemeNorm]);
	#if PASSWORD_PATCH
	if (passwd) {
		censort = ecalloc(1, sizeof(text));
		memset(censort, '.', strlen(text));
		drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0
			#if PANGO_PATCH
			, False
			#endif // PANGO_PATCH
		);
		drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0
			#if PANGO_PATCH
			, False
			#endif // PANGO_PATCH
		);
		free(censort);
	} else {
		drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0
			#if PANGO_PATCH
			, False
			#endif // PANGO_PATCH
		);
	}
	#else
	drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0
		#if PANGO_PATCH
		, False
		#endif // PANGO_PATCH
	);
	#endif // PASSWORD_PATCH

	curpos = TEXTW(text) - TEXTW(&text[cursor]);
	if ((curpos += lrpad / 2 - 1) < w) {
	curpos += lrpad / 2 - 1;

	#if VI_MODE_PATCH
	if (using_vi_mode && text[0] != '\0') {
		drw_setscheme(drw, scheme[SchemeCursor]);
		char vi_char[] = {text[cursor], '\0'};
		drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh, 0, vi_char, 0
			#if PANGO_PATCH
			, False
			#endif // PANGO_PATCH
		);
	} else if (using_vi_mode) {
		drw_setscheme(drw, scheme[SchemeNorm]);
		drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0);
	} else
	#endif // VI_MODE_PATCH
	if (curpos < w) {
		#if CARET_SCHEME_PATCH
		drw_setscheme(drw, scheme[SchemeCaret]);
		#else
		drw_setscheme(drw, scheme[SchemeNorm]);
		#endif // CARET_SCHEME_PATCH
		#if CARET_WIDTH_PATCH && LINE_HEIGHT_PATCH
		drw_rect(drw, x + curpos, 2 + (bh-fh)/2, caret_width, fh - 4, 1, 0);
		#elif CARET_WIDTH_PATCH
		drw_rect(drw, x + curpos, 2, caret_width, bh - 4, 1, 0);
		#elif LINE_HEIGHT_PATCH
		drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0);
		#else
		drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
		#endif // LINE_HEIGHT_PATCH
	}
	#endif // SCROLL_PATCH

	#if NUMBERS_PATCH
	recalculatenumbers();
	rpad = TEXTW(numbers);
	#if BARPADDING_PATCH
	rpad += 2 * sp;
	#endif // BARPADDING_PATCH
	#if BORDER_PATCH
	rpad += border_width;
	#endif // BORDER_PATCH
	#endif // NUMBERS_PATCH

	#if QUIET_PATCH
	if (quiet && strlen(text) == 0) {
		#if DYNAMIC_HEIGHT_PATCH
		if (lines > 0)
			XResizeWindow(dpy, win, mw, bh);
		#endif // DYNAMIC_HEIGHT_PATCH
		goto skip_item_listing;
	}
	#endif // QUIET_PATCH

	if (lines > 0) {
		#if DYNAMIC_HEIGHT_PATCH || GRID_PATCH
		int i = 0;
		#endif // DYNAMIC_HEIGHT_PATCH | GRID_PATCH

		#if GRID_PATCH
		/* draw grid */
		for (item = curr; item != next; item = item->right, i++) {
			if (columns) {
				#if VERTFULL_PATCH
				drawitem(
					item,
					0 + ((i / lines) *  (mw / columns)),
					y + (((i % lines) + 1) * bh),
					mw / columns
				);
				#else
				drawitem(
					item,
					x + ((i / lines) *  ((mw - x) / columns)),
					y + (((i % lines) + 1) * bh),
					(mw - x) / columns
				);
				#endif // VERTFULL_PATCH
			} else {
				#if VERTFULL_PATCH
				drawitem(item, 0, y += bh, mw);
				#else
				drawitem(item, x, y += bh, mw - x);
				#endif // VERTFULL_PATCH
			}
		}
		#if DYNAMIC_HEIGHT_PATCH
		if (columns) {
			XResizeWindow(dpy, win, mw, (MIN(i, lines) + 1) * bh);
		} else {
			XResizeWindow(dpy, win, mw, (i + 1) * bh);
		}
		#endif // DYNAMIC_HEIGHT_PATCH

		#else
		/* draw vertical list */
		for (item = curr; item != next; item = item->right)
		for (item = curr; item != next; item = item->right) {
			#if DYNAMIC_HEIGHT_PATCH
			i++;
			#endif // DYNAMIC_HEIGHT_PATCH
			#if VERTFULL_PATCH
			drawitem(item, 0, y += bh, mw);
			#else
			drawitem(item, x, y += bh, mw - x);
			#endif // VERTFULL_PATCH
		}
		#if DYNAMIC_HEIGHT_PATCH
		XResizeWindow(dpy, win, mw, (i + 1) * bh);
		#endif // DYNAMIC_HEIGHT_PATCH
		#endif // GRID_PATCH
	} else if (matches) {
		/* draw horizontal list */
		x += inputw;
		#if SYMBOLS_PATCH
		w = TEXTW(symbol_1);
		#else
		w = TEXTW("<");
		#endif // SYMBOLS_PATCH
		if (curr->left) {
			drw_setscheme(drw, scheme[SchemeNorm]);
			drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
			#if SYMBOLS_PATCH
			drw_text(drw, x, 0, w, bh, lrpad / 2, symbol_1, 0
			#else
			drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0
			#endif // SYMBOLS_PATCH
				#if PANGO_PATCH
				, True
				#endif // PANGO_PATCH
			);
		}
		x += w;
		for (item = curr; item != next; item = item->right)
			x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
		for (item = curr; item != next; item = item->right) {
			#if SYMBOLS_PATCH
			stw = TEXTW(symbol_2);
			#else
			stw = TEXTW(">");
			#endif // SYMBOLS_PATCH
			#if TSV_PATCH && !SEPARATOR_PATCH
			itw = textw_clamp(item->stext, mw - x - stw - rpad);
			#else
			itw = textw_clamp(item->text, mw - x - stw - rpad);
			#endif // TSV_PATCH
			x = drawitem(item, x, 0, itw);
		}
		if (next) {
			#if SYMBOLS_PATCH
			w = TEXTW(symbol_2);
			#else
			w = TEXTW(">");
			#endif // SYMBOLS_PATCH
			drw_setscheme(drw, scheme[SchemeNorm]);
			drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
			drw_text(drw, mw - w - rpad, 0, w, bh, lrpad / 2
				#if SYMBOLS_PATCH
				, symbol_2
				#else
				, ">"
				#endif // SYMBOLS_PATCH
				, 0
				#if PANGO_PATCH
				, True
				#endif // PANGO_PATCH
			);
		}
	}

	#if QUIET_PATCH
skip_item_listing:
	#endif // QUIET_PATCH

	#if NUMBERS_PATCH
	drw_setscheme(drw, scheme[SchemeNorm]);
	#if PANGO_PATCH
	drw_text(drw, mw - rpad, 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0, False);
	#else
	drw_text(drw, mw - rpad, 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0);
	#endif // PANGO_PATCH
	#endif // NUMBERS_PATCH
	drw_map(drw, win, 0, 0, mw, mh);
	#if NON_BLOCKING_STDIN_PATCH
	XFlush(dpy);
	#endif // NON_BLOCKING_STDIN_PATCH
}

static void


@@ 217,7 815,9 @@ grabfocus(void)
		XGetInputFocus(dpy, &focuswin, &revertwin);
		if (focuswin == win)
			return;
		#if !MANAGED_PATCH
		XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
		#endif // MANAGED_PATCH
		nanosleep(&ts, NULL);
	}
	die("cannot grab focus");


@@ 229,13 829,23 @@ grabkeyboard(void)
	struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000  };
	int i;

	#if MANAGED_PATCH
	if (embed || managed)
	#else
	if (embed)
	#endif // MANAGED_PATCH
		return;
	/* try to grab keyboard, we may have to wait for another process to ungrab */
	for (i = 0; i < 1000; i++) {
		if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
		                  GrabModeAsync, CurrentTime) == GrabSuccess)
		                  GrabModeAsync, CurrentTime) == GrabSuccess) {
			#if MOUSE_SUPPORT_PATCH
			/* one off attempt at grabbing the mouse pointer to avoid interactions
			 * with other windows while dmenu is active */
			XGrabPointer(dpy, DefaultRootWindow(dpy), True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
			#endif // MOUSE_SUPPORT_PATCH
			return;
		}
		nanosleep(&ts, NULL);
	}
	die("cannot grab keyboard");


@@ 244,6 854,17 @@ grabkeyboard(void)
static void
match(void)
{
	#if DYNAMIC_OPTIONS_PATCH
	if (dynamic && *dynamic)
		refreshoptions();
	#endif // DYNAMIC_OPTIONS_PATCH

	#if FUZZYMATCH_PATCH
	if (fuzzy) {
		fuzzymatch();
		return;
	}
	#endif
	static char **tokv = NULL;
	static int tokn = 0;



@@ 251,6 872,12 @@ match(void)
	int i, tokc = 0;
	size_t len, textsize;
	struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
	#if HIGHPRIORITY_PATCH
	struct item *lhpprefix, *hpprefixend;
	#endif // HIGHPRIORITY_PATCH
	#if NON_BLOCKING_STDIN_PATCH
	int preserve = 0;
	#endif // NON_BLOCKING_STDIN_PATCH

	strcpy(buf, text);
	/* separate input text into tokens to be matched individually */


@@ 259,22 886,78 @@ match(void)
			die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
	len = tokc ? strlen(tokv[0]) : 0;

	#if PREFIXCOMPLETION_PATCH
	if (use_prefix) {
		matches = lprefix = matchend = prefixend = NULL;
		textsize = strlen(text);
	} else {
		matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
		textsize = strlen(text) + 1;
	}
	#else
	matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
	textsize = strlen(text) + 1;
	for (item = items; item && item->text; item++) {
	#endif // PREFIXCOMPLETION_PATCH
	#if HIGHPRIORITY_PATCH
	lhpprefix = hpprefixend = NULL;
	#endif // HIGHPRIORITY_PATCH
	#if NON_BLOCKING_STDIN_PATCH && DYNAMIC_OPTIONS_PATCH
	for (item = items; item && (!(dynamic && *dynamic) || item->text); item = (dynamic && *dynamic) ? item + 1 : item->next)
	#elif NON_BLOCKING_STDIN_PATCH
	for (item = items; item; item = item->next)
	#else
	for (item = items; item && item->text; item++)
	#endif
	{
		for (i = 0; i < tokc; i++)
			if (!fstrstr(item->text, tokv[i]))
				break;
		#if DYNAMIC_OPTIONS_PATCH
		if (i != tokc && !(dynamic && *dynamic)) /* not all tokens match */
			continue;
		#else
		if (i != tokc) /* not all tokens match */
			continue;
		#endif // DYNAMIC_OPTIONS_PATCH
		#if HIGHPRIORITY_PATCH
		/* exact matches go first, then prefixes with high priority, then prefixes, then substrings */
		#else
		/* exact matches go first, then prefixes, then substrings */
		#endif // HIGHPRIORITY_PATCH
		#if NO_SORT_PATCH
		if (!sortmatches)
 			appenditem(item, &matches, &matchend);
 		else
		#endif // NO_SORT_PATCH
		if (!tokc || !fstrncmp(text, item->text, textsize))
			appenditem(item, &matches, &matchend);
		#if HIGHPRIORITY_PATCH
		else if (item->hp && !fstrncmp(tokv[0], item->text, len))
			appenditem(item, &lhpprefix, &hpprefixend);
		#endif // HIGHPRIORITY_PATCH
		else if (!fstrncmp(tokv[0], item->text, len))
			appenditem(item, &lprefix, &prefixend);
		#if PREFIXCOMPLETION_PATCH
		else if (!use_prefix)
		#else
		else
		#endif // PREFIXCOMPLETION_PATCH
			appenditem(item, &lsubstr, &substrend);
		#if NON_BLOCKING_STDIN_PATCH
		if (sel == item)
			preserve = 1;
		#endif // NON_BLOCKING_STDIN_PATCH
	}
	#if HIGHPRIORITY_PATCH
	if (lhpprefix) {
		if (matches) {
			matchend->right = lhpprefix;
			lhpprefix->left = matchend;
		} else
			matches = lhpprefix;
		matchend = hpprefixend;
	}
	#endif // HIGHPRIORITY_PATCH
	if (lprefix) {
		if (matches) {
			matchend->right = lprefix;


@@ 283,7 966,12 @@ match(void)
			matches = lprefix;
		matchend = prefixend;
	}
	if (lsubstr) {
	#if PREFIXCOMPLETION_PATCH
	if (!use_prefix && lsubstr)
	#else
	if (lsubstr)
	#endif // PREFIXCOMPLETION_PATCH
	{
		if (matches) {
			matchend->right = lsubstr;
			lsubstr->left = matchend;


@@ 291,7 979,19 @@ match(void)
			matches = lsubstr;
		matchend = substrend;
	}
	#if NON_BLOCKING_STDIN_PATCH
	if (!preserve)
	#endif // NON_BLOCKING_STDIN_PATCH
	curr = sel = matches;

	#if INSTANT_PATCH
	if (instant && matches && matches==matchend && !lsubstr) {
		printitem(matches);
		cleanup();
		exit(0);
	}
	#endif // INSTANT_PATCH

	calcoffsets();
}



@@ 300,12 1000,30 @@ insert(const char *str, ssize_t n)
{
	if (strlen(text) + n > sizeof text - 1)
		return;

	#if REJECTNOMATCH_PATCH
	static char last[BUFSIZ] = "";
	if (reject_no_match) {
		/* store last text value in case we need to revert it */
		memcpy(last, text, BUFSIZ);
	}
	#endif // REJECTNOMATCH_PATCH

	/* move existing text out of the way, insert new text, and update cursor */
	memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
	if (n > 0)
		memcpy(&text[cursor], str, n);
	cursor += n;
	match();

	#if REJECTNOMATCH_PATCH
	if (!matches && reject_no_match) {
		/* revert to last text value if theres no match */
		memcpy(text, last, BUFSIZ);
		cursor -= n;
		match();
	}
	#endif // REJECTNOMATCH_PATCH
}

static size_t


@@ 340,8 1058,16 @@ keypress(XKeyEvent *ev)
{
	char buf[64];
	int len;
	#if PREFIXCOMPLETION_PATCH
	struct item * item;
	#endif // PREFIXCOMPLETION_PATCH
	KeySym ksym = NoSymbol;
	Status status;
	#if GRID_PATCH && GRIDNAV_PATCH
	int i;
	struct item *tmpsel;
	bool offscreen = false;
	#endif // GRIDNAV_PATCH

	len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
	switch (status) {


@@ 354,8 1080,50 @@ keypress(XKeyEvent *ev)
		break;
	}

	#if VI_MODE_PATCH
	if (using_vi_mode) {
		vi_keypress(ksym, ev);
		return;
	}

	if (vi_mode &&
			   (ksym == global_esc.ksym &&
				(ev->state & global_esc.state) == global_esc.state)) {
		using_vi_mode = 1;
		if (cursor)
			cursor = nextrune(-1);
		goto draw;
	}
	#endif // VI_MODE_PATCH

	if (ev->state & ControlMask) {
		switch(ksym) {
		#if FZFEXPECT_PATCH
		case XK_a: expect("ctrl-a", ev); ksym = XK_Home;      break;
		case XK_b: expect("ctrl-b", ev); ksym = XK_Left;      break;
		case XK_c: expect("ctrl-c", ev); ksym = XK_Escape;    break;
		case XK_d: expect("ctrl-d", ev); ksym = XK_Delete;    break;
		case XK_e: expect("ctrl-e", ev); ksym = XK_End;       break;
		case XK_f: expect("ctrl-f", ev); ksym = XK_Right;     break;
		case XK_g: expect("ctrl-g", ev); ksym = XK_Escape;    break;
		case XK_h: expect("ctrl-h", ev); ksym = XK_BackSpace; break;
		case XK_i: expect("ctrl-i", ev); ksym = XK_Tab;       break;
		case XK_j: expect("ctrl-j", ev); ksym = XK_Down;      break;
		case XK_J:/* fallthrough */
		case XK_l: expect("ctrl-l", ev); break;
		case XK_m: expect("ctrl-m", ev); /* fallthrough */
		case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
		case XK_n: expect("ctrl-n", ev); ksym = XK_Down; break;
		case XK_p: expect("ctrl-p", ev); ksym = XK_Up;   break;
		case XK_o: expect("ctrl-o", ev); break;
		case XK_q: expect("ctrl-q", ev); break;
		#if !NAVHISTORY_PATCH
		case XK_r: expect("ctrl-r", ev); break;
		#endif // NAVHISTORY_PATCH
		case XK_s: expect("ctrl-s", ev); break;
		case XK_t: expect("ctrl-t", ev); break;
		case XK_k: expect("ctrl-k", ev); ksym = XK_Up; break;
		#else
		case XK_a: ksym = XK_Home;      break;
		case XK_b: ksym = XK_Left;      break;
		case XK_c: ksym = XK_Escape;    break;


@@ 376,20 1144,53 @@ keypress(XKeyEvent *ev)
			text[cursor] = '\0';
			match();
			break;
		#endif // FZFEXPECT_PATCH
		#if FZFEXPECT_PATCH
		case XK_u: expect("ctrl-u", ev); /* delete left */
		#else
		case XK_u: /* delete left */
		#endif // FZFEXPECT_PATCH
			insert(NULL, 0 - cursor);
			break;
		#if FZFEXPECT_PATCH
		case XK_w: expect("ctrl-w", ev); /* delete word */
		#else
		case XK_w: /* delete word */
		#endif // FZFEXPECT_PATCH
			while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
				insert(NULL, nextrune(-1) - cursor);
			while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
				insert(NULL, nextrune(-1) - cursor);
			break;
		#if FZFEXPECT_PATCH || CTRL_V_TO_PASTE_PATCH
		case XK_v:
		#if FZFEXPECT_PATCH
			expect("ctrl-v", ev);
		#endif // FZFEXPECT_PATCH
		case XK_V:
			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
			                  utf8, utf8, win, CurrentTime);
			return;
		#endif // FZFEXPECT_PATCH | CTRL_V_TO_PASTE_PATCH
		#if NAVHISTORY_PATCH
		case XK_r:
			togglehistoryitems();
			match();
			goto draw;
		#endif // NAVHISTORY_PATCH
		#if FZFEXPECT_PATCH
		case XK_y: expect("ctrl-y", ev); /* paste selection */
		#else
		case XK_y: /* paste selection */
		#endif // FZFEXPECT_PATCH
		case XK_Y:
			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
			                  utf8, utf8, win, CurrentTime);
			return;
		#if FZFEXPECT_PATCH
		case XK_x: expect("ctrl-x", ev); break;
		case XK_z: expect("ctrl-z", ev); break;
		#endif // FZFEXPECT_PATCH
		case XK_Left:
		case XK_KP_Left:
			movewordedge(-1);


@@ 400,6 1201,13 @@ keypress(XKeyEvent *ev)
			goto draw;
		case XK_Return:
		case XK_KP_Enter:
			#if RESTRICT_RETURN_PATCH
			if (restrict_return)
				break;
			#endif // RESTRICT_RETURN_PATCH
			#if MULTI_SELECTION_PATCH
			selsel();
			#endif // MULTI_SELECTION_PATCH
			break;
		case XK_bracketleft:
			cleanup();


@@ 421,6 1229,16 @@ keypress(XKeyEvent *ev)
		case XK_j: ksym = XK_Next;  break;
		case XK_k: ksym = XK_Prior; break;
		case XK_l: ksym = XK_Down;  break;
		#if NAVHISTORY_PATCH
		case XK_p:
			navhistory(-1);
			buf[0]=0;
			break;
		case XK_n:
			navhistory(1);
			buf[0]=0;
			break;
		#endif // NAVHISTORY_PATCH
		default:
			return;
		}


@@ 474,6 1292,26 @@ insert:
		break;
	case XK_Left:
	case XK_KP_Left:
		#if GRID_PATCH && GRIDNAV_PATCH
		if (columns > 1) {
			if (!sel)
				return;
			tmpsel = sel;
			for (i = 0; i < lines; i++) {
				if (!tmpsel->left ||  tmpsel->left->right != tmpsel)
					return;
				if (tmpsel == curr)
					offscreen = true;
				tmpsel = tmpsel->left;
			}
			sel = tmpsel;
			if (offscreen) {
				curr = prev;
				calcoffsets();
			}
			break;
		}
		#endif // GRIDNAV_PATCH
		if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
			cursor = nextrune(-1);
			break;


@@ 504,16 1342,47 @@ insert:
		break;
	case XK_Return:
	case XK_KP_Enter:
		puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
		#if RESTRICT_RETURN_PATCH
		if (restrict_return && (!sel || ev->state & (ShiftMask | ControlMask)))
			break;
		#endif // RESTRICT_RETURN_PATCH
		#if !MULTI_SELECTION_PATCH
		printcurrent(ev->state);
		#endif // MULTI_SELECTION_PATCH
		if (!(ev->state & ControlMask)) {
			#if MULTI_SELECTION_PATCH
			printselected(ev->state);
			#endif // MULTI_SELECTION_PATCH
			cleanup();
			exit(0);
		}
		#if !MULTI_SELECTION_PATCH
		if (sel)
			sel->out = 1;
		#endif // MULTI_SELECTION_PATCH
		break;
	case XK_Right:
	case XK_KP_Right:
		#if GRID_PATCH && GRIDNAV_PATCH
		if (columns > 1) {
			if (!sel)
				return;
			tmpsel = sel;
			for (i = 0; i < lines; i++) {
				if (!tmpsel->right ||  tmpsel->right->left != tmpsel)
					return;
				tmpsel = tmpsel->right;
				if (tmpsel == next)
					offscreen = true;
			}
			sel = tmpsel;
			if (offscreen) {
				curr = next;
				calcoffsets();
			}
			break;
		}
		#endif // GRIDNAV_PATCH
		if (text[cursor] != '\0') {
			cursor = nextrune(+1);
			break;


@@ 529,16 1398,48 @@ insert:
		}
		break;
	case XK_Tab:
		#if PREFIXCOMPLETION_PATCH
		if (!matches)
			break; /* cannot complete no matches */
		#if FUZZYMATCH_PATCH
		/* only do tab completion if all matches start with prefix */
		for (item = matches; item && item->text; item = item->right)
			if (item->text[0] != text[0])
				goto draw;
		#endif // FUZZYMATCH_PATCH
		strncpy(text, matches->text, sizeof text - 1);
		text[sizeof text - 1] = '\0';
		len = cursor = strlen(text); /* length of longest common prefix */
		for (item = matches; item && item->text; item = item->right) {
			cursor = 0;
			while (cursor < len && text[cursor] == item->text[cursor])
				cursor++;
			len = cursor;
		}
		memset(text + len, '\0', strlen(text) - len);
		#else
		if (!sel)
			return;
		cursor = strnlen(sel->text, sizeof text - 1);
		memcpy(text, sel->text, cursor);
		text[cursor] = '\0';
		match();
		#endif // PREFIXCOMPLETION_PATCH
		break;
	}

draw:
	#if INCREMENTAL_PATCH
	if (incremental) {
		puts(text);
		fflush(stdout);
	}
	#endif // INCREMENTAL_PATCH
	#if VI_MODE_PATCH
	if (using_vi_mode && text[cursor] == '\0')
		--cursor;
	#endif // VI_MODE_PATCH

	drawmenu();
}



@@ 561,12 1462,133 @@ paste(void)
}

static void
printitem(struct item *item)
{
	if (!item)
		return;

	#if NAVHISTORY_PATCH
	addhistoryitem(item);
	#endif // NAVHISTORY_PATCH

	#if PIPEOUT_PATCH
	if (item->text[0] == startpipe[0]) {
		strncpy(item->text + strlen(item->text),pipeout,8);
		puts(item->text+1);
		return;
	}
	#endif // PIPEOUT_PATCH

	#if PRINTINDEX_PATCH
	if (print_index) {
		printf("%d\n", item->index);
		return;
	}
	#endif // PRINTINDEX_PATCH

	#if SEPARATOR_PATCH
	puts(item->text_output);
	#else
	puts(item->text);
	#endif // SEPARATOR_PATCH
}

static void
printtext(char *text)
{
	if (!text || !strlen(text))
		return;

	#if NAVHISTORY_PATCH
	addhistory(text);
	#endif // NAVHISTORY_PATCH

	#if PIPEOUT_PATCH
	if (text[0] == startpipe[0]) {
		strncpy(text + strlen(text),pipeout,8);
		puts(text+1);
		return;
	}
	#endif // PIPEOUT_PATCH

	puts(text);
}

static void
printcurrent(unsigned int state)
{
	#if PRINTINPUTTEXT_PATCH
	if (sel && (use_text_input == !!(state & ShiftMask)))
	#else
	if (sel && !(state & ShiftMask))
	#endif // PRINTINPUTTEXT_PATCH
	{
		printitem(sel);
	} else {
		printtext(text);
	}
}

#if ALPHA_PATCH
static void
xinitvisual(void)
{
	XVisualInfo *infos;
	XRenderPictFormat *fmt;
	int nitems;
	int i;

	XVisualInfo tpl = {
		.screen = screen,
		.depth = 32,
		.class = TrueColor
	};
	long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;

	infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
	visual = NULL;
	for(i = 0; i < nitems; i ++) {
		fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
		if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
			visual = infos[i].visual;
			depth = infos[i].depth;
			cmap = XCreateColormap(dpy, root, visual, AllocNone);
			useargb = 1;
			break;
		}
	}

	XFree(infos);

	if (!visual || !opacity) {
		visual = DefaultVisual(dpy, screen);
		depth = DefaultDepth(dpy, screen);
		cmap = DefaultColormap(dpy, screen);
	}
}
#endif // ALPHA_PATCH

#if !NON_BLOCKING_STDIN_PATCH
static void
readstdin(void)
{
	char *line = NULL;
	size_t i, itemsiz = 0, linesiz = 0;
	#if SEPARATOR_PATCH
	char *p;
	#elif TSV_PATCH
	char *buf, *p;
	#endif // SEPARATOR_PATCH | TSV_PATCH

	size_t i, linesiz, itemsiz = 0;
	ssize_t len;

	#if PASSWORD_PATCH
	if (passwd) {
		inputw = lines = 0;
		return;
	}
	#endif // PASSWORD_PATCH

	/* read each line from stdin and add it to the item list */
	for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
		if (i + 1 >= itemsiz) {


@@ 576,27 1598,94 @@ readstdin(void)
		}
		if (line[len - 1] == '\n')
			line[len - 1] = '\0';

		if (!(items[i].text = strdup(line)))
			die("strdup:");
		items[i].width = TEXTW(line);

		#if SEPARATOR_PATCH
		if (separator && (p = sepchr(items[i].text, separator)) != NULL) {
			*p = '\0';
			items[i].text_output = ++p;
		} else {
			items[i].text_output = items[i].text;
		}
		if (separator_reverse) {
			p = items[i].text;
			items[i].text = items[i].text_output;
			items[i].text_output = p;
		}
		#elif TSV_PATCH
		if (!(buf = strdup(line)))
			die("cannot strdup %u bytes:", strlen(line) + 1);
		if ((p = strchr(buf, '\t')))
			*p = '\0';
		items[i].stext = buf;
		#endif // SEPARATOR_PATCH | TSV_PATCH
		#if MULTI_SELECTION_PATCH
		items[i].id = i; /* for multiselect */
		#if PRINTINDEX_PATCH
		items[i].index = i;
		#endif // PRINTINDEX_PATCH
		#elif PRINTINDEX_PATCH
		items[i].index = i;
		#else
		items[i].out = 0;
		#endif // MULTI_SELECTION_PATCH | PRINTINDEX_PATCH

		#if HIGHPRIORITY_PATCH
		items[i].hp = arrayhas(hpitems, hplength, items[i].text);
		#endif // HIGHPRIORITY_PATCH
	}
	free(line);
	if (items)
		items[i].text = NULL;
	lines = MIN(lines, i);
}
#endif // NON_BLOCKING_STDIN_PATCH

static void
#if NON_BLOCKING_STDIN_PATCH
readevent(void)
#else
run(void)
#endif // NON_BLOCKING_STDIN_PATCH
{
	XEvent ev;

	#if PRESELECT_PATCH
	int i;
	#endif // PRESELECT_PATCH
	while (!XNextEvent(dpy, &ev)) {
		#if PRESELECT_PATCH
		if (preselected) {
			for (i = 0; i < preselected; i++) {
				if (sel && sel->right && (sel = sel->right) == next) {
					curr = next;
					calcoffsets();
				}
			}
			drawmenu();
			preselected = 0;
		}
		#endif // PRESELECT_PATCH
		#if INPUTMETHOD_PATCH
		if (XFilterEvent(&ev, None))
			continue;
		if (composing)
			continue;
		#else
		if (XFilterEvent(&ev, win))
			continue;
		#endif // INPUTMETHOD_PATCH
		switch(ev.type) {
		#if MOUSE_SUPPORT_PATCH
		case ButtonPress:
			buttonpress(&ev);
			break;
		#if MOTION_SUPPORT_PATCH
		case MotionNotify:
			motionevent(&ev.xbutton);
			break;
		#endif // MOTION_SUPPORT_PATCH
		#endif // MOUSE_SUPPORT_PATCH
		case DestroyNotify:
			if (ev.xdestroywindow.window != win)
				break;


@@ 631,6 1720,11 @@ setup(void)
{
	int x, y, i, j;
	unsigned int du;
	#if RELATIVE_INPUT_WIDTH_PATCH
	unsigned int tmp, minstrlen = 0, curstrlen = 0;
	int numwidthchecks = 100;
	struct item *item;
	#endif // RELATIVE_INPUT_WIDTH_PATCH
	XSetWindowAttributes swa;
	XIM xim;
	Window w, dw, *dws;


@@ 642,22 1736,45 @@ setup(void)
	int a, di, n, area = 0;
#endif
	/* init appearance */
	for (j = 0; j < SchemeLast; j++) {
	#if XRESOURCES_PATCH
	for (j = 0; j < SchemeLast; j++)
		#if ALPHA_PATCH
		scheme[j] = drw_scm_create(drw, (const char**)colors[j], alphas[j], 2);
		#else
		scheme[j] = drw_scm_create(drw, (const char**)colors[j], 2);
	}
	for (j = 0; j < SchemeOut; ++j) {
		for (i = 0; i < 2; ++i)
			free(colors[j][i]);
	}
		#endif // ALPHA_PATCH
	#else
	for (j = 0; j < SchemeLast; j++)
		#if ALPHA_PATCH
		scheme[j] = drw_scm_create(drw, colors[j], alphas[j], 2);
		#else
		scheme[j] = drw_scm_create(drw, colors[j], 2);
		#endif // ALPHA_PATCH
	#endif // XRESOURCES_PATCH

	clip = XInternAtom(dpy, "CLIPBOARD",   False);
	utf8 = XInternAtom(dpy, "UTF8_STRING", False);
	#if WMTYPE_PATCH
	type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
	dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
	#endif // WMTYPE_PATCH

	/* calculate menu geometry */
	#if PANGO_PATCH
	bh = drw->font->h + 2;
	#else
	bh = drw->fonts->h + 2;
	#endif // PANGO_PATCH
	#if LINE_HEIGHT_PATCH
	bh = MAX(bh,lineheight);	/* make a menu line AT LEAST 'lineheight' tall */
	#endif // LINE_HEIGHT_PATCH
	lines = MAX(lines, 0);
	mh = (lines + 1) * bh;
	#if CENTER_PATCH && PANGO_PATCH
	promptw = (prompt && *prompt) ? TEXTWM(prompt) - lrpad / 4 : 0;
	#elif CENTER_PATCH
	promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
	#endif // CENTER_PATCH
#ifdef XINERAMA
	i = 0;
	if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {


@@ 684,16 1801,35 @@ setup(void)
				if (INTERSECT(x, y, 1, 1, info[i]) != 0)
					break;

		if (centered) {
		#if CENTER_PATCH
		if (center) {
			#if XYW_PATCH
			mw = (dmw>0 ? dmw : MIN(MAX(max_textw() + promptw, min_width), info[i].width));
			#else
			mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width);
			#endif // XYW_PATCH
			x = info[i].x_org + ((info[i].width  - mw) / 2);
			y = info[i].y_org + ((info[i].height - mh) / menu_height_ratio);
			y = info[i].y_org + ((info[i].height - mh) / 2);
		} else {
			#if XYW_PATCH
			x = info[i].x_org + dmx;
			y = info[i].y_org + (topbar ? dmy : info[i].height - mh - dmy);
			mw = (dmw>0 ? dmw : info[i].width);
			#else
			x = info[i].x_org;
			y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
			mw = info[i].width;
			#endif // XYW_PATCH
		}

		#elif XYW_PATCH
		x = info[i].x_org + dmx;
		y = info[i].y_org + (topbar ? dmy : info[i].height - mh - dmy);
		mw = (dmw>0 ? dmw : info[i].width);
		#else
		x = info[i].x_org;
		y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
		mw = info[i].width;
		#endif // CENTER_PATCH / XYW_PATCH
		XFree(info);
	} else
#endif


@@ 701,36 1837,131 @@ setup(void)
		if (!XGetWindowAttributes(dpy, parentwin, &wa))
			die("could not get embedding window attributes: 0x%lx",
			    parentwin);

		if (centered) {
		#if CENTER_PATCH
		if (center) {
			#if XYW_PATCH
			mw = (dmw>0 ? dmw : MIN(MAX(max_textw() + promptw, min_width), wa.width));
			#else
			mw = MIN(MAX(max_textw() + promptw, min_width), wa.width);
			#endif // XYW_PATCH
			x = (wa.width  - mw) / 2;
			y = (wa.height - mh) / 2;
		} else {
			#if XYW_PATCH
			x = dmx;
			y = topbar ? dmy : wa.height - mh - dmy;
			mw = (dmw>0 ? dmw : wa.width);
			#else
			x = 0;
			y = topbar ? 0 : wa.height - mh;
			mw = wa.width;
			#endif // XYW_PATCH
		}
		#elif XYW_PATCH
		x = dmx;
		y = topbar ? dmy : wa.height - mh - dmy;
		mw = (dmw>0 ? dmw : wa.width);
		#else
		x = 0;
		y = topbar ? 0 : wa.height - mh;
		mw = wa.width;
		#endif // CENTER_PATCH / XYW_PATCH
	}
	#if !CENTER_PATCH
	#if PANGO_PATCH
	promptw = (prompt && *prompt) ? TEXTWM(prompt) - lrpad / 4 : 0;
	#else
	promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
	inputw = mw / 3; /* input width: ~33% of monitor width */
	#endif // PANGO_PATCH
	#endif // CENTER_PATCH
	#if RELATIVE_INPUT_WIDTH_PATCH
	for (item = items; !lines && item && item->text; ++item) {
		curstrlen = strlen(item->text);
		if (numwidthchecks || minstrlen < curstrlen) {
			numwidthchecks = MAX(numwidthchecks - 1, 0);
			minstrlen = MAX(minstrlen, curstrlen);
			if ((tmp = textw_clamp(item->text, mw/3)) > inputw) {
				inputw = tmp;
				if (tmp == mw/3)
					break;
			}
		}
	}
	#else
	inputw = mw / 3; /* input width: ~33.33% of monitor width */
	#endif // RELATIVE_INPUT_WIDTH_PATCH
	match();

	/* create menu window */
	#if MANAGED_PATCH
	swa.override_redirect = managed ? False : True;
	#else
	swa.override_redirect = True;
	#endif // MANAGED_PATCH
	#if ALPHA_PATCH
	swa.background_pixel = 0;
	swa.colormap = cmap;
	#else
	swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
	win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
	                    CopyFromParent, CopyFromParent, CopyFromParent,
	                    CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
	#endif // ALPHA_PATCH
	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask
		#if MOUSE_SUPPORT_PATCH
		| ButtonPressMask
		#if MOTION_SUPPORT_PATCH
		| PointerMotionMask
		#endif // MOTION_SUPPORT_PATCH
		#endif // MOUSE_SUPPORT_PATCH
	;
	win = XCreateWindow(
		dpy, root,
		#if BARPADDING_PATCH && BORDER_PATCH
		x + sp, y + vp - (topbar ? 0 : border_width * 2), mw - 2 * sp - border_width * 2, mh, border_width,
		#elif BARPADDING_PATCH
		x + sp, y + vp, mw - 2 * sp, mh, 0,
		#elif BORDER_PATCH
		x, y - (topbar ? 0 : border_width * 2), mw - border_width * 2, mh, border_width,
		#else
		x, y, mw, mh, 0,
		#endif // BORDER_PATCH | BARPADDING_PATCH
		#if ALPHA_PATCH
		depth, InputOutput, visual,
		CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &swa
		#else
		CopyFromParent, CopyFromParent, CopyFromParent,
		CWOverrideRedirect | CWBackPixel | CWEventMask, &swa
		#endif // ALPHA_PATCH
	);
	#if BORDER_PATCH
	if (border_width)
		XSetWindowBorder(dpy, win, scheme[SchemeBorder][ColBg].pixel);
	#endif // BORDER_PATCH
	XSetClassHint(dpy, win, &ch);
	#if WMTYPE_PATCH
	XChangeProperty(dpy, win, type, XA_ATOM, 32, PropModeReplace,
			(unsigned char *) &dock, 1);
	#endif // WMTYPE_PATCH

	/* input methods */
	if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
		die("XOpenIM failed: could not open input device");

	#if INPUTMETHOD_PATCH
	init_input_method(xim);
	#else
	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
	                XNClientWindow, win, XNFocusWindow, win, NULL);
	#endif // INPUTMETHOD_PATCH

	#if MANAGED_PATCH
	if (managed) {
		XTextProperty prop;
		char *windowtitle = prompt != NULL ? prompt : "dmenu";
		Xutf8TextListToTextProperty(dpy, &windowtitle, 1, XUTF8StringStyle, &prop);
		XSetWMName(dpy, win, &prop);
		XSetTextProperty(dpy, win, &prop, XInternAtom(dpy, "_NET_WM_NAME", False));
		XFree(prop.value);
	}
	#endif // MANAGED_PATCH

	XMapRaised(dpy, win);
	if (embed) {


@@ 741,8 1972,13 @@ setup(void)
				XSelectInput(dpy, dws[i], FocusChangeMask);
			XFree(dws);
		}
		#if !INPUTMETHOD_PATCH
		grabfocus();
		#endif // INPUTMETHOD_PATCH
	}
	#if INPUTMETHOD_PATCH
	grabfocus();
	#endif // INPUTMETHOD_PATCH
	drw_resize(drw, mw, mh);
	drawmenu();
}


@@ 750,91 1986,391 @@ setup(void)
static void
usage(void)
{
	die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
	    "             [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
}

void
readxresources(void) {
	XrmInitialize();

	char* xrm;
	if ((xrm = XResourceManagerString(drw->dpy))) {
		char *type;
		XrmDatabase xdb = XrmGetStringDatabase(xrm);
		XrmValue xval;

		if (XrmGetResource(xdb, "dmenu.font", "*", &type, &xval))
			fonts[0] = strdup(xval.addr);
		else
			fonts[0] = strdup(fonts[0]);
		if (XrmGetResource(xdb, "dmenu.background", "*", &type, &xval))
			colors[SchemeNorm][ColBg] = strdup(xval.addr);
		else
			colors[SchemeNorm][ColBg] = strdup(colors[SchemeNorm][ColBg]);
		if (XrmGetResource(xdb, "dmenu.foreground", "*", &type, &xval))
			colors[SchemeNorm][ColFg] = strdup(xval.addr);
		else
			colors[SchemeNorm][ColFg] = strdup(colors[SchemeNorm][ColFg]);
		if (XrmGetResource(xdb, "dmenu.selbackground", "*", &type, &xval))
			colors[SchemeSel][ColBg] = strdup(xval.addr);
		else
			colors[SchemeSel][ColBg] = strdup(colors[SchemeSel][ColBg]);
		if (XrmGetResource(xdb, "dmenu.selforeground", "*", &type, &xval))
			colors[SchemeSel][ColFg] = strdup(xval.addr);
		else
			colors[SchemeSel][ColFg] = strdup(colors[SchemeSel][ColFg]);

		XrmDestroyDatabase(xdb);
	}
	die("usage: dmenu [-bv"
		#if CENTER_PATCH
		"c"
		#endif
		#if !NON_BLOCKING_STDIN_PATCH
		"f"
		#endif // NON_BLOCKING_STDIN_PATCH
		#if QUIET_PATCH
		"q"
		#endif // QUIET_PATCH
		#if INCREMENTAL_PATCH
		"r"
		#endif // INCREMENTAL_PATCH
		#if CASEINSENSITIVE_PATCH
		"s"
		#else
		"i"
		#endif // CASEINSENSITIVE_PATCH
		#if INSTANT_PATCH
		"n"
		#endif // INSTANT_PATCH
		#if PRINTINPUTTEXT_PATCH
		"t"
		#endif // PRINTINPUTTEXT_PATCH
		#if PREFIXCOMPLETION_PATCH
		"x"
		#endif // PREFIXCOMPLETION_PATCH
		#if FUZZYMATCH_PATCH
		"F"
		#endif // FUZZYMATCH_PATCH
		#if PASSWORD_PATCH
		"P"
		#endif // PASSWORD_PATCH
		#if NO_SORT_PATCH
		"S"
		#endif // NO_SORT_PATCH
		#if REJECTNOMATCH_PATCH
		"R" // (changed from r to R due to conflict with INCREMENTAL_PATCH)
		#endif // REJECTNOMATCH_PATCH
		#if RESTRICT_RETURN_PATCH
		"1"
		#endif // RESTRICT_RETURN_PATCH
		"] "
		#if CARET_WIDTH_PATCH
		"[-cw caret_width] "
		#endif // CARET_WIDTH_PATCH
		#if VI_MODE_PATCH
		"[-vi] "
		#endif // VI_MODE_PATCH
		#if MANAGED_PATCH
		"[-wm] "
		#endif // MANAGED_PATCH
		#if GRID_PATCH
		"[-g columns] "
		#endif // GRID_PATCH
		"[-l lines] [-p prompt] [-fn font] [-m monitor]"
		"\n             [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"
		#if DYNAMIC_OPTIONS_PATCH || FZFEXPECT_PATCH || ALPHA_PATCH || BORDER_PATCH || HIGHPRIORITY_PATCH
		"\n            "
		#endif
		#if DYNAMIC_OPTIONS_PATCH
		" [-dy command]"
		#endif // DYNAMIC_OPTIONS_PATCH
		#if FZFEXPECT_PATCH
		" [-ex expectkey]"
		#endif // FZFEXPECT_PATCH
		#if ALPHA_PATCH
		" [-o opacity]"
		#endif // ALPHA_PATCH
		#if BORDER_PATCH
		" [-bw width]"
		#endif // BORDER_PATCH
		#if HIGHPRIORITY_PATCH
		" [-hb color] [-hf color] [-hp items]"
		#endif // HIGHPRIORITY_PATCH
		#if INITIALTEXT_PATCH || LINE_HEIGHT_PATCH || PRESELECT_PATCH || NAVHISTORY_PATCH || XYW_PATCH
		"\n            "
		#endif
		#if INITIALTEXT_PATCH
		" [-it text]"
		#endif // INITIALTEXT_PATCH
		#if LINE_HEIGHT_PATCH
		" [-h height]"
		#endif // LINE_HEIGHT_PATCH
		#if PRESELECT_PATCH
		" [-ps index]"
		#endif // PRESELECT_PATCH
		#if NAVHISTORY_PATCH
		" [-H histfile]"
		#endif // NAVHISTORY_PATCH
		#if XYW_PATCH
		" [-X xoffset] [-Y yoffset] [-W width]" // (arguments made upper case due to conflicts)
		#endif // XYW_PATCH
		#if HIGHLIGHT_PATCH
		"\n             [-nhb color] [-nhf color] [-shb color] [-shf color]" // highlight colors
		#endif // HIGHLIGHT_PATCH
		#if SEPARATOR_PATCH
		"\n             [-d separator] [-D separator]"
		#endif // SEPARATOR_PATCH
		"\n");
}

int
main(int argc, char *argv[])
{
	XWindowAttributes wa;
	int i, fast = 0;
	int i;
	#if !NON_BLOCKING_STDIN_PATCH
	int fast = 0;
	#endif // NON_BLOCKING_STDIN_PATCH

	#if XRESOURCES_PATCH
	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
		fputs("warning: no locale support\n", stderr);
	#if INPUTMETHOD_PATCH
	if (!XSetLocaleModifiers(""))
		fputs("warning: could not set locale modifiers", stderr);
	#endif // INPUTMETHOD_PATCH
	if (!(dpy = XOpenDisplay(NULL)))
		die("cannot open display");

	/* These need to be checked before we init the visuals and read X resources. */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-v")) { /* prints version information */
			puts("dmenu-"VERSION);
			exit(0);
		} else if (!strcmp(argv[i], "-w")) {
			argv[i][0] = '\0';
			embed = strdup(argv[++i]);
		#if ALPHA_PATCH
		} else if (!strcmp(argv[i], "-o")) {  /* opacity, pass -o 0 to disable alpha */
			opacity = atoi(argv[++i]);
		#endif // ALPHA_PATCH
		} else {
			continue;
		}
		argv[i][0] = '\0'; // mark as used
	}

	screen = DefaultScreen(dpy);
	root = RootWindow(dpy, screen);
	if (!embed || !(parentwin = strtol(embed, NULL, 0)))
		parentwin = root;
	if (!XGetWindowAttributes(dpy, parentwin, &wa))
		die("could not get embedding window attributes: 0x%lx",
		    parentwin);

	#if ALPHA_PATCH
	xinitvisual();
	drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
	#else
	drw = drw_create(dpy, screen, root, wa.width, wa.height);
	#endif // ALPHA_PATCH
	readxresources();
	#endif // XRESOURCES_PATCH

	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '\0')
			continue;

	for (i = 1; i < argc; i++)
		/* these options take no arguments */
		if (!strcmp(argv[i], "-v")) {      /* prints version information */
			puts("dmenu-"VERSION);
			exit(0);
		} else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
		} else if (!strcmp(argv[i], "-b")) { /* appears at the bottom of the screen */
			topbar = 0;
		else if (!strcmp(argv[i], "-f"))   /* grabs keyboard before reading stdin */
		#if CENTER_PATCH
		} else if (!strcmp(argv[i], "-c")) { /* toggles centering of dmenu window on screen */
			center = !center;
		#endif // CENTER_PATCH
		#if !NON_BLOCKING_STDIN_PATCH
		} else if (!strcmp(argv[i], "-f")) { /* grabs keyboard before reading stdin */
			fast = 1;
		else if (!strcmp(argv[i], "-c"))   /* centers dmenu on screen */
			centered = 1;
		else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
		#endif // NON_BLOCKING_STDIN_PATCH
		#if INCREMENTAL_PATCH
		} else if (!strcmp(argv[i], "-r")) { /* incremental */
			incremental = !incremental;
		#endif // INCREMENTAL_PATCH
		#if QUIET_PATCH
		} else if (!strcmp(argv[i], "-q")) { /* quiet, don't list items if search is empty */
			quiet = !quiet;
		#endif // QUIET_PATCH
		#if CASEINSENSITIVE_PATCH
		} else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
			fstrncmp = strncmp;
			fstrstr = strstr;
		#else
		} else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
			fstrncmp = strncasecmp;
			fstrstr = cistrstr;
		#endif // CASEINSENSITIVE_PATCH
		#if VI_MODE_PATCH
		} else if (!strcmp(argv[i], "-vi")) {
			if (i + 1 < argc) {
				if (!strcmp(argv[i+1], "0")) {
					start_mode = 0;
					i++;
				} else if (!strcmp(argv[i+1], "1")) {
					start_mode = 1;
					i++;
				}
			}
			vi_mode = 1;
			using_vi_mode = start_mode;
			global_esc.ksym = XK_Escape;
			global_esc.state = 0;
		#endif // VI_MODE_PATCH
		#if MANAGED_PATCH
		} else if (!strcmp(argv[i], "-wm")) { /* display as managed wm window */
			managed = 1;
		#endif // MANAGED_PATCH
		#if INSTANT_PATCH
		} else if (!strcmp(argv[i], "-n")) { /* instant select only match */
			instant = !instant;
		#endif // INSTANT_PATCH
		#if PRINTINPUTTEXT_PATCH
		} else if (!strcmp(argv[i], "-t")) { /* favors text input over selection */
			use_text_input = 1;
		#endif // PRINTINPUTTEXT_PATCH
		#if PREFIXCOMPLETION_PATCH
		} else if (!strcmp(argv[i], "-x")) { /* invert use_prefix */
			use_prefix = !use_prefix;
		#endif // PREFIXCOMPLETION_PATCH
		#if FUZZYMATCH_PATCH
		} else if (!strcmp(argv[i], "-F")) { /* disable/enable fuzzy matching, depends on default */
			fuzzy = !fuzzy;
		#endif // FUZZYMATCH_PATCH
		#if PASSWORD_PATCH
		} else if (!strcmp(argv[i], "-P")) { /* is the input a password */
			passwd = 1;
		#endif // PASSWORD_PATCH
		#if FZFEXPECT_PATCH
		} else if (!strcmp(argv[i], "-ex")) { /* expect key */
			expected = argv[++i];
		#endif // FZFEXPECT_PATCH
		#if REJECTNOMATCH_PATCH
		} else if (!strcmp(argv[i], "-R")) { /* reject input which results in no match */
			reject_no_match = 1;
		#endif // REJECTNOMATCH_PATCH
		#if NO_SORT_PATCH
		} else if (!strcmp(argv[i], "-S")) { /* do not sort matches */
			sortmatches = 0;
		#endif // NO_SORT_PATCH
		#if PRINTINDEX_PATCH
		} else if (!strcmp(argv[i], "-ix")) { /* adds ability to return index in list */
			print_index = 1;
		#endif // PRINTINDEX_PATCH
		#if RESTRICT_RETURN_PATCH
		} else if (!strcmp(argv[i], "-1")) {
			restrict_return = 1;
		#endif // RESTRICT_RETURN_PATCH
		} else if (i + 1 == argc)
			usage();
		/* these options take one argument */
		#if NAVHISTORY_PATCH
		else if (!strcmp(argv[i], "-H"))
			histfile = argv[++i];
		#endif // NAVHISTORY_PATCH
		#if GRID_PATCH
		else if (!strcmp(argv[i], "-g")) {   /* number of columns in grid */
			columns = atoi(argv[++i]);
			if (columns && lines == 0)
				lines = 1;
		}
		#endif // GRID_PATCH
		else if (!strcmp(argv[i], "-l"))   /* number of lines in vertical list */
			lines = atoi(argv[++i]);
		#if XYW_PATCH
		else if (!strcmp(argv[i], "-X"))   /* window x offset */
			dmx = atoi(argv[++i]);
		else if (!strcmp(argv[i], "-Y"))   /* window y offset (from bottom up if -b) */
			dmy = atoi(argv[++i]);
		else if (!strcmp(argv[i], "-W"))   /* make dmenu this wide */
			dmw = atoi(argv[++i]);
		#endif // XYW_PATCH
		else if (!strcmp(argv[i], "-m"))
			mon = atoi(argv[++i]);
		#if ALPHA_PATCH && !XRESOURCES_PATCH
		else if (!strcmp(argv[i], "-o"))  /* opacity, pass -o 0 to disable alpha */
			opacity = atoi(argv[++i]);
		#endif // ALPHA_PATCH
		else if (!strcmp(argv[i], "-p"))   /* adds prompt to left of input field */
			prompt = argv[++i];
		else if (!strcmp(argv[i], "-fn"))  /* font or font set */
			tempfonts = argv[++i];
			#if PANGO_PATCH
			font = argv[++i];
			#else
			fonts[0] = argv[++i];
			#endif // PANGO_PATCH
		#if LINE_HEIGHT_PATCH
		else if(!strcmp(argv[i], "-h")) { /* minimum height of one menu line */
			lineheight = atoi(argv[++i]);
			lineheight = MAX(lineheight, min_lineheight); /* reasonable default in case of value too small/negative */
		}
		#endif // LINE_HEIGHT_PATCH
		else if (!strcmp(argv[i], "-nb"))  /* normal background color */
			colortemp[0] = argv[++i];
			colors[SchemeNorm][ColBg] = argv[++i];
		else if (!strcmp(argv[i], "-nf"))  /* normal foreground color */
			colortemp[1] = argv[++i];
			colors[SchemeNorm][ColFg] = argv[++i];
		else if (!strcmp(argv[i], "-sb"))  /* selected background color */
			colortemp[2] = argv[++i];
			colors[SchemeSel][ColBg] = argv[++i];
		else if (!strcmp(argv[i], "-sf"))  /* selected foreground color */
			colortemp[3] = argv[++i];
			colors[SchemeSel][ColFg] = argv[++i];
		else if (!strcmp(argv[i], "-ob"))  /* outline background color */
			colors[SchemeOut][ColBg] = argv[++i];
		else if (!strcmp(argv[i], "-of"))  /* outline foreground color */
			colors[SchemeOut][ColFg] = argv[++i];
		#if HIGHPRIORITY_PATCH
		else if (!strcmp(argv[i], "-hb"))  /* high priority background color */
			colors[SchemeHp][ColBg] = argv[++i];
		else if (!strcmp(argv[i], "-hf")) /* low priority background color */
			colors[SchemeHp][ColFg] = argv[++i];
		else if (!strcmp(argv[i], "-hp"))
			hpitems = tokenize(argv[++i], ",", &hplength);
 		#endif // HIGHPRIORITY_PATCH
		#if HIGHLIGHT_PATCH
		else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */
			colors[SchemeNormHighlight][ColBg] = argv[++i];
		else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */
			colors[SchemeNormHighlight][ColFg] = argv[++i];
		else if (!strcmp(argv[i], "-shb")) /* selected hi background color */
			colors[SchemeSelHighlight][ColBg] = argv[++i];
		else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */
			colors[SchemeSelHighlight][ColFg] = argv[++i];
		#endif // HIGHLIGHT_PATCH
		#if CARET_WIDTH_PATCH
		else if (!strcmp(argv[i], "-cw"))  /* sets caret witdth */
			caret_width = atoi(argv[++i]);
		#endif // CARET_WIDTH_PATCH
		#if !XRESOURCES_PATCH
		else if (!strcmp(argv[i], "-w"))   /* embedding window id */
			embed = argv[++i];
		else
		#endif // XRESOURCES_PATCH
		#if SEPARATOR_PATCH
		else if (!strcmp(argv[i], "-d"))   /* field separator */
		{
			sepchr = strchr;
			separator = argv[++i][0];
			separator_reverse = argv[i][1] == '|';
		}
		else if (!strcmp(argv[i], "-D")) /* greedy field separator */
		{
			sepchr = strrchr;
			separator = argv[++i][0];
			separator_reverse = argv[i][1] == '|';
		}
		#endif // SEPARATOR_PATCH
		#if PRESELECT_PATCH
		else if (!strcmp(argv[i], "-ps"))   /* preselected item */
			preselected = atoi(argv[++i]);
		#endif // PRESELECT_PATCH
		#if DYNAMIC_OPTIONS_PATCH
		else if (!strcmp(argv[i], "-dy"))  /* dynamic command to run */
			dynamic = argv[++i];
		#endif // DYNAMIC_OPTIONS_PATCH
		#if BORDER_PATCH
		else if (!strcmp(argv[i], "-bw"))  /* border width around dmenu */
			border_width = atoi(argv[++i]);
		#endif // BORDER_PATCH
		#if INITIALTEXT_PATCH
		else if (!strcmp(argv[i], "-it")) {   /* adds initial text */
			const char * text = argv[++i];
			insert(text, strlen(text));
		}
		#endif // INITIALTEXT_PATCH
		else {
			usage();
		}
	}

	#if XRESOURCES_PATCH
	#if PANGO_PATCH
	if (!drw_font_create(drw, font))
		die("no fonts could be loaded.");
	#else
	if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts)))
		die("no fonts could be loaded.");
	#endif // PANGO_PATCH
	#else // !XRESOURCES_PATCH
	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
		fputs("warning: no locale support\n", stderr);
	#if INPUTMETHOD_PATCH
	if (!XSetLocaleModifiers(""))
		fputs("warning: could not set locale modifiers", stderr);
	#endif // INPUTMETHOD_PATCH
	if (!(dpy = XOpenDisplay(NULL)))
		die("cannot open display");
	screen = DefaultScreen(dpy);


@@ 844,38 2380,72 @@ main(int argc, char *argv[])
	if (!XGetWindowAttributes(dpy, parentwin, &wa))
		die("could not get embedding window attributes: 0x%lx",
		    parentwin);

	#if ALPHA_PATCH
	xinitvisual();
	drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
	#else
	drw = drw_create(dpy, screen, root, wa.width, wa.height);
	readxresources();
	/* Now we check whether to override xresources with commandline parameters */
	if ( tempfonts )
	   fonts[0] = strdup(tempfonts);
	if ( colortemp[0])
	   colors[SchemeNorm][ColBg] = strdup(colortemp[0]);
	if ( colortemp[1])
	   colors[SchemeNorm][ColFg] = strdup(colortemp[1]);
	if ( colortemp[2])
	   colors[SchemeSel][ColBg]  = strdup(colortemp[2]);
	if ( colortemp[3])
	   colors[SchemeSel][ColFg]  = strdup(colortemp[3]);
	#endif // ALPHA_PATCH

	if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts)))
	#if PANGO_PATCH
	if (!drw_font_create(drw, font))
		die("no fonts could be loaded.");
	#else
	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
		die("no fonts could be loaded.");
	#endif // PANGO_PATCH
	#endif // XRESOURCES_PATCH

	free(fonts[0]);
	#if PANGO_PATCH
	lrpad = drw->font->h;
	#else
	lrpad = drw->fonts->h;
	#endif // PANGO_PATCH

	#if BARPADDING_PATCH
	sp = sidepad;
	vp = (topbar ? vertpad : - vertpad);
	#endif // BARPADDING_PATCH

	#if LINE_HEIGHT_PATCH
	if (lineheight == -1)
		#if PANGO_PATCH
		lineheight = drw->font->h * 2.5;
		#else
		lineheight = drw->fonts->h * 2.5;
		#endif // PANGO_PATCH
	#endif // LINE_HEIGHT_PATCH

#ifdef __OpenBSD__
	if (pledge("stdio rpath", NULL) == -1)
		die("pledge");
#endif
	#if NAVHISTORY_PATCH
	loadhistory();
	#endif // NAVHISTORY_PATCH

	#if NON_BLOCKING_STDIN_PATCH
	grabkeyboard();
	#else
	if (fast && !isatty(0)) {
		grabkeyboard();
		#if DYNAMIC_OPTIONS_PATCH
		if (!(dynamic && *dynamic))
			readstdin();
		#else
		readstdin();
		#endif // DYNAMIC_OPTIONS_PATCH
	} else {
		#if DYNAMIC_OPTIONS_PATCH
		if (!(dynamic && *dynamic))
			readstdin();
		#else
		readstdin();
		#endif // DYNAMIC_OPTIONS_PATCH
		grabkeyboard();
	}
	#endif // NON_BLOCKING_STDIN_PATCH
	setup();
	run();


M dmenu_run => dmenu_run +4 -0
@@ 1,2 1,6 @@
#!/bin/sh
export _JAVA_AWT_WM_NONREPARENTING=1
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

# Uncomment for the NAVHISTORY patch (and remove the exec above)
#dmenu_path | dmenu -H "${XDG_CACHE_HOME:-$HOME/.cache/}/dmenu_run.hist" "$@" | ${SHELL:-"/bin/sh"} &
\ No newline at end of file

M drw.c => drw.c +299 -12
@@ 5,9 5,15 @@
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>

#include "patches.h"
#include "drw.h"
#include "util.h"

#if BIDI_PATCH && !PANGO_PATCH
#include <fribidi.h>
#endif //BIDI_PATCH

#if !PANGO_PATCH || HIGHLIGHT_PATCH
#define UTF_INVALID 0xFFFD

static int


@@ 46,8 52,42 @@ utf8decode(const char *s_in, long *u, int *err)
	return len;
}

#if HIGHLIGHT_PATCH
int
utf8len(const char *c)
{
	long utf8codepoint = 0;
	int utf8err = 0;
	return utf8decode(c, &utf8codepoint, &utf8err);
}
#endif // HIGHLIGHT_PATCH
#endif // PANGO_PATCH

#if BIDI_PATCH && !PANGO_PATCH
static char fribidi_text[BUFSIZ] = "";

static void
apply_fribidi(const char *str)
{
	FriBidiStrIndex len = strlen(str);
	FriBidiChar logical[BUFSIZ];
	FriBidiChar visual[BUFSIZ];
	FriBidiParType base = FRIBIDI_PAR_ON;
	FriBidiCharSet charset;

	charset = fribidi_parse_charset("UTF-8");
	len = fribidi_charset_to_unicode(charset, str, len, logical);
	fribidi_log2vis(logical, len, &base, visual, NULL, NULL, NULL);
	fribidi_unicode_to_charset(charset, visual, len, fribidi_text);
}
#endif //BIDI_PATCH

Drw *
#if ALPHA_PATCH
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
#else
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
#endif // ALPHA_PATCH
{
	Drw *drw = ecalloc(1, sizeof(Drw));



@@ 56,8 96,16 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
	drw->root = root;
	drw->w = w;
	drw->h = h;
	#if ALPHA_PATCH
	drw->visual = visual;
	drw->depth = depth;
	drw->cmap = cmap;
	drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
	drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
	#else
	drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
	drw->gc = XCreateGC(dpy, root, 0, NULL);
	#endif // ALPHA_PATCH
	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);

	return drw;


@@ 73,7 121,11 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
	drw->h = h;
	if (drw->drawable)
		XFreePixmap(drw->dpy, drw->drawable);
	#if ALPHA_PATCH
	drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
	#else
	drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
	#endif // ALPHA_PATCH
}

void


@@ 81,10 133,49 @@ drw_free(Drw *drw)
{
	XFreePixmap(drw->dpy, drw->drawable);
	XFreeGC(drw->dpy, drw->gc);
	#if PANGO_PATCH
	drw_font_free(drw->font);
	#else
	drw_fontset_free(drw->fonts);
	#endif // PANGO_PATCH
	free(drw);
}

#if PANGO_PATCH
/* This function is an implementation detail. Library users should use
 * drw_font_create instead.
 */
static Fnt *
xfont_create(Drw *drw, const char *fontname)
{
	Fnt *font;
	PangoFontMap *fontmap;
	PangoContext *context;
	PangoFontDescription *desc;
	PangoFontMetrics *metrics;

	if (!fontname) {
		die("no font specified.");
	}

	font = ecalloc(1, sizeof(Fnt));
	font->dpy = drw->dpy;

	fontmap = pango_xft_get_font_map(drw->dpy, drw->screen);
	context = pango_font_map_create_context(fontmap);
	desc = pango_font_description_from_string(fontname);
	font->layout = pango_layout_new(context);
	pango_layout_set_font_description(font->layout, desc);

	metrics = pango_context_get_metrics(context, desc, pango_language_from_string ("en-us"));
	font->h = pango_font_metrics_get_height(metrics) / PANGO_SCALE;

	pango_font_metrics_unref(metrics);
	g_object_unref(context);

	return font;
}
#else
/* This function is an implementation detail. Library users should use
 * drw_fontset_create instead.
 */


@@ 119,6 210,21 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
		die("no font specified.");
	}

	#if NO_COLOR_EMOJI_PATCH
	/* Do not allow using color fonts. This is a workaround for a BadLength
	 * error from Xft with color glyphs. Modelled on the Xterm workaround. See
	 * https://bugzilla.redhat.com/show_bug.cgi?id=1498269
	 * https://lists.suckless.org/dev/1701/30932.html
	 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
	 * and lots more all over the internet.
	 */
	FcBool iscol;
	if (FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
		XftFontClose(drw->dpy, xfont);
		return NULL;
	}
	#endif // NO_COLOR_EMOJI_PATCH

	font = ecalloc(1, sizeof(Fnt));
	font->xfont = xfont;
	font->pattern = pattern;


@@ 127,18 233,38 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)

	return font;
}
#endif // PANGO_PATCH

static void
xfont_free(Fnt *font)
{
	if (!font)
		return;
	#if PANGO_PATCH
	if (font->layout)
		g_object_unref(font->layout);
	#else
	if (font->pattern)
		FcPatternDestroy(font->pattern);
	XftFontClose(font->dpy, font->xfont);
	#endif // PANGO_PATCH
	free(font);
}

#if PANGO_PATCH
Fnt*
drw_font_create(Drw* drw, const char *font)
{
	Fnt *fnt = NULL;

	if (!drw || !font)
		return NULL;

	fnt = xfont_create(drw, font);

	return (drw->font = fnt);
}
#else
Fnt*
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
{


@@ 156,7 282,16 @@ drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
	}
	return (drw->fonts = ret);
}
#endif // PANGO_PATCH

#if PANGO_PATCH
void
drw_font_free(Fnt *font)
{
	if (font)
		xfont_free(font);
}
#else
void
drw_fontset_free(Fnt *font)
{


@@ 165,22 300,39 @@ drw_fontset_free(Fnt *font)
		xfont_free(font);
	}
}
#endif // PANGO_PATCH

void
#if ALPHA_PATCH
drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
#else
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
#endif // ALPHA_PATCH
{
	if (!drw || !dest || !clrname)
		return;

	#if ALPHA_PATCH
	if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
	                       clrname, dest))
		die("error, cannot allocate color '%s'", clrname);

	dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
	#else
	if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
	                       DefaultColormap(drw->dpy, drw->screen),
	                       clrname, dest))
		die("error, cannot allocate color '%s'", clrname);
	#endif // ALPHA_PATCH
}

/* Create color schemes. */
Clr *
#if ALPHA_PATCH
drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
#else
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
#endif // ALPHA_PATCH
{
	size_t i;
	Clr *ret;


@@ 190,7 342,11 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
		return NULL;

	for (i = 0; i < clrcount; i++)
		#if ALPHA_PATCH
		drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
		#else
		drw_clr_create(drw, &ret[i], clrnames[i]);
		#endif // ALPHA_PATCH
	return ret;
}



@@ 218,12 374,14 @@ drw_scm_free(Drw *drw, Clr *scm, size_t clrcount)
	free(scm);
}

#if !PANGO_PATCH
void
drw_setfontset(Drw *drw, Fnt *set)
{
	if (drw)
		drw->fonts = set;
}
#endif // PANGO_PATCH

void
drw_setscheme(Drw *drw, Clr *scm)


@@ 244,6 402,77 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
		XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
}

#if PANGO_PATCH
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup)
{
	char buf[1024];
	int i, ty, th;
	unsigned int ew, eh;
	XftDraw *d = NULL;
	size_t len;
	int render = x || y || w || h;

	if (!drw || (render && !drw->scheme) || !text || !drw->font)
		return 0;

	if (!render) {
		w = invert ? invert : ~invert;
	} else {
		XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
		#if ALPHA_PATCH
		d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
		#else
		d = XftDrawCreate(drw->dpy, drw->drawable,
		                  DefaultVisual(drw->dpy, drw->screen),
		                  DefaultColormap(drw->dpy, drw->screen));
		#endif // ALPHA_PATCH
		x += lpad;
		w -= lpad;
	}

	len = strlen(text);

	if (len) {
		drw_font_getexts(drw->font, text, len, &ew, &eh, markup);
		th = eh;
		/* shorten text if necessary */
		for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--) {
			drw_font_getexts(drw->font, text, len, &ew, &eh, markup);
			if (eh > th)
				th = eh;
		}

		if (len) {
			memcpy(buf, text, len);
			buf[len] = '\0';
			if (len < strlen(text))
				for (i = len; i && i > len - 3; buf[--i] = '.')
					; /* NOP */

			if (render) {
				ty = y + (h - th) / 2;
				if (markup)
					pango_layout_set_markup(drw->font->layout, buf, len);
				else
					pango_layout_set_text(drw->font->layout, buf, len);
				pango_xft_render_layout(d, &drw->scheme[invert ? ColBg : ColFg],
					drw->font->layout, x * PANGO_SCALE, ty * PANGO_SCALE);
				if (markup) /* clear markup attributes */
					pango_layout_set_attributes(drw->font->layout, NULL);
			}
			x += ew;
			w -= ew;
		}
	}

	if (d)
		XftDrawDestroy(d);

	return x + (render ? w : 0);
}
#else
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{


@@ 262,6 491,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
	/* keep track of a couple codepoints for which we have no match. */
	static unsigned int nomatches[128], ellipsis_width, invalid_width;
	static const char invalid[] = "�";
	const char *ellipsis = "...";

	if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
		return 0;


@@ 271,22 501,30 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
	} else {
		XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
		if (w < lpad)
			return x + w;
		#if ALPHA_PATCH
		d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
		#else
		d = XftDrawCreate(drw->dpy, drw->drawable,
		                  DefaultVisual(drw->dpy, drw->screen),
		                  DefaultColormap(drw->dpy, drw->screen));
		#endif // ALPHA_PATCH
		x += lpad;
		w -= lpad;
	}

	usedfont = drw->fonts;
	if (!ellipsis_width && render)
		ellipsis_width = drw_fontset_getwidth(drw, "...");
		ellipsis_width = drw_fontset_getwidth(drw, ellipsis);
	if (!invalid_width && render)
		invalid_width = drw_fontset_getwidth(drw, invalid);

	#if BIDI_PATCH
	apply_fribidi(text);
	text = fribidi_text;
	#endif // BIDI_PATCH

	usedfont = drw->fonts;
	while (1) {
		ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
		ew = ellipsis_len = utf8err = utf8strlen = 0;
		utf8str = text;
		nextfont = NULL;
		while (*text) {


@@ 312,12 550,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
						else
							utf8strlen = ellipsis_len;
					} else if (curfont == usedfont) {
						text += utf8charlen;
 						text += utf8charlen;
						utf8strlen += utf8err ? 0 : utf8charlen;
						ew += utf8err ? 0 : tmpw;
					} else {
						nextfont = curfont;
					}
 					} else {
 						nextfont = curfont;
 					}
					break;
				}
			}


@@ 333,7 571,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
				ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
				XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
				                  usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
			}
 			}
			x += ew;
			w -= ew;
		}


@@ 343,8 581,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
			x += invalid_width;
			w -= invalid_width;
		}
		if (render && overflow)
			drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
		if (render && overflow && ellipsis_w)
			drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, ellipsis, invert);

		if (!*text || overflow) {
			break;


@@ 376,6 614,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
			fcpattern = FcPatternDuplicate(drw->fonts->pattern);
			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
			#if NO_COLOR_EMOJI_PATCH
			FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
			#endif // NO_COLOR_EMOJI_PATCH

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


@@ 404,6 645,7 @@ no_match:

	return x + (render ? w : 0);
}
#endif // PANGO_PATCH

void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)


@@ 415,6 657,24 @@ drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
	XSync(drw->dpy, False);
}

#if PANGO_PATCH
unsigned int
drw_font_getwidth(Drw *drw, const char *text, Bool markup)
{
	if (!drw || !drw->font || !text)
		return 0;
	return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup);
}

unsigned int
drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
{
	unsigned int tmp = 0;
	if (drw && drw->font && text && n)
		tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n, True);
	return MIN(n, tmp);
}
#else
unsigned int
drw_fontset_getwidth(Drw *drw, const char *text)
{


@@ 431,7 691,29 @@ drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
		tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
	return MIN(n, tmp);
}
#endif // PANGO_PATCH

#if PANGO_PATCH
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup)
{
	if (!font || !text)
		return;

	PangoRectangle r;
	if (markup)
		pango_layout_set_markup(font->layout, text, len);
	else
		pango_layout_set_text(font->layout, text, len);
	pango_layout_get_extents(font->layout, 0, &r);
	if (markup) /* clear markup attributes */
		pango_layout_set_attributes(font->layout, NULL);
	if (w)
		*w = r.width / PANGO_SCALE;
	if (h)
		*h = r.height / PANGO_SCALE;
}
#else
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{


@@ 446,6 728,7 @@ drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w,
	if (h)
		*h = font->h;
}
#endif // PANGO_PATCH

Cur *
drw_cur_create(Drw *drw, int shape)


@@ 469,3 752,7 @@ drw_cur_free(Drw *drw, Cur *cursor)
	XFreeCursor(drw->dpy, cursor->cursor);
	free(cursor);
}

#if SCROLL_PATCH
#include "patch/scroll.c"
#endif

M drw.h => drw.h +50 -1
@@ 1,5 1,10 @@
/* See LICENSE file for copyright and license details. */

#if PANGO_PATCH
#include <pango/pango.h>
#include <pango/pangoxft.h>
#endif // PANGO_PATCH

typedef struct {
	Cursor cursor;
} Cur;


@@ 7,9 12,13 @@ typedef struct {
typedef struct Fnt {
	Display *dpy;
	unsigned int h;
	#if PANGO_PATCH
	PangoLayout *layout;
	#else
	XftFont *xfont;
	FcPattern *pattern;
	struct Fnt *next;
	#endif // PANGO_PATCH
} Fnt;

enum { ColFg, ColBg }; /* Clr scheme index */


@@ 20,28 29,58 @@ typedef struct {
	Display *dpy;
	int screen;
	Window root;
	#if ALPHA_PATCH
	Visual *visual;
	unsigned int depth;
	Colormap cmap;
	#endif // ALPHA_PATCH
	Drawable drawable;
	GC gc;
	Clr *scheme;
	#if PANGO_PATCH
	Fnt *font;
	#else
	Fnt *fonts;
	#endif // PANGO_PATCH
} Drw;

/* Drawable abstraction */
#if ALPHA_PATCH
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap);
#else
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
#endif // ALPHA_PATCH
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);

/* Fnt abstraction */
#if PANGO_PATCH
Fnt *drw_font_create(Drw* drw, const char *font);
void drw_font_free(Fnt* set);
unsigned int drw_font_getwidth(Drw *drw, const char *text, Bool markup);
unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup);
#else
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_fontset_free(Fnt* set);
unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
#endif // PANGO_PATCH

#if HIGHLIGHT_PATCH
int utf8len(const char *c);
#endif // HIGHLIGHT_PATCH

/* Colorscheme abstraction */
#if ALPHA_PATCH
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
#else
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
void drw_clr_free(Drw *drw, Clr *c);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
#endif // ALPHA_PATCH
void drw_clr_free(Drw *drw, Clr *c);
void drw_scm_free(Drw *drw, Clr *scm, size_t clrcount);

/* Cursor abstraction */


@@ 49,12 88,22 @@ Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *drw, Cur *cursor);

/* Drawing context manipulation */
#if !PANGO_PATCH
void drw_setfontset(Drw *drw, Fnt *set);
#endif // PANGO_PATCH
void drw_setscheme(Drw *drw, Clr *scm);

/* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
#if PANGO_PATCH
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup);
#else
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
#endif // PANGO_PATCH

/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

#if SCROLL_PATCH
#include "patch/scroll.h"
#endif

A patch/center.c => patch/center.c +8 -0
@@ 0,0 1,8 @@
static int
max_textw(void)
{
	int len = 0;
	for (struct item *item = items; item && item->text; item++)
		len = MAX(TEXTW(item->text), len);
	return len;
}

A patch/dynamicoptions.c => patch/dynamicoptions.c +90 -0
@@ 0,0 1,90 @@
static void
refreshoptions(void)
{
	int dynlen = strlen(dynamic);
	char* cmd= malloc(dynlen + strlen(text) + 2);
	if (cmd == NULL)
		die("malloc:");
	sprintf(cmd, "%s %s", dynamic, text);
	FILE *stream = popen(cmd, "r");
	if (!stream)
		die("popen(%s):", cmd);
	readstream(stream);
	int pc = pclose(stream);
	if (pc == -1)
		die("pclose:");
	free(cmd);
	curr = sel = items;
}

static void
readstream(FILE* stream)
{
	char buf[sizeof text], *p;
	size_t i, imax = 0, size = 0;
	unsigned int tmpmax = 0;

	/* read each line from stdin and add it to the item list */
	for (i = 0; fgets(buf, sizeof buf, stream); i++) {
		if (i + 1 >= size / sizeof *items)
			if (!(items = realloc(items, (size += BUFSIZ))))
				die("cannot realloc %u bytes:", size);
		if ((p = strchr(buf, '\n')))
			*p = '\0';
		if (!(items[i].text = strdup(buf)))
			die("cannot strdup %u bytes:", strlen(buf) + 1);
		#if SEPARATOR_PATCH
		if (separator && (p = sepchr(items[i].text, separator)) != NULL) {
			*p = '\0';
			items[i].text_output = ++p;
		} else {
			items[i].text_output = items[i].text;
		}
		if (separator_reverse) {
			p = items[i].text;
			items[i].text = items[i].text_output;
			items[i].text_output = p;
		}
		#elif TSV_PATCH
		if ((p = strchr(buf, '\t')))
			*p = '\0';
		if (!(items[i].stext = strdup(buf)))
			die("cannot strdup %u bytes:", strlen(buf) + 1);
		#endif // TSV_PATCH
		#if MULTI_SELECTION_PATCH
		items[i].id = i;
		#else
		items[i].out = 0;
		#endif // MULTI_SELECTION_PATCH
		#if HIGHPRIORITY_PATCH
		items[i].hp = arrayhas(hpitems, hplength, items[i].text);
		#endif // HIGHPRIORITY_PATCH
		#if PANGO_PATCH
		drw_font_getexts(drw->font, buf, strlen(buf), &tmpmax, NULL, True);
		#else
		drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
		#endif // PANGO_PATCH
		if (tmpmax > inputw) {
			inputw = tmpmax;
			imax = i;
		}
	}

	/* If the command did not give any output at all, then do not clear the existing items */
	if (!i)
		return;

	if (items)
		items[i].text = NULL;
	#if PANGO_PATCH
	inputw = items ? TEXTWM(items[imax].text) : 0;
	#else
	inputw = items ? TEXTW(items[imax].text) : 0;
	#endif // PANGO_PATCH
	if (!dynamic || !*dynamic)
		lines = MIN(lines, i);
	else {
		text[0] = '\0';
		cursor = 0;
	}
}

A patch/dynamicoptions.h => patch/dynamicoptions.h +2 -0
@@ 0,0 1,2 @@
static void refreshoptions(void);
static void readstream(FILE* stream);

A patch/fuzzymatch.c => patch/fuzzymatch.c +115 -0
@@ 0,0 1,115 @@
#include <math.h>

int
compare_distance(const void *a, const void *b)
{
	struct item *da = *(struct item **) a;
	struct item *db = *(struct item **) b;

	if (!db)
		return 1;
	if (!da)
		return -1;

	return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1;
}

void
fuzzymatch(void)
{
	/* bang - we have so much memory */
	struct item *it;
	struct item **fuzzymatches = NULL;
	char c;
	int number_of_matches = 0, i, pidx, sidx, eidx;
	int text_len = strlen(text), itext_len;
	#if HIGHPRIORITY_PATCH
	struct item *lhpprefix, *hpprefixend;
	lhpprefix = hpprefixend = NULL;
	#endif // HIGHPRIORITY_PATCH
	matches = matchend = NULL;

	/* walk through all items */
	for (it = items; it && it->text; it++) {
		if (text_len) {
			itext_len = strlen(it->text);
			pidx = 0; /* pointer */
			sidx = eidx = -1; /* start of match, end of match */
			/* walk through item text */
			for (i = 0; i < itext_len && (c = it->text[i]); i++) {
				/* fuzzy match pattern */
				if (!fstrncmp(&text[pidx], &c, 1)) {
					if (sidx == -1)
						sidx = i;
					pidx++;
					if (pidx == text_len) {
						eidx = i;
						break;
					}
				}
			}
			/* build list of matches */
			if (eidx != -1) {
				/* compute distance */
				/* add penalty if match starts late (log(sidx+2))
				 * add penalty for long a match without many matching characters */
				it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);
				/* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */
				appenditem(it, &matches, &matchend);
				number_of_matches++;
			}
		} else {
			appenditem(it, &matches, &matchend);
		}
	}

	if (number_of_matches) {
		/* initialize array with matches */
		if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*))))
			die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*));
		for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) {
			fuzzymatches[i] = it;
		}

		#if NO_SORT_PATCH
		if (sortmatches)
		#endif // NO_SORT_PATCH
		/* sort matches according to distance */
		qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);
		/* rebuild list of matches */
		matches = matchend = NULL;
		for (i = 0, it = fuzzymatches[i];  i < number_of_matches && it && \
				it->text; i++, it = fuzzymatches[i]) {
			#if HIGHPRIORITY_PATCH
			#if NO_SORT_PATCH
			if (sortmatches && it->hp)
			#else
			if (it->hp)
			#endif // NO_SORT_PATCH
				appenditem(it, &lhpprefix, &hpprefixend);
			else
				appenditem(it, &matches, &matchend);
			#else
			appenditem(it, &matches, &matchend);
			#endif // HIGHPRIORITY_PATCH
		}
		free(fuzzymatches);
	}
	#if HIGHPRIORITY_PATCH
	if (lhpprefix) {
		hpprefixend->right = matches;
		matches = lhpprefix;
	}
	#endif // HIGHPRIORITY_PATCH
	curr = sel = matches;

	#if INSTANT_PATCH
	if (instant && matches && matches==matchend) {
		printitem(matches);
		cleanup();
		exit(0);
	}
	#endif // INSTANT_PATCH

	calcoffsets();
}

A patch/fzfexpect.c => patch/fzfexpect.c +33 -0
@@ 0,0 1,33 @@
static char *expected;
#if MULTI_SELECTION_PATCH
void
expect(char *expect, XKeyEvent *ev)
{
	if (sel && expected && strstr(expected, expect)) {
		if (expected && sel && !(ev->state & ShiftMask))
			puts(expect);
		printselected(ev->state);
		cleanup();
		exit(1);
	} else if (!sel && expected && strstr(expected, expect)) {
		puts(expect);
		cleanup();
		exit(1);
	}
}
#else
void
expect(char *expect, XKeyEvent *ignored)
{
	if (sel && expected && strstr(expected, expect)) {
		puts(expect);
		printitem(sel);
		cleanup();
		exit(1);
	} else if (!sel && expected && strstr(expected, expect)){
		puts(expect);
		cleanup();
		exit(1);
	}
}
#endif // MULTI_SELECTION_PATCH

A patch/fzfexpect.h => patch/fzfexpect.h +1 -0
@@ 0,0 1,1 @@
static void expect(char *expect, XKeyEvent *ev);

A patch/highlight.c => patch/highlight.c +107 -0
@@ 0,0 1,107 @@
static void
#if EMOJI_HIGHLIGHT_PATCH
drawhighlights(struct item *item, char *output, int x, int y, int maxw)
#else
drawhighlights(struct item *item, int x, int y, int maxw)
#endif // EMOJI_HIGHLIGHT_PATCH
{
	char restorechar, tokens[sizeof text], *highlight,  *token;
	int indent, highlightlen;

	#if FUZZYMATCH_PATCH
	int i;
	#endif // FUZZYMATCH_PATCH

	#if EMOJI_HIGHLIGHT_PATCH
	char *itemtext = output;
	#elif TSV_PATCH && !SEPARATOR_PATCH
	char *itemtext = item->stext;
	#else
	char *itemtext = item->text;
	#endif // EMOJI_HIGHLIGHT_PATCH | TSV_PATCH

	if (!(strlen(itemtext) && strlen(text)))
		return;

	/* Do not highlight items scheduled for output */
	#if MULTI_SELECTION_PATCH
	if (issel(item->id))
		return;
	#else
	if (item->out)
		return;
	#endif // MULTI_SELECTION_PATCH

	drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : SchemeNormHighlight]);

	#if FUZZYMATCH_PATCH
	if (fuzzy) {
		for (i = 0, highlight = itemtext; *highlight && text[i];) {
			highlightlen = utf8len(highlight);
			#if FUZZYMATCH_PATCH
			if (!fstrncmp(&(*highlight), &text[i], highlightlen))
			#else
			if (*highlight == text[i])
			#endif // FUZZYMATCH_PATCH
			{
				/* get indentation */
				restorechar = *highlight;
				*highlight = '\0';
				indent = TEXTW(itemtext) - lrpad;
				*highlight = restorechar;

				/* highlight character */
				restorechar = highlight[highlightlen];
				highlight[highlightlen] = '\0';
				drw_text(
					drw,
					x + indent + (lrpad / 2),
					y,
					MIN(maxw - indent - lrpad, TEXTW(highlight) - lrpad),
					bh, 0, highlight, 0
					#if PANGO_PATCH
					, True
					#endif // PANGO_PATCH
				);
				highlight[highlightlen] = restorechar;
				i += highlightlen;
			}
			highlight++;
		}
		return;
	}
	#endif // FUZZYMATCH_PATCH

	strcpy(tokens, text);
	for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) {
		highlight = fstrstr(itemtext, token);
		while (highlight) {
			// Move item str end, calc width for highlight indent, & restore
			highlightlen = highlight - itemtext;
			restorechar = *highlight;
			itemtext[highlightlen] = '\0';
			indent = TEXTW(itemtext);
			itemtext[highlightlen] = restorechar;

			// Move highlight str end, draw highlight, & restore
			restorechar = highlight[strlen(token)];
			highlight[strlen(token)] = '\0';
			if (indent - (lrpad / 2) - 1 < maxw)
				drw_text(
					drw,
					x + indent - (lrpad / 2) - 1,
					y,
					MIN(maxw - indent, TEXTW(highlight) - lrpad),
					bh, 0, highlight, 0
					#if PANGO_PATCH
					, True
					#endif // PANGO_PATCH
				);
			highlight[strlen(token)] = restorechar;

			if (strlen(highlight) - strlen(token) < strlen(token))
				break;
			highlight = fstrstr(highlight + strlen(token), token);
		}
	}
}

A patch/highpriority.c => patch/highpriority.c +35 -0
@@ 0,0 1,35 @@
static char **hpitems = NULL;
static int hplength = 0;

static char **
tokenize(char *source, const char *delim, int *llen)
{
	int listlength = 0, list_size = 0;
	char **list = NULL, *token;

	token = strtok(source, delim);
	while (token) {
		if (listlength + 1 >= list_size) {
			if (!(list = realloc(list, (list_size += 8) * sizeof(*list))))
				die("Unable to realloc %zu bytes\n", list_size * sizeof(*list));
		}
		if (!(list[listlength] = strdup(token)))
			die("Unable to strdup %zu bytes\n", strlen(token) + 1);
		token = strtok(NULL, delim);
		listlength++;
	}

	*llen = listlength;
	return list;
}

static int
arrayhas(char **list, int length, char *item) {
	for (int i = 0; i < length; i++) {
		int len1 = strlen(list[i]);
		int len2 = strlen(item);
		if (fstrncmp(list[i], item, len1 > len2 ? len2 : len1) == 0)
			return 1;
	}
	return 0;
}

A patch/highpriority.h => patch/highpriority.h +1 -0
@@ 0,0 1,1 @@
static int arrayhas(char **list, int length, char *item);

A patch/include.c => patch/include.c +42 -0
@@ 0,0 1,42 @@
#if CENTER_PATCH
#include "center.c"
#endif
#if HIGHLIGHT_PATCH
#include "highlight.c"
#endif
#if FUZZYMATCH_PATCH
#include "fuzzymatch.c"
#endif
#if FZFEXPECT_PATCH
#include "fzfexpect.c"
#endif
#if HIGHPRIORITY_PATCH
#include "highpriority.c"
#endif
#if INPUTMETHOD_PATCH
#include "inputmethod.c"
#endif
#if DYNAMIC_OPTIONS_PATCH
#include "dynamicoptions.c"
#endif
#if MULTI_SELECTION_PATCH
#include "multiselect.c"
#endif
#if MOUSE_SUPPORT_PATCH
#include "mousesupport.c"
#endif
#if NAVHISTORY_PATCH
#include "navhistory.c"
#endif
#if VI_MODE_PATCH
#include "vi_mode.c"
#endif
#if NON_BLOCKING_STDIN_PATCH
#include "nonblockingstdin.c"
#endif
#if NUMBERS_PATCH
#include "numbers.c"
#endif
#if XRESOURCES_PATCH
#include "xresources.c"
#endif

A patch/include.h => patch/include.h +30 -0
@@ 0,0 1,30 @@
#if DYNAMIC_OPTIONS_PATCH
#include "dynamicoptions.h"
#endif
#if FZFEXPECT_PATCH
#include "fzfexpect.h"
#endif
#if INPUTMETHOD_PATCH
#include "inputmethod.h"
#endif
#if MOUSE_SUPPORT_PATCH
#include "mousesupport.h"
#endif
#if MULTI_SELECTION_PATCH
#include "multiselect.h"
#endif
#if NAVHISTORY_PATCH
#include "navhistory.h"
#endif
#if VI_MODE_PATCH
#include "vi_mode.h"
#endif
#if HIGHPRIORITY_PATCH
#include "highpriority.h"
#endif
#if NON_BLOCKING_STDIN_PATCH
#include "nonblockingstdin.h"
#endif
#if NUMBERS_PATCH
#include "numbers.h"
#endif

A patch/inputmethod.c => patch/inputmethod.c +196 -0
@@ 0,0 1,196 @@
static size_t nextrunetext(const char *text, size_t position, int inc)
{
	ssize_t n;

	/* return location of next utf8 rune in the given direction (+1 or -1) */
	for (n = position + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
		;
	return n;
}

/* return bytes from beginning of text to nth utf8 rune to the right */
static size_t runebytes(const char *text, size_t n)
{
	size_t ret;

	ret = 0;
	while (n-- > 0)
		ret += nextrunetext(text + ret, 0, 1);
	return ret;
}

/* return number of characters from beginning of text to nth byte to the right
 */
static size_t runechars(const char *text, size_t n)
{
	size_t ret, i;

	ret = i = 0;
	while (i < n) {
		i += nextrunetext(text + i, 0, 1);
		ret++;
	}
	return ret;
}

/* move caret on pre-edit text */
static void preeditcaret(XIC xic, XPointer clientdata, XPointer calldata)
{
	XIMPreeditCaretCallbackStruct *pcaret;

	(void)xic;
	pcaret = (XIMPreeditCaretCallbackStruct *)calldata;
	if (!pcaret)
		return;
	switch (pcaret->direction) {
	case XIMForwardChar:
		cursor = nextrune(+1);
		break;
	case XIMBackwardChar:
		cursor = nextrune(-1);
		break;
	case XIMForwardWord:
		movewordedge(+1);
		break;
	case XIMBackwardWord:
		movewordedge(-1);
		break;
	case XIMLineStart:
		cursor = 0;
		break;
	case XIMLineEnd:
		if (preview[cursor] != '\0')
			cursor = strlen(preview);
		break;
	case XIMAbsolutePosition:
		cursor = runebytes(text, pcaret->position);
		break;
	case XIMDontChange:
		/* do nothing */
		break;
	case XIMCaretUp:
	case XIMCaretDown:
	case XIMNextLine:
	case XIMPreviousLine:
		/* not implemented */
		break;
	}
	pcaret->position = runechars(preview, cursor);
}

/* start input method pre-editing */
static int preeditstart(XIC xic, XPointer clientdata, XPointer calldata)
{
	(void)xic;
	(void)calldata;
	(void)clientdata;
	composing = 1;
	printf("PREEDIT\n");
	return BUFSIZ;
}

/* end input method pre-editing */
static void preeditdone(XIC xic, XPointer clientdata, XPointer calldata)
{
	(void)xic;
	(void)clientdata;
	(void)calldata;
	printf("DONE\n");

	composing = 0;
}

/* draw input method pre-edit text */
static void preeditdraw(XIC xic, XPointer clientdata, XPointer calldata)
{
	XIMPreeditDrawCallbackStruct *pdraw;
	size_t beg, dellen, inslen, endlen;

	printf("DRAW\n");

	(void)xic;
	pdraw = (XIMPreeditDrawCallbackStruct *)calldata;
	if (!pdraw)
		return;

	/* we do not support wide characters */
	if (pdraw->text && pdraw->text->encoding_is_wchar == True) {
		fputs("warning: wchar is not supportecd; use utf8", stderr);
		return;
	}

	beg = runebytes(text, pdraw->chg_first);
	dellen = runebytes(preview + beg, pdraw->chg_length);
	inslen = pdraw->text ? runebytes(pdraw->text->string.multi_byte, pdraw->text->length) : 0;
	endlen = 0;
	if (beg + dellen < strlen(preview))
		endlen = strlen(preview + beg + dellen);

	/* we cannot change text past the end of our pre-edit string */

	if (beg + dellen >= BUFSIZ || beg + inslen >= BUFSIZ)
		return;

	/* get space for text to be copied, and copy it */
	memmove(preview + beg + inslen, preview + beg + dellen, endlen + 1);
	if (pdraw->text && pdraw->text->length)
		memcpy(preview + beg, pdraw->text->string.multi_byte, inslen);
	(preview + beg + inslen + endlen)[0] = '\0';

	/* get caret position */
	cursor = runebytes(text, pdraw->caret);
}

static void init_input_method(XIM xim)
{
	XVaNestedList preedit = NULL;
	XICCallback start, done, draw, caret;
	XIMStyle preeditstyle;
	XIMStyle statusstyle;
	XIMStyles *imstyles;
	int i;

	/* get styles supported by input method */
	if (XGetIMValues(xim, XNQueryInputStyle, &imstyles, NULL) != NULL)
		fputs("XGetIMValues: could not obtain input method values", stderr);

	/* check whether input method support on-the-spot pre-editing */
	preeditstyle = XIMPreeditNothing;
	statusstyle = XIMStatusNothing;
	for (i = 0; i < imstyles->count_styles; i++) {
		if (imstyles->supported_styles[i] & XIMPreeditCallbacks) {
			preeditstyle = XIMPreeditCallbacks;
			break;
		}
	}

	/* create callbacks for the input context */
	start.client_data = NULL;
	done.client_data = NULL;
	draw.client_data = (XPointer)text;
	caret.client_data = (XPointer)text;
	start.callback = (XICProc)preeditstart;
	done.callback = (XICProc)preeditdone;
	draw.callback = (XICProc)preeditdraw;
	caret.callback = (XICProc)preeditcaret;

	/* create list of values for input context */
	preedit = XVaCreateNestedList(0, XNPreeditStartCallback, &start, XNPreeditDoneCallback,
				      &done, XNPreeditDrawCallback, &draw, XNPreeditCaretCallback,
				      &caret, NULL);
	if (preedit == NULL)
		fputs("XVaCreateNestedList: could not create nested list", stderr);

	xic = XCreateIC(xim, XNInputStyle, preeditstyle | statusstyle, XNPreeditAttributes, preedit,
			XNClientWindow, win, XNFocusWindow, win, NULL);
	XFree(preedit);

	long eventmask;
	/* get events the input method is interested in */
	if (XGetICValues(xic, XNFilterEvents, &eventmask, NULL))
		fputs("XGetICValues: could not obtain input context values", stderr);

	XSelectInput(dpy, win,
		     ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonPressMask |
			     PointerMotionMask | eventmask);
}

A patch/inputmethod.h => patch/inputmethod.h +11 -0
@@ 0,0 1,11 @@
static int composing;
static char preview[512] = "";

static size_t nextrunetext(const char *text, size_t position, int inc);
static size_t runebytes(const char *text, size_t n);
static size_t runechars(const char *text, size_t n);
static void preeditcaret(XIC xic, XPointer clientdata, XPointer calldata);
static int preeditstart(XIC xic, XPointer clientdata, XPointer calldata);
static void preeditdone(XIC xic, XPointer clientdata, XPointer calldata);
static void preeditdraw(XIC xic, XPointer clientdata, XPointer calldata);
static void init_input_method(XIM xim);

A patch/mousesupport.c => patch/mousesupport.c +249 -0
@@ 0,0 1,249 @@
void
buttonpress(XEvent *e)
{
	struct item *item;
	XButtonPressedEvent *ev = &e->xbutton;
	int x = 0, y = 0, h = bh, w;
	#if GRID_PATCH
	int i, cols;
	#endif // GRID_PATCH

	if (ev->window != win) {
		/* automatically close dmenu if the user clicks outside of dmenu, but
		 * ignore the scroll wheel and buttons above that */
		if (ev->button <= Button3) {
			exit(1);
		}
		return;
	}

	/* right-click: exit */
	if (ev->button == Button3)
		exit(1);

	if (prompt && *prompt)
		x += promptw;

	/* input field */
	w = (lines > 0 || !matches) ? mw - x : inputw;

	/* left-click on input: clear input,
	 * NOTE: if there is no left-arrow the space for < is reserved so
	 *       add that to the input width */
	#if SYMBOLS_PATCH
	if (ev->button == Button1 &&
	   ((lines <= 0 && ev->x >= 0 && ev->x <= x + w +
	   ((!prev || !curr->left) ? TEXTW(symbol_1) : 0)) ||
	   (lines > 0 && ev->y >= y && ev->y <= y + h))) {
		insert(NULL, -cursor);
		drawmenu();
		return;
	}
	#else
	if (ev->button == Button1 &&
	   ((lines <= 0 && ev->x >= 0 && ev->x <= x + w +
	   ((!prev || !curr->left) ? TEXTW("<") : 0)) ||
	   (lines > 0 && ev->y >= y && ev->y <= y + h))) {
		insert(NULL, -cursor);
		drawmenu();
		return;
	}
	#endif // SYMBOLS_PATCH
	/* middle-mouse click: paste selection */
	if (ev->button == Button2) {
		XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
		                  utf8, utf8, win, CurrentTime);
		drawmenu();
		return;
	}
	/* scroll up */
	if (ev->button == Button4 && prev) {
		sel = curr = prev;
		calcoffsets();
		drawmenu();
		return;
	}
	/* scroll down */
	if (ev->button == Button5 && next) {
		sel = curr = next;
		calcoffsets();
		drawmenu();
		return;
	}
	if (ev->button != Button1)
		return;
	if (ev->state & ShiftMask)
		return;
	if (lines > 0) {
		#if GRID_PATCH
		cols = columns ? columns : 1;
		for (i = 0, item = curr; item != next; item = item->right, i++) {
			if (
				(ev->y >= y + ((i % lines) + 1) * bh) && // line y start
				(ev->y <= y + ((i % lines) + 2) * bh) && // line y end
				(ev->x >= x + ((i / lines) * (w / cols))) && // column x start
				(ev->x <= x + ((i / lines + 1) * (w / cols))) // column x end
			) {
				clickitem(item, ev);
				return;
			}
		}
		#else
		/* vertical list: (ctrl)left-click on item */
		for (item = curr; item != next; item = item->right) {
			y += h;
			if (ev->y >= y && ev->y <= (y + h)) {
				clickitem(item, ev);
				return;
			}
		}
		#endif // GRID_PATCH
	} else if (matches) {
		/* left-click on left arrow */
		x += inputw;
		#if SYMBOLS_PATCH
		w = TEXTW(symbol_1);
		#else
		w = TEXTW("<");
		#endif // SYMBOLS_PATCH
		if (prev && curr->left) {
			if (ev->x >= x && ev->x <= x + w) {
				sel = curr = prev;
				calcoffsets();
				drawmenu();
				return;
			}
		}
		/* horizontal list: (ctrl)left-click on item */
		for (item = curr; item != next; item = item->right) {
			x += w;
			#if SYMBOLS_PATCH
			w = MIN(TEXTW(item->text), mw - x - TEXTW(symbol_2));
			#else
			w = MIN(TEXTW(item->text), mw - x - TEXTW(">"));
			#endif // SYMBOLS_PATCH
			if (ev->x >= x && ev->x <= x + w) {
				clickitem(item, ev);
				return;
			}
		}
		/* left-click on right arrow */
		#if SYMBOLS_PATCH
		w = TEXTW(symbol_2);
		#else
		w = TEXTW(">");
		#endif // SYMBOLS_PATCH
		x = mw - w;
		if (next && ev->x >= x && ev->x <= x + w) {
			sel = curr = next;
			calcoffsets();
			drawmenu();
			return;
		}
	}
}

static void
clickitem(struct item *item, XButtonEvent *ev)
{
	#if RESTRICT_RETURN_PATCH
	if (restrict_return && (ev->state & (ShiftMask | ControlMask)))
		return;
	#endif // RESTRICT_RETURN_PATCH

	#if !MULTI_SELECTION_PATCH
	printitem(item);
	#endif // MULTI_SELECTION_PATCH

	sel = item;
	if (!(ev->state & ControlMask)) {
		#if MULTI_SELECTION_PATCH
		printselected(ev->state);
		#endif // MULTI_SELECTION_PATCH
		cleanup();
		exit(0);
	}

	#if MULTI_SELECTION_PATCH
	selsel();
	#else
	sel->out = 1;
	#endif // MULTI_SELECTION_PATCH
	drawmenu();
}

#if MOTION_SUPPORT_PATCH
void
motionevent(XButtonEvent *ev)
{
	struct item *item;
	int x = 0, y = 0, w;
	#if GRID_PATCH
	int i, cols;
	#endif // GRID_PATCH

	if (ev->window != win || matches == 0)
		return;

	if (prompt && *prompt)
		x += promptw;

	if (lines > 0) {
		/* input field */
		w = mw - x;
		#if GRID_PATCH
		cols = columns ? columns : 1;
		/* grid view or vertical list */
		for (i = 0, item = curr; item != next; item = item->right, i++) {
			if (
				(ev->y >= y + ((i % lines) + 1) * bh) && // line y start
				(ev->y <= y + ((i % lines) + 2) * bh) && // line y end
				(ev->x >= x + ((i / lines) * (w / cols))) && // column x start
				(ev->x <= x + ((i / lines + 1) * (w / cols))) // column x end
			) {
				sel = item;
				calcoffsets();
				drawmenu();
				break;
			}
		}
		#else
		/* vertical list */
		w = mw - x;
		for (item = curr; item != next; item = item->right) {
			y += bh;
			if (ev->y >= y && ev->y <= (y + bh)) {
				sel = item;
				calcoffsets();
				drawmenu();
				break;
			}
		}
		#endif // GRID_PATCH
		return;
	}

	/* left-click on left arrow */
	x += inputw;
	#if SYMBOLS_PATCH
	w = TEXTW(symbol_1);
	#else
	w = TEXTW("<");
	#endif // SYMBOLS_PATCH
	/* horizontal list */
	for (item = curr; item != next; item = item->right) {
		x += w;
		#if SYMBOLS_PATCH
		w = MIN(TEXTW(item->text), mw - x - TEXTW(symbol_2));
		#else
		w = MIN(TEXTW(item->text), mw - x - TEXTW(">"));
		#endif // SYMBOLS_PATCH
		if (ev->x >= x && ev->x <= x + w) {
			sel = item;
			calcoffsets();
			drawmenu();
			break;
		}
	}
}
#endif

A patch/mousesupport.h => patch/mousesupport.h +5 -0
@@ 0,0 1,5 @@
static void buttonpress(XEvent *e);
static void clickitem(struct item *item, XButtonEvent *ev);
#if MOTION_SUPPORT_PATCH
static void motionevent(XButtonEvent *ev);
#endif // MOTION_SUPPORT_PATCH

A patch/multiselect.c => patch/multiselect.c +41 -0
@@ 0,0 1,41 @@
static int
issel(size_t id)
{
	for (int i = 0;i < selidsize;i++)
		if (selid[i] == id)
			return 1;
	return 0;
}

static void
printselected(unsigned int state)
{
	for (int i = 0; i < selidsize; i++) {
		if (selid[i] != -1 && (!sel || sel->id != selid[i])) {
			printitem(&items[selid[i]]);
		}
	}

	printcurrent(state);
}

static void
selsel(void)
{
	if (!sel)
		return;
	if (issel(sel->id)) {
		for (int i = 0; i < selidsize; i++)
			if (selid[i] == sel->id)
				selid[i] = -1;
	} else {
		for (int i = 0; i < selidsize; i++)
			if (selid[i] == -1) {
				selid[i] = sel->id;
				return;
			}
		selidsize++;
		selid = realloc(selid, (selidsize + 1) * sizeof(int));
		selid[selidsize - 1] = sel->id;
	}
}

A patch/multiselect.h => patch/multiselect.h +2 -0
@@ 0,0 1,2 @@
static int issel(size_t id);
static void printselected(unsigned int state);

A patch/navhistory.c => patch/navhistory.c +234 -0
@@ 0,0 1,234 @@
static char *histfile;
static char **history;
static size_t histsz, histpos;
static size_t cap = 0;
static struct item *backup_items = NULL;

void
cleanhistory(void)
{
	int i;

	for (i = 0; i < histsz; i++) {
		free(history[i]);
	}
	free(history);
}

void
loadhistory(void)
{
	FILE *fp = NULL;
	size_t llen;
	char *line;

	if (!histfile) {
		return;
	}

	fp = fopen(histfile, "r");
	if (!fp) {
		return;
	}

	for (;;) {
		line = NULL;
		llen = 0;
		if (-1 == getline(&line, &llen, fp)) {
			if (ferror(fp)) {
				die("failed to read history");
			}
			free(line);
			break;
		}

		addhistory(line);
		free(line);
	}
	histpos = histsz;

	if (fclose(fp)) {
		die("failed to close file %s", histfile);
	}
}

void
navhistory(int dir)
{
	static char def[BUFSIZ];
	char *p = NULL;
	size_t len = 0;

	if (!history || histpos + 1 == 0)
		return;

	if (histsz == histpos) {
		strncpy(def, text, sizeof(def));
	}

	switch(dir) {
	case 1:
		if (histpos < histsz - 1) {
			p = history[++histpos];
		} else if (histpos == histsz - 1) {
			p = def;
			histpos++;
		}
		break;
	case -1:
		if (histpos > 0) {
			p = history[--histpos];
		}
		break;
	}
	if (p == NULL) {
		return;
	}

	len = MIN(strlen(p), BUFSIZ - 1);
	strncpy(text, p, len);
	text[len] = '\0';
	cursor = len;
	match();
}

void
addhistory(char *input)
{
	unsigned int i;

	if (!histfile ||
	    0 == maxhist ||
	    0 == strlen(input)) {
		return;
	}

	strtok(input, "\n");

	if (histnodup) {
		for (i = 0; i < histsz; i++) {
			if (!strcmp(input, history[i])) {
				return;
			}
		}
	}

	if (cap == histsz) {
		reallochistory();
	}

	history[histsz] = strdup(input);
	histsz++;
}

void
addhistoryitem(struct item *item)
{
	#if SEPARATOR_PATCH
	if (separator && item->text_output && item->text != item->text_output) {
		int histlen = strlen(item->text) + strlen(item->text_output) + 2;
		char *histitem = ecalloc(histlen + 1, sizeof(char *));
		snprintf(histitem, histlen, "%s%c%s", item->text, separator, item->text_output);
		addhistory(histitem);
		free(histitem);
		return;
	}
	#endif // SEPARATOR_PATCH

	addhistory(item->text);
}

void
reallochistory(void)
{
	size_t oldcap = cap;
	cap += 64;
	char **newhistory = realloc(history, cap * sizeof *history);
	if (!newhistory) {
		die("failed to realloc memory");
	}

	history = newhistory;
	memset(history + oldcap, 0, (cap - oldcap) * sizeof *history);
}

void
togglehistoryitems(void)
{
	int i;
	#if SEPARATOR_PATCH || TSV_PATCH
	char *p;
	#endif // SEPARATOR_PATCH

	if (!histfile)
		return;

	if (backup_items) {
		restorebackupitems();
		return;
	}

	backup_items = items;
	items = calloc(histsz + 1, sizeof(struct item));
	if (!items) {
		die("cannot allocate memory");
	}

	for (i = 0; i < histsz; i++) {
		items[i].text = strdup(history[i]);
		#if SEPARATOR_PATCH
		if (separator && (p = sepchr(items[i].text, separator)) != NULL) {
			*p = '\0';
			items[i].text_output = ++p;
		} else {
			items[i].text_output = items[i].text;
		}
		#elif TSV_PATCH
		items[i].stext = strdup(items[i].text);
		if ((p = strchr(items[i].stext, '\t')))
			*p = '\0';
		#endif // SEPARATOR_PATCH
	}
}

void
restorebackupitems(void)
{
	size_t i;

	if (!backup_items)
		return;

	for (i = 0; items && items[i].text; ++i) {
		free(items[i].text);
	}
	free(items);

	items = backup_items;
	backup_items = NULL;
}

void
savehistory(void)
{
	unsigned int i;
	FILE *fp;

	if (!histfile || 0 == maxhist)
		return;

	fp = fopen(histfile, "w");
	if (!fp) {
		die("failed to open %s", histfile);
	}

	for (i = histsz < maxhist ? 0 : histsz - maxhist; i < histsz; i++) {
		if (0 >= fprintf(fp, "%s\n", history[i])) {
			die("failed to write to %s", histfile);
		}
	}

	if (fclose(fp)) {
		die("failed to close file %s", histfile);
	}
}

A patch/navhistory.h => patch/navhistory.h +9 -0
@@ 0,0 1,9 @@
static void addhistory(char *input);
static void addhistoryitem(struct item *item);
static void cleanhistory(void);
static void loadhistory(void);
static void navhistory(int dir);
static void reallochistory(void);
static void restorebackupitems(void);
static void savehistory(void);
static void togglehistoryitems(void);

A patch/nonblockingstdin.c => patch/nonblockingstdin.c +68 -0
@@ 0,0 1,68 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>

static void
readstdin(void)
{
	static size_t max = 0;
	static struct item **end = &items;

	char buf[sizeof text], *p, *maxstr;
	struct item *item;

	#if PASSWORD_PATCH
	if (passwd) {
		inputw = lines = 0;
		return;
	}
	#endif // PASSWORD_PATCH

	/* read each line from stdin and add it to the item list */
	while (fgets(buf, sizeof buf, stdin)) {
		if (!(item = malloc(sizeof *item)))
			die("cannot malloc %u bytes:", sizeof *item);
		if ((p = strchr(buf, '\n')))
			*p = '\0';
		if (!(item->text = strdup(buf)))
			die("cannot strdup %u bytes:", strlen(buf)+1);
		if (strlen(item->text) > max) {
			max = strlen(maxstr = item->text);
			#if PANGO_PATCH
			inputw = maxstr ? TEXTWM(maxstr) : 0;
			#else
			inputw = maxstr ? TEXTW(maxstr) : 0;
			#endif // PANGO_PATCH
		}
		*end = item;
		end = &item->next;
		item->next = NULL;
		item->out = 0;
	}
	match();
	drawmenu();
}

static void
run(void)
{
	fd_set fds;
	int flags, xfd = XConnectionNumber(dpy);

	if ((flags = fcntl(0, F_GETFL)) == -1)
		die("cannot get stdin control flags:");
	if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
		die("cannot set stdin control flags:");
	for (;;) {
		FD_ZERO(&fds);
		FD_SET(xfd, &fds);
		if (!feof(stdin))
			FD_SET(0, &fds);
		if (select(xfd + 1, &fds, NULL, NULL, NULL) == -1)
			die("cannot multiplex input:");
		if (FD_ISSET(xfd, &fds))
			readevent();
		if (FD_ISSET(0, &fds))
			readstdin();
	}
}

A patch/nonblockingstdin.h => patch/nonblockingstdin.h +1 -0
@@ 0,0 1,1 @@
static void readevent();

A patch/numbers.c => patch/numbers.c +16 -0
@@ 0,0 1,16 @@
static char numbers[NUMBERSBUFSIZE] = "";

static void
recalculatenumbers(void)
{
	unsigned int numer = 0, denom = 0;
	struct item *item;
	if (matchend) {
		numer++;
		for (item = matchend; item && item->left; item = item->left)
			numer++;
	}
	for (item = items; item && item->text; item++)
		denom++;
	snprintf(numbers, NUMBERSBUFSIZE, "%d/%d", numer, denom);
}

A patch/numbers.h => patch/numbers.h +4 -0
@@ 0,0 1,4 @@
#define NUMBERSMAXDIGITS      100
#define NUMBERSBUFSIZE        (NUMBERSMAXDIGITS * 2) + 1

static void recalculatenumbers(void);

A patch/scroll.c => patch/scroll.c +178 -0
@@ 0,0 1,178 @@
int
utf8nextchar(const char *str, int len, int i, int inc)
{
	int n;

	for (n = i + inc; n + inc >= 0 && n + inc <= len
	  && (str[n] & 0xc0) == 0x80; n += inc)
		;
	return n;
}

int
drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align)
{
	int ty;
	unsigned int ew;
	XftDraw *d = NULL;
	Fnt *usedfont, *curfont, *nextfont;
	size_t len;
	int utf8strlen, utf8charlen, render = x || y || w || h;
	long utf8codepoint = 0;
	const char *utf8str;
	FcCharSet *fccharset;
	FcPattern *fcpattern;
	FcPattern *match;
	XftResult result;
	int charexists = 0;
	int utf8err = 0;
	int i, n;

	if (!drw || (render && !drw->scheme) || !text || !drw->fonts || textlen <= 0
	  || (align != AlignL && align != AlignR))
		return 0;

	if (!render) {
		w = ~w;
	} else {
		XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel);
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
		#if ALPHA_PATCH
		d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
		#else
		d = XftDrawCreate(drw->dpy, drw->drawable,
		                  DefaultVisual(drw->dpy, drw->screen),
		                  DefaultColormap(drw->dpy, drw->screen));
		#endif // ALPHA_PATCH
	}

	#if BIDI_PATCH
	apply_fribidi(text);
	text = fribidi_text;
	#endif // BIDI_PATCH

	usedfont = drw->fonts;
	i = align == AlignL ? 0 : textlen;
	x = align == AlignL ? x : x + w;
	while (1) {
		utf8strlen = 0;
		nextfont = NULL;
		/* if (align == AlignL) */
		utf8str = text + i;

		while ((align == AlignL && i < textlen) || (align == AlignR && i > 0)) {
			if (align == AlignL) {
				utf8charlen = utf8decode(text + i, &utf8codepoint, &utf8err);
				if (!utf8charlen) {
					textlen = i;
					break;
				}
			} else {
				n = utf8nextchar(text, textlen, i, -1);
				utf8charlen = utf8decode(text + n, &utf8codepoint, &utf8err);
				if (!utf8charlen) {
					textlen -= i;
					text += i;
					i = 0;
					break;
				}
			}
			for (curfont = drw->fonts; curfont; curfont = curfont->next) {
				charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
				if (charexists) {
					if (curfont == usedfont) {
						utf8strlen += utf8charlen;
						i += align == AlignL ? utf8charlen : -utf8charlen;
					} else {
						nextfont = curfont;
					}
					break;
				}
			}

			if (!charexists || nextfont)
				break;
			else
				charexists = 0;
		}

		if (align == AlignR)
			utf8str = text + i;

		if (utf8strlen) {
			drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
			/* shorten text if necessary */
			if (align == AlignL) {
				for (len = utf8strlen; len && ew > w; ) {
					len = utf8nextchar(utf8str, len, len, -1);
					drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
				}
			} else {
				for (len = utf8strlen; len && ew > w; ) {
					n = utf8nextchar(utf8str, len, 0, +1);
					utf8str += n;
					len -= n;
					drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
				}
			}

			if (len) {
				if (render) {
					ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
					XftDrawStringUtf8(d, &drw->scheme[ColFg],
					                  usedfont->xfont, align == AlignL ? x : x - ew, ty, (XftChar8 *)utf8str, len);
				}
				x += align == AlignL ? ew : -ew;
				w -= ew;
			}
			if (len < utf8strlen)
				break;
		}

		if ((align == AlignR && i <= 0) || (align == AlignL && i >= textlen)) {
			break;
		} else if (nextfont) {
			charexists = 0;
			usedfont = nextfont;
		} else {
			/* Regardless of whether or not a fallback font is found, the
			 * character must be drawn. */
			charexists = 1;

			fccharset = FcCharSetCreate();
			FcCharSetAddChar(fccharset, utf8codepoint);

			if (!drw->fonts->pattern) {
				/* Refer to the comment in xfont_create for more information. */
				die("the first font in the cache must be loaded from a font string.");
			}

			fcpattern = FcPatternDuplicate(drw->fonts->pattern);
			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);

			FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
			FcDefaultSubstitute(fcpattern);
			match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);

			FcCharSetDestroy(fccharset);
			FcPatternDestroy(fcpattern);

			if (match) {
				usedfont = xfont_create(drw, NULL, match);
				if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
					for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
						; /* NOP */
					curfont->next = usedfont;
				} else {
					xfont_free(usedfont);
					usedfont = drw->fonts;
				}
			}
		}
	}
	if (d)
		XftDrawDestroy(d);

	return x;
}

A patch/scroll.h => patch/scroll.h +3 -0
@@ 0,0 1,3 @@
enum { AlignL, AlignR };

int drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align);

A patch/vi_mode.c => patch/vi_mode.c +188 -0
@@ 0,0 1,188 @@
static unsigned int using_vi_mode = 0;

void
vi_keypress(KeySym ksym, const XKeyEvent *ev)
{
	static const size_t quit_len = LENGTH(quit_keys);
	if (ev->state & ControlMask) {
		switch(ksym) {
		/* movement */
		case XK_d: /* fallthrough */
			if (next) {
				sel = curr = next;
				calcoffsets();
				goto draw;
			} else
				ksym = XK_G;
			break;
		case XK_u:
			if (prev) {
				sel = curr = prev;
				calcoffsets();
				goto draw;
			} else
				ksym = XK_g;
			break;
		case XK_p: /* fallthrough */
		case XK_P: break;
		case XK_c:
			cleanup();
			exit(1);
		case XK_Return: /* fallthrough */
		case XK_KP_Enter: break;
		default: return;
		}
	}

	switch(ksym) {
	/* movement */
	case XK_0:
		cursor = 0;
		break;
	case XK_dollar:
		if (text[cursor + 1] != '\0') {
			cursor = strlen(text) - 1;
			break;
		}
		break;
	case XK_b:
		movewordedge(-1);
		break;
	case XK_e:
		cursor = nextrune(+1);
		movewordedge(+1);
		if (text[cursor] == '\0')
			--cursor;
		else
			cursor = nextrune(-1);
		break;
	case XK_g:
		if (sel == matches) {
			break;
		}
		sel = curr = matches;
		calcoffsets();
		break;
	case XK_G:
		if (next) {
			/* jump to end of list and position items in reverse */
			curr = matchend;
			calcoffsets();
			curr = prev;
			calcoffsets();
			while (next && (curr = curr->right))
				calcoffsets();
		}
		sel = matchend;
		break;
	case XK_h:
		if (cursor)
			cursor = nextrune(-1);
		break;
	case XK_j:
		if (sel && sel->right && (sel = sel->right) == next) {
			curr = next;
			calcoffsets();
		}
		break;
	case XK_k:
		if (sel && sel->left && (sel = sel->left)->right == curr) {
			curr = prev;
			calcoffsets();
		}
		break;
	case XK_l:
		if (text[cursor] != '\0' && text[cursor + 1] != '\0')
			cursor = nextrune(+1);
		else if (text[cursor] == '\0' && cursor)
			--cursor;
		break;
	case XK_w:
		movewordedge(+1);
		if (text[cursor] != '\0' && text[cursor + 1] != '\0')
			cursor = nextrune(+1);
		else if (cursor)
			--cursor;
		break;
	/* insertion */
	case XK_a:
		cursor = nextrune(+1);
		/* fallthrough */
	case XK_i:
		using_vi_mode = 0;
		break;
	case XK_A:
		if (text[cursor] != '\0')
			cursor = strlen(text);
		using_vi_mode = 0;
		break;
	case XK_I:
		cursor = using_vi_mode = 0;
		break;
	case XK_p:
		if (text[cursor] != '\0')
			cursor = nextrune(+1);
		XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
							utf8, utf8, win, CurrentTime);
		return;
	case XK_P:
		XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
							utf8, utf8, win, CurrentTime);
		return;
	/* deletion */
	case XK_D:
		text[cursor] = '\0';
		if (cursor)
			cursor = nextrune(-1);
		match();
		break;
	case XK_x:
		cursor = nextrune(+1);
		insert(NULL, nextrune(-1) - cursor);
		if (text[cursor] == '\0' && text[0] != '\0')
			--cursor;
		match();
		break;
	/* misc. */
	case XK_Return:
	case XK_KP_Enter:
		#if RESTRICT_RETURN_PATCH
		if (restrict_return && (!sel || ev->state & (ShiftMask | ControlMask)))
			break;
		#endif // RESTRICT_RETURN_PATCH
		#if !MULTI_SELECTION_PATCH
		printcurrent(ev->state);
		#endif // MULTI_SELECTION_PATCH
		if (!(ev->state & ControlMask)) {
			#if MULTI_SELECTION_PATCH
			printselected(ev->state);
			#endif // MULTI_SELECTION_PATCH
			cleanup();
			exit(0);
		}
		#if !MULTI_SELECTION_PATCH
		if (sel)
			sel->out = 1;
		#endif // MULTI_SELECTION_PATCH
		break;
		break;
	case XK_Tab:
		if (!sel)
			return;
		strncpy(text, sel->text, sizeof text - 1);
		text[sizeof text - 1] = '\0';
		cursor = strlen(text) - 1;
		match();
		break;
	default:
		for (size_t i = 0; i < quit_len; ++i)
			if (quit_keys[i].ksym == ksym &&
				(quit_keys[i].state & ev->state) == quit_keys[i].state) {
				cleanup();
				exit(1);
			}
	}

draw:
	drawmenu();
}

A patch/vi_mode.h => patch/vi_mode.h +6 -0
@@ 0,0 1,6 @@
typedef struct {
	KeySym ksym;
	unsigned int state;
} Key;

static void vi_keypress(KeySym ksym, const XKeyEvent *ev);

A patch/xresources.c => patch/xresources.c +102 -0
@@ 0,0 1,102 @@
#include <X11/Xresource.h>

void
readxresources(void)
{
	XrmInitialize();

	char* xrm;
	if ((xrm = XResourceManagerString(drw->dpy))) {
		char *type;
		XrmDatabase xdb = XrmGetStringDatabase(xrm);
		XrmValue xval;

		if (XrmGetResource(xdb, "dmenu.font", "*", &type, &xval))
			#if PANGO_PATCH
			strcpy(font, xval.addr);
			#else
			fonts[0] = strdup(xval.addr);
			#endif // PANGO_PATCH
		#if !PANGO_PATCH
		else
			fonts[0] = strdup(fonts[0]);
		#endif // PANGO_PATCH
		if (XrmGetResource(xdb, "dmenu.background", "*", &type, &xval))
			colors[SchemeNorm][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.foreground", "*", &type, &xval))
			colors[SchemeNorm][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.selbackground", "*", &type, &xval))
			colors[SchemeSel][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.selforeground", "*", &type, &xval))
			colors[SchemeSel][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.outbackground", "*", &type, &xval))
			colors[SchemeOut][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.outforeground", "*", &type, &xval))
			colors[SchemeOut][ColFg] = strdup(xval.addr);
		#if MORECOLOR_PATCH
		if (XrmGetResource(xdb, "dmenu.midbackground", "*", &type, &xval))
			colors[SchemeMid][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.midforeground", "*", &type, &xval))
			colors[SchemeMid][ColFg] = strdup(xval.addr);
		#endif // MORECOLOR_PATCH
		#if BORDER_PATCH
		if (XrmGetResource(xdb, "dmenu.bordercolor", "*", &type, &xval))
			colors[SchemeBorder][ColBg] = strdup(xval.addr);
		#endif // BORDER_PATCH
		#if HIGHLIGHT_PATCH
		if (XrmGetResource(xdb, "dmenu.selhlbackground", "*", &type, &xval))
			colors[SchemeSelHighlight][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.selhlforeground", "*", &type, &xval))
			colors[SchemeSelHighlight][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.hlbackground", "*", &type, &xval))
			colors[SchemeNormHighlight][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.hlforeground", "*", &type, &xval))
			colors[SchemeNormHighlight][ColFg] = strdup(xval.addr);
		#endif // HIGHLIGHT_PATCH
		#if HIGHPRIORITY_PATCH
		if (XrmGetResource(xdb, "dmenu.hpbackground", "*", &type, &xval))
			colors[SchemeHp][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.hpforeground", "*", &type, &xval))
			colors[SchemeHp][ColFg] = strdup(xval.addr);
		#endif // HIGHPRIORITY_PATCH
		#if EMOJI_HIGHLIGHT_PATCH
		if (XrmGetResource(xdb, "dmenu.hoverbackground", "*", &type, &xval))
			colors[SchemeHover][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.hoverforeground", "*", &type, &xval))
			colors[SchemeHover][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.greenbackground", "*", &type, &xval))
			colors[SchemeGreen][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.greenforeground", "*", &type, &xval))
			colors[SchemeGreen][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.yellowbackground", "*", &type, &xval))
			colors[SchemeYellow][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.yellowforeground", "*", &type, &xval))
			colors[SchemeYellow][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.bluebackground", "*", &type, &xval))
			colors[SchemeBlue][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.blueforeground", "*", &type, &xval))
			colors[SchemeBlue][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.purplebackground", "*", &type, &xval))
			colors[SchemePurple][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.purpleforeground", "*", &type, &xval))
			colors[SchemePurple][ColFg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.redbackground", "*", &type, &xval))
			colors[SchemeRed][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.redforeground", "*", &type, &xval))
			colors[SchemeRed][ColFg] = strdup(xval.addr);
		#endif // EMOJI_HIGHLIGHT_PATCH
		#if VI_MODE_PATCH
		if (XrmGetResource(xdb, "dmenu.cursorbackground", "*", &type, &xval))
			colors[SchemeCursor][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.cursorforeground", "*", &type, &xval))
			colors[SchemeCursor][ColFg] = strdup(xval.addr);
		#endif // VI_MODE_PATCH
		#if CARET_SCHEME_PATCH
		if (XrmGetResource(xdb, "dmenu.caretbackground", "*", &type, &xval))
			colors[SchemeCaret][ColBg] = strdup(xval.addr);
		if (XrmGetResource(xdb, "dmenu.caretforeground", "*", &type, &xval))
			colors[SchemeCaret][ColFg] = strdup(xval.addr);
		#endif // CARET_SCHEME_PATCH
		XrmDestroyDatabase(xdb);
	}
}

A patches.def.h => patches.def.h +405 -0
@@ 0,0 1,405 @@
/* Patches */

/* The alpha patch adds transparency for the dmenu window.
 * You need to uncomment the corresponding line in config.mk to use the -lXrender library
 * when including this patch.
 * https://github.com/bakkeby/patches/blob/master/dmenu/dmenu-alpha-5.0_20210725_523aa08.diff
 */
#define ALPHA_PATCH 0

/* This adds padding for dmenu in similar fashion to the similarly named patch for dwm. The idea
 * is to have dmenu appear on top of the bar when using said patch in dwm.
 * https://github.com/bakkeby/patches/wiki/barpadding
 */
#define BARPADDING_PATCH 0

/* This patch adds proper support for Right-To-Left (RTL) languages, such as Hebrew,
 * Arabic, and Farsi.
 *
 * Texts combining both RTL and LTR languages are displayed correctly. This is
 * achieved using the GNU FriBiDi library, which is an additional dependency for
 * this patch.
 *
 * You need to uncomment the corresponding line in config.mk to use the fribidi library.
 * https://tools.suckless.org/dmenu/patches/bidi/
 */
#define BIDI_PATCH 0

/* This patch adds a border around the dmenu window. It is intended to be used with the center
 * or xyw patches, to make the menu stand out from similarly coloured windows.
 * http://tools.suckless.org/dmenu/patches/border/
 */
#define BORDER_PATCH 0

/* The caret scheme patch, a.k.a. colored caret patch, adds the option to change the color
 * of the caret via the SchemeCaret color scheme.
 *
 * https://tools.suckless.org/dmenu/patches/colored-caret/
 */
#define CARET_SCHEME_PATCH 0

/* By default the caret in dmenu has a width of 2 pixels. This patch makes that configurable
 * as well as overridable via a command line option.
 * https://github.com/DarkSamus669/dmenu-patches/blob/main/dmenu-caretwidth-5.2.diff
 */
#define CARET_WIDTH_PATCH 0

/* This patch makes dmenu case-insensitive by default, replacing the
 * case-insensitive -i option with a case sensitive -s option.
 * http://tools.suckless.org/dmenu/patches/case-insensitive/
 */
#define CASEINSENSITIVE_PATCH 0

/* This patch centers dmenu in the middle of the screen.
 * https://tools.suckless.org/dmenu/patches/center/
 */
#define CENTER_PATCH 0

/* Minor patch to enable the use of Ctrl+v (XA_PRIMARY) and Ctrl+Shift+v (CLIPBOARD) to paste.
 * By default dmenu only supports Ctrl+y and Ctrl+Shift+y to paste.
 */
#define CTRL_V_TO_PASTE_PATCH 0

/* This patch dyamically changes the size of the dmenu window based on how many items are
 * drawn in a vertical view. For this to work set a higher maximum of lines, e.g. -l 500.
 * https://gist.github.com/mircodz/1d9b88db958089bb08adbf45eb53b66f
 */
#define DYNAMIC_HEIGHT_PATCH 0

/* This patch adds a flag (-dy) which makes dmenu run the command given to it whenever input
 * is changed with the current input as the last argument and update the option list according
 * to the output of that command.
 * https://tools.suckless.org/dmenu/patches/dynamicoptions/
 */
#define DYNAMIC_OPTIONS_PATCH 0

/* This patch will allow for emojis on the left side with a colored background when selected.
 * To test this try running:
 *    $ echo -e ":b here\n:p there\n:r and here" | ./dmenu -p "Search..." -W 400 -l 20 -i -h -1
 * NB: the original patch came embedded with the the xyw patch, the morecolors patch and the
 * line height patch and as such is intended to be combined with these.
 * https://tools.suckless.org/dmenu/patches/emoji-highlight/
 */
#define EMOJI_HIGHLIGHT_PATCH 0

/* This patch adds support for fuzzy-matching to dmenu, allowing users to type non-consecutive
 * portions of the string to be matched.
 * https://tools.suckless.org/dmenu/patches/fuzzymatch/
 */
#define FUZZYMATCH_PATCH 0

/* Adds fzf-like functionality for dmenu.
 * Refer to https://github.com/DAFF0D11/dafmenu/ for documentation and example use cases.
 * https://github.com/DAFF0D11/dafmenu/blob/master/patches/dmenu-fzfexpect-5.1.diff
 */
#define FZFEXPECT_PATCH 0

/* Allows dmenu's entries to be rendered in a grid by adding a new -g flag to specify
 * the number of grid columns. The -g and -l options can be used together to create a
 * G columns * L lines grid.
 * https://tools.suckless.org/dmenu/patches/grid/
 */
#define GRID_PATCH 0

/* This patch adds the ability to move left and right through a grid.
 * This patch depends on the grid patch.
 * https://tools.suckless.org/dmenu/patches/gridnav/
 */
#define GRIDNAV_PATCH 0

/* This patch highlights the individual characters of matched text for each dmenu list entry.
 * If combined with the fuzzymatch patch then fuzzy highlight will be used for highlighting
 * depending on whether fuzzy matching is enabled.
 *
 * Known issue: highlighting does not work properly when pango markup is used
 *
 * https://tools.suckless.org/dmenu/patches/highlight/
 * https://tools.suckless.org/dmenu/patches/fuzzyhighlight/
 */
#define HIGHLIGHT_PATCH 0

/* This will automatically sort the search result so that high priority items are shown first.
 * https://tools.suckless.org/dmenu/patches/highpriority/
 */
#define HIGHPRIORITY_PATCH 0

/* This patch causes dmenu to print out the current text each time a key is pressed.
 * https://tools.suckless.org/dmenu/patches/incremental/
 */
#define INCREMENTAL_PATCH 0

/* This patch adds an option to provide preselected text.
 * https://tools.suckless.org/dmenu/patches/initialtext/
 */
#define INITIALTEXT_PATCH 0

/* Adds support for input methods (fctix, ibus, etc.) allowing the user to change the
 * keyboard layout while dmenu is open.
 * https://github.com/bakkeby/dmenu-flexipatch/pull/22
 */
#define INPUTMETHOD_PATCH 0

/* This patch adds a flag which will cause dmenu to select an item immediately if there
 * is only one matching option left.
 * https://tools.suckless.org/dmenu/patches/instant/
 */
#define INSTANT_PATCH 0

/* This patch adds a '-h' option which sets the minimum height of a dmenu line. This helps
 * integrate dmenu with other UI elements that require a particular vertical size.
 * http://tools.suckless.org/dmenu/patches/line-height/
 */
#define LINE_HEIGHT_PATCH 0

/* This patch adds a -wm flag which sets override_redirect to false; thus letting your window
 * manager manage the dmenu window.
 *
 * This may be helpful in contexts where you don't want to exclusively bind dmenu or want to
 * treat dmenu more as a "window" rather than as an overlay.
 * https://tools.suckless.org/dmenu/patches/managed/
 */
#define MANAGED_PATCH 0

/* This patch adds an additional color scheme for highlighting entries adjacent to the current
 * selection.
 * https://tools.suckless.org/dmenu/patches/morecolor/
 */
#define MORECOLOR_PATCH 0

/* This patch adds basic mouse support for dmenu.
 * https://tools.suckless.org/dmenu/patches/mouse-support/
 */
#define MOUSE_SUPPORT_PATCH 0

/* Expands the above to support mouse hovering.
 * https://tools.suckless.org/dmenu/patches/mouse-support/
 */
#define MOTION_SUPPORT_PATCH 0

/* Without this patch when you press Ctrl+Enter dmenu just outputs current item and it is not
 * possible to undo that.
 * With this patch dmenu will output all selected items only on exit. It is also possible to
 * deselect any selected item.
 * Also refer to the dmenu_run replacement on the below URL that supports multiple selections.
 *
 * This patch is not compatible with, and takes precedence over, the json, printinputtext,
 * pipeout and non-blocking stdin patches.
 *
 * https://tools.suckless.org/dmenu/patches/multi-selection/
 */
#define MULTI_SELECTION_PATCH 0

/* This patch provides dmenu the ability for history navigation similar to that of bash.
 *
 * If you take this patch then it is recommended that you also uncomment the line in the
 * dmenu_run script which replaces the exec command.
 *
 * https://tools.suckless.org/dmenu/patches/navhistory/
 */
#define NAVHISTORY_PATCH 0

/* This patch adds back in the workaround for a BadLength error in the Xft library when color
 * glyphs are used. This is for systems that do not have an updated version of the Xft library
 * (or generally prefer monochrome fonts).
 */
#define NO_COLOR_EMOJI_PATCH 0

/* Adds the -S option to disable sorting menu items after matching. Useful, for example, when menu
 * items are sorted by their frequency of use (using an external cache) and the most frequently
 * selected items should always appear first regardless of how they were exact, prefix, or
 * substring matches.
 * https://tools.suckless.org/dmenu/patches/no-sort/
 */
#define NO_SORT_PATCH 0

/* This is a patch to have dmenu read stdin in a non blocking way, making it wait for input both
 * from stdin and from X. This means that you can continue feeding dmenu while you type.
 * This patch is meant to be used along with the incremental patch, so that you can use stdout
 * to feed stdin.
 *
 * This patch is not compatible with the json and multi-selection patches, both of which takes
 * precedence over this patch.
 *
 * https://tools.suckless.org/dmenu/patches/non_blocking_stdin/
 */
#define NON_BLOCKING_STDIN_PATCH 0

/* Adds text which displays the number of matched and total items in the top right corner of dmenu.
 * https://tools.suckless.org/dmenu/patches/numbers/
 */
#define NUMBERS_PATCH 0

/* This patch adds simple markup for dmenu using pango markup.
 * This depends on the pango library v1.44 or greater.
 * You need to uncomment the corresponding lines in config.mk to use the pango libraries
 * when including this patch.
 *
 * Note that the pango patch is incompatible with the scroll patch and will result in
 * compilation errors if both are enabled.
 *
 * Known issue: not compatible with the scroll patch
 *
 * Also see:
 * https://developer.gnome.org/pygtk/stable/pango-markup-language.html
 * https://github.com/StillANixRookie/dmenu-pango
 */
#define PANGO_PATCH 0

/* With this patch dmenu will not directly display the keyboard input, but instead replace
 * it with dots. All data from stdin will be ignored.
 * https://tools.suckless.org/dmenu/patches/password/
 */
#define PASSWORD_PATCH 0

/* This patch allows the selected text to be piped back out with dmenu. This can be useful if you
 * want to display the output of a command on the screen.
 * Only text starting with the character '#' is piped out by default.
 *
 * This patch is not compatible with the json and multi-select patches, both of which takes
 * precedence over this one.
 *
 * https://tools.suckless.org/dmenu/patches/pipeout/
 */
#define PIPEOUT_PATCH 0

/* Lifted from the listfullwidth patch this simple change just avoids colors for the prompt (with
 * the -p option or in config.h) by making it use the same style as the rest of the input field.
 * The rest of the listfullwidth patch is covered by the vertfull patch.
 * https://tools.suckless.org/dmenu/patches/listfullwidth/
 */
#define PLAIN_PROMPT_PATCH 0

/* This patch changes the behaviour of matched items and the Tab key to allow tab completion.
 * https://tools.suckless.org/dmenu/patches/prefix-completion/
 */
#define PREFIXCOMPLETION_PATCH 0

/* This patch adds an option -ps to specify an item by providing the index that should be
 * pre-selected.
 * https://tools.suckless.org/dmenu/patches/preselect/
 */
#define PRESELECT_PATCH 0

/* This patch allows dmenu to print out the 0-based index of matched text instead of the matched
 * text itself. This can be useful in cases where you would like to select entries from one array
 * of text but index into another, or when you are selecting from an ordered list of non-unique
 * items.
 * https://tools.suckless.org/dmenu/patches/printindex/
 */
#define PRINTINDEX_PATCH 0

/* This patch adds a flag (-t) which makes Return key to ignore selection and print the input
 * text to stdout. The flag basically swaps the functions of Return and Shift+Return hotkeys.
 *
 * This patch is not compatible with the multi-select and json patches, both of which takes
 * precedence over this one.
 *
 * https://tools.suckless.org/dmenu/patches/printinputtext/
 */
#define PRINTINPUTTEXT_PATCH 0

/* This patch adds a flag (-q) which makes dmenu not show any items if the search string is
 * empty.
 * https://github.com/baskerville/dmenu_qxyw/blob/master/dmenu_qxyw-hg.diff
 */
#define QUIET_PATCH 0

/* This patch adds a new flag to dmenu with which text input will be rejected if it would
 * result in no matching item.
 * https://tools.suckless.org/dmenu/patches/reject-no-match/
 */
#define REJECTNOMATCH_PATCH 0

/* The input width used to be relative to the input options prior to commit e1e1de7:
 * https://git.suckless.org/dmenu/commit/e1e1de7b3b8399cba90ddca9613f837b2dbef7b9.html
 *
 * This had a performance hit when using large data sets and was removed in favour of having the
 * input width take up 1/3rd of the available space.
 *
 * This option adds that feature back in with some performance optimisations at the cost of
 * accuracy and correctness.
 */
#define RELATIVE_INPUT_WIDTH_PATCH 0

/* This patch adds a '-1' option which disables Shift-Return and Ctrl-Return.
 * This guarantees that dmenu will only output one item, and that item was read from stdin.
 * The original patch used '-r'. This was changed to '-1' to avoid conflict with the incremental
 * patch.
 * https://tools.suckless.org/dmenu/patches/restrict-return/
 */
#define RESTRICT_RETURN_PATCH 0

/* This patch adds support for text scrolling and no longer appends '...' for long input as
 * it can handle long text.
 *
 * Known issue: not compatible with the pango patch
 *
 * https://tools.suckless.org/dmenu/patches/scroll/
 */
#define SCROLL_PATCH 0

/* This patch adds -d and -D flags which separates the input into two halves; one half to be
 * displayed in dmenu and the other to be printed to stdout. This patch takes precedence over
 * the TSV patch.
 * https://tools.suckless.org/dmenu/patches/separator/
 */
#define SEPARATOR_PATCH 0

/* This patch allows the symbols, which are printed in dmenu to indicate that either the input
 * is too long or there are too many options to be shown in dmenu in one line, to be defined.
 * https://tools.suckless.org/dmenu/patches/symbols/
 */
#define SYMBOLS_PATCH 0

/* With this patch dmenu will split input lines at first tab character and only display first
 * part, but it will perform matching on and output full lines as usual.
 *
 * This can be useful if you want to separate data and representation, for example, a music
 * player wrapper can display only a track title to user, but still supply full filename to
 * the underlying script.
 * https://tools.suckless.org/dmenu/patches/tsv/
 */
#define TSV_PATCH 0

/* This patch prevents dmenu from indenting items at the same level as the prompt length.
 * https://tools.suckless.org/dmenu/patches/vertfull/
 */
#define VERTFULL_PATCH 0

/* This patch adds basic vi mode capabilities to dmenu.
 *   - movements inside typed text with [h|l|w|b|e|0|$]
 *   - movements through list with [j|k|g|G|C-d|C-u]
 *   - standard insertions with [a|A|i|I]
 *   - paste after|before cursor with [p|P], use ctrl to use clipboard
 *   - delete from cursor to eol with D
 *   - delete the character under cursor with x
 *   - Enter and Tab will work like normal
 *
 * https://tools.suckless.org/dmenu/patches/vi-mode/
 */
#define VI_MODE_PATCH 0

/* Adds extended window manager hints such as _NET_WM_WINDOW_TYPE and _NET_WM_WINDOW_TYPE_DOCK.
 * https://github.com/Baitinq/dmenu/blob/master/patches/dmenu-wm_type.diff
 */
#define WMTYPE_PATCH 0

/* This patch adds the ability to configure dmenu via Xresources. At startup, dmenu will read and
 * apply the resources named below:
 *    dmenu.font          : font or font set
 *    dmenu.background    : normal background color
 *    dmenu.foreground    : normal foreground color
 *    dmenu.selbackground : selected background color
 *    dmenu.selforeground : selected foreground color
 *
 * See patch/xresources.c for more color settings.
 *
 * https://tools.suckless.org/dmenu/patches/xresources/
 */
#define XRESOURCES_PATCH 0

/* This patch adds options for specifying dmenu window position and width.
 * The center patch takes precedence over the XYW patch if enabled.
 * https://tools.suckless.org/dmenu/patches/xyw/
 */
#define XYW_PATCH 0

A patches.h => patches.h +182 -0
@@ 0,0 1,182 @@
/*
 * This file contains patch control flags.
 *
 * Kris Yotam's dmenu build - patches enabled:
 * - alpha (transparency)
 * - border (visible border when centered)
 * - caseinsensitive (case-insensitive by default)
 * - center (centered on screen)
 * - ctrl_v_to_paste (Ctrl+V paste support)
 * - fuzzymatch (fuzzy matching)
 * - highlight (highlight matched chars)
 * - line_height (configurable line height)
 * - mouse_support (click to select)
 * - numbers (show match count)
 * - password (hide input with -P)
 * - rejectnomatch (reject non-matching input)
 * - xresources (pywal compatible)
 */

/* Patches */

/* The alpha patch adds transparency for the dmenu window. */
#define ALPHA_PATCH 1

/* Adds padding for dmenu similar to dwm barpadding. */
#define BARPADDING_PATCH 0

/* Right-To-Left language support (requires fribidi). */
#define BIDI_PATCH 0

/* Adds a border around the dmenu window. */
#define BORDER_PATCH 1

/* Colored caret option via SchemeCaret. */
#define CARET_SCHEME_PATCH 0

/* Configurable caret width. */
#define CARET_WIDTH_PATCH 0

/* Case-insensitive by default. */
#define CASEINSENSITIVE_PATCH 1

/* Centers dmenu in the middle of the screen. */
#define CENTER_PATCH 1

/* Ctrl+v to paste support. */
#define CTRL_V_TO_PASTE_PATCH 1

/* Dynamically changes dmenu window size based on items. */
#define DYNAMIC_HEIGHT_PATCH 0

/* Dynamic options based on input. */
#define DYNAMIC_OPTIONS_PATCH 0

/* Emoji highlight with colored background. */
#define EMOJI_HIGHLIGHT_PATCH 0

/* Fuzzy matching support. */
#define FUZZYMATCH_PATCH 1

/* Fzf-like functionality. */
#define FZFEXPECT_PATCH 0

/* Grid layout for entries. */
#define GRID_PATCH 0

/* Grid navigation with arrows. */
#define GRIDNAV_PATCH 0

/* Highlight matched characters. */
#define HIGHLIGHT_PATCH 1

/* Sort results by priority. */
#define HIGHPRIORITY_PATCH 0

/* Print current text on each keypress. */
#define INCREMENTAL_PATCH 0

/* Preselected text option. */
#define INITIALTEXT_PATCH 0

/* Input method support (fcitx, ibus). */
#define INPUTMETHOD_PATCH 0

/* Auto-select if only one match. */
#define INSTANT_PATCH 0

/* Minimum line height option. */
#define LINE_HEIGHT_PATCH 1

/* Let window manager manage dmenu window. */
#define MANAGED_PATCH 0

/* Additional color scheme for adjacent items. */
#define MORECOLOR_PATCH 0

/* Mouse support for clicking items. */
#define MOUSE_SUPPORT_PATCH 1

/* Mouse hover support. */
#define MOTION_SUPPORT_PATCH 0

/* Multi-selection with Ctrl+Enter. */
#define MULTI_SELECTION_PATCH 0

/* Bash-like history navigation. */
#define NAVHISTORY_PATCH 0

/* Workaround for BadLength with color glyphs. */
#define NO_COLOR_EMOJI_PATCH 0

/* Disable sorting after matching. */
#define NO_SORT_PATCH 0

/* Non-blocking stdin reading. */
#define NON_BLOCKING_STDIN_PATCH 0

/* Show matched/total count. */
#define NUMBERS_PATCH 1

/* Pango markup support. */
#define PANGO_PATCH 0

/* Password mode - hide input with dots. */
#define PASSWORD_PATCH 1

/* Pipe selected text back out. */
#define PIPEOUT_PATCH 0

/* Plain prompt without colors. */
#define PLAIN_PROMPT_PATCH 0

/* Tab completion support. */
#define PREFIXCOMPLETION_PATCH 0

/* Preselect item by index. */
#define PRESELECT_PATCH 0

/* Print index instead of text. */
#define PRINTINDEX_PATCH 0

/* Return prints input text instead of selection. */
#define PRINTINPUTTEXT_PATCH 0

/* Don't show items if search is empty. */
#define QUIET_PATCH 0

/* Reject input if no match. */
#define REJECTNOMATCH_PATCH 1

/* Input width relative to options. */
#define RELATIVE_INPUT_WIDTH_PATCH 0

/* Disable Shift/Ctrl+Return. */
#define RESTRICT_RETURN_PATCH 0

/* Text scrolling for long input. */
#define SCROLL_PATCH 0

/* Separator for display vs output. */
#define SEPARATOR_PATCH 0

/* Custom symbols for overflow. */
#define SYMBOLS_PATCH 0

/* TSV input support. */
#define TSV_PATCH 0

/* Vertical full width items. */
#define VERTFULL_PATCH 0

/* Vi mode keybindings. */
#define VI_MODE_PATCH 0

/* Extended window manager hints. */
#define WMTYPE_PATCH 0

/* Xresources support for colors/settings. */
#define XRESOURCES_PATCH 1

/* Position and width options. */
#define XYW_PATCH 0

M util.c => util.c +6 -7
@@ 1,5 1,4 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>


@@ 11,17 10,17 @@ void
die(const char *fmt, ...)
{
	va_list ap;
	int saved_errno;

	saved_errno = errno;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);

	if (fmt[0] && fmt[strlen(fmt)-1] == ':')
		fprintf(stderr, " %s", strerror(saved_errno));
	fputc('\n', stderr);
	if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
		fputc(' ', stderr);
		perror(NULL);
	} else {
		fputc('\n', stderr);
	}

	exit(1);
}

M util.h => util.h +4 -0
@@ 1,7 1,11 @@
/* See LICENSE file for copyright and license details. */

#ifndef MAX
#define MAX(A, B)               ((A) > (B) ? (A) : (B))
#endif
#ifndef MIN
#define MIN(A, B)               ((A) < (B) ? (A) : (B))
#endif
#define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
#define LENGTH(X)               (sizeof (X) / sizeof (X)[0])