New stuff, finalizing things, fixing bugs

This commit is contained in:
Alexis Jhon Gaspar 2023-09-17 20:00:51 +08:00
parent 066ba2fedb
commit 66fcd5c583
156 changed files with 8720 additions and 2279 deletions

View file

@ -1,18 +1,49 @@
# Suckless Utilities version 6.3
# Suckless Utilities version 6.4
## About
These are my builds of suckless software such as dwm and st.
It's simple to compile these things.
These are my builds of suckless software such as dwm and st, based on the work for flexipatch by bakkeby. This aims for much more streamlined configuration and patching than 6.3 (which becomes more complicated over time and whenever more patches are integrated).
This was designed to save me some sanity in maintaining it as well as easily integrating requested patches, whenever it drops from the flexipatch upstream. This should be easy to hack and build, and should be as fast as the previous versions of my build.
## Included software
- dwm
- dmenu
- st
- slstatus
- tabbed
- sfm
- spmenu
- dwmblocks-async
- slock
## Notes
Additionals are spmenu configs made by myself, as well as scripts for `spmenu_run`. If you prefer dmenu, it still exists, and could be launched via `Win/Super/Cmd+Alt+S`, while `Win/Super/Cmd+S` would launch `spmenu_run -d` by default (only with .desktop entries, while `Win/Super/Cmd+Shift+S` would launch `spmenu_run` in a similar fashion to dmenu.
### Compatibility notes
For those who want the old version, check out `oldmain`. I don't plan on maintaining it myself since the flexipatch base means much more flexibility over codebase updates as well as new patches.
Note that there are some programs that is included here, mainly for compatibility or choice reasons. While slstatus is pretty barebones compared to dwmblocks-async, it is included on the repo if one decides not to have statuscmd, for example. This aims to be also compatible with already existing setups.
### Keybind notes
In the documentation for this suite, <kbd>Mod4Key</kbd> would be defined as <kbd>⊞ Win</kbd>/<kbd>⌘ Cmd</kbd>/<kbd>❖ Super</kbd>, depending on whichever keyboard do you use.
In most cases, you probably have only <kbd>⊞ Win</kbd>, but I added <kbd>⌘ Cmd</kbd> and <kbd>❖ Super</kbd> for Mac and advanced Linux/Unix users, respectively.
If one uses ChromeOS, <kbd>⊞ Win</kbd> equals to the <kbd>🔍 Search</kbd> key. But I don't know who uses X11 window managers inside ChromeOS.
For new to dwm, <kbd>MODKEY</kbd> or <kbd>Mod1Mask</kbd> is the <kbd>Alt</kbd> key.
### spmenu notes
Additionals are spmenu configs made by myself, as well as scripts for `spmenu_run`. If you prefer dmenu, it still exists, and could be launched via <kbd>⊞ Win</kbd>/<kbd>⌘ Cmd</kbd>/<kbd>❖ Super</kbd>+<kbd>Alt</kbd>+<kbd>S</kbd>, while <kbd>⊞ Win</kbd>/<kbd>⌘ Cmd</kbd>/<kbd>❖ Super</kbd>+<kbd>S</kbd> would launch `spmenu_run -d` by default (only with .desktop entries, while <kbd>⊞ Win</kbd>/<kbd>⌘ Cmd</kbd>/<kbd>❖ Super</kbd>+<kbd>Shift</kbd>+<kbd>S</kbd> would launch `spmenu_run` in a similar fashion to dmenu.
Some user scripts are also included, which has it's own set of dependencies. For example, `clipmenu-spmenu` needs `xsel` and `clipnotify`. These are optional, however.
```
clipmenu-spmenu dependencies:
- xsel
- clipnotify (included on the folder)
- clipnotify
screenshot-spmenu dependencies:
- curl
@ -25,19 +56,23 @@ screenshot-spmenu dependencies:
wallpaper-spmenu dependencies:
- xwallpaper
```
## Building
1. Install necessary tools and libraries
Additionally, spmenu will not work on macOS, so use `dmenu` instead.
## Building
### Prerequisites
```
Linux/Unix users:
- xorg (including drivers of course)
- base-devel (or build-essential/s)
- libX11(-devel or -dev)
- libXft(-devel or -dev)
- libXcb(-devel or -dev)
- libXrender(-devel or -dev)
- libXinerama(-devel or -dev)
- freetype(-devel or -dev)
- fontconfig(-devel or -dev)
- Nerd Fonts (for slstatus)
- Nerd Fonts (Hack as default, can be changed manually)
- imlibs2(-devel or -dev)
- picom (for transparency)
- feh (optional)
@ -64,11 +99,15 @@ For spmenu:
- libconfig(-devel or -dev)
- OpenSSL or libssl(-devel or -dev)
- meson
Refer to patches.def.h and config.mk for additional patch-related requirements.
```
### Compiling the whole thing
1. Install necessary tools and libraries
2. Clone this repository (`git clone --recurse-submodules`)
3. Change directory to what suckless software do you want to use
4. Remove the `config.h` file, to make sure all patches are applied correctly
4. Remove the `config.h`, and `patches.h` files, to make sure all patches are applied correctly
5. Copy `make clean install` and paste it on your terminal
6. Building the spmenu submodule included in this repo (by speedie) would strictly use meson as it's build system.
1. For that, `cd` to the spmenu folder.
@ -76,46 +115,53 @@ For spmenu:
3. Run `ninja -C build` for building the binaries.
4. Install via `meson install -C build`, and it'll prompt you if you would like to use sudo if not run as root.
7. Insert dwm, slstatus and/or st inside your `.xinitrc` using your favorite text editor (usually located in `/home/<username>/.xinitrc`)
8. Start it and done!
7. Insert dwm, slstatus and/or st inside your `.xinitrc` using your favorite text editor (usually located in `$HOME/.xinitrc`)
- Additionally, a script called `startdwm` located in `desktop` could be installed in `/usr/local/bin` which could be used to launch dwm on display managers, such as GDM or SDDM.
- `startdwm` could be also used as the xinitrc script by putting it under $HOME and renaming it to `.xinitrc`.
8. Install the `dwmblocks` scripts (in `scripts/dwmblocks`) to your `$PATH`.
9. Start it and done!
## Current bugs
- ~~Taskbar not working properly~~ (fixed in commit [e9015f2](https://github.com/Lucas-mother3/suckless-utils/commit/e9015f2d2a09ef66f1c9e188b277c89d23635195) & [7085f9](https://github.com/Lucas-mother3/suckless-utils/commit/7085f97d80fc203d6f54d0209af07007c0347880)). Thanks, [Speedie](https://speedie.gq)!
- ~~Unhiding a hidden window (using the show/hide function) and if it's the only program running, crashes dwm~~
- ~~Alt-tab crashes dwm altogther (idk man)~~
## Future plans
- [ ] Rebase the dwm build to dwm-flexipatch (maybe under a new branch with a VM debug environment?)
- [ ] Integrate barmodules if the dwm-flexipatch rewrite did happen
- [ ] Version jump from 6.3 -> 6.4
- [x] Rebase the dwm build to dwm-flexipatch (maybe under a new branch with a VM debug environment?)
- [x] Integrate barmodules if the dwm-flexipatch rewrite did happen
- [x] Version jump from 6.3 -> 6.4
- [ ] Potentially making this project into a desktop environment, when I feel it's ready to do so
- [ ] Use `spmenu-desktop-launcher` if it's mature/usable, retaining `spmenu_run` for backwards compatibility with existing scripts
- [ ] Making a wiki for documenting functions in this build, as well as other important information about the project
## Patching even further
Patching everything is as easy as downloading the diff file, use the `patch` command and apply changes.
Patching everything is as easy as editing the `patches.def.h` file included in the repo. Unlike 6.3, which had a complicated codebase, 6.4 aims for a much more streamlined process of patching things, unlike the previous version which would mean using `patch` and manually editing files whenever something isn't patched up properly.
But, since this is a heavily patched version of everything, I wouldn't recommend patching even further unless if you know what you're doing.
A huge thanks for bakkeby on the work for making suckless software easier to patch, meaning more people could modify and configure the code to their liking.
## Contributing to the project
Contributions are welcome, as long as it follows the defined rules in [the CONTRIBUTING document](/CONTRIBUTING.md).
Documentations are also welcome, in fact, I do need someone who could maintain documentation for the project's inner workings.
## How the versioning system works
Suckless Uilities (the whole package and not the individual components) are versioned under the current version of the repo's dwm.
Even if dwm 6.4 releases, if the repo still uses dwm 6.3 for compatibility reasons, the whole package will be still be Suckless Utilities 6.3.
Suckless Utilities (the whole package and not the individual components) are versioned under the current version of the repo's dwm.
Even if dwm(-flexipatch) 6.5 releases, if the repo still uses dwm(-flexipatch) 6.4 for compatibility reasons, the whole package will still be Suckless Utilities 6.4.
## Licensing
All programs are licensed under the MIT License, which sucks, and worse than GNU GPL, but hey, it's better than proprietary code!
All programs are licensed under the MIT License, except for some submodules, which might have different licenses (for example, GPLv2).
## Screenshots
![Screenshot of neofetch](/pics/neofetch.png)
![Screenshot of random applications (Spotify, Space Cadet Pinball, NCSA Mosaic)](/pics/random.png)
![Screenshot of random applications](/pics/random.png)
## Special thanks
* [Speedie](https://speedie.gq) for helping me out with this and providing me with patches
* [Speedie](https://speedie.site) for helping me out with this and providing me with patches
* [The suckless team](https://suckless.org) for maintaining suckless software suck less
* [bakkeby](https://github.com/bakkeby) for creating dwm-flexipatch and related projects, making it possible to easily integrate patches
## Mirrors
* [GitHub](https://github.com/Lucas-mother3/suckless-utils) - Main mirror
* [GitLab](https://gitlab.com/Lucas-mother3/suckless-utils) - Secondary (and backup)
* [BitBucket](https://bitbucket.org/Lucas-mother3/suckless-utils) - Secondary backup
* [speedie.site](https://git.speedie.site/Lucas-mother3/suckless-utils) - Mirror of gitlab
* [speedie.site](https://git.speedie.site/Lucas-mother3/suckless-utils) - Mirror of gitlab

8
desktop/startdwm Executable file
View file

@ -0,0 +1,8 @@
#!/bin/sh
systemctl --user restart clipmenud.service # restarts clipmenud, if installed
wal -i "/usr/local/share/wallpapers/opensuse.png" --backend colorz # could be changed
picom -b # launches compositor
/usr/local/bin/dwmblocks & # launches slstatus/dwmblocks
/usr/bin/dunst & # launches dunst daemon
paplay /usr/share/sounds/Oxygen-Sys-Special.ogg # autoplay sound, optional
exec /usr/local/bin/dwm # launch dwm

View file

@ -1,4 +1,5 @@
*.o
config.h
patches.h
dmenu
stest
config.h

View file

@ -20,7 +20,10 @@ options:
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)

265
dmenu-flexipatch/README.md Normal file
View file

@ -0,0 +1,265 @@
Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this dmenu 5.2 (0fe460d,
2023-04-05) project has a different take on patching. It uses preprocessor directives to decide
whether or not to include a patch during build time. Essentially this means that this build, for
better or worse, contains both the patched _and_ the original code. The aim being that you can
select which patches to include and the build will contain that code and nothing more.
For example to include the `alpha` patch then you would only need to flip this setting from 0
to 1 in [patches.h](https://github.com/bakkeby/dmenu-flexipatch/blob/master/patches.def.h):
```c
#define ALPHA_PATCH 1
```
Once you have found out what works for you and what doesn't then you should be in a better position
to choose patches should you want to start patching from scratch.
Alternatively if you have found the patches you want, but don't want the rest of the flexipatch
entanglement on your plate then you may want to have a look at
[flexipatch-finalizer](https://github.com/bakkeby/flexipatch-finalizer); a custom pre-processor
tool that removes all the unused flexipatch code leaving you with a build that contains the patches
you selected.
Refer to [https://tools.suckless.org/dmenu/](https://tools.suckless.org/dmenu/) for details on
dmenu, how to install it and how it works.
Browsing patches? There is a [map of patches](https://coggle.it/diagram/YjT2DD6jBM9dayf3) diagram which tries to organise patches into categories.
---
### Changelog:
2023-06-15 - Added the caret width patch
2022-09-05 - Removed the json patch due to maintenance and compatibility reasons, added the
separator patch
2022-09-04 - Added the fzfexpect patch
2022-06-21 - Adding barpadding patch and relative input width patch
2022-03-02 - Bump to 5.1
2021-05-23 - Adding support for `ctrl+v` to paste and adding emoji-highlight patch
2021-05-17 - Added the restrict return, no sort, gridnav and plain-prompt (listfullwidth) patches
2021-05-15 - Added the tsv and printindex patches
2020-08-08 - Added the json, symbols, managed, morecolor, multi-selection and preselect patches
2020-08-05 - Added the grid, highlight, highpriority, dynamic options and numbers patches
2020-06-13 - Added the pango patch
2020-06-10 - Added the case-insensitive patch
2020-05-29 - Added the alpha patch (derived from Baitinq's [build](https://github.com/Baitinq/dmenu))
and the color emoji patch
2020-04-05 - Added fuzzyhighlight patch
2020-02-09 - Added revised border patch (adding command line parameter for setting border width)
2019-12-29 - Added xresources patch
2019-10-16 - Introduced [flexipatch-finalizer](https://github.com/bakkeby/flexipatch-finalizer)
2019-09-18 - Added border, center, fuzzymatch, incremental, initialtext, instant, line-height,
mouse-support, navhistory, non-blocking-stdin, password, pipeout, printinputtext,
rejectnomatch, scroll, vertfull, wmtype and xyw patches
### Patches included:
- [alpha](https://github.com/bakkeby/patches/blob/master/dmenu/dmenu-alpha-5.0_20210725_523aa08.diff)
- adds transparency for the dmenu window
- [barpadding](https://github.com/bakkeby/patches/wiki/barpadding)
- adds padding for dmenu in similar fashion to the [barpadding](https://dwm.suckless.org/patches/barpadding/)
patch for dwm
- [border](http://tools.suckless.org/dmenu/patches/border/)
- adds a border around the dmenu window
- [caret-width](https://github.com/DarkSamus669/dmenu-patches/blob/main/dmenu-caretwidth-5.2.diff)
- makes the caret width configurable and overridable via a command line option
- [case-insensitive](http://tools.suckless.org/dmenu/patches/case-insensitive/)
- makes dmenu case-insensitive by default, replacing the case-insensitive `-i` option with a
case sensitive `-s` option
- [center](https://tools.suckless.org/dmenu/patches/center/)
- this patch centers dmenu in the middle of the screen
- color_emoji
- enables color emoji in dmenu by removing a workaround for a BadLength error in the Xft
library when color glyphs are used
- enabling this will crash dmenu on encountering such glyphs unless you also have an updated
Xft library that can handle them
- [dynamic_options](https://tools.suckless.org/dmenu/patches/dynamicoptions/)
- 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
- [emoji-highlight](https://tools.suckless.org/dmenu/patches/emoji-highlight/)
- this patch will allow for emojis on the left side with a colored background when selected
- [fuzzyhighlight](https://tools.suckless.org/dmenu/patches/fuzzyhighlight/)
- intended to be combined with the fuzzymatch patch, this makes it so that fuzzy matches are
highlighted
- [fuzzymatch](https://tools.suckless.org/dmenu/patches/fuzzymatch/)
- adds support for fuzzy-matching to dmenu, allowing users to type non-consecutive portions
of the string to be matched
- [fzfexpect](https://github.com/DAFF0D11/dafmenu/blob/master/patches/dmenu-fzfexpect-5.1.diff)
- adds fzf expect functionality in dmenu
- [grid](https://tools.suckless.org/dmenu/patches/grid/)
- 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
- [gridnav](https://tools.suckless.org/dmenu/patches/gridnav/)
- adds the ability to move left and right through a grid (when using the grid patch)
- [highlight](https://tools.suckless.org/dmenu/patches/highlight/)
- this patch highlights the individual characters of matched text for each dmenu list entry
- [highpriority](https://tools.suckless.org/dmenu/patches/highpriority/)
- this patch will automatically sort the search result so that high priority items are shown
first
- [incremental](https://tools.suckless.org/dmenu/patches/incremental/)
- this patch causes dmenu to print out the current text each time a key is pressed
- [initialtext](https://tools.suckless.org/dmenu/patches/initialtext/)
- adds an option to provide preselected text
- [instant](https://tools.suckless.org/dmenu/patches/instant/)
- adds a flag that will cause dmenu to select an item immediately if there is only one
matching option left
- [~json~](https://tools.suckless.org/dmenu/patches/json/)
- ~adds basic support for json files~
- [line-height](http://tools.suckless.org/dmenu/patches/line-height/)
- 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
- [managed](https://tools.suckless.org/dmenu/patches/managed/)
- 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
- [morecolor](https://tools.suckless.org/dmenu/patches/morecolor/)
- adds an additional color scheme for highlighting entries adjacent to the current selection
- [mouse-support](https://tools.suckless.org/dmenu/patches/mouse-support/)
- adds basic mouse support for dmenu
- [multi-selection](https://tools.suckless.org/dmenu/patches/multi-selection/)
- 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
- [navhistory](https://tools.suckless.org/dmenu/patches/navhistory/)
- provides dmenu the ability for history navigation similar to that of bash
- [no-sort](https://tools.suckless.org/dmenu/patches/no-sort/)
- 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
- [non-blocking-stdin](https://tools.suckless.org/dmenu/patches/non_blocking_stdin/)
- 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
- the patch is meant to be used along with the incremental patch in order to use stdout to
feed stdin
- [numbers](https://tools.suckless.org/dmenu/patches/numbers/)
- adds text which displays the number of matched and total items in the top right corner of
dmenu
- [pango](https://github.com/StillANixRookie/dmenu-pango/)
- adds simple markup for dmenu using pango markup
- [password](https://tools.suckless.org/dmenu/patches/password/)
- with this patch dmenu will not directly display the keyboard input, but instead replace it
with dots
- all data from stdin will be ignored
- [pipeout](https://tools.suckless.org/dmenu/patches/pipeout/)
- 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
- [plain-prompt](https://tools.suckless.org/dmenu/patches/listfullwidth/)
- simple change that avoids colors for the prompt by making it use the same style as the
rest of the input field
- [prefix-completion](https://tools.suckless.org/dmenu/patches/prefix-completion/)
- changes the behaviour of matched items and the Tab key to allow tab completion
- [preselect](https://tools.suckless.org/dmenu/patches/preselect/)
- adds an option `-ps` to preselect an item by providing the index that should be pre-selected
- [printindex](https://tools.suckless.org/dmenu/patches/printindex/)
- 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
- [printinputtext](https://tools.suckless.org/dmenu/patches/printinputtext/)
- this patch adds a flag `-t` which makes Return key ignore selection and print the input
text to stdout
- the flag basically swaps the functions of Return and Shift+Return hotkeys
- [rejectnomatch](https://tools.suckless.org/dmenu/patches/reject-no-match/)
- adds a new flag to dmenu with which text input will be rejected if it would result in no
matching item
- relative_input_width
- prior to commit [e1e1de7](https://git.suckless.org/dmenu/commit/e1e1de7b3b8399cba90ddca9613f837b2dbef7b9.html)
the input width was calculated based on the input options
- this feature was removed in favour of hardcoding the input width to always take up 1/3rd of
the available space
- this patch adds that feature back in with some bespoke performance optimisations at the cost
of accuracy and correctness
- [restrict-return](https://tools.suckless.org/dmenu/patches/restrict-return/)
- 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
- [scroll](https://tools.suckless.org/dmenu/patches/scroll/)
- this patch adds support for text scrolling
- it doesn't append `...` for long input anymore as it can handle long text
- [separator](https://tools.suckless.org/dmenu/patches/separator/)
- 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
- [symbols](https://tools.suckless.org/dmenu/patches/symbols/)
- 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
- [tsv](https://tools.suckless.org/dmenu/patches/tsv/)
- makes dmenu split input lines at first tab character and only display first part, but it
will perform matching on and output full lines as usual
- can be useful if you want to separate data and representation
- [vertfull](https://tools.suckless.org/dmenu/patches/vertfull/)
- prevents dmenu from indenting items at the same level as the prompt length
- [wmtype](https://github.com/Baitinq/dmenu/blob/master/patches/dmenu-wm_type.diff)
- adds extended window manager hints such as \_NET_WM_WINDOW_TYPE and \_NET_WM_WINDOW_TYPE_DOCK
- [xresources](https://tools.suckless.org/dmenu/patches/xresources/)
- allows dmenu to read font and colors from Xresources
- note that with this patch the Xresources settings takes precedence over command line arguments
- [xyw](https://tools.suckless.org/dmenu/patches/xyw/)
- adds options for specifying dmenu window position and width

View file

@ -0,0 +1,153 @@
/* 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 */
#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 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 */
#if PANGO_PATCH
static char font[] = "Hack Nerd Font 12";
#else
#if XRESOURCES_PATCH
static char *fonts[] =
#else
static const char *fonts[] =
#endif // XRESOURCES_PATCH
{
"Hack Nerd Font:size=12"
};
#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 */
#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 || FUZZYHIGHLIGHT_PATCH
[SchemeSelHighlight] = { OPAQUE, baralpha, borderalpha },
[SchemeNormHighlight] = { OPAQUE, baralpha, borderalpha },
#endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_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
};
#endif // ALPHA_PATCH
static
#if !XRESOURCES_PATCH
const
#endif // XRESOURCES_PATCH
char *colors[][2] = {
/* fg bg */
[SchemeNorm] = { "#bbbbbb", "#222222" },
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
#if BORDER_PATCH
[SchemeBorder] = { "#000000", "#005577" },
#endif // BORDER_PATCH
#if MORECOLOR_PATCH
[SchemeMid] = { "#eeeeee", "#770000" },
#endif // MORECOLOR_PATCH
#if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH
[SchemeSelHighlight] = { "#ffc978", "#005577" },
[SchemeNormHighlight] = { "#ffc978", "#222222" },
#endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_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
};
/* -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 BORDER_PATCH
/* Size of the window border */
static unsigned int border_width = 0;
#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

View file

@ -1,5 +1,5 @@
# dmenu version
VERSION = 5.1
VERSION = 5.2
# paths
PREFIX = /usr/local
@ -15,19 +15,26 @@ XINERAMAFLAGS = -DXINERAMA
# freetype
FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2
# alpha
XRENDERLIBS = -lXrender
# OpenBSD (uncomment)
#FREETYPEINC = $(X11INC)/freetype2
#MANPREFIX = ${PREFIX}/man
# uncomment on RHEL for strcasecmp
#EXTRAFLAGS=-D_GNU_SOURCE
# Uncomment this for the alpha patch / ALPHA_PATCH
XRENDER = -lXrender
# 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) ${XRENDERLIBS}
INCS = -I$(X11INC) -I$(FREETYPEINC) ${PANGOINC}
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm $(XRENDER) ${PANGOLIB}
# 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)

View file

@ -4,8 +4,6 @@ dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-bfiv ]
.RB [ \-g
.IR columns ]
.RB [ \-l
.IR lines ]
.RB [ \-m
@ -49,11 +47,8 @@ is faster, but will lock up X until stdin reaches end\-of\-file.
.B \-i
dmenu matches menu items case insensitively.
.TP
.BI \-g " columns"
dmenu lists items in a grid with the given number of columns.
.TP
.BI \-l " lines"
dmenu lists items in a grid with the given number of lines.
dmenu lists items vertically, with the given number of lines.
.TP
.BI \-m " monitor"
dmenu is displayed on the monitor number supplied. Monitor numbers are starting

2220
dmenu-flexipatch/dmenu.c Normal file

File diff suppressed because it is too large Load diff

6
dmenu-flexipatch/dmenu_run Executable file
View file

@ -0,0 +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"} &

View file

@ -5,9 +5,11 @@
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include "patches.h"
#include "drw.h"
#include "util.h"
#if !PANGO_PATCH
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
@ -59,9 +61,14 @@ utf8decode(const char *c, long *u, size_t clen)
return len;
}
#endif // PANGO_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));
@ -70,11 +77,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;
@ -90,7 +102,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
@ -98,10 +114,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.
*/
@ -136,6 +191,7 @@ 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
@ -144,10 +200,11 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
* and lots more all over the internet.
*/
FcBool iscol;
if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && 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;
@ -157,18 +214,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)
{
@ -186,7 +263,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)
{
@ -195,24 +281,40 @@ 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
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
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;
@ -222,16 +324,22 @@ drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], si
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;
}
#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)
@ -252,15 +360,80 @@ 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)
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 ty;
unsigned int ew;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
size_t i, 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, NULL, markup);
/* shorten text if necessary */
for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
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 - drw->font->h) / 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)
{
int i, ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
@ -268,24 +441,37 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
FcPattern *fcpattern;
FcPattern *match;
XftResult result;
int charexists = 0;
int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */
enum { nomatches_len = 64 };
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
const char *ellipsis = "...";
static unsigned int ellipsis_width = 0;
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
if (!render) {
w = ~w;
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;
}
usedfont = drw->fonts;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, ellipsis);
while (1) {
utf8strlen = 0;
ew = ellipsis_len = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
@ -293,46 +479,53 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
} else {
nextfont = curfont;
drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
if (ew + ellipsis_width <= w) {
/* keep track where the ellipsis still fits */
ellipsis_x = x + ew;
ellipsis_w = w - ew;
ellipsis_len = utf8strlen;
}
if (ew + tmpw > w) {
overflow = 1;
/* called from drw_fontset_getwidth_clamp():
* it wants the width AFTER the overflow
*/
if (!render)
x += tmpw;
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
ew += tmpw;
} else {
nextfont = curfont;
}
break;
}
}
if (!charexists || nextfont)
if (overflow || !charexists || nextfont)
break;
else
charexists = 0;
}
if (utf8strlen) {
drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* shorten text if necessary */
for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
if (len) {
memcpy(buf, utf8str, len);
buf[len] = '\0';
if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
}
x += ew;
w -= ew;
}
if (render) {
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;
}
if (render && overflow && ellipsis_w)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, ellipsis, invert);
if (!*text) {
if (!*text || overflow) {
break;
} else if (nextfont) {
charexists = 0;
@ -342,6 +535,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */
charexists = 1;
for (i = 0; i < nomatches_len; ++i) {
/* avoid calling XftFontMatch if we know we won't find a match */
if (utf8codepoint == nomatches.codepoint[i])
goto no_match;
}
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
@ -353,7 +552,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);
@ -370,6 +571,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont;
} else {
xfont_free(usedfont);
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
no_match:
usedfont = drw->fonts;
}
}
@ -380,6 +583,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
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)
@ -391,6 +595,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)
{
@ -399,6 +621,37 @@ drw_fontset_getwidth(Drw *drw, const char *text)
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
unsigned int
drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
{
unsigned int tmp = 0;
if (drw && drw->fonts && text && 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 = font->h;
}
#else
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{
@ -413,6 +666,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)
@ -436,3 +690,7 @@ drw_cur_free(Drw *drw, Cur *cursor)
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}
#if SCROLL_PATCH
#include "patch/scroll.c"
#endif

View file

@ -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,41 +29,75 @@ 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
/* 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);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
#endif // ALPHA_PATCH
/* Cursor abstraction */
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

View file

@ -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;
}

View file

@ -0,0 +1,91 @@
static void
refreshoptions()
{
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 = separator_greedy ?
strrchr(items[i].text, separator) : strchr(items[i].text, separator))) {
*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;
}
}

View file

@ -0,0 +1,2 @@
static void refreshoptions();
static void readstream(FILE* stream);

View file

@ -0,0 +1,66 @@
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
{
int i, indent;
char *highlight;
char c;
#if EMOJI_HIGHLIGHT_PATCH
char *itemtext = output;
#elif TSV_PATCH && !SEPARATOR_PATCH
char *itemtext = item->stext;
#else
char *itemtext = item->text;
#endif // 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]);
for (i = 0, highlight = itemtext; *highlight && text[i];) {
#if FUZZYMATCH_PATCH
if (!fstrncmp(&(*highlight), &text[i], 1))
#else
if (*highlight == text[i])
#endif // FUZZYMATCH_PATCH
{
/* get indentation */
c = *highlight;
*highlight = '\0';
indent = TEXTW(itemtext) - lrpad;
*highlight = c;
/* highlight character */
c = highlight[1];
highlight[1] = '\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[1] = c;
i++;
}
highlight++;
}
}

View file

@ -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) {
puts(matches->text);
cleanup();
exit(0);
}
#endif // INSTANT_PATCH
calcoffsets();
}

View file

@ -0,0 +1,39 @@
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);
for (int i = 0; i < selidsize; i++)
if (selid[i] != -1 && (!sel || sel->id != selid[i]))
puts(items[selid[i]].text);
if (sel && !(ev->state & ShiftMask)) {
puts(sel->text);
} else
puts(text);
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);
puts(sel->text);
cleanup();
exit(1);
} else if (!sel && expected && strstr(expected, expect)){
puts(expect);
cleanup();
exit(1);
}
}
#endif // MULTI_SELECTION_PATCH

View file

@ -0,0 +1 @@
static void expect(char *expect, XKeyEvent *ev);

View file

@ -0,0 +1,60 @@
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 indentx, highlightlen;
#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
/* 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]);
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';
indentx = TEXTW(itemtext);
itemtext[highlightlen] = restorechar;
// Move highlight str end, draw highlight, & restore
restorechar = highlight[strlen(token)];
highlight[strlen(token)] = '\0';
if (indentx - (lrpad / 2) - 1 < maxw)
drw_text(
drw,
x + indentx - (lrpad / 2) - 1,
y,
MIN(maxw - indentx, 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);
}
}
}

View file

@ -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;
}

View file

@ -0,0 +1,2 @@
static int arrayhas(char **list, int length, char *item);

View file

@ -0,0 +1,38 @@
#if CENTER_PATCH
#include "center.c"
#endif
#if FUZZYHIGHLIGHT_PATCH
#include "fuzzyhighlight.c"
#elif 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 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 NON_BLOCKING_STDIN_PATCH
#include "nonblockingstdin.c"
#endif
#if NUMBERS_PATCH
#include "numbers.c"
#endif
#if XRESOURCES_PATCH
#include "xresources.c"
#endif

View file

@ -0,0 +1,18 @@
#if DYNAMIC_OPTIONS_PATCH
#include "dynamicoptions.h"
#endif
#if FZFEXPECT_PATCH
#include "fzfexpect.h"
#endif
#if MULTI_SELECTION_PATCH
#include "multiselect.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

View file

@ -0,0 +1,159 @@
static void
buttonpress(XEvent *e)
{
struct item *item;
XButtonPressedEvent *ev = &e->xbutton;
int x = 0, y = 0, h = bh, w;
if (ev->window != win)
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 & ~ControlMask)
return;
if (lines > 0) {
/* vertical list: (ctrl)left-click on item */
w = mw - x;
for (item = curr; item != next; item = item->right) {
y += h;
if (ev->y >= y && ev->y <= (y + h)) {
#if !MULTI_SELECTION_PATCH
puts(item->text);
#endif // MULTI_SELECTION_PATCH
if (!(ev->state & ControlMask)) {
#if MULTI_SELECTION_PATCH
sel = item;
selsel();
printsel(ev->state);
#endif // MULTI_SELECTION_PATCH
exit(0);
}
sel = item;
if (sel) {
#if MULTI_SELECTION_PATCH
selsel();
#else
sel->out = 1;
#endif // MULTI_SELECTION_PATCH
drawmenu();
}
return;
}
}
} 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) {
#if !MULTI_SELECTION_PATCH
puts(item->text);
#endif // MULTI_SELECTION_PATCH
if (!(ev->state & ControlMask)) {
#if MULTI_SELECTION_PATCH
sel = item;
selsel();
printsel(ev->state);
#endif // MULTI_SELECTION_PATCH
exit(0);
}
sel = item;
if (sel) {
#if MULTI_SELECTION_PATCH
selsel();
#else
sel->out = 1;
#endif // MULTI_SELECTION_PATCH
drawmenu();
}
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;
}
}
}

View file

@ -0,0 +1,53 @@
static int
issel(size_t id)
{
for (int i = 0;i < selidsize;i++)
if (selid[i] == id)
return 1;
return 0;
}
static void
printsel(unsigned int state)
{
for (int i = 0;i < selidsize;i++)
if (selid[i] != -1 && (!sel || sel->id != selid[i])) {
#if PRINTINDEX_PATCH
if (print_index)
printf("%d\n", selid[i]);
else
#endif // PRINTINDEX_PATCH
puts(items[selid[i]].text);
}
if (sel && !(state & ShiftMask)) {
#if PRINTINDEX_PATCH
if (print_index)
printf("%d\n", sel->index);
else
#endif // PRINTINDEX_PATCH
puts(sel->text);
} else
puts(text);
}
static void
selsel()
{
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;
}
}

View file

@ -0,0 +1 @@
static int issel(size_t id);

View file

@ -0,0 +1,126 @@
static char *histfile;
static char **history;
static size_t histsz, histpos;
static void
loadhistory(void)
{
FILE *fp = NULL;
static size_t cap = 0;
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;
}
if (cap == histsz) {
cap += 64 * sizeof(char*);
history = realloc(history, cap);
if (!history) {
die("failed to realloc memory");
}
}
strtok(line, "\n");
history[histsz] = line;
histsz++;
}
histpos = histsz;
if (fclose(fp)) {
die("failed to close file %s", histfile);
}
}
static 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();
}
static void
savehistory(char *input)
{
unsigned int i;
FILE *fp;
if (!histfile ||
0 == maxhist ||
0 == strlen(input)) {
goto out;
}
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 (histsz == 0 || !histnodup || (histsz > 0 && strcmp(input, history[histsz-1]) != 0)) { /* TODO */
if (0 >= fputs(input, fp)) {
die("failed to write to %s", histfile);
}
}
if (fclose(fp)) {
die("failed to close file %s", histfile);
}
out:
for (i = 0; i < histsz; i++) {
free(history[i]);
}
free(history);
}

View file

@ -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();
}
}

View file

@ -0,0 +1 @@
static void readevent();

View file

@ -0,0 +1,16 @@
static char numbers[NUMBERSBUFSIZE] = "";
static void
recalculatenumbers()
{
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);
}

View file

@ -0,0 +1,4 @@
#define NUMBERSMAXDIGITS 100
#define NUMBERSBUFSIZE (NUMBERSMAXDIGITS * 2) + 1
static void recalculatenumbers();

View file

@ -0,0 +1,168 @@
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 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);
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
}
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, MIN(textlen - i, UTF_SIZ));
if (!utf8charlen) {
textlen = i;
break;
}
} else {
n = utf8nextchar(text, textlen, i, -1);
utf8charlen = utf8decode(text + n, &utf8codepoint, MIN(textlen - n, UTF_SIZ));
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;
}

View file

@ -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);

View file

@ -0,0 +1,90 @@
#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 || FUZZYHIGHLIGHT_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 | FUZZYHIGHLIGHT_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
XrmDestroyDatabase(xdb);
}
}

View file

@ -0,0 +1,355 @@
/* 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 1
/* 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 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
/* 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 1
/* 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 1
/* 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 1
/* 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 make it so that fuzzy matches gets highlighted and is therefore meant
* to be used together with the fuzzymatch patch.
* https://tools.suckless.org/dmenu/patches/fuzzyhighlight/
*/
#define FUZZYHIGHLIGHT_PATCH 1
/* 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 1
/* 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 1
/* 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 1
/* This patch highlights the individual characters of matched text for each dmenu list entry.
* The fuzzy highlight patch takes precedence over this patch.
* https://tools.suckless.org/dmenu/patches/highlight/
*/
#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
/* 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 1
/* 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 1
/* This patch adds basic mouse support for dmenu.
* https://tools.suckless.org/dmenu/patches/mouse-support/
*/
#define MOUSE_SUPPORT_PATCH 1
/* 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 1
/* 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 1
/* 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 1
/* 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.
*
* Note that the pango patch does not protect against the BadLength error from Xft
* when color glyphs are used, which means that dmenu will crash if color emoji is used.
*
* If you need color emoji then you may want to install this patched library from the AUR:
* https://aur.archlinux.org/packages/libxft-bgra/
*
* A long term fix for the libXft library is pending approval of this pull request:
* https://gitlab.freedesktop.org/xorg/lib/libxft/-/merge_requests/1
*
* 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 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.
* https://tools.suckless.org/dmenu/patches/scroll/
*/
#define SCROLL_PATCH 1
/* 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
/* 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 1
/* 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

View file

@ -6,18 +6,9 @@
#include "util.h"
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}
void
die(const char *fmt, ...) {
die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
@ -33,3 +24,13 @@ die(const char *fmt, ...) {
exit(1);
}
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}

View file

@ -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))
void die(const char *fmt, ...);

View file

@ -1,34 +0,0 @@
/* 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 */
/* -fn option overrides fonts[0]; default X11 font or font set */
static const char *fonts[] = {
"Hack Nerd Font:size=11:"
};
static const char *prompt = NULL; /* -p option; prompt to the left of input field */
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 },
};
static const char *colors[SchemeLast][2] = {
/* fg bg */
[SchemeNorm] = { "#bbbbbb", "#222222" },
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
[SchemeMid] = { "#eeeeee", "#770000" },
};
/* -l and -g options; controls number of lines and columns in grid if > 0 */
static unsigned int lines = 0;
static unsigned int columns = 0;
/*
* Characters not considered part of a word while deleting words
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";

View file

@ -1,847 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#ifdef XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
#include <X11/Xft/Xft.h>
#include "drw.h"
#include "util.h"
/* 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)))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
#define OPAQUE 0xffU
/* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeMid, SchemeLast }; /* color schemes */
struct item {
char *text;
struct item *left, *right;
int out;
};
static char text[BUFSIZ] = "";
static char *embed;
static int bh, mw, mh;
static int inputw = 0, promptw;
static int lrpad; /* sum of left and right padding */
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;
static Atom clip, utf8;
static Display *dpy;
static Window root, parentwin, win;
static XIC xic;
static int useargb = 0;
static Visual *visual;
static int depth;
static Colormap cmap;
static Drw *drw;
static Clr *scheme[SchemeLast];
#include "config.h"
static char * cistrstr(const char *s, const char *sub);
static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
static char *(*fstrstr)(const char *, const char *) = cistrstr;
static void
appenditem(struct item *item, struct item **list, struct item **last)
{
if (*last)
(*last)->right = item;
else
*list = item;
item->left = *last;
item->right = NULL;
*last = item;
}
static void
calcoffsets(void)
{
int i, n;
if (lines > 0)
n = lines * columns * bh;
else
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* 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 : MIN(TEXTW(next->text), n)) > n)
break;
for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n)
break;
}
static void
cleanup(void)
{
size_t i;
XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < SchemeLast; i++)
free(scheme[i]);
drw_free(drw);
XSync(dpy, False);
XCloseDisplay(dpy);
}
static char *
cistrstr(const char *h, const char *n)
{
size_t i;
if (!n[0])
return (char *)h;
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;
}
return NULL;
}
static int
drawitem(struct item *item, int x, int y, int w)
{
if (item == sel)
drw_setscheme(drw, scheme[SchemeSel]);
else if (item->left == sel || item->right == sel)
drw_setscheme(drw, scheme[SchemeMid]);
else if (item->out)
drw_setscheme(drw, scheme[SchemeOut]);
else
drw_setscheme(drw, scheme[SchemeNorm]);
return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
}
static void
drawmenu(void)
{
unsigned int curpos;
struct item *item;
int x = 0, y = 0, w;
drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, 0, 0, mw, mh, 1, 1);
if (prompt && *prompt) {
drw_setscheme(drw, scheme[SchemeSel]);
x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
}
/* draw input field */
w = (lines > 0 || !matches) ? mw - x : inputw;
drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
curpos = TEXTW(text) - TEXTW(&text[cursor]);
if ((curpos += lrpad / 2 - 1) < w) {
drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
}
if (lines > 0) {
/* draw grid */
int i = 0;
for (item = curr; item != next; item = item->right, i++)
drawitem(
item,
x + ((i / lines) * ((mw - x) / columns)),
y + (((i % lines) + 1) * bh),
(mw - x) / columns
);
} else if (matches) {
/* draw horizontal list */
x += inputw;
w = TEXTW("<");
if (curr->left) {
drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
}
x += w;
for (item = curr; item != next; item = item->right)
x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">")));
if (next) {
w = TEXTW(">");
drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
}
}
drw_map(drw, win, 0, 0, mw, mh);
}
static void
grabfocus(void)
{
struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 };
Window focuswin;
int i, revertwin;
for (i = 0; i < 100; ++i) {
XGetInputFocus(dpy, &focuswin, &revertwin);
if (focuswin == win)
return;
XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
nanosleep(&ts, NULL);
}
die("cannot grab focus");
}
static void
grabkeyboard(void)
{
struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
int i;
if (embed)
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)
return;
nanosleep(&ts, NULL);
}
die("cannot grab keyboard");
}
static void
match(void)
{
static char **tokv = NULL;
static int tokn = 0;
char buf[sizeof text], *s;
int i, tokc = 0;
size_t len, textsize;
struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
strcpy(buf, text);
/* separate input text into tokens to be matched individually */
for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
die("cannot realloc %u bytes:", tokn * sizeof *tokv);
len = tokc ? strlen(tokv[0]) : 0;
matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
textsize = strlen(text) + 1;
for (item = items; item && item->text; item++) {
for (i = 0; i < tokc; i++)
if (!fstrstr(item->text, tokv[i]))
break;
if (i != tokc) /* not all tokens match */
continue;
/* exact matches go first, then prefixes, then substrings */
if (!tokc || !fstrncmp(text, item->text, textsize))
appenditem(item, &matches, &matchend);
else if (!fstrncmp(tokv[0], item->text, len))
appenditem(item, &lprefix, &prefixend);
else
appenditem(item, &lsubstr, &substrend);
}
if (lprefix) {
if (matches) {
matchend->right = lprefix;
lprefix->left = matchend;
} else
matches = lprefix;
matchend = prefixend;
}
if (lsubstr) {
if (matches) {
matchend->right = lsubstr;
lsubstr->left = matchend;
} else
matches = lsubstr;
matchend = substrend;
}
curr = sel = matches;
calcoffsets();
}
static void
insert(const char *str, ssize_t n)
{
if (strlen(text) + n > sizeof text - 1)
return;
/* 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();
}
static size_t
nextrune(int inc)
{
ssize_t n;
/* return location of next utf8 rune in the given direction (+1 or -1) */
for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
;
return n;
}
static void
movewordedge(int dir)
{
if (dir < 0) { /* move cursor to the start of the word*/
while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
} else { /* move cursor to the end of the word */
while (text[cursor] && strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
while (text[cursor] && !strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
}
}
static void
keypress(XKeyEvent *ev)
{
char buf[32];
int len;
KeySym ksym;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
switch (status) {
default: /* XLookupNone, XBufferOverflow */
return;
case XLookupChars:
goto insert;
case XLookupKeySym:
case XLookupBoth:
break;
}
if (ev->state & ControlMask) {
switch(ksym) {
case XK_a: ksym = XK_Home; break;
case XK_b: ksym = XK_Left; break;
case XK_c: ksym = XK_Escape; break;
case XK_d: ksym = XK_Delete; break;
case XK_e: ksym = XK_End; break;
case XK_f: ksym = XK_Right; break;
case XK_g: ksym = XK_Escape; break;
case XK_h: ksym = XK_BackSpace; break;
case XK_i: ksym = XK_Tab; break;
case XK_j: /* fallthrough */
case XK_J: /* fallthrough */
case XK_m: /* fallthrough */
case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
case XK_n: ksym = XK_Down; break;
case XK_p: ksym = XK_Up; break;
case XK_k: /* delete right */
text[cursor] = '\0';
match();
break;
case XK_u: /* delete left */
insert(NULL, 0 - cursor);
break;
case XK_w: /* delete word */
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;
case XK_y: /* paste selection */
case XK_Y:
XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime);
return;
case XK_Left:
case XK_KP_Left:
movewordedge(-1);
goto draw;
case XK_Right:
case XK_KP_Right:
movewordedge(+1);
goto draw;
case XK_Return:
case XK_KP_Enter:
break;
case XK_bracketleft:
cleanup();
exit(1);
default:
return;
}
} else if (ev->state & Mod1Mask) {
switch(ksym) {
case XK_b:
movewordedge(-1);
goto draw;
case XK_f:
movewordedge(+1);
goto draw;
case XK_g: ksym = XK_Home; break;
case XK_G: ksym = XK_End; break;
case XK_h: ksym = XK_Up; break;
case XK_j: ksym = XK_Next; break;
case XK_k: ksym = XK_Prior; break;
case XK_l: ksym = XK_Down; break;
default:
return;
}
}
switch(ksym) {
default:
insert:
if (!iscntrl(*buf))
insert(buf, len);
break;
case XK_Delete:
case XK_KP_Delete:
if (text[cursor] == '\0')
return;
cursor = nextrune(+1);
/* fallthrough */
case XK_BackSpace:
if (cursor == 0)
return;
insert(NULL, nextrune(-1) - cursor);
break;
case XK_End:
case XK_KP_End:
if (text[cursor] != '\0') {
cursor = strlen(text);
break;
}
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_Escape:
cleanup();
exit(1);
case XK_Home:
case XK_KP_Home:
if (sel == matches) {
cursor = 0;
break;
}
sel = curr = matches;
calcoffsets();
break;
case XK_Left:
case XK_KP_Left:
if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
cursor = nextrune(-1);
break;
}
if (lines > 0)
return;
/* fallthrough */
case XK_Up:
case XK_KP_Up:
if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
calcoffsets();
}
break;
case XK_Next:
case XK_KP_Next:
if (!next)
return;
sel = curr = next;
calcoffsets();
break;
case XK_Prior:
case XK_KP_Prior:
if (!prev)
return;
sel = curr = prev;
calcoffsets();
break;
case XK_Return:
case XK_KP_Enter:
puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
if (!(ev->state & ControlMask)) {
cleanup();
exit(0);
}
if (sel)
sel->out = 1;
break;
case XK_Right:
case XK_KP_Right:
if (text[cursor] != '\0') {
cursor = nextrune(+1);
break;
}
if (lines > 0)
return;
/* fallthrough */
case XK_Down:
case XK_KP_Down:
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
calcoffsets();
}
break;
case XK_Tab:
if (!sel)
return;
strncpy(text, sel->text, sizeof text - 1);
text[sizeof text - 1] = '\0';
cursor = strlen(text);
match();
break;
}
draw:
drawmenu();
}
static void
paste(void)
{
char *p, *q;
int di;
unsigned long dl;
Atom da;
/* we have been given the current selection, now insert it into input */
if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
== Success && p) {
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
XFree(p);
}
drawmenu();
}
static void
xinitvisual()
{
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) {
visual = DefaultVisual(dpy, screen);
depth = DefaultDepth(dpy, screen);
cmap = DefaultColormap(dpy, screen);
}
}
static void
readstdin(void)
{
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, stdin); 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);
items[i].out = 0;
drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
if (tmpmax > inputw) {
inputw = tmpmax;
imax = i;
}
}
if (items)
items[i].text = NULL;
inputw = items ? TEXTW(items[imax].text) : 0;
lines = MIN(lines, i);
}
static void
run(void)
{
XEvent ev;
while (!XNextEvent(dpy, &ev)) {
if (XFilterEvent(&ev, win))
continue;
switch(ev.type) {
case DestroyNotify:
if (ev.xdestroywindow.window != win)
break;
cleanup();
exit(1);
case Expose:
if (ev.xexpose.count == 0)
drw_map(drw, win, 0, 0, mw, mh);
break;
case FocusIn:
/* regrab focus from parent window */
if (ev.xfocus.window != win)
grabfocus();
break;
case KeyPress:
keypress(&ev.xkey);
break;
case SelectionNotify:
if (ev.xselection.property == utf8)
paste();
break;
case VisibilityNotify:
if (ev.xvisibility.state != VisibilityUnobscured)
XRaiseWindow(dpy, win);
break;
}
}
}
static void
setup(void)
{
int x, y, i, j;
unsigned int du;
XSetWindowAttributes swa;
XIM xim;
Window w, dw, *dws;
XWindowAttributes wa;
XClassHint ch = {"dmenu", "dmenu"};
#ifdef XINERAMA
XineramaScreenInfo *info;
Window pw;
int a, di, n, area = 0;
#endif
/* init appearance */
for (j = 0; j < SchemeLast; j++)
scheme[j] = drw_scm_create(drw, colors[j], alphas[j], 2);
clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False);
/* calculate menu geometry */
bh = drw->fonts->h + 2;
lines = MAX(lines, 0);
mh = (lines + 1) * bh;
#ifdef XINERAMA
i = 0;
if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
XGetInputFocus(dpy, &w, &di);
if (mon >= 0 && mon < n)
i = mon;
else if (w != root && w != PointerRoot && w != None) {
/* find top-level window containing current input focus */
do {
if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
XFree(dws);
} while (w != root && w != pw);
/* find xinerama screen with which the window intersects most */
if (XGetWindowAttributes(dpy, pw, &wa))
for (j = 0; j < n; j++)
if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
area = a;
i = j;
}
}
/* no focused window is on screen, so use pointer location instead */
if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
for (i = 0; i < n; i++)
if (INTERSECT(x, y, 1, 1, info[i]) != 0)
break;
x = info[i].x_org;
y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
mw = info[i].width;
XFree(info);
} else
#endif
{
if (!XGetWindowAttributes(dpy, parentwin, &wa))
die("could not get embedding window attributes: 0x%lx",
parentwin);
x = 0;
y = topbar ? 0 : wa.height - mh;
mw = wa.width;
}
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
inputw = MIN(inputw, mw/3);
match();
/* create menu window */
swa.override_redirect = True;
swa.background_pixel = 0;
swa.colormap = cmap;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
depth, InputOutput, visual,
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &swa);
XSetClassHint(dpy, win, &ch);
/* input methods */
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
die("XOpenIM failed: could not open input device");
xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, win, XNFocusWindow, win, NULL);
XMapRaised(dpy, win);
if (embed) {
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
for (i = 0; i < du && dws[i] != win; ++i)
XSelectInput(dpy, dws[i], FocusChangeMask);
XFree(dws);
}
grabfocus();
}
drw_resize(drw, mw, mh);
drawmenu();
}
static void
usage(void)
{
fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
" [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr);
exit(1);
}
int
main(int argc, char *argv[])
{
XWindowAttributes wa;
int i, fast = 0;
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 */
topbar = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
fstrncmp = strncmp;
fstrstr = strstr;
} else if (i + 1 == argc)
usage();
/* these options take one argument */
else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */
columns = atoi(argv[++i]);
if (lines == 0) lines = 1;
} else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */
lines = atoi(argv[++i]);
if (columns == 0) columns = 1;
} else if (!strcmp(argv[i], "-m"))
mon = atoi(argv[++i]);
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 */
fonts[0] = argv[++i];
else if (!strcmp(argv[i], "-nb")) /* normal background color */
colors[SchemeNorm][ColBg] = argv[++i];
else if (!strcmp(argv[i], "-nf")) /* normal foreground color */
colors[SchemeNorm][ColFg] = argv[++i];
else if (!strcmp(argv[i], "-sb")) /* selected background color */
colors[SchemeSel][ColBg] = argv[++i];
else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
colors[SchemeSel][ColFg] = argv[++i];
else if (!strcmp(argv[i], "-w")) /* embedding window id */
embed = argv[++i];
else
usage();
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL)))
die("cannot open display");
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);
xinitvisual();
drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
#ifdef __OpenBSD__
if (pledge("stdio rpath", NULL) == -1)
die("pledge");
#endif
if (fast && !isatty(0)) {
grabkeyboard();
readstdin();
} else {
readstdin();
grabkeyboard();
}
setup();
run();
return 1; /* unreachable */
}

View file

@ -1,2 +0,0 @@
#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

View file

@ -1,281 +0,0 @@
From 5e232dc79ba3c0b791f3d9ddfab2fcecca19f18d Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Thu, 28 May 2020 16:58:12 +0200
Subject: [PATCH] Adding alpha patch
---
config.def.h | 9 +++++++++
config.mk | 6 +++++-
dmenu.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++------
drw.c | 26 +++++++++++++-----------
drw.h | 9 ++++++---
5 files changed, 84 insertions(+), 22 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1edb647..5c512d3 100644
--- a/config.def.h
+++ b/config.def.h
@@ -7,6 +7,15 @@ static const char *fonts[] = {
"monospace:size=10"
};
static const char *prompt = NULL; /* -p option; prompt to the left of input field */
+
+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 },
+};
+
static const char *colors[SchemeLast][2] = {
/* fg bg */
[SchemeNorm] = { "#bbbbbb", "#222222" },
diff --git a/config.mk b/config.mk
index 05d5a3e..ee4991a 100644
--- a/config.mk
+++ b/config.mk
@@ -15,12 +15,16 @@ XINERAMAFLAGS = -DXINERAMA
# freetype
FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2
+
+# alpha
+XRENDERLIBS = -lXrender
+
# OpenBSD (uncomment)
#FREETYPEINC = $(X11INC)/freetype2
# includes and libs
INCS = -I$(X11INC) -I$(FREETYPEINC)
-LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) ${XRENDERLIBS}
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
diff --git a/dmenu.c b/dmenu.c
index 65f25ce..31efafc 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -24,6 +24,7 @@
* MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+#define OPAQUE 0xffU
/* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
@@ -50,6 +51,11 @@ static Display *dpy;
static Window root, parentwin, win;
static XIC xic;
+static int useargb = 0;
+static Visual *visual;
+static int depth;
+static Colormap cmap;
+
static Drw *drw;
static Clr *scheme[SchemeLast];
@@ -518,6 +524,43 @@ paste(void)
drawmenu();
}
+static void
+xinitvisual()
+{
+ 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) {
+ visual = DefaultVisual(dpy, screen);
+ depth = DefaultDepth(dpy, screen);
+ cmap = DefaultColormap(dpy, screen);
+ }
+}
+
static void
readstdin(void)
{
@@ -602,7 +645,7 @@ setup(void)
#endif
/* init appearance */
for (j = 0; j < SchemeLast; j++)
- scheme[j] = drw_scm_create(drw, colors[j], 2);
+ scheme[j] = drw_scm_create(drw, colors[j], alphas[j], 2);
clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False);
@@ -657,14 +700,14 @@ setup(void)
/* create menu window */
swa.override_redirect = True;
- swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ swa.background_pixel = 0;
+ swa.colormap = cmap;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
- CopyFromParent, CopyFromParent, CopyFomParent,
- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+ depth, InputOutput, visual,
+ CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &swa);
XSetClassHint(dpy, win, &ch);
-
/* input methods */
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
die("XOpenIM failed: could not open input device");
@@ -747,7 +790,8 @@ main(int argc, char *argv[])
if (!XGetWindowAttributes(dpy, parentwin, &wa))
die("could not get embedding window attributes: 0x%lx",
parentwin);
- drw = drw_create(dpy, screen, root, wa.width, wa.height);
+ xinitvisual();
+ drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
diff --git a/drw.c b/drw.c
index 4cdbcbe..fe3aadd 100644
--- a/drw.c
+++ b/drw.c
@@ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen)
}
Drw *
-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
+drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
{
Drw *drw = ecalloc(1, sizeof(Drw));
@@ -70,8 +70,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
drw->root = root;
drw->w = w;
drw->h = h;
- drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
- drw->gc = XCreateGC(dpy, root, 0, NULL);
+ 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);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
@@ -87,7 +90,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
drw->h = h;
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
- drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
}
void
@@ -194,21 +197,22 @@ drw_fontset_free(Fnt *font)
}
void
-drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
+drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
{
if (!drw || !dest || !clrname)
return;
- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
- DefaultColormap(drw->dpy, drw->screen),
+ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
+
+ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
{
size_t i;
Clr *ret;
@@ -218,7 +222,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
return NULL;
for (i = 0; i < clrcount; i++)
- drw_clr_create(drw, &ret[i], clrnames[i]);
+ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
return ret;
}
@@ -274,9 +278,7 @@ 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);
- d = XftDrawCreate(drw->dpy, drw->drawable,
- DefaultVisual(drw->dpy, drw->screen),
- DefaultColormap(drw->dpy, drw->screen));
+ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
x += lpad;
w -= lpad;
}
diff --git a/drw.h b/drw.h
index 4c67419..4f66f0d 100644
--- a/drw.h
+++ b/drw.h
@@ -20,6 +20,9 @@ typedef struct {
Display *dpy;
int screen;
Window root;
+ Visual *visual;
+ unsigned int depth;
+ Colormap cmap;
Drawable drawable;
GC gc;
Clr *scheme;
@@ -27,7 +30,7 @@ typedef struct {
} Drw;
/* Drawable abstraction */
-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
+Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap);
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);
@@ -38,8 +41,8 @@ unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
/* Colorscheme abstraction */
-void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
+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);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);
--
2.34.1
r

View file

@ -1,42 +0,0 @@
From 54acbdf72083a5eae5783eed42e162424ab2cec2 Mon Sep 17 00:00:00 2001
From: Kim Torgersen <kim@torgersen.se>
Date: Sat, 23 May 2020 14:48:28 +0200
Subject: [PATCH] case-insensitive item matching default, case-sensitive option
(-s)
---
dmenu.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/dmenu.c b/dmenu.c
index 65f25ce..855df59 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -55,8 +55,9 @@ static Clr *scheme[SchemeLast];
#include "config.h"
-static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
-static char *(*fstrstr)(const char *, const char *) = strstr;
+static char * cistrstr(const char *s, const char *sub);
+static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
+static char *(*fstrstr)(const char *, const char *) = cistrstr;
static void
appenditem(struct item *item, struct item **list, struct item **last)
@@ -709,9 +710,9 @@ main(int argc, char *argv[])
topbar = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
- fstrncmp = strncasecmp;
- fstrstr = cistrstr;
+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
+ fstrncmp = strncmp;
+ fstrstr = strstr;
} else if (i + 1 == argc)
usage();
/* these options take one argument */
--
2.26.2

View file

@ -1,107 +0,0 @@
From 39ab9676914bd0d8105d0f96bbd7611a53077438 Mon Sep 17 00:00:00 2001
From: Miles Alan <m@milesalan.com>
Date: Sat, 4 Jul 2020 11:19:04 -0500
Subject: [PATCH] Add -g option to display entries in the given number of grid
columns
This option can be used in conjunction with -l to format dmenu's options in
arbitrary size grids. For example, to create a 4 column by 6 line grid, you
could use: dmenu -g 4 -l 6
---
config.def.h | 3 ++-
dmenu.1 | 7 ++++++-
dmenu.c | 22 ++++++++++++++++------
3 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1edb647..96cf3c9 100644
--- a/config.def.h
+++ b/config.def.h
@@ -13,8 +13,9 @@ static const char *colors[SchemeLast][2] = {
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
};
-/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+/* -l and -g options; controls number of lines and columns in grid if > 0 */
static unsigned int lines = 0;
+static unsigned int columns = 0;
/*
* Characters not considered part of a word while deleting words
diff --git a/dmenu.1 b/dmenu.1
index 323f93c..d0a734a 100644
--- a/dmenu.1
+++ b/dmenu.1
@@ -4,6 +4,8 @@ dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-bfiv ]
+.RB [ \-g
+.IR columns ]
.RB [ \-l
.IR lines ]
.RB [ \-m
@@ -47,8 +49,11 @@ is faster, but will lock up X until stdin reaches end\-of\-file.
.B \-i
dmenu matches menu items case insensitively.
.TP
+.BI \-g " columns"
+dmenu lists items in a grid with the given number of columns.
+.TP
.BI \-l " lines"
-dmenu lists items vertically, with the given number of lines.
+dmenu lists items in a grid with the given number of lines.
.TP
.BI \-m " monitor"
dmenu is displayed on the monitor number supplied. Monitor numbers are starting
diff --git a/dmenu.c b/dmenu.c
index 6b8f51b..d79b6bb 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -77,7 +77,7 @@ calcoffsets(void)
int i, n;
if (lines > 0)
- n = lines * bh;
+ n = lines * columns * bh;
else
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* calculate which items will begin the next page and previous page */
@@ -152,9 +152,15 @@ drawmenu(void)
}
if (lines > 0) {
- /* draw vertical list */
- for (item = curr; item != next; item = item->right)
- drawitem(item, x, y += bh, mw - x);
+ /* draw grid */
+ int i = 0;
+ for (item = curr; item != next; item = item->right, i++)
+ drawitem(
+ item,
+ x + ((i / lines) * ((mw - x) / columns)),
+ y + (((i % lines) + 1) * bh),
+ (mw - x) / columns
+ );
} else if (matches) {
/* draw horizontal list */
x += inputw;
@@ -708,9 +714,13 @@ main(int argc, char *argv[])
} else if (i + 1 == argc)
usage();
/* these options take one argument */
- else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
+ else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */
+ columns = atoi(argv[++i]);
+ if (lines == 0) lines = 1;
+ } else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */
lines = atoi(argv[++i]);
- else if (!strcmp(argv[i], "-m"))
+ if (columns == 0) columns = 1;
+ } else if (!strcmp(argv[i], "-m"))
mon = atoi(argv[++i]);
else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */
prompt = argv[++i];
--
2.23.1

View file

@ -1,47 +0,0 @@
From 4bf895be219ae00394a5cde901dc43ec6dcb3759 Mon Sep 17 00:00:00 2001
From: Tanner Babcock <babkock@gmail.com>
Date: Sun, 22 Sep 2019 03:07:26 -0500
Subject: [PATCH] Additional color scheme, for adjacent entries
---
config.def.h | 1 +
dmenu.c | 4 +++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/config.def.h b/config.def.h
index 1edb647..767c88f 100644
--- a/config.def.h
+++ b/config.def.h
@@ -12,6 +12,7 @@ static const char *colors[SchemeLast][2] = {
[SchemeNorm] = { "#bbbbbb", "#222222" },
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
+ [SchemeMid] = { "#eeeeee", "#770000" },
};
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines = 0;
diff --git a/dmenu.c b/dmenu.c
index 65f25ce..0a5c08d 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -26,7 +26,7 @@
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeOut, SchemeMid, SchemeLast }; /* color schemes */
struct item {
char *text;
@@ -118,6 +118,8 @@ drawitem(struct item *item, int x, int y, int w)
{
if (item == sel)
drw_setscheme(drw, scheme[SchemeSel]);
+ else if (item->left == sel || item->right == sel)
+ drw_setscheme(drw, scheme[SchemeMid]);
else if (item->out)
drw_setscheme(drw, scheme[SchemeOut]);
else
--
2.23.0

View file

@ -1,144 +0,0 @@
diff --git a/dmenu.c b/dmenu.c
index d95e6c6..75a79d0 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -518,6 +518,119 @@ draw:
drawmenu();
}
+static void
+buttonpress(XEvent *e)
+{
+ struct item *item;
+ XButtonPressedEvent *ev = &e->xbutton;
+ int x = 0, y = 0, h = bh, w;
+
+ if (ev->window != win)
+ 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 (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;
+ }
+ /* 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 & ~ControlMask)
+ return;
+ if (lines > 0) {
+ /* vertical list: (ctrl)left-click on item */
+ w = mw - x;
+ for (item = curr; item != next; item = item->right) {
+ y += h;
+ if (ev->y >= y && ev->y <= (y + h)) {
+ puts(item->text);
+ if (!(ev->state & ControlMask))
+ exit(0);
+ sel = item;
+ if (sel) {
+ sel->out = 1;
+ drawmenu();
+ }
+ return;
+ }
+ }
+ } else if (matches) {
+ /* left-click on left arrow */
+ x += inputw;
+ w = TEXTW("<");
+ 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;
+ w = MIN(TEXTW(item->text), mw - x - TEXTW(">"));
+ if (ev->x >= x && ev->x <= x + w) {
+ puts(item->text);
+ if (!(ev->state & ControlMask))
+ exit(0);
+ sel = item;
+ if (sel) {
+ sel->out = 1;
+ drawmenu();
+ }
+ return;
+ }
+ }
+ /* left-click on right arrow */
+ w = TEXTW(">");
+ x = mw - w;
+ if (next && ev->x >= x && ev->x <= x + w) {
+ sel = curr = next;
+ calcoffsets();
+ drawmenu();
+ return;
+ }
+ }
+}
+
static void
paste(void)
{
@@ -579,6 +692,9 @@ run(void)
break;
cleanup();
exit(1);
+ case ButtonPress:
+ buttonpress(&ev);
+ break;
case Expose:
if (ev.xexpose.count == 0)
drw_map(drw, win, 0, 0, mw, mh);
@@ -676,7 +792,8 @@ setup(void)
/* create menu window */
swa.override_redirect = True;
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
- swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask |
+ ButtonPressMask;
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);

View file

@ -1,245 +0,0 @@
diff --git a/dmenu.c b/dmenu.c
index 5c835dd..71efe52 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -131,9 +131,10 @@ drawitem(struct item *item, int x, int y, int w)
static void
drawmenu(void)
{
- unsigned int curpos;
+ static int curpos, oldcurlen;
struct item *item;
int x = 0, y = 0, w;
+ int curlen, rcurlen;
drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, 0, 0, mw, mh, 1, 1);
@@ -144,14 +145,21 @@ drawmenu(void)
}
/* draw input field */
w = (lines > 0 || !matches) ? mw - x : inputw;
- drw_setscheme(drw, scheme[SchemeNorm]);
- drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
+ w -= lrpad / 2;
+ x += lrpad / 2;
- curpos = TEXTW(text) - TEXTW(&text[cursor]);
- if ((curpos += lrpad / 2 - 1) < w) {
- drw_setscheme(drw, scheme[SchemeNorm]);
- drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
- }
+ rcurlen = drw_fontset_getwidth(drw, text + cursor);
+ curlen = drw_fontset_getwidth(drw, text) - 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_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);
+ drw_rect(drw, x + curpos - 1, 2, 2, bh - 4, 1, 0);
if (lines > 0) {
/* draw vertical list */
diff --git a/drw.c b/drw.c
index c638323..bfffbc1 100644
--- a/drw.c
+++ b/drw.c
@@ -364,6 +364,175 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
return x + (render ? w : 0);
}
+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 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);
+ d = XftDrawCreate(drw->dpy, drw->drawable,
+ DefaultVisual(drw->dpy, drw->screen),
+ DefaultColormap(drw->dpy, drw->screen));
+ }
+
+ 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, MIN(textlen - i, UTF_SIZ));
+ if (!utf8charlen) {
+ textlen = i;
+ break;
+ }
+ } else {
+ n = utf8nextchar(text, textlen, i, -1);
+ utf8charlen = utf8decode(text + n, &utf8codepoint, MIN(textlen - n, UTF_SIZ));
+ 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;
+}
+
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
diff --git a/drw.h b/drw.h
index 4c67419..b66a83e 100644
--- a/drw.h
+++ b/drw.h
@@ -13,6 +13,7 @@ typedef struct Fnt {
} Fnt;
enum { ColFg, ColBg }; /* Clr scheme index */
+enum { AlignL, AlignR };
typedef XftColor Clr;
typedef struct {
@@ -52,6 +53,7 @@ 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);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
+int drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

View file

@ -1,46 +0,0 @@
#!/bin/sh
# dfmpeg
# dmenu gui for recording your screen with ffmpeg
# Licensed under MIT, written by speedie
# https://github.com/speediegamer/dfmpeg
defaultConfig() {
DFMPEG_DOTDIR="$HOME/.config/dfmpeg" # The directory where dotfiles are
DFMPEG_RESOLUTION="1920x1080" # The resolution to record in
DFMPEG_AUDIO_DEVICE="alsa" # How to capture audio (alsa/pulseaudio)
DFMPEG_FRAME_RATE="60" # Frame rate to capture in.
DFMPEG_RECORD_DEVICE="x11grab" # Probably do not change.
DFMPEG_OUTPUT_PATH="$HOME/Videos" # Path where videos will be saved.
DFMPEG_OUTPUT_FORMAT="mp4" # What format to use
DFMPEG_OUTPUT_FILENAME="$DFMPEG_OUTPUT_PATH/Dfmpeg-Output-$(date +"%d-%d-%y-%T").$DFMPEG_OUTPUT_FORMAT" # File name of the output. Probably don't need to change.
DFMPEG_WH=":0.0" # Width and height, no need to change, defaults should work.
DFMPEG_DMENU="dmenu" # Path to dmenu
DFMPEG_TERM="$TERMINAL" # Terminal to use when editing the configuration file.
DFMPEG_EDITOR="vim" # Editor to edit the config file with
}
defaultConfig
. "$DFMPEG_DOTDIR"/dfmpegrc
startrec="ffmpeg -f $DFMPEG_RECORD_DEVICE -s $DFMPEG_RESOLUTION -i $DFMPEG_WH -f $DFMPEG_AUDIO_DEVICE -r $DFMPEG_FRAME_RATE -i default $DFMPEG_OUTPUT_FILENAME"
startrec_no_audio="ffmpeg -f $DFMPEG_RECORD_DEVICE -s $DFMPEG_RESOLUTION -r $DFMPEG_FRAME_RATE -i $DFMPEG_WH $DFMPEG_OUTPUT_FILENAME"
stoprec="pkill ffmpeg"
dfmpeg_ver="2022-04-03-r1"
about_screen="dfmpeg $dfmpeg_ver."
about_screen_2="Licensed under MIT, written by speedie + contributors."
about_screen_3="https://github.com/speediegamer/dfmpeg"
case "$(printf 'Start\nStop\nStart-No-Audio\nConfigure\nExit\nAbout' | dmenu -p 'Record your screen:')" in
"Exit") DFMPEG_STATUS=idle && exit 0 ;;
"Start") DFMPEG_STATUS=recording && touch /tmp/dfmpeg-recording && $startrec && exit 0 ;;
"Stop") $stoprec && rm /tmp/dfmpeg-recording && DFMPEG_STATUS=idle ;;
"Configure") $DFMPEG_TERM $DFMPEG_EDITOR $DFMPEG_DOTDIR/dfmpegrc ;;
"Start-No-Audio") DFMPEG_STATUS=recording && touch /tmp/dfmpeg-recording && $startrec_no_audio && exit 0 ;;
"About")
echo $about_screen > $DFMPEG_DOTDIR/dfmpeg_about
echo $about_screen_2 >> $DFMPEG_DOTDIR/dfmpeg_about
echo $about_screen_3 >> $DFMPEG_DOTDIR/dfmpeg_about
$DFMPEG_TERM $DFMPEG_EDITOR $DFMPEG_DOTDIR/dfmpeg_about
;;
esac
exit 0 # This fixes a small bug.

View file

@ -1,3 +0,0 @@
# These are supported funding model platforms
custom: ["https://suckless.org/donations/", "https://paypal.me/dwmflexipatch"]

View file

@ -168,45 +168,45 @@ static const char dmenufont[] = "Hack Nerd Font:size=11";
static char c000000[] = "#000000"; // placeholder value
static char normfgcolor[] = "#bbbbbb";
static char normbgcolor[] = "#222222";
static char normbordercolor[] = "#444444";
static char normfloatcolor[] = "#db8fd9";
static char normfgcolor[] = "#ffffff";
static char normbgcolor[] = "#173f4f";
static char normbordercolor[] = "#173f4f";
static char normfloatcolor[] = "#124f5f";
static char selfgcolor[] = "#eeeeee";
static char selbgcolor[] = "#005577";
static char selbordercolor[] = "#005577";
static char selfloatcolor[] = "#005577";
static char selbgcolor[] = "#153a49";
static char selbordercolor[] = "#153a49";
static char selfloatcolor[] = "#153a49";
static char titlenormfgcolor[] = "#bbbbbb";
static char titlenormbgcolor[] = "#222222";
static char titlenormbordercolor[] = "#444444";
static char titlenormfloatcolor[] = "#db8fd9";
static char titlenormfgcolor[] = "#ffffff";
static char titlenormbgcolor[] = "#173f4f";
static char titlenormbordercolor[] = "#173f4f";
static char titlenormfloatcolor[] = "#124f5f";
static char titleselfgcolor[] = "#eeeeee";
static char titleselbgcolor[] = "#005577";
static char titleselbordercolor[] = "#005577";
static char titleselfloatcolor[] = "#005577";
static char titleselbgcolor[] = "#153a49";
static char titleselbordercolor[] = "#153a49";
static char titleselfloatcolor[] = "#153a49";
static char tagsnormfgcolor[] = "#bbbbbb";
static char tagsnormbgcolor[] = "#222222";
static char tagsnormbordercolor[] = "#444444";
static char tagsnormfloatcolor[] = "#db8fd9";
static char tagsnormfgcolor[] = "#ffffff";
static char tagsnormbgcolor[] = "#173f4f";
static char tagsnormbordercolor[] = "#173f4f";
static char tagsnormfloatcolor[] = "#124f5f";
static char tagsselfgcolor[] = "#eeeeee";
static char tagsselbgcolor[] = "#005577";
static char tagsselbordercolor[] = "#005577";
static char tagsselfloatcolor[] = "#005577";
static char tagsselbgcolor[] = "#153a49";
static char tagsselbordercolor[] = "#153a49";
static char tagsselfloatcolor[] = "#153a49";
static char hidnormfgcolor[] = "#005577";
static char hidnormfgcolor[] = "#153a49";
static char hidselfgcolor[] = "#227799";
static char hidnormbgcolor[] = "#222222";
static char hidselbgcolor[] = "#222222";
static char hidnormbgcolor[] = "#173f4f";
static char hidselbgcolor[] = "#173f4f";
static char urgfgcolor[] = "#bbbbbb";
static char urgbgcolor[] = "#222222";
static char urgfgcolor[] = "#ffffff";
static char urgbgcolor[] = "#173f4f";
static char urgbordercolor[] = "#ff0000";
static char urgfloatcolor[] = "#db8fd9";
static char urgfloatcolor[] = "#124f5f";
#if RENAMED_SCRATCHPADS_PATCH
static char scratchselfgcolor[] = "#FFF7D4";
@ -905,7 +905,7 @@ static const Key keys[] = {
{ Mod4Mask, XK_f, spawn, SHCMD("spmenu_run -fm -a '-g 4 -l 10'") },
/* spmenu scripts down here */
{ Mod4Mask, XK_v, spawn, SHCMD("clipmenu-spmenu") },
{ Mod4Mask|ShiftMask, XK_Print, spawn, SHCMD("screenshot-spmenu -f") },
{ Mod4Mask, XK_Print, spawn, SHCMD("screenshot-spmenu -f") },
{ Mod4Mask|Mod1Mask, XK_Print, spawn, SHCMD("screenshot-spmenu -s") },
{ Mod4Mask, XK_p, spawn, SHCMD("pirokit") },
{ Mod4Mask|ShiftMask, XK_w, spawn, SHCMD("wallpaper-spmenu") },
@ -917,6 +917,8 @@ static const Key keys[] = {
{ Mod4Mask, XK_u, spawn, SHCMD("volume-dunst up") },
{ Mod4Mask, XK_d, spawn, SHCMD("volume-dunst down") },
{ Mod4Mask, XK_m, spawn, SHCMD("volume-dunst mute") },
/* slock must be installed */
{ Mod4Mask, XK_l, spawn, SHCMD("slock") },
#if KEYMODES_PATCH
{ MODKEY, XK_Escape, setkeymode, {.ui = COMMANDMODE} },
#endif // KEYMODES_PATCH
@ -928,9 +930,8 @@ static const Key keys[] = {
{ MODKEY, XK_s, rioresize, {0} },
/* suckless-utils keymaps */
{ MODKEY|ShiftMask, XK_t, spawn, SHCMD("tabbed -r 2 st -w ''") },
{ MODKEY|ShiftMask, XK_i, spawn, SHCMD("firefox") },
{ MODKEY|ShiftMask, XK_i, spawn, SHCMD("firefox") },
{ Mod4Mask, XK_e, spawn, SHCMD("st -T sfm sfm") },
{ Mod4Mask, XK_Print, spawn, SHCMD("maim ~/Pictures/Screenshot_$(date +%s).png") },
#endif // RIODRAW_PATCH
{ MODKEY, XK_b, togglebar, {0} },
#if TOGGLETOPBAR_PATCH
@ -1093,7 +1094,7 @@ static const Key keys[] = {
{ MODKEY, XK_o, winview, {0} },
#endif // WINVIEW_PATCH
#if XRDB_PATCH && !BAR_VTCOLORS_PATCH
{ MODKEY, XK_F5, xrdb, {.v = NULL } },
{ MODKEY, XK_F5, xrdb, {.v = NULL } },
#endif // XRDB_PATCH
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },

View file

@ -46,7 +46,7 @@ XRENDER = -lXrender
XEXTLIB = -lXext
# Uncomment this for the swallow patch / SWALLOW_PATCH
#XCBLIBS = -lX11-xcb -lxcb -lxcb-res
XCBLIBS = -lX11-xcb -lxcb -lxcb-res
# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH
IMLIB2LIBS = -lImlib2

View file

@ -1,7 +1,7 @@
[Desktop Entry]
Encoding=UTF-8
Name=Dwm
Name=dwm-flexipatch (X11)
Comment=Dynamic window manager
Exec=dwm
Exec=/usr/local/bin/startdwm
Icon=dwm
Type=XSession

View file

@ -16,47 +16,47 @@ loadxrdb()
xrdb = XrmGetStringDatabase(resm);
if (xrdb != NULL) {
XRDB_LOAD_COLOR("dwm.color0", normbordercolor);
XRDB_LOAD_COLOR("dwm.color15", normfgcolor);
XRDB_LOAD_COLOR("dwm.color0", normbgcolor);
XRDB_LOAD_COLOR("dwm.color4", normfgcolor);
XRDB_LOAD_COLOR("dwm.color8", selbordercolor);
XRDB_LOAD_COLOR("dwm.color4", selbgcolor);
XRDB_LOAD_COLOR("dwm.color10", normbordercolor);
XRDB_LOAD_COLOR("dwm.color10", normfloatcolor);
XRDB_LOAD_COLOR("dwm.color0", selfgcolor);
XRDB_LOAD_COLOR("dwm.selbordercolor", selbordercolor);
XRDB_LOAD_COLOR("dwm.selfloatcolor", selfloatcolor);
XRDB_LOAD_COLOR("dwm.titlenormfgcolor", titlenormfgcolor);
XRDB_LOAD_COLOR("dwm.titlenormbgcolor", titlenormbgcolor);
XRDB_LOAD_COLOR("dwm.titlenormbordercolor", titlenormbordercolor);
XRDB_LOAD_COLOR("dwm.titlenormfloatcolor", titlenormfloatcolor);
XRDB_LOAD_COLOR("dwm.titleselfgcolor", titleselfgcolor);
XRDB_LOAD_COLOR("dwm.titleselbgcolor", titleselbgcolor);
XRDB_LOAD_COLOR("dwm.titleselbordercolor", titleselbordercolor);
XRDB_LOAD_COLOR("dwm.titleselfloatcolor", titleselfloatcolor);
XRDB_LOAD_COLOR("dwm.tagsnormfgcolor", tagsnormfgcolor);
XRDB_LOAD_COLOR("dwm.tagsnormbgcolor", tagsnormbgcolor);
XRDB_LOAD_COLOR("dwm.tagsnormbordercolor", tagsnormbordercolor);
XRDB_LOAD_COLOR("dwm.tagsnormfloatcolor", tagsnormfloatcolor);
XRDB_LOAD_COLOR("dwm.tagsselfgcolor", tagsselfgcolor);
XRDB_LOAD_COLOR("dwm.tagsselbgcolor", tagsselbgcolor);
XRDB_LOAD_COLOR("dwm.tagsselbordercolor", tagsselbordercolor);
XRDB_LOAD_COLOR("dwm.tagsselfloatcolor", tagsselfloatcolor);
XRDB_LOAD_COLOR("dwm.hidnormfgcolor", hidnormfgcolor);
XRDB_LOAD_COLOR("dwm.hidnormbgcolor", hidnormbgcolor);
XRDB_LOAD_COLOR("dwm.hidselfgcolor", hidselfgcolor);
XRDB_LOAD_COLOR("dwm.hidselbgcolor", hidselbgcolor);
XRDB_LOAD_COLOR("dwm.urgfgcolor", urgfgcolor);
XRDB_LOAD_COLOR("dwm.urgbgcolor", urgbgcolor);
XRDB_LOAD_COLOR("dwm.urgbordercolor", urgbordercolor);
XRDB_LOAD_COLOR("dwm.urgfloatcolor", urgfloatcolor);
XRDB_LOAD_COLOR("dwm.color14", selbgcolor);
XRDB_LOAD_COLOR("dwm.color12", selbordercolor);
XRDB_LOAD_COLOR("dwm.color12", selfloatcolor);
XRDB_LOAD_COLOR("dwm.color15", titlenormfgcolor);
XRDB_LOAD_COLOR("dwm.color0", titlenormbgcolor);
XRDB_LOAD_COLOR("dwm.color10", titlenormbordercolor);
XRDB_LOAD_COLOR("dwm.color10", titlenormfloatcolor);
XRDB_LOAD_COLOR("dwm.color0", titleselfgcolor);
XRDB_LOAD_COLOR("dwm.color14", titleselbgcolor);
XRDB_LOAD_COLOR("dwm.color12", titleselbordercolor);
XRDB_LOAD_COLOR("dwm.color12", titleselfloatcolor);
XRDB_LOAD_COLOR("dwm.color15", tagsnormfgcolor);
XRDB_LOAD_COLOR("dwm.color0", tagsnormbgcolor);
XRDB_LOAD_COLOR("dwm.color10", tagsnormbordercolor);
XRDB_LOAD_COLOR("dwm.color10", tagsnormfloatcolor);
XRDB_LOAD_COLOR("dwm.color0", tagsselfgcolor);
XRDB_LOAD_COLOR("dwm.color14", tagsselbgcolor);
XRDB_LOAD_COLOR("dwm.color12", tagsselbordercolor);
XRDB_LOAD_COLOR("dwm.color12", tagsselfloatcolor);
XRDB_LOAD_COLOR("dwm.color15", hidnormfgcolor);
XRDB_LOAD_COLOR("dwm.color0", hidnormbgcolor);
XRDB_LOAD_COLOR("dwm.color0", hidselfgcolor);
XRDB_LOAD_COLOR("dwm.color14", hidselbgcolor);
XRDB_LOAD_COLOR("dwm.color15", urgfgcolor);
XRDB_LOAD_COLOR("dwm.color3", urgbgcolor);
XRDB_LOAD_COLOR("dwm.color11", urgbordercolor);
XRDB_LOAD_COLOR("dwm.color11", urgfloatcolor);
#if RENAMED_SCRATCHPADS_PATCH
XRDB_LOAD_COLOR("dwm.scratchselfgcolor", scratchselfgcolor);
XRDB_LOAD_COLOR("dwm.scratchselbgcolor", scratchselbgcolor);
XRDB_LOAD_COLOR("dwm.scratchselbordercolor", scratchselbordercolor);
XRDB_LOAD_COLOR("dwm.scratchselfloatcolor", scratchselfloatcolor);
XRDB_LOAD_COLOR("dwm.scratchnormfgcolor", scratchnormfgcolor);
XRDB_LOAD_COLOR("dwm.scratchnormbgcolor", scratchnormbgcolor);
XRDB_LOAD_COLOR("dwm.scratchnormbordercolor", scratchnormbordercolor);
XRDB_LOAD_COLOR("dwm.scratchnormfloatcolor", scratchnormfloatcolor);
XRDB_LOAD_COLOR("dwm.color0", scratchselfgcolor);
XRDB_LOAD_COLOR("dwm.color14", scratchselbgcolor);
XRDB_LOAD_COLOR("dwm.color14", scratchselbordercolor);
XRDB_LOAD_COLOR("dwm.color14", scratchselfloatcolor);
XRDB_LOAD_COLOR("dwm.color8", scratchnormfgcolor);
XRDB_LOAD_COLOR("dwm.color13", scratchnormbgcolor);
XRDB_LOAD_COLOR("dwm.color13", scratchnormbordercolor);
XRDB_LOAD_COLOR("dwm.color13", scratchnormfloatcolor);
#endif // RENAMED_SCRATCHPADS_PATCH
#if BAR_FLEXWINTITLE_PATCH
XRDB_LOAD_COLOR("dwm.normTTBbgcolor", normTTBbgcolor);

View file

@ -130,7 +130,7 @@
*
* https://dwm.suckless.org/patches/tag-previews/
*/
#define BAR_TAGPREVIEW_PATCH 0
#define BAR_TAGPREVIEW_PATCH 1
/* Show status in bar */
#define BAR_STATUS_PATCH 1
@ -423,7 +423,7 @@
* Essentially this way the colors you use in your regular tty is "mirrored" to dwm.
* https://dwm.suckless.org/patches/vtcolors/
*/
#define BAR_VTCOLORS_PATCH 1
#define BAR_VTCOLORS_PATCH 0
/* This patch allows client windows to be hidden. This code was originally part of awesomebar,
* but has been separated out so that other bar modules can take advantage of it.
@ -532,7 +532,7 @@
* respective stack in tiled layout.
* https://dwm.suckless.org/patches/cfacts/
*/
#define CFACTS_PATCH 0
#define CFACTS_PATCH 1
/* This patch allows color attributes to be set through the command line.
* https://dwm.suckless.org/patches/cmdcustomize/
@ -575,14 +575,14 @@
/* Similarly to the dragmfact patch this allows you to click and drag clients to change the
* cfact to adjust the client's size in the stack. This patch depends on the cfacts patch.
*/
#define DRAGCFACT_PATCH 0
#define DRAGCFACT_PATCH 1
/* This patch lets you resize the split in the tile layout (i.e. modify mfact) by holding
* the modkey and dragging the mouse.
* This patch can be a bit wonky with other layouts, but generally works.
* https://dwm.suckless.org/patches/dragmfact/
*/
#define DRAGMFACT_PATCH 0
#define DRAGMFACT_PATCH 1
/* Simple dwmc client using a fork of fsignal to communicate with dwm.
* To use this either copy the patch/dwmc shell script to somewhere in your path or
@ -597,7 +597,7 @@
* it will be opened on the first tag.
* https://dwm.suckless.org/patches/emptyview/
*/
#define EMPTYVIEW_PATCH 0
#define EMPTYVIEW_PATCH 1
/* This patch allows the user to change size and placement of floating windows using only the
* keyboard. It also allows for temporary vertical and horizontal extension of windows similar
@ -612,7 +612,7 @@
* This patch takes precedence over the fakefullscreen client patch below.
* https://dwm.suckless.org/patches/fakefullscreen/
*/
#define FAKEFULLSCREEN_PATCH 0
#define FAKEFULLSCREEN_PATCH 1
/* Similarly to the fakefullscreen patch this patch only allows clients to "fullscreen" into
* the space currently given to them.
@ -826,13 +826,13 @@
/* Removes the border when there is only one window visible.
* https://dwm.suckless.org/patches/noborder/
*/
#define NOBORDER_PATCH 0
#define NOBORDER_PATCH 1
/* Enable modifying or removing dmenu in config.def.h which resulted previously in a
* compilation error because two lines of code hardcode dmenu into dwm.
* https://dwm.suckless.org/patches/nodmenu/
*/
#define NODMENU_PATCH 0
#define NODMENU_PATCH 1
/* This patch allows for toggleable client button bindings that have no modifiers.
* This can, for example, allow you to move or resize using the mouse alone without holding
@ -948,7 +948,7 @@
* Additionally dwm can quit cleanly by using kill -TERM dwmpid.
* https://dwm.suckless.org/patches/restartsig/
*/
#define RESTARTSIG_PATCH 0
#define RESTARTSIG_PATCH 1
/* Adds rio-like drawing to resize the selected client.
* This depends on an external tool slop being installed.
@ -987,10 +987,10 @@
* https://lists.suckless.org/hackers/2004/17205.html
* https://dwm.suckless.org/patches/scratchpads/
*/
#define SCRATCHPADS_PATCH 0
#define SCRATCHPADS_PATCH 1
/* Minor alteration of the above allowing clients to keep their size and position when shown */
#define SCRATCHPADS_KEEP_POSITION_AND_SIZE_PATCH 0
#define SCRATCHPADS_KEEP_POSITION_AND_SIZE_PATCH 1
/* This alternative patch enables a scratchpad feature in dwm similar to the scratchpad
* feature in i3wm.
@ -1064,7 +1064,7 @@
/* This variant of the shiftview patch adds left and right circular shift through tags,
* but skips tags where there are no clients.
*/
#define SHIFTVIEW_CLIENTS_PATCH 0
#define SHIFTVIEW_CLIENTS_PATCH 1
/* This patch makes dwm obey even "soft" sizehints for new clients. Any window
* that requests a specific initial size will be floated and set to that size.
@ -1133,7 +1133,7 @@
/* Adds toggleable keyboard shortcut to make a client 'sticky', i.e. visible on all tags.
* https://dwm.suckless.org/patches/sticky/
*/
#define STICKY_PATCH 0
#define STICKY_PATCH 1
/* This patch adds "window swallowing" to dwm as known from Plan 9's windowing system rio.
* Clients marked with isterminal in config.h swallow a window opened by any child process,
@ -1150,7 +1150,7 @@
*
* https://dwm.suckless.org/patches/swallow/
*/
#define SWALLOW_PATCH 0
#define SWALLOW_PATCH 1
/* This patch depends on the pertag patch and makes it possible to switch focus with a single
* shortcut (MOD+s) instead of having to think if you should use mod-j or mod-k for reaching
@ -1199,7 +1199,7 @@
/* Adds keyboard shortcuts to move all (or only floating) windows from one tag to another.
* https://dwm.suckless.org/patches/tagall/
*/
#define TAGALL_PATCH 0
#define TAGALL_PATCH 1
/* This patch allows you to move all visible windows on a monitor to an adjacent monitor.
* https://github.com/bakkeby/patches/blob/master/dwm/dwm-tagallmon-6.2.diff

View file

@ -1,6 +0,0 @@
#!/bin/sh
systemctl --user restart clipmenud.service
xrdb -merge ~/.Xresources &
/usr/local/bin/slstatus &
paplay /usr/share/sounds/Oxygen-Sys-Special.ogg
exec ./dwm

View file

@ -0,0 +1,6 @@
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 79
AlignArrayOfStructures: Left
AlignConsecutiveMacros: true
AllowShortFunctionsOnASingleLine: None

6
dwmblocks-async/.clangd Normal file
View file

@ -0,0 +1,6 @@
CompileFlags:
Add:
- "-I."
- "-I./inc"
- "-I.."
- "-I../inc"

3
dwmblocks-async/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build/
.cache/
dwmblocks

339
dwmblocks-async/LICENSE Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

54
dwmblocks-async/Makefile Normal file
View file

@ -0,0 +1,54 @@
.POSIX:
BIN := dwmblocks
BUILD_DIR := build
SRC_DIR := src
INC_DIR := inc
VERBOSE := 0
PREFIX := /usr/local
CC = cc
CFLAGS := -Wall -Wextra -Ofast -I. -I$(INC_DIR)
CFLAGS += -Wall -Wextra -Wno-missing-field-initializers
LDLIBS := -lX11
VPATH := $(SRC_DIR)
OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(wildcard $(SRC_DIR)/*.c))
OBJS += $(patsubst %.c,$(BUILD_DIR)/%.o,$(wildcard *.c))
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin
# Prettify output
PRINTF := @printf "%-8s %s\n"
ifeq ($(VERBOSE), 0)
Q := @
endif
all: $(BUILD_DIR)/$(BIN)
$(BUILD_DIR)/$(BIN): $(OBJS)
$(PRINTF) "LD" $@
$Q$(LINK.o) $^ $(LDLIBS) -o $@
$(BUILD_DIR)/%.o: %.c config.h | $(BUILD_DIR)
$(PRINTF) "CC" $@
$Q$(COMPILE.c) -o $@ $<
$(BUILD_DIR):
$(PRINTF) "MKDIR" $@
$Qmkdir -p $@
clean:
$(PRINTF) "CLEAN" $(BUILD_DIR)
$Q$(RM) $(BUILD_DIR)/*
install: $(BUILD_DIR)/$(BIN)
$(PRINTF) "INSTALL" $(INSTALL_DIR)/$(BIN)
$Qinstall -D -m 755 $< $(INSTALL_DIR)/$(BIN)
uninstall:
$(PRINTF) "RM" $(INSTALL_DIR)/$(BIN)
$Q$(RM) $(INSTALL_DIR)/$(BIN)
.PHONY: all clean install uninstall

161
dwmblocks-async/README.md Normal file
View file

@ -0,0 +1,161 @@
# dwmblocks-async
A [`dwm`](https://dwm.suckless.org) status bar that has a modular, async
design, so it is always responsive. Imagine `i3blocks`, but for `dwm`.
![A lean config of dwmblocks-async.](preview.png)
## Features
- [Modular](#modifying-the-blocks)
- Lightweight
- [Suckless](https://suckless.org/philosophy)
- Blocks:
- [Clickable](#clickable-blocks)
- Loaded asynchronously
- [Updates can be externally triggered](#signalling-changes)
- Compatible with `i3blocks` scripts
> Additionally, this build of `dwmblocks` is more optimized and fixes the
> flickering of the status bar when scrolling.
## Why `dwmblocks`?
In `dwm`, you have to set the status bar through an infinite loop, like so:
```sh
while :; do
xsetroot -name "$(date)"
sleep 30
done
```
This is inefficient when running multiple commands that need to be updated at
different frequencies. For example, to display an unread mail count and a clock
in the status bar:
```sh
while :; do
xsetroot -name "$(mailCount) $(date)"
sleep 60
done
```
Both are executed at the same rate, which is wasteful. Ideally, the mail
counter would be updated every thirty minutes, since there's a limit to the
number of requests I can make using Gmail's APIs (as a free user).
`dwmblocks` allows you to divide the status bar into multiple blocks, each of
which can be updated at its own interval. This effectively addresses the
previous issue, because the commands in a block are only executed once within
that time frame.
## Why `dwmblocks-async`?
The magic of `dwmblocks-async` is in the `async` part. Since vanilla
`dwmblocks` executes the commands of each block sequentially, it leads to
annoying freezes. In cases where one block takes several seconds to execute,
like in the mail and date blocks example from above, the delay is clearly
visible. Fire up a new instance of `dwmblocks` and you'll see!
With `dwmblocks-async`, the computer executes each block asynchronously
(simultaneously).
## Installation
Clone this repository, modify `config.h` appropriately, then compile the
program:
```sh
git clone https://github.com/UtkarshVerma/dwmblocks-async.git
cd dwmblocks-async
vi config.h
sudo make install
```
## Usage
To set `dwmblocks-async` as your status bar, you need to run it as a background
process on startup. One way is to add the following to `~/.xinitrc`:
```sh
# The binary of `dwmblocks-async` is named `dwmblocks`
dwmblocks &
```
### Modifying the blocks
You can define your status bar blocks in `config.c`:
```c
Block blocks[] = {
...
{"volume", 0, 5},
{"date", 1800, 1},
...
}
```
Each block has the following properties:
| Property | Description |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Command | The command you wish to execute in your block. |
| Update interval | Time in seconds, after which you want the block to update. If `0`, the block will never be updated. |
| Update signal | Signal to be used for triggering the block. Must be a positive integer. If `0`, a signal won't be set up for the block and it will be unclickable. |
Apart from defining the blocks, features can be toggled through `config.h`:
```c
// Maximum possible length of output from block, expressed in number of characters.
#define CMDLENGTH 50
// The status bar's delimiter that appears in between each block.
#define DELIMITER " "
// Adds a leading delimiter to the status bar, useful for powerline.
#define LEADING_DELIMITER 1
// Enable clickability for blocks. See the "Clickable blocks" section below.
#define CLICKABLE_BLOCKS 1
```
### Signalling changes
Most status bars constantly rerun all scripts every few seconds. This is an
option here, but a superior choice is to give your block a signal through which
you can indicate it to update on relevant event, rather than have it rerun
idly.
For example, the volume block has the update signal `5` by default. I run
`kill -39 $(pidof dwmblocks)` alongside my volume shortcuts in `dwm` to only
update it when relevant. Just add `34` to your signal number! You could also
run `pkill -RTMIN+5 dwmblocks`, but it's slower.
To refresh all the blocks, run `kill -10 $(pidof dwmblocks)` or
`pkill -SIGUSR1 dwmblocks`.
> All blocks must have different signal numbers!
### Clickable blocks
Like `i3blocks`, this build allows you to build in additional actions into your
scripts in response to click events. You can check out
[my status bar scripts](https://github.com/UtkarshVerma/dotfiles/tree/main/.local/bin/statusbar)
as references for using the `$BLOCK_BUTTON` variable.
To use this feature, define the `CLICKABLE_BLOCKS` feature macro in your
`config.h`:
```c
#define CLICKABLE_BLOCKS 1
```
Apart from that, you need `dwm` to be patched with
[statuscmd](https://dwm.suckless.org/patches/statuscmd/).
## Credits
This work would not have been possible without
[Luke's build of dwmblocks](https://github.com/LukeSmithxyz/dwmblocks) and
[Daniel Bylinka's statuscmd patch](https://dwm.suckless.org/patches/statuscmd/).

15
dwmblocks-async/config.c Normal file
View file

@ -0,0 +1,15 @@
#include "config.h"
#include "block.h"
#include "util.h"
Block blocks[] = {
{"sb-disk", 1800, 1 },
{"sb-memory", 10, 2 },
{"sb-loadavg", 5, 3 },
{"sb-record", 0, 4 },
{"sb-volume", 1, 5 },
{"sb-date", 1, 6 },
};
const unsigned short blockCount = LEN(blocks);

6
dwmblocks-async/config.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#define CLICKABLE_BLOCKS 1 // Enable clickability for blocks
#define CMDLENGTH 45 // Trim block output to this length
#define DELIMITER " " // Delimiter string used to separate blocks
#define LEADING_DELIMITER 0 // Whether a leading separator should be used

15
dwmblocks-async/inc/bar.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include "block.h"
#include "config.h"
#include "util.h"
typedef struct {
char *current;
char *previous;
} BarStatus;
extern unsigned short debugMode;
void initStatus(BarStatus *);
void freeStatus(BarStatus *);
void writeStatus(BarStatus *);

View file

@ -0,0 +1,17 @@
#pragma once
#include "config.h"
typedef struct {
const char *command;
const unsigned int interval;
const unsigned int signal;
int pipe[2];
char output[CMDLENGTH * 4 + 1];
} Block;
extern Block blocks[];
extern const unsigned short blockCount;
void execBlock(const Block *, const char *);
void execBlocks(unsigned int);
void updateBlock(Block *);

View file

@ -0,0 +1,8 @@
#pragma once
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
#define MAX(a, b) (a > b ? a : b)
int gcd(int, int);
void closePipe(int[2]);
void trimUTF8(char*, unsigned int);

View file

@ -0,0 +1,5 @@
#pragma once
int setupX();
int closeX();
void setXRootName(char *);

BIN
dwmblocks-async/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

62
dwmblocks-async/src/bar.c Normal file
View file

@ -0,0 +1,62 @@
#include "bar.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "block.h"
#include "x11.h"
void initStatus(BarStatus *status) {
const unsigned int statusLength =
(blockCount * (LEN(blocks[0].output) - 1)) +
(blockCount - 1 + LEADING_DELIMITER) * (LEN(DELIMITER) - 1);
status->current = (char *)malloc(statusLength);
status->previous = (char *)malloc(statusLength);
status->current[0] = '\0';
status->previous[0] = '\0';
}
void freeStatus(BarStatus *status) {
free(status->current);
free(status->previous);
}
int updateStatus(BarStatus *status) {
strcpy(status->previous, status->current);
status->current[0] = '\0';
for (int i = 0; i < blockCount; i++) {
Block *block = blocks + i;
if (strlen(block->output)) {
#if LEADING_DELIMITER
strcat(status->current, DELIMITER);
#else
if (status->current[0]) strcat(status->current, DELIMITER);
#endif
#if CLICKABLE_BLOCKS
if (!debugMode && block->signal) {
char signal[] = {block->signal, '\0'};
strcat(status->current, signal);
}
#endif
strcat(status->current, block->output);
}
}
return strcmp(status->current, status->previous);
}
void writeStatus(BarStatus *status) {
// Only write out if status has changed
if (!updateStatus(status)) return;
if (debugMode) {
printf("%s\n", status->current);
return;
}
setXRootName(status->current);
}

View file

@ -0,0 +1,72 @@
#include "block.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
static int execLock = 0;
void execBlock(const Block *block, const char *button) {
unsigned short i = block - blocks;
// Ensure only one child process exists per block at an instance
if (execLock & 1 << i) return;
// Lock execution of block until current instance finishes execution
execLock |= 1 << i;
if (fork() == 0) {
close(block->pipe[0]);
dup2(block->pipe[1], STDOUT_FILENO);
close(block->pipe[1]);
if (button) setenv("BLOCK_BUTTON", button, 1);
FILE *file = popen(block->command, "r");
if (!file) {
printf("\n");
exit(EXIT_FAILURE);
}
// Buffer will hold both '\n' and '\0'
char buffer[LEN(block->output) + 1];
if (fgets(buffer, LEN(buffer), file) == NULL) {
// Send an empty line in case of no output
printf("\n");
exit(EXIT_SUCCESS);
}
pclose(file);
// Trim to the max possible UTF-8 capacity
trimUTF8(buffer, LEN(buffer));
printf("%s\n", buffer);
exit(EXIT_SUCCESS);
}
}
void execBlocks(unsigned int time) {
for (int i = 0; i < blockCount; i++) {
const Block *block = blocks + i;
if (time == 0 ||
(block->interval != 0 && time % block->interval == 0)) {
execBlock(block, NULL);
}
}
}
void updateBlock(Block *block) {
char buffer[LEN(block->output)];
int bytesRead = read(block->pipe[0], buffer, LEN(buffer));
// String from pipe will always end with '\n'
buffer[bytesRead - 1] = '\0';
strcpy(block->output, buffer);
// Remove execution lock for the current block
execLock &= ~(1 << (block - blocks));
}

157
dwmblocks-async/src/main.c Normal file
View file

@ -0,0 +1,157 @@
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include "bar.h"
#include "block.h"
#include "util.h"
#include "x11.h"
static unsigned short statusContinue = 1;
unsigned short debugMode = 0;
static int epollFD, signalFD;
static unsigned int timerTick = 0, maxInterval = 1;
void signalHandler() {
struct signalfd_siginfo info;
read(signalFD, &info, sizeof(info));
unsigned int signal = info.ssi_signo;
static unsigned int timer = 0;
switch (signal) {
case SIGALRM:
// Schedule the next timer event and execute blocks
alarm(timerTick);
execBlocks(timer);
// Wrap `timer` to the interval [1, `maxInterval`]
timer = (timer + timerTick - 1) % maxInterval + 1;
return;
case SIGUSR1:
// Update all blocks on receiving SIGUSR1
execBlocks(0);
return;
}
for (int j = 0; j < blockCount; j++) {
const Block *block = blocks + j;
if (block->signal == signal - SIGRTMIN) {
char button[4]; // value can't be more than 255;
sprintf(button, "%d", info.ssi_int & 0xff);
execBlock(block, button);
break;
}
}
}
void termHandler() {
statusContinue = 0;
}
void setupSignals() {
sigset_t handledSignals;
sigemptyset(&handledSignals);
sigaddset(&handledSignals, SIGUSR1);
sigaddset(&handledSignals, SIGALRM);
// Append all block signals to `handledSignals`
for (int i = 0; i < blockCount; i++)
if (blocks[i].signal > 0)
sigaddset(&handledSignals, SIGRTMIN + blocks[i].signal);
// Create a signal file descriptor for epoll to watch
signalFD = signalfd(-1, &handledSignals, 0);
// Block all realtime and handled signals
for (int i = SIGRTMIN; i <= SIGRTMAX; i++) sigaddset(&handledSignals, i);
sigprocmask(SIG_BLOCK, &handledSignals, NULL);
// Handle termination signals
signal(SIGINT, termHandler);
signal(SIGTERM, termHandler);
// Avoid zombie subprocesses
struct sigaction signalAction;
signalAction.sa_handler = SIG_DFL;
sigemptyset(&signalAction.sa_mask);
signalAction.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &signalAction, 0);
}
void statusLoop() {
// Update all blocks initially
raise(SIGALRM);
BarStatus status;
initStatus(&status);
struct epoll_event events[blockCount + 1];
while (statusContinue) {
int eventCount = epoll_wait(epollFD, events, LEN(events), 100);
for (int i = 0; i < eventCount; i++) {
unsigned short id = events[i].data.u32;
if (id < blockCount) {
updateBlock(blocks + id);
} else {
signalHandler();
}
}
if (eventCount != -1) writeStatus(&status);
}
freeStatus(&status);
}
void init() {
epollFD = epoll_create(blockCount);
struct epoll_event event = {
.events = EPOLLIN,
};
for (int i = 0; i < blockCount; i++) {
// Append each block's pipe's read end to `epollFD`
pipe(blocks[i].pipe);
event.data.u32 = i;
epoll_ctl(epollFD, EPOLL_CTL_ADD, blocks[i].pipe[0], &event);
// Calculate the max interval and tick size for the timer
if (blocks[i].interval) {
maxInterval = MAX(blocks[i].interval, maxInterval);
timerTick = gcd(blocks[i].interval, timerTick);
}
}
setupSignals();
// Watch signal file descriptor as well
event.data.u32 = blockCount;
epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event);
}
int main(const int argc, const char *argv[]) {
if (setupX()) {
fprintf(stderr, "%s\n", "dwmblocks: Failed to open display");
return 1;
}
for (int i = 0; i < argc; i++) {
if (!strcmp("-d", argv[i])) {
debugMode = 1;
break;
}
}
init();
statusLoop();
if (closeX())
fprintf(stderr, "%s\n", "dwmblocks: Failed to close display");
close(epollFD);
close(signalFD);
for (int i = 0; i < blockCount; i++) closePipe(blocks[i].pipe);
return 0;
}

View file

@ -0,0 +1,41 @@
#include "util.h"
#include <unistd.h>
int gcd(int a, int b) {
int temp;
while (b > 0) {
temp = a % b;
a = b;
b = temp;
}
return a;
}
void closePipe(int pipe[2]) {
close(pipe[0]);
close(pipe[1]);
}
void trimUTF8(char* buffer, unsigned int size) {
int length = (size - 1) / 4;
int count = 0, j = 0;
char ch = buffer[j];
while (ch != '\0' && ch != '\n' && count < length) {
// Skip continuation bytes, if any
int skip = 1;
while ((ch & 0xc0) > 0x80) {
ch <<= 1;
skip++;
}
j += skip;
ch = buffer[j];
count++;
}
// Trim trailing newline and spaces
buffer[j] = ' ';
while (j >= 0 && buffer[j] == ' ') j--;
buffer[j + 1] = '\0';
}

25
dwmblocks-async/src/x11.c Normal file
View file

@ -0,0 +1,25 @@
#include "x11.h"
#include <X11/Xlib.h>
static Display *display;
static Window rootWindow;
int setupX() {
display = XOpenDisplay(NULL);
if (!display) {
return 1;
}
rootWindow = DefaultRootWindow(display);
return 0;
}
int closeX() {
return XCloseDisplay(display);
}
void setXRootName(char *str) {
XStoreName(display, rootWindow, str);
XFlush(display);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 KiB

12
scripts/dwmblocks/musicup Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
# This loop will update the music statusbar module whenever a command changes the
# music player's status. mpd and must be running on X's start for this to work.
{
playerctl metadata -F -f "{{title}}{{artist}}{{status}}" &
while ! mpc idleloop 2>/dev/null; do
sleep 1
done
} | while read -r _; do
pkill -RTMIN+2 "$STATUSBAR"
done

53
scripts/dwmblocks/sb-battery Executable file
View file

@ -0,0 +1,53 @@
#!/bin/sh
# Prints all batteries, their percentage remaining and an emoji corresponding
# to charge status (🔌 for plugged up, 🔋 for discharging on battery, etc.).
notify() {
notify-send -i battery-good-symbolic \
-h string:x-canonical-private-synchronous:battery \
"Battery" "$1" -t 4000
}
case "$BLOCK_BUTTON" in
1) notify "$(acpi -b | awk -F ': |, ' '{printf "%s\n%s\n", $2, $4}')" ;;
6) "$TERMINAL" -e "$EDITOR" "$0" ;;
esac
. sb-theme
# Loop through all attached batteries and format the info
for battery in /sys/class/power_supply/BAT?*; do
# If non-first battery, print a space separator.
[ -n "${capacity+x}" ] && printf " "
capacity="$(cat "$battery/capacity" 2>&1)"
if [ "$capacity" -gt 90 ]; then
status=" "
elif [ "$capacity" -gt 60 ]; then
status=" "
elif [ "$capacity" -gt 40 ]; then
status=" "
elif [ "$capacity" -gt 10 ]; then
status=" "
else
status=" "
fi
# Sets up the status and capacity
case "$(cat "$battery/status" 2>&1)" in
Full) status=" " ;;
Discharging)
if [ "$capacity" -le 20 ]; then
status="$status"
color=1
fi
;;
Charging) status="󰚥$status" ;;
"Not charging") status=" " ;;
Unknown) status="? $status" ;;
*) exit 1 ;;
esac
# Prints the info
display "$status$capacity%" "$color"
done && echo

23
scripts/dwmblocks/sb-date Executable file
View file

@ -0,0 +1,23 @@
#!/bin/sh
# Displays the current time in HH:MM:SS (AM|PM)
notify() {
notify-send -i office-calendar-symbolic \
-h string:x-canonical-private-synchronous:"$1" "$@"
}
case $BLOCK_BUTTON in
1)
notify "This Month" "$(cal --color=always |
sed "s|..7m|<span color='$(xrdb -get color1)'>|;s|..0m|</span>|")"
appointments="$(calcurse -d1 \
--format-apt "• <i>%S - %E</i>\n <span foreground='$(xrdb -get color6)'>%m</span>\n" \
--format-event "• <span foreground='$(xrdb -get color6)'>%m</span>\n")"
[ -n "$appointments" ] && notify "Appointments" "$appointments"
;;
2) setsid -f "$TERMINAL" -e calcurse ;;
6) "$TERMINAL" -e "$EDITOR" "$0" ;;
esac
. sb-theme
display " $(date '+%I:%M:%S %p')"

16
scripts/dwmblocks/sb-disk Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
# Displays the disk usages of root and home partitions.
case $BLOCK_BUTTON in
6) "$TERMINAL" -e "$EDITOR" "$0" ;;
esac
. sb-theme
fetchDiskUsage() {
avail=$(df "$1" | awk 'NR==2 {printf "%2.1f", ($4 / 1024 / 1024)}')
percentUsed=$(df "$1" | awk -F'[^0-9]*' 'NR==2 {print $5}')
[ "$percentUsed" -le 10 ] && color=10
display "${avail}G" "$color"
}
echo "$(display 󰋊) $(fetchDiskUsage /)$(display "|")$(fetchDiskUsage /home)"

13
scripts/dwmblocks/sb-loadavg Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh
# Displays the average CPU load.
case $BLOCK_BUTTON in
6) "$TERMINAL" -e "$EDITOR" "$0" ;;
esac
threshold=5
load=$(cut -d' ' -f1 /proc/loadavg)
. sb-theme
[ 1 -eq "$(echo "$load > $threshold" | bc)" ] && color=9
display " $load" "$color"

34
scripts/dwmblocks/sb-mail Executable file
View file

@ -0,0 +1,34 @@
#!/bin/sh
# Displays unread mail count for my email addresses.
# Keep retrying until internet is there
while ! wget --spider -q http://example.com; do
sleep 10
done
tokens="token.json college_token.json"
icons=" : "
# Display work mail info during work hours only
hour="$(date +"%H")"
if [ "$(date +"%u")" -lt 6 ] && [ "$hour" -gt 9 ] && [ "$hour" -le 18 ]; then
tokens="$tokens work_token.json"
icons="$icons: "
fi
counts=""
for token in $tokens; do
tokenFile="$XDG_CACHE_HOME/qgmail/$token"
[ ! -f "$tokenFile" ] && continue
icon="${icons%%:*}"
icons="${icons#*:}"
i=$((i + 1))
count="$(qgmail --token "$tokenFile" request \
users.labels.get me INBOX | jq -r '.messagesUnread // empty')"
counts="$counts${count:+"${counts:+" "}$icon$count"}"
done
[ -z "$counts" ] && exit
. sb-theme
display "$counts"

9
scripts/dwmblocks/sb-memory Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
# Displays RAM usage.
case $BLOCK_BUTTON in
6) "$TERMINAL" -e "$EDITOR" "$0" ;;
esac
. sb-theme
display " $(free --mebi | awk 'NR==2 {printf ("%2.2fGiB\n", ($3 / 1024))}')"

7
scripts/dwmblocks/sb-mic Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
# Display an icon if the microphone is muted.
if pactl get-source-mute @DEFAULT_SOURCE@ | grep -q yes; then
. sb-theme
display ""
fi

27
scripts/dwmblocks/sb-music Executable file
View file

@ -0,0 +1,27 @@
#!/bin/sh
# Display currently playing music metadata.
case "$BLOCK_BUTTON" in
1) musicctl prev ;;
2) musicctl toggle ;;
3) musicctl next ;;
4) musicctl seek +5 ;;
5) musicctl seek -5 ;;
6) terminal -e "$EDITOR" "$0" ;;
esac
set -- --player spotify,mpv,%any
icon=""
pausedIcon=""
if status="$(playerctl "$@" status 2>&1)" && [ "$status" != "Stopped" ]; then
[ "$status" = "Paused" ] && icon="$pausedIcon"
currentSong="$(playerctl "$@" metadata --format "{{title}} - {{artist}}")"
else
[ "$(mpc status "%state%")" = "paused" ] && icon="$pausedIcon"
currentSong="$(mpc current -f "%title%[ - %artist%]")"
fi
[ -z "$currentSong" ] && exit
. sb-theme
display "$icon $currentSong"

7
scripts/dwmblocks/sb-record Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
# Displays an indicator during screen records.
if [ -f /tmp/record ]; then
. sb-theme
display ""
fi

32
scripts/dwmblocks/sb-theme Executable file
View file

@ -0,0 +1,32 @@
#!/bin/sh
# Utility functions for theming statusbar scripts.
display() {
if [ -n "$2" ]; then
color="$2"
else
case "$(basename "$0")" in
sb-mail) color=13 ;;
sb-music) color=14 ;;
sb-disk) color=10 ;;
sb-memory) color=15 ;;
sb-loadavg) color=11 ;;
sb-mic) color=9 ;;
sb-record) color=9 ;;
sb-volume) color=15 ;;
sb-battery) color=14 ;;
sb-date) color=12 ;;
sb-network) color=9 ;;
*) color=15 ;;
esac
fi
case "$STATUSBAR" in
"dwmblocks")
echo "^C$color^$1"
;;
*)
echo "$1"
;;
esac
}

28
scripts/dwmblocks/sb-volume Executable file
View file

@ -0,0 +1,28 @@
#!/bin/sh
case $BLOCK_BUTTON in
1) setsid -f "$TERMINAL" -e pulsemixer ;;
2) pamixer -t ;;
4) pamixer -i 3 ;;
5) pamixer -d 3 ;;
6) "$TERMINAL" -e "$EDITOR" "$0" ;;
esac
. sb-theme
if [ "$(pamixer --get-mute)" = true ]; then
display " " 9
exit
fi
vol="$(pamixer --get-volume)"
[ $? -ne 0 ] && [ -z "$vol" ] && exit 1
if [ "$vol" -gt 40 ]; then
icon=" "
elif [ "$vol" -gt 15 ]; then
icon=" "
else
icon=" "
fi
display "$icon$vol%"

5
slock-flexipatch/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
slock
*.o
dwm
config.h
patches.h

24
slock-flexipatch/LICENSE Normal file
View file

@ -0,0 +1,24 @@
MIT/X Consortium License
© 2015-2016 Markus Teich <markus.teich@stusta.mhn.de>
© 2014 Dimitris Papastamos <sin@2f30.org>
© 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2014-2016 Laslo Hunhold <dev@frign.de>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

65
slock-flexipatch/Makefile Normal file
View file

@ -0,0 +1,65 @@
# slock - simple screen locker
# See LICENSE file for copyright and license details.
include config.mk
SRC = slock.c ${COMPATSRC}
OBJ = ${SRC:.c=.o}
all: options slock
options:
@echo slock build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${OBJ}: config.h config.mk arg.h util.h patches.h
config.h:
@echo creating $@ from config.def.h
@cp config.def.h $@
patches.h:
@echo creating $@ from patches.def.h
@cp patches.def.h $@
slock: ${OBJ}
@echo CC -o $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
clean:
@echo cleaning
@rm -f slock ${OBJ} slock-${VERSION}.tar.gz
dist: clean
@echo creating dist tarball
@mkdir -p slock-${VERSION}
@cp -R LICENSE Makefile README slock.1 config.mk \
${SRC} config.def.h arg.h util.h slock-${VERSION}
@tar -cf slock-${VERSION}.tar slock-${VERSION}
@gzip slock-${VERSION}.tar
@rm -rf slock-${VERSION}
install: all
@echo installing executable file to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f slock ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/slock
@chmod u+s ${DESTDIR}${PREFIX}/bin/slock
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" <slock.1 >${DESTDIR}${MANPREFIX}/man1/slock.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/slock.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/slock
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/slock.1
.PHONY: all options clean dist install uninstall

24
slock-flexipatch/README Normal file
View file

@ -0,0 +1,24 @@
slock - simple screen locker
============================
simple screen locker utility for X.
Requirements
------------
In order to build slock you need the Xlib header files.
Installation
------------
Edit config.mk to match your local setup (slock is installed into
the /usr/local namespace by default).
Afterwards enter the following command to build and install slock
(if necessary as root):
make clean install
Running slock
-------------
Simply invoke the 'slock' command. To get out of it, enter your password.

Some files were not shown because too many files have changed in this diff Show more