commit 097f38a4bd85760e5cf322eb973d2f671673c289
parent ae389797788cc66d3b93c92fe8639d68d47b8b39
Author: Kris Yotam <krisyotam@protonmail.com>
Date: Fri, 13 Feb 2026 14:53:48 -0600
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
Diffstat:
43 files changed, 4905 insertions(+), 207 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,4 @@
+*.o
+dmenu
+stest
+config.h
diff --git a/Makefile b/Makefile
@@ -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)
diff --git a/README.md b/README.md
@@ -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
---
diff --git a/config.def.h b/config.def.h
@@ -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
diff --git a/config.h b/config.h
@@ -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
diff --git a/config.mk b/config.mk
@@ -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)
diff --git a/dmenu.1 b/dmenu.1
@@ -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
diff --git a/dmenu.c b/dmenu.c
@@ -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();
diff --git a/dmenu_run b/dmenu_run
@@ -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
diff --git a/drw.c b/drw.c
@@ -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
diff --git a/drw.h b/drw.h
@@ -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
diff --git a/patch/center.c b/patch/center.c
@@ -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;
+}
diff --git a/patch/dynamicoptions.c b/patch/dynamicoptions.c
@@ -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;
+ }
+}
diff --git a/patch/dynamicoptions.h b/patch/dynamicoptions.h
@@ -0,0 +1,2 @@
+static void refreshoptions(void);
+static void readstream(FILE* stream);
diff --git a/patch/fuzzymatch.c b/patch/fuzzymatch.c
@@ -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();
+}
diff --git a/patch/fzfexpect.c b/patch/fzfexpect.c
@@ -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
diff --git a/patch/fzfexpect.h b/patch/fzfexpect.h
@@ -0,0 +1 @@
+static void expect(char *expect, XKeyEvent *ev);
diff --git a/patch/highlight.c b/patch/highlight.c
@@ -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);
+ }
+ }
+}
diff --git a/patch/highpriority.c b/patch/highpriority.c
@@ -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;
+}
diff --git a/patch/highpriority.h b/patch/highpriority.h
@@ -0,0 +1 @@
+static int arrayhas(char **list, int length, char *item);
diff --git a/patch/include.c b/patch/include.c
@@ -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
diff --git a/patch/include.h b/patch/include.h
@@ -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
diff --git a/patch/inputmethod.c b/patch/inputmethod.c
@@ -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);
+}
diff --git a/patch/inputmethod.h b/patch/inputmethod.h
@@ -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);
diff --git a/patch/mousesupport.c b/patch/mousesupport.c
@@ -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
diff --git a/patch/mousesupport.h b/patch/mousesupport.h
@@ -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
diff --git a/patch/multiselect.c b/patch/multiselect.c
@@ -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;
+ }
+}
diff --git a/patch/multiselect.h b/patch/multiselect.h
@@ -0,0 +1,2 @@
+static int issel(size_t id);
+static void printselected(unsigned int state);
diff --git a/patch/navhistory.c b/patch/navhistory.c
@@ -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);
+ }
+}
diff --git a/patch/navhistory.h b/patch/navhistory.h
@@ -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);
diff --git a/patch/nonblockingstdin.c b/patch/nonblockingstdin.c
@@ -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();
+ }
+}
diff --git a/patch/nonblockingstdin.h b/patch/nonblockingstdin.h
@@ -0,0 +1 @@
+static void readevent();
diff --git a/patch/numbers.c b/patch/numbers.c
@@ -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);
+}
diff --git a/patch/numbers.h b/patch/numbers.h
@@ -0,0 +1,4 @@
+#define NUMBERSMAXDIGITS 100
+#define NUMBERSBUFSIZE (NUMBERSMAXDIGITS * 2) + 1
+
+static void recalculatenumbers(void);
diff --git a/patch/scroll.c b/patch/scroll.c
@@ -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;
+}
diff --git a/patch/scroll.h b/patch/scroll.h
@@ -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);
diff --git a/patch/vi_mode.c b/patch/vi_mode.c
@@ -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();
+}
diff --git a/patch/vi_mode.h b/patch/vi_mode.h
@@ -0,0 +1,6 @@
+typedef struct {
+ KeySym ksym;
+ unsigned int state;
+} Key;
+
+static void vi_keypress(KeySym ksym, const XKeyEvent *ev);
diff --git a/patch/xresources.c b/patch/xresources.c
@@ -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);
+ }
+}
diff --git a/patches.def.h b/patches.def.h
@@ -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
diff --git a/patches.h b/patches.h
@@ -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
diff --git a/util.c b/util.c
@@ -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);
}
diff --git a/util.h b/util.h
@@ -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])