diff --git a/README.md b/README.md index 402ba5f..553b866 100644 --- a/README.md +++ b/README.md @@ -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, Mod4Key would be defined as ⊞ Win/⌘ Cmd/❖ Super, depending on whichever keyboard do you use. + +In most cases, you probably have only ⊞ Win, but I added ⌘ Cmd and ❖ Super for Mac and advanced Linux/Unix users, respectively. + +If one uses ChromeOS, ⊞ Win equals to the 🔍 Search key. But I don't know who uses X11 window managers inside ChromeOS. + +For new to dwm, MODKEY or Mod1Mask is the Alt 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 ⊞ Win/⌘ Cmd/❖ Super+Alt+S, while ⊞ Win/⌘ Cmd/❖ Super+S would launch `spmenu_run -d` by default (only with .desktop entries, while ⊞ Win/⌘ Cmd/❖ Super+Shift+S 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//.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 \ No newline at end of file diff --git a/desktop/startdwm b/desktop/startdwm new file mode 100755 index 0000000..1740e8e --- /dev/null +++ b/desktop/startdwm @@ -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 diff --git a/dmenu/.gitignore b/dmenu-flexipatch/.gitignore similarity index 71% rename from dmenu/.gitignore rename to dmenu-flexipatch/.gitignore index d29fa65..3ef527d 100644 --- a/dmenu/.gitignore +++ b/dmenu-flexipatch/.gitignore @@ -1,4 +1,5 @@ *.o +config.h +patches.h dmenu stest -config.h diff --git a/dmenu/LICENSE b/dmenu-flexipatch/LICENSE similarity index 100% rename from dmenu/LICENSE rename to dmenu-flexipatch/LICENSE diff --git a/dmenu/Makefile b/dmenu-flexipatch/Makefile similarity index 95% rename from dmenu/Makefile rename to dmenu-flexipatch/Makefile index a03a95c..f4be9d1 100644 --- a/dmenu/Makefile +++ b/dmenu-flexipatch/Makefile @@ -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) diff --git a/dmenu/README b/dmenu-flexipatch/README similarity index 100% rename from dmenu/README rename to dmenu-flexipatch/README diff --git a/dmenu-flexipatch/README.md b/dmenu-flexipatch/README.md new file mode 100644 index 0000000..be2c349 --- /dev/null +++ b/dmenu-flexipatch/README.md @@ -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 diff --git a/dmenu/arg.h b/dmenu-flexipatch/arg.h similarity index 100% rename from dmenu/arg.h rename to dmenu-flexipatch/arg.h diff --git a/dmenu-flexipatch/config.def.h b/dmenu-flexipatch/config.def.h new file mode 100644 index 0000000..92c42a9 --- /dev/null +++ b/dmenu-flexipatch/config.def.h @@ -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 diff --git a/dmenu/config.mk b/dmenu-flexipatch/config.mk similarity index 57% rename from dmenu/config.mk rename to dmenu-flexipatch/config.mk index 56e4b51..29b96ca 100644 --- a/dmenu/config.mk +++ b/dmenu-flexipatch/config.mk @@ -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) diff --git a/dmenu/dmenu.1 b/dmenu-flexipatch/dmenu.1 similarity index 94% rename from dmenu/dmenu.1 rename to dmenu-flexipatch/dmenu.1 index d0a734a..323f93c 100644 --- a/dmenu/dmenu.1 +++ b/dmenu-flexipatch/dmenu.1 @@ -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 diff --git a/dmenu-flexipatch/dmenu.c b/dmenu-flexipatch/dmenu.c new file mode 100644 index 0000000..5d20ed8 --- /dev/null +++ b/dmenu-flexipatch/dmenu.c @@ -0,0 +1,2220 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#include "patches.h" +/* Patch incompatibility overrides */ +#if MULTI_SELECTION_PATCH +#undef NON_BLOCKING_STDIN_PATCH +#undef PIPEOUT_PATCH +#undef PRINTINPUTTEXT_PATCH +#endif // MULTI_SELECTION_PATCH + +#include "drw.h" +#include "util.h" +#if GRIDNAV_PATCH +#include +#endif // GRIDNAV_PATCH + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#if PANGO_PATCH +#define TEXTW(X) (drw_font_getwidth(drw, (X), False) + lrpad) +#define TEXTWM(X) (drw_font_getwidth(drw, (X), True) + lrpad) +#else +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#endif // PANGO_PATCH +#if ALPHA_PATCH +#define OPAQUE 0xffU +#define OPACITY "_NET_WM_WINDOW_OPACITY" +#endif // ALPHA_PATCH + +/* enums */ +enum { + SchemeNorm, + SchemeSel, + SchemeOut, + #if BORDER_PATCH + SchemeBorder, + #endif // BORDER_PATCH + #if MORECOLOR_PATCH + SchemeMid, + #endif // MORECOLOR_PATCH + #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + SchemeNormHighlight, + SchemeSelHighlight, + #endif // HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHPRIORITY_PATCH + SchemeHp, + #endif // HIGHPRIORITY_PATCH + #if EMOJI_HIGHLIGHT_PATCH + SchemeHover, + SchemeGreen, + SchemeYellow, + SchemeBlue, + SchemePurple, + SchemeRed, + #endif // EMOJI_HIGHLIGHT_PATCH + SchemeLast, +}; /* color schemes */ + +struct item { + char *text; + #if SEPARATOR_PATCH + char *text_output; + #elif TSV_PATCH + char *stext; + #endif // SEPARATOR_PATCH | TSV_PATCH + struct item *left, *right; + #if NON_BLOCKING_STDIN_PATCH + struct item *next; + #endif // NON_BLOCKING_STDIN_PATCH + #if MULTI_SELECTION_PATCH + int id; /* for multiselect */ + #else + int out; + #endif // MULTI_SELECTION_PATCH + #if HIGHPRIORITY_PATCH + int hp; + #endif // HIGHPRIORITY_PATCH + #if FUZZYMATCH_PATCH + double distance; + #endif // FUZZYMATCH_PATCH + #if PRINTINDEX_PATCH + int index; + #endif // PRINTINDEX_PATCH +}; + +static char text[BUFSIZ] = ""; +#if PIPEOUT_PATCH +static char pipeout[8] = " | dmenu"; +#endif // PIPEOUT_PATCH +static char *embed; +#if SEPARATOR_PATCH +static char separator; +static int separator_greedy; +static int separator_reverse; +#endif // SEPARATOR_PATCH +static int bh, mw, mh; +#if XYW_PATCH +static int dmx = 0, dmy = 0; /* put dmenu at these x and y offsets */ +static unsigned int dmw = 0; /* make dmenu this wide */ +#endif // XYW_PATCH +static int inputw = 0, promptw; +#if PASSWORD_PATCH +static int passwd = 0; +#endif // PASSWORD_PATCH +static int lrpad; /* sum of left and right padding */ +#if BARPADDING_PATCH +static int vp; /* vertical padding for bar */ +static int sp; /* side padding for bar */ +#endif // BARPADDING_PATCH +#if REJECTNOMATCH_PATCH +static int reject_no_match = 0; +#endif // REJECTNOMATCH_PATCH +static size_t cursor; +static struct item *items = NULL; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; +#if PRINTINDEX_PATCH +static int print_index = 0; +#endif // PRINTINDEX_PATCH +#if MANAGED_PATCH +static int managed = 0; +#endif // MANAGED_PATCH +#if MULTI_SELECTION_PATCH +static int *selid = NULL; +static unsigned int selidsize = 0; +#endif // MULTI_SELECTION_PATCH +#if NO_SORT_PATCH +static unsigned int sortmatches = 1; +#endif // NO_SORT_PATCH +#if PRINTINPUTTEXT_PATCH +static int use_text_input = 0; +#endif // PRINTINPUTTEXT_PATCH +#if PRESELECT_PATCH +static unsigned int preselected = 0; +#endif // PRESELECT_PATCH +#if EMOJI_HIGHLIGHT_PATCH +static int commented = 0; +static int animated = 0; +#endif // EMOJI_HIGHLIGHT_PATCH + +static Atom clip, utf8; +#if WMTYPE_PATCH +static Atom type, dock; +#endif // WMTYPE_PATCH +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +#if ALPHA_PATCH +static int useargb = 0; +static Visual *visual; +static int depth; +static Colormap cmap; +#endif // ALPHA_PATCH + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +#include "patch/include.h" + +#include "config.h" + +#if CASEINSENSITIVE_PATCH +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; +#else +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; +#endif // CASEINSENSITIVE_PATCH + +static unsigned int +textw_clamp(const char *str, unsigned int n) +{ + unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; + return MIN(w, n); +} + +static void appenditem(struct item *item, struct item **list, struct item **last); +static void calcoffsets(void); +static void cleanup(void); +static char * cistrstr(const char *s, const char *sub); +static int drawitem(struct item *item, int x, int y, int w); +static void drawmenu(void); +static void grabfocus(void); +static void grabkeyboard(void); +static void match(void); +static void insert(const char *str, ssize_t n); +static size_t nextrune(int inc); +static void movewordedge(int dir); +static void keypress(XKeyEvent *ev); +static void paste(void); +#if ALPHA_PATCH +static void xinitvisual(void); +#endif // ALPHA_PATCH +static void readstdin(void); +static void run(void); +static void setup(void); +static void usage(void); + +#include "patch/include.c" + +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, rpad = 0; + + if (lines > 0) { + #if GRID_PATCH + if (columns) + n = lines * columns * bh; + else + n = lines * bh; + #else + n = lines * bh; + #endif // GRID_PATCH + } else { + #if NUMBERS_PATCH + rpad = TEXTW(numbers); + #endif // NUMBERS_PATCH + #if SYMBOLS_PATCH + n = mw - (promptw + inputw + TEXTW(symbol_1) + TEXTW(symbol_2) + rpad); + #else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">") + rpad); + #endif // SYMBOLS_PATCH + } + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : textw_clamp(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]); + for (i = 0; items && items[i].text; ++i) + free(items[i].text); + free(items); + #if HIGHPRIORITY_PATCH + for (i = 0; i < hplength; ++i) + free(hpitems[i]); + free(hpitems); + #endif // HIGHPRIORITY_PATCH + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); + #if MULTI_SELECTION_PATCH + free(selid); + #endif // MULTI_SELECTION_PATCH +} + +static char * +cistrstr(const char *s, const char *sub) +{ + size_t len; + + for (len = strlen(sub); *s; s++) + if (!strncasecmp(s, sub, len)) + return (char *)s; + return NULL; +} + +static int +drawitem(struct item *item, int x, int y, int w) +{ + int r; + #if TSV_PATCH && !SEPARATOR_PATCH + char *text = item->stext; + #else + char *text = item->text; + #endif // TSV_PATCH + + #if EMOJI_HIGHLIGHT_PATCH + int iscomment = 0; + if (text[0] == '>') { + if (text[1] == '>') { + iscomment = 3; + switch (text[2]) { + case 'r': + drw_setscheme(drw, scheme[SchemeRed]); + break; + case 'g': + drw_setscheme(drw, scheme[SchemeGreen]); + break; + case 'y': + drw_setscheme(drw, scheme[SchemeYellow]); + break; + case 'b': + drw_setscheme(drw, scheme[SchemeBlue]); + break; + case 'p': + drw_setscheme(drw, scheme[SchemePurple]); + break; + #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + case 'h': + drw_setscheme(drw, scheme[SchemeNormHighlight]); + break; + #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + case 's': + drw_setscheme(drw, scheme[SchemeSel]); + break; + default: + iscomment = 1; + drw_setscheme(drw, scheme[SchemeNorm]); + break; + } + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + iscomment = 1; + } + } else if (text[0] == ':') { + iscomment = 2; + if (item == sel) { + switch (text[1]) { + case 'r': + drw_setscheme(drw, scheme[SchemeRed]); + break; + case 'g': + drw_setscheme(drw, scheme[SchemeGreen]); + break; + case 'y': + drw_setscheme(drw, scheme[SchemeYellow]); + break; + case 'b': + drw_setscheme(drw, scheme[SchemeBlue]); + break; + case 'p': + drw_setscheme(drw, scheme[SchemePurple]); + break; + #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + case 'h': + drw_setscheme(drw, scheme[SchemeNormHighlight]); + break; + #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + case 's': + drw_setscheme(drw, scheme[SchemeSel]); + break; + default: + drw_setscheme(drw, scheme[SchemeSel]); + iscomment = 0; + break; + } + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + } + } + #endif // EMOJI_HIGHLIGHT_PATCH + + #if EMOJI_HIGHLIGHT_PATCH + int temppadding = 0; + if (iscomment == 2) { + if (text[2] == ' ') { + #if PANGO_PATCH + temppadding = drw->font->h * 3; + #else + temppadding = drw->fonts->h * 3; + #endif // PANGO_PATCH + animated = 1; + char dest[1000]; + strcpy(dest, text); + dest[6] = '\0'; + drw_text(drw, x, y + , temppadding + #if LINE_HEIGHT_PATCH + , MAX(lineheight, bh) + #else + , bh + #endif // LINE_HEIGHT_PATCH + , temppadding / 2.6 + , dest + 3 + , 0 + #if PANGO_PATCH + , True + #endif // PANGO_PATCH + ); + iscomment = 6; + drw_setscheme(drw, sel == item ? scheme[SchemeHover] : scheme[SchemeNorm]); + } + } + + char *output; + if (commented) { + static char onestr[2]; + onestr[0] = text[0]; + onestr[1] = '\0'; + output = onestr; + } else { + output = text; + } + #endif // EMOJI_HIGHLIGHT_PATCH + + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + #if HIGHPRIORITY_PATCH + else if (item->hp) + drw_setscheme(drw, scheme[SchemeHp]); + #endif // HIGHPRIORITY_PATCH + #if MORECOLOR_PATCH + else if (item->left == sel || item->right == sel) + drw_setscheme(drw, scheme[SchemeMid]); + #endif // MORECOLOR_PATCH + #if MULTI_SELECTION_PATCH + else if (issel(item->id)) + #else + else if (item->out) + #endif // MULTI_SELECTION_PATCH + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + r = drw_text(drw + #if EMOJI_HIGHLIGHT_PATCH + , x + ((iscomment == 6) ? temppadding : 0) + #else + , x + #endif // EMOJI_HIGHLIGHT_PATCH + , y + , w + , bh + #if EMOJI_HIGHLIGHT_PATCH + , commented ? (bh - TEXTW(output) - lrpad) / 2 : lrpad / 2 + #else + , lrpad / 2 + #endif // EMOJI_HIGHLIGHT_PATCH + #if EMOJI_HIGHLIGHT_PATCH + , output + iscomment + #else + , text + #endif // EMOJI_HIGHLIGHT_PATCH + , 0 + #if PANGO_PATCH + , True + #endif // PANGO_PATCH + ); + #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if EMOJI_HIGHLIGHT_PATCH + drawhighlights(item, output + iscomment, x + ((iscomment == 6) ? temppadding : 0), y, w); + #else + drawhighlights(item, x, y, w); + #endif // EMOJI_HIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + return r; +} + +static void +drawmenu(void) +{ + #if SCROLL_PATCH + static int curpos, oldcurlen; + int curlen, rcurlen; + #else + unsigned int curpos; + #endif // SCROLL_PATCH + struct item *item; + int x = 0, y = 0, w, rpad = 0, itw = 0, stw = 0; + #if LINE_HEIGHT_PATCH && PANGO_PATCH + int fh = drw->font->h; + #elif LINE_HEIGHT_PATCH + int fh = drw->fonts->h; + #endif // LINE_HEIGHT_PATCH + #if PASSWORD_PATCH + char *censort; + #endif // PASSWORD_PATCH + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + #if !PLAIN_PROMPT_PATCH + drw_setscheme(drw, scheme[SchemeSel]); + #endif // PLAIN_PROMPT_PATCH + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0 + #if PANGO_PATCH + , True + #endif // PANGO_PATCH + ); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + + #if SCROLL_PATCH + w -= lrpad / 2; + x += lrpad / 2; + rcurlen = TEXTW(text + cursor) - lrpad; + curlen = TEXTW(text) - lrpad - rcurlen; + curpos += curlen - oldcurlen; + curpos = MIN(w, MAX(0, curpos)); + curpos = MAX(curpos, w - rcurlen); + curpos = MIN(curpos, curlen); + oldcurlen = curlen; + + drw_setscheme(drw, scheme[SchemeNorm]); + #if PASSWORD_PATCH + if (passwd) { + censort = ecalloc(1, sizeof(text)); + memset(censort, '.', strlen(text)); + drw_text_align(drw, x, 0, curpos, bh, censort, cursor, AlignR); + drw_text_align(drw, x + curpos, 0, w - curpos, bh, censort + cursor, strlen(censort) - cursor, AlignL); + free(censort); + } else { + drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR); + drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL); + } + #else + drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR); + drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL); + #endif // PASSWORD_PATCH + #if LINE_HEIGHT_PATCH + drw_rect(drw, x + curpos - 1, 2 + (bh-fh)/2, 2, fh - 4, 1, 0); + #else + drw_rect(drw, x + curpos - 1, 2, 2, bh - 4, 1, 0); + #endif // LINE_HEIGHT_PATCH + #else // !SCROLL_PATCH + drw_setscheme(drw, scheme[SchemeNorm]); + #if PASSWORD_PATCH + if (passwd) { + censort = ecalloc(1, sizeof(text)); + memset(censort, '.', strlen(text)); + drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0 + #if PANGO_PATCH + , False + #endif // PANGO_PATCH + ); + drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0 + #if PANGO_PATCH + , False + #endif // PANGO_PATCH + ); + free(censort); + } else { + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0 + #if PANGO_PATCH + , False + #endif // PANGO_PATCH + ); + } + #else + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0 + #if PANGO_PATCH + , False + #endif // PANGO_PATCH + ); + #endif // PASSWORD_PATCH + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + #if CARET_WIDTH_PATCH && LINE_HEIGHT_PATCH + drw_rect(drw, x + curpos, 2 + (bh-fh)/2, caret_width, fh - 4, 1, 0); + #elif CARET_WIDTH_PATCH + drw_rect(drw, x + curpos, 2, caret_width, bh - 4, 1, 0); + #elif LINE_HEIGHT_PATCH + drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0); + #else + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + #endif // LINE_HEIGHT_PATCH + } + #endif // SCROLL_PATCH + + #if NUMBERS_PATCH + recalculatenumbers(); + rpad = TEXTW(numbers); + #if BARPADDING_PATCH + rpad += 2 * sp; + #endif // BARPADDING_PATCH + #if BORDER_PATCH + rpad += border_width; + #endif // BORDER_PATCH + #endif // NUMBERS_PATCH + if (lines > 0) { + #if GRID_PATCH + /* draw grid */ + int i = 0; + for (item = curr; item != next; item = item->right, i++) + if (columns) + #if VERTFULL_PATCH + drawitem( + item, + 0 + ((i / lines) * (mw / columns)), + y + (((i % lines) + 1) * bh), + mw / columns + ); + #else + drawitem( + item, + x + ((i / lines) * ((mw - x) / columns)), + y + (((i % lines) + 1) * bh), + (mw - x) / columns + ); + #endif // VERTFULL_PATCH + else + #if VERTFULL_PATCH + drawitem(item, 0, y += bh, mw); + #else + drawitem(item, x, y += bh, mw - x); + #endif // VERTFULL_PATCH + #else + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + #if VERTFULL_PATCH + drawitem(item, 0, y += bh, mw); + #else + drawitem(item, x, y += bh, mw - x); + #endif // VERTFULL_PATCH + #endif // GRID_PATCH + } 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 + #if PANGO_PATCH + , True + #endif // PANGO_PATCH + ); + } + x += w; + for (item = curr; item != next; item = item->right) { + #if SYMBOLS_PATCH + stw = TEXTW(symbol_2); + #else + stw = TEXTW(">"); + #endif // SYMBOLS_PATCH + #if TSV_PATCH && !SEPARATOR_PATCH + itw = textw_clamp(item->stext, mw - x - stw - rpad); + #else + itw = textw_clamp(item->text, mw - x - stw - rpad); + #endif // TSV_PATCH + x = drawitem(item, x, 0, itw); + } + if (next) { + #if SYMBOLS_PATCH + w = TEXTW(symbol_2); + #else + w = TEXTW(">"); + #endif // SYMBOLS_PATCH + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w - rpad, 0, w, bh, lrpad / 2 + #if SYMBOLS_PATCH + , symbol_2 + #else + , ">" + #endif // SYMBOLS_PATCH + , 0 + #if PANGO_PATCH + , True + #endif // PANGO_PATCH + ); + } + } + #if NUMBERS_PATCH + drw_setscheme(drw, scheme[SchemeNorm]); + #if PANGO_PATCH + drw_text(drw, mw - rpad, 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0, False); + #else + drw_text(drw, mw - rpad, 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0); + #endif // PANGO_PATCH + #endif // NUMBERS_PATCH + drw_map(drw, win, 0, 0, mw, mh); + #if NON_BLOCKING_STDIN_PATCH + XFlush(dpy); + #endif // NON_BLOCKING_STDIN_PATCH +} + +static void +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; + #if !MANAGED_PATCH + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + #endif // MANAGED_PATCH + nanosleep(&ts, NULL); + } + die("cannot grab focus"); +} + +static void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + #if MANAGED_PATCH + if (embed || managed) + #else + if (embed) + #endif // MANAGED_PATCH + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} + +static void +match(void) +{ + #if DYNAMIC_OPTIONS_PATCH + if (dynamic && *dynamic) + refreshoptions(); + #endif // DYNAMIC_OPTIONS_PATCH + + #if FUZZYMATCH_PATCH + if (fuzzy) { + fuzzymatch(); + return; + } + #endif + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + #if HIGHPRIORITY_PATCH + struct item *lhpprefix, *hpprefixend; + #endif // HIGHPRIORITY_PATCH + #if NON_BLOCKING_STDIN_PATCH + int preserve = 0; + #endif // NON_BLOCKING_STDIN_PATCH + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %zu bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + #if PREFIXCOMPLETION_PATCH + if (use_prefix) { + matches = lprefix = matchend = prefixend = NULL; + textsize = strlen(text); + } else { + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + } + #else + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + #endif // PREFIXCOMPLETION_PATCH + #if HIGHPRIORITY_PATCH + lhpprefix = hpprefixend = NULL; + #endif // HIGHPRIORITY_PATCH + #if NON_BLOCKING_STDIN_PATCH && DYNAMIC_OPTIONS_PATCH + for (item = items; item && (!(dynamic && *dynamic) || item->text); item = (dynamic && *dynamic) ? item + 1 : item->next) + #elif NON_BLOCKING_STDIN_PATCH + for (item = items; item; item = item->next) + #else + for (item = items; item && item->text; item++) + #endif + { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + #if DYNAMIC_OPTIONS_PATCH + if (i != tokc && !(dynamic && *dynamic)) /* not all tokens match */ + continue; + #else + if (i != tokc) /* not all tokens match */ + continue; + #endif // DYNAMIC_OPTIONS_PATCH + #if HIGHPRIORITY_PATCH + /* exact matches go first, then prefixes with high priority, then prefixes, then substrings */ + #else + /* exact matches go first, then prefixes, then substrings */ + #endif // HIGHPRIORITY_PATCH + #if NO_SORT_PATCH + if (!sortmatches) + appenditem(item, &matches, &matchend); + else + #endif // NO_SORT_PATCH + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + #if HIGHPRIORITY_PATCH + else if (item->hp && !fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lhpprefix, &hpprefixend); + #endif // HIGHPRIORITY_PATCH + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + #if PREFIXCOMPLETION_PATCH + else if (!use_prefix) + #else + else + #endif // PREFIXCOMPLETION_PATCH + appenditem(item, &lsubstr, &substrend); + #if NON_BLOCKING_STDIN_PATCH + if (sel == item) + preserve = 1; + #endif // NON_BLOCKING_STDIN_PATCH + } + #if HIGHPRIORITY_PATCH + if (lhpprefix) { + if (matches) { + matchend->right = lhpprefix; + lhpprefix->left = matchend; + } else + matches = lhpprefix; + matchend = hpprefixend; + } + #endif // HIGHPRIORITY_PATCH + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + #if PREFIXCOMPLETION_PATCH + if (!use_prefix && lsubstr) + #else + if (lsubstr) + #endif // PREFIXCOMPLETION_PATCH + { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + #if NON_BLOCKING_STDIN_PATCH + if (!preserve) + #endif // NON_BLOCKING_STDIN_PATCH + curr = sel = matches; + + #if INSTANT_PATCH + if (instant && matches && matches==matchend && !lsubstr) { + puts(matches->text); + cleanup(); + exit(0); + } + #endif // INSTANT_PATCH + + calcoffsets(); +} + +static void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + + #if REJECTNOMATCH_PATCH + static char last[BUFSIZ] = ""; + if (reject_no_match) { + /* store last text value in case we need to revert it */ + memcpy(last, text, BUFSIZ); + } + #endif // REJECTNOMATCH_PATCH + + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); + + #if REJECTNOMATCH_PATCH + if (!matches && reject_no_match) { + /* revert to last text value if theres no match */ + memcpy(text, last, BUFSIZ); + cursor -= n; + match(); + } + #endif // REJECTNOMATCH_PATCH +} + +static size_t +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[64]; + int len; + #if PREFIXCOMPLETION_PATCH + struct item * item; + #endif // PREFIXCOMPLETION_PATCH + KeySym ksym = NoSymbol; + Status status; + #if GRID_PATCH && GRIDNAV_PATCH + int i; + struct item *tmpsel; + bool offscreen = false; + #endif // GRIDNAV_PATCH + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: /* composed string from input method */ + goto insert; + case XLookupKeySym: + case XLookupBoth: /* a KeySym and a string are returned: use keysym */ + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + #if FZFEXPECT_PATCH + case XK_a: expect("ctrl-a", ev); ksym = XK_Home; break; + case XK_b: expect("ctrl-b", ev); ksym = XK_Left; break; + case XK_c: expect("ctrl-c", ev); ksym = XK_Escape; break; + case XK_d: expect("ctrl-d", ev); ksym = XK_Delete; break; + case XK_e: expect("ctrl-e", ev); ksym = XK_End; break; + case XK_f: expect("ctrl-f", ev); ksym = XK_Right; break; + case XK_g: expect("ctrl-g", ev); ksym = XK_Escape; break; + case XK_h: expect("ctrl-h", ev); ksym = XK_BackSpace; break; + case XK_i: expect("ctrl-i", ev); ksym = XK_Tab; break; + case XK_j: expect("ctrl-j", ev); ksym = XK_Down; break; + case XK_J:/* fallthrough */ + case XK_l: expect("ctrl-l", ev); break; + case XK_m: expect("ctrl-m", ev); /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: expect("ctrl-n", ev); ksym = XK_Down; break; + case XK_p: expect("ctrl-p", ev); ksym = XK_Up; break; + case XK_o: expect("ctrl-o", ev); break; + case XK_q: expect("ctrl-q", ev); break; + case XK_r: expect("ctrl-r", ev); break; + case XK_s: expect("ctrl-s", ev); break; + case XK_t: expect("ctrl-t", ev); break; + case XK_k: expect("ctrl-k", ev); ksym = XK_Up; break; + #else + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + 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; + #endif // FZFEXPECT_PATCH + #if FZFEXPECT_PATCH + case XK_u: expect("ctrl-u", ev); /* delete left */ + #else + case XK_u: /* delete left */ + #endif // FZFEXPECT_PATCH + insert(NULL, 0 - cursor); + break; + #if FZFEXPECT_PATCH + case XK_w: expect("ctrl-w", ev); /* delete word */ + #else + case XK_w: /* delete word */ + #endif // FZFEXPECT_PATCH + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + break; + #if FZFEXPECT_PATCH || CTRL_V_TO_PASTE_PATCH + case XK_v: + #if FZFEXPECT_PATCH + expect("ctrl-v", ev); + #endif // FZFEXPECT_PATCH + case XK_V: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + #endif // FZFEXPECT_PATCH | CTRL_V_TO_PASTE_PATCH + #if FZFEXPECT_PATCH + case XK_y: expect("ctrl-y", ev); /* paste selection */ + #else + case XK_y: /* paste selection */ + #endif // FZFEXPECT_PATCH + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + #if FZFEXPECT_PATCH + case XK_x: expect("ctrl-x", ev); break; + case XK_z: expect("ctrl-z", ev); break; + #endif // FZFEXPECT_PATCH + case XK_Left: + case XK_KP_Left: + movewordedge(-1); + goto draw; + case XK_Right: + case XK_KP_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + #if RESTRICT_RETURN_PATCH + if (restrict_return) + break; + #endif // RESTRICT_RETURN_PATCH + #if MULTI_SELECTION_PATCH + selsel(); + #endif // MULTI_SELECTION_PATCH + break; + case XK_bracketleft: + cleanup(); + 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; + #if NAVHISTORY_PATCH + case XK_p: + navhistory(-1); + buf[0]=0; + break; + case XK_n: + navhistory(1); + buf[0]=0; + break; + #endif // NAVHISTORY_PATCH + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl((unsigned char)*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 GRID_PATCH && GRIDNAV_PATCH + if (columns > 1) { + if (!sel) + return; + tmpsel = sel; + for (i = 0; i < lines; i++) { + if (!tmpsel->left || tmpsel->left->right != tmpsel) + return; + if (tmpsel == curr) + offscreen = true; + tmpsel = tmpsel->left; + } + sel = tmpsel; + if (offscreen) { + curr = prev; + calcoffsets(); + } + break; + } + #endif // GRIDNAV_PATCH + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + 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: + #if RESTRICT_RETURN_PATCH + if (restrict_return && (!sel || ev->state & (ShiftMask | ControlMask))) + break; + #endif // RESTRICT_RETURN_PATCH + #if !MULTI_SELECTION_PATCH + #if PIPEOUT_PATCH + #if PRINTINPUTTEXT_PATCH + if (sel && ( + (use_text_input && (ev->state & ShiftMask)) || + (!use_text_input && !(ev->state & ShiftMask)) + )) + #else + if (sel && !(ev->state & ShiftMask)) + #endif // PRINTINPUTTEXT_PATCH + { + if (sel->text[0] == startpipe[0]) { + strncpy(sel->text + strlen(sel->text),pipeout,8); + puts(sel->text+1); + } + #if PRINTINDEX_PATCH + if (print_index) + printf("%d\n", sel->index); + else + #endif // PRINTINDEX_PATCH + puts(sel->text); + } else { + if (text[0] == startpipe[0]) { + strncpy(text + strlen(text),pipeout,8); + puts(text+1); + } + puts(text); + } + #elif PRINTINPUTTEXT_PATCH + if (use_text_input) + puts((sel && (ev->state & ShiftMask)) ? sel->text : text); + #if PRINTINDEX_PATCH + else if (print_index) + printf("%d\n", (sel && !(ev->state & ShiftMask)) ? sel->index : -1); + #endif // PRINTINDEX_PATCH + else + #if SEPARATOR_PATCH + puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); + #else + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + #endif // SEPARATOR_PATCH + #elif PRINTINDEX_PATCH + if (print_index) + printf("%d\n", (sel && !(ev->state & ShiftMask)) ? sel->index : -1); + else + #if SEPARATOR_PATCH + puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); + #else + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + #endif // SEPARATOR_PATCH + #elif SEPARATOR_PATCH + puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); + #else + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + #endif // PIPEOUT_PATCH | PRINTINPUTTEXT_PATCH | PRINTINDEX_PATCH + #endif // MULTI_SELECTION_PATCH + if (!(ev->state & ControlMask)) { + #if NAVHISTORY_PATCH + savehistory((sel && !(ev->state & ShiftMask)) + ? sel->text : text); + #endif // NAVHISTORY_PATCH + #if MULTI_SELECTION_PATCH + printsel(ev->state); + #endif // MULTI_SELECTION_PATCH + cleanup(); + exit(0); + } + #if !MULTI_SELECTION_PATCH + if (sel) + sel->out = 1; + #endif // MULTI_SELECTION_PATCH + break; + case XK_Right: + case XK_KP_Right: + #if GRID_PATCH && GRIDNAV_PATCH + if (columns > 1) { + if (!sel) + return; + tmpsel = sel; + for (i = 0; i < lines; i++) { + if (!tmpsel->right || tmpsel->right->left != tmpsel) + return; + tmpsel = tmpsel->right; + if (tmpsel == next) + offscreen = true; + } + sel = tmpsel; + if (offscreen) { + curr = next; + calcoffsets(); + } + break; + } + #endif // GRIDNAV_PATCH + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + 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 PREFIXCOMPLETION_PATCH + if (!matches) + break; /* cannot complete no matches */ + #if FUZZYMATCH_PATCH + /* only do tab completion if all matches start with prefix */ + for (item = matches; item && item->text; item = item->right) + if (item->text[0] != text[0]) + goto draw; + #endif // FUZZYMATCH_PATCH + strncpy(text, matches->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + len = cursor = strlen(text); /* length of longest common prefix */ + for (item = matches; item && item->text; item = item->right) { + cursor = 0; + while (cursor < len && text[cursor] == item->text[cursor]) + cursor++; + len = cursor; + } + memset(text + len, '\0', strlen(text) - len); + #else + if (!sel) + return; + cursor = strnlen(sel->text, sizeof text - 1); + memcpy(text, sel->text, cursor); + text[cursor] = '\0'; + match(); + #endif // PREFIXCOMPLETION_PATCH + break; + } + +draw: + #if INCREMENTAL_PATCH + if (incremental) { + puts(text); + fflush(stdout); + } + #endif // INCREMENTAL_PATCH + 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(); +} + +#if ALPHA_PATCH +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 || !opacity) { + visual = DefaultVisual(dpy, screen); + depth = DefaultDepth(dpy, screen); + cmap = DefaultColormap(dpy, screen); + } +} +#endif // ALPHA_PATCH + +#if !NON_BLOCKING_STDIN_PATCH +static void +readstdin(void) +{ + char *line = NULL; + #if SEPARATOR_PATCH + char *p; + #elif TSV_PATCH + char *buf, *p; + #endif // SEPARATOR_PATCH | TSV_PATCH + + size_t i, linesiz, itemsiz = 0; + ssize_t len; + + #if PASSWORD_PATCH + if (passwd) { + inputw = lines = 0; + return; + } + #endif // PASSWORD_PATCH + + /* read each line from stdin and add it to the item list */ + for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) { + if (i + 1 >= itemsiz) { + itemsiz += 256; + if (!(items = realloc(items, itemsiz * sizeof(*items)))) + die("cannot realloc %zu bytes:", itemsiz * sizeof(*items)); + } + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + + if (!(items[i].text = strdup(line))) + die("strdup:"); + #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 (!(buf = strdup(line))) + die("cannot strdup %u bytes:", strlen(line) + 1); + if ((p = strchr(buf, '\t'))) + *p = '\0'; + items[i].stext = buf; + #endif // SEPARATOR_PATCH | TSV_PATCH + #if MULTI_SELECTION_PATCH + items[i].id = i; /* for multiselect */ + #if PRINTINDEX_PATCH + items[i].index = i; + #endif // PRINTINDEX_PATCH + #elif PRINTINDEX_PATCH + items[i].index = i; + #else + items[i].out = 0; + #endif // MULTI_SELECTION_PATCH | PRINTINDEX_PATCH + + #if HIGHPRIORITY_PATCH + items[i].hp = arrayhas(hpitems, hplength, items[i].text); + #endif // HIGHPRIORITY_PATCH + } + free(line); + if (items) + items[i].text = NULL; + lines = MIN(lines, i); +} +#endif // NON_BLOCKING_STDIN_PATCH + +static void +#if NON_BLOCKING_STDIN_PATCH +readevent(void) +#else +run(void) +#endif // NON_BLOCKING_STDIN_PATCH +{ + XEvent ev; + #if PRESELECT_PATCH + int i; + #endif // PRESELECT_PATCH + + while (!XNextEvent(dpy, &ev)) { + #if PRESELECT_PATCH + if (preselected) { + for (i = 0; i < preselected; i++) { + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + } + drawmenu(); + preselected = 0; + } + #endif // PRESELECT_PATCH + if (XFilterEvent(&ev, win)) + continue; + switch(ev.type) { + #if MOUSE_SUPPORT_PATCH + case ButtonPress: + buttonpress(&ev); + break; + #endif // MOUSE_SUPPORT_PATCH + 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; + #if RELATIVE_INPUT_WIDTH_PATCH + unsigned int tmp, minstrlen = 0, curstrlen = 0; + int numwidthchecks = 100; + struct item *item; + #endif // RELATIVE_INPUT_WIDTH_PATCH + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"dmenu", "dmenu"}; +#ifdef XINERAMA + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; +#endif + /* init appearance */ + #if XRESOURCES_PATCH + for (j = 0; j < SchemeLast; j++) + #if ALPHA_PATCH + scheme[j] = drw_scm_create(drw, (const char**)colors[j], alphas[j], 2); + #else + scheme[j] = drw_scm_create(drw, (const char**)colors[j], 2); + #endif // ALPHA_PATCH + #else + for (j = 0; j < SchemeLast; j++) + #if ALPHA_PATCH + scheme[j] = drw_scm_create(drw, colors[j], alphas[j], 2); + #else + scheme[j] = drw_scm_create(drw, colors[j], 2); + #endif // ALPHA_PATCH + #endif // XRESOURCES_PATCH + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + #if WMTYPE_PATCH + type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + #endif // WMTYPE_PATCH + + /* calculate menu geometry */ + #if PANGO_PATCH + bh = drw->font->h + 2; + #else + bh = drw->fonts->h + 2; + #endif // PANGO_PATCH + #if LINE_HEIGHT_PATCH + bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */ + #endif // LINE_HEIGHT_PATCH + lines = MAX(lines, 0); + mh = (lines + 1) * bh; + #if CENTER_PATCH && PANGO_PATCH + promptw = (prompt && *prompt) ? TEXTWM(prompt) - lrpad / 4 : 0; + #elif CENTER_PATCH + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + #endif // CENTER_PATCH +#ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + 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; + + #if CENTER_PATCH + if (center) { + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); + x = info[i].x_org + ((info[i].width - mw) / 2); + y = info[i].y_org + ((info[i].height - mh) / 2); + } else { + #if XYW_PATCH + x = info[i].x_org + dmx; + y = info[i].y_org + (topbar ? dmy : info[i].height - mh - dmy); + mw = (dmw>0 ? dmw : info[i].width); + #else + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + #endif // XYW_PATCH + } + #elif XYW_PATCH + x = info[i].x_org + dmx; + y = info[i].y_org + (topbar ? dmy : info[i].height - mh - dmy); + mw = (dmw>0 ? dmw : info[i].width); + #else + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + #endif // CENTER_PATCH / XYW_PATCH + XFree(info); + } else +#endif + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + #if CENTER_PATCH + if (center) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + #if XYW_PATCH + x = dmx; + y = topbar ? dmy : wa.height - mh - dmy; + mw = (dmw>0 ? dmw : wa.width); + #else + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + #endif // XYW_PATCH + } + #elif XYW_PATCH + x = dmx; + y = topbar ? dmy : wa.height - mh - dmy; + mw = (dmw>0 ? dmw : wa.width); + #else + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + #endif // CENTER_PATCH / XYW_PATCH + } + #if !CENTER_PATCH + #if PANGO_PATCH + promptw = (prompt && *prompt) ? TEXTWM(prompt) - lrpad / 4 : 0; + #else + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + #endif // PANGO_PATCH + #endif // CENTER_PATCH + #if RELATIVE_INPUT_WIDTH_PATCH + for (item = items; !lines && item && item->text; ++item) { + curstrlen = strlen(item->text); + if (numwidthchecks || minstrlen < curstrlen) { + numwidthchecks = MAX(numwidthchecks - 1, 0); + minstrlen = MAX(minstrlen, curstrlen); + if ((tmp = textw_clamp(item->text, mw/3)) > inputw) { + inputw = tmp; + if (tmp == mw/3) + break; + } + } + } + #else + inputw = mw / 3; /* input width: ~33.33% of monitor width */ + #endif // RELATIVE_INPUT_WIDTH_PATCH + match(); + + /* create menu window */ + #if MANAGED_PATCH + swa.override_redirect = managed ? False : True; + #else + swa.override_redirect = True; + #endif // MANAGED_PATCH + #if ALPHA_PATCH + swa.background_pixel = 0; + swa.colormap = cmap; + #else + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + #endif // ALPHA_PATCH + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask + #if MOUSE_SUPPORT_PATCH + | ButtonPressMask + #endif // MOUSE_SUPPORT_PATCH + ; + win = XCreateWindow( + dpy, root, + #if BARPADDING_PATCH && BORDER_PATCH + x + sp, y + vp - (topbar ? 0 : border_width * 2), mw - 2 * sp - border_width * 2, mh, border_width, + #elif BARPADDING_PATCH + x + sp, y + vp, mw - 2 * sp, mh, 0, + #elif BORDER_PATCH + x, y - (topbar ? 0 : border_width * 2), mw - border_width * 2, mh, border_width, + #else + x, y, mw, mh, 0, + #endif // BORDER_PATCH | BARPADDING_PATCH + #if ALPHA_PATCH + depth, InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &swa + #else + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa + #endif // ALPHA_PATCH + ); + #if BORDER_PATCH + if (border_width) + XSetWindowBorder(dpy, win, scheme[SchemeBorder][ColBg].pixel); + #endif // BORDER_PATCH + XSetClassHint(dpy, win, &ch); + #if WMTYPE_PATCH + XChangeProperty(dpy, win, type, XA_ATOM, 32, PropModeReplace, + (unsigned char *) &dock, 1); + #endif // WMTYPE_PATCH + + + /* input methods */ + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed: could not open input device"); + + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + #if MANAGED_PATCH + if (managed) { + XTextProperty prop; + char *windowtitle = prompt != NULL ? prompt : "dmenu"; + Xutf8TextListToTextProperty(dpy, &windowtitle, 1, XUTF8StringStyle, &prop); + XSetWMName(dpy, win, &prop); + XSetTextProperty(dpy, win, &prop, XInternAtom(dpy, "_NET_WM_NAME", False)); + XFree(prop.value); + } + #endif // MANAGED_PATCH + + XMapRaised(dpy, win); + if (embed) { + XReparentWindow(dpy, win, parentwin, x, y); + 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) +{ + die("usage: dmenu [-bv" + #if CENTER_PATCH + "c" + #endif + #if !NON_BLOCKING_STDIN_PATCH + "f" + #endif // NON_BLOCKING_STDIN_PATCH + #if INCREMENTAL_PATCH + "r" + #endif // INCREMENTAL_PATCH + #if CASEINSENSITIVE_PATCH + "s" + #else + "i" + #endif // CASEINSENSITIVE_PATCH + #if INSTANT_PATCH + "n" + #endif // INSTANT_PATCH + #if PRINTINPUTTEXT_PATCH + "t" + #endif // PRINTINPUTTEXT_PATCH + #if PREFIXCOMPLETION_PATCH + "x" + #endif // PREFIXCOMPLETION_PATCH + #if FUZZYMATCH_PATCH + "F" + #endif // FUZZYMATCH_PATCH + #if PASSWORD_PATCH + "P" + #endif // PASSWORD_PATCH + #if NO_SORT_PATCH + "S" + #endif // NO_SORT_PATCH + #if REJECTNOMATCH_PATCH + "R" // (changed from r to R due to conflict with INCREMENTAL_PATCH) + #endif // REJECTNOMATCH_PATCH + #if RESTRICT_RETURN_PATCH + "1" + #endif // RESTRICT_RETURN_PATCH + "] " + #if CARET_WIDTH_PATCH + "[-cw caret_width] " + #endif // CARET_WIDTH_PATCH + #if MANAGED_PATCH + "[-wm] " + #endif // MANAGED_PATCH + #if GRID_PATCH + "[-g columns] " + #endif // GRID_PATCH + "[-l lines] [-p prompt] [-fn font] [-m monitor]" + "\n [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]" + #if DYNAMIC_OPTIONS_PATCH || FZFEXPECT_PATCH || ALPHA_PATCH || BORDER_PATCH || HIGHPRIORITY_PATCH + "\n " + #endif + #if DYNAMIC_OPTIONS_PATCH + " [-dy command]" + #endif // DYNAMIC_OPTIONS_PATCH + #if FZFEXPECT_PATCH + " [-ex expectkey]" + #endif // FZFEXPECT_PATCH + #if ALPHA_PATCH + " [-o opacity]" + #endif // ALPHA_PATCH + #if BORDER_PATCH + " [-bw width]" + #endif // BORDER_PATCH + #if HIGHPRIORITY_PATCH + " [-hb color] [-hf color] [-hp items]" + #endif // HIGHPRIORITY_PATCH + #if INITIALTEXT_PATCH || LINE_HEIGHT_PATCH || PRESELECT_PATCH || NAVHISTORY_PATCH || XYW_PATCH + "\n " + #endif + #if INITIALTEXT_PATCH + " [-it text]" + #endif // INITIALTEXT_PATCH + #if LINE_HEIGHT_PATCH + " [-h height]" + #endif // LINE_HEIGHT_PATCH + #if PRESELECT_PATCH + " [-ps index]" + #endif // PRESELECT_PATCH + #if NAVHISTORY_PATCH + " [-H histfile]" + #endif // NAVHISTORY_PATCH + #if XYW_PATCH + " [-X xoffset] [-Y yoffset] [-W width]" // (arguments made upper case due to conflicts) + #endif // XYW_PATCH + #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + "\n [-nhb color] [-nhf color] [-shb color] [-shf color]" // highlight colors + #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #if SEPARATOR_PATCH + "\n [-d separator] [-D separator]" + #endif // SEPARATOR_PATCH + "\n"); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i; + #if !NON_BLOCKING_STDIN_PATCH + int fast = 0; + #endif // NON_BLOCKING_STDIN_PATCH + + #if XRESOURCES_PATCH + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(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); + + #if ALPHA_PATCH + /* These need to be checked before we init the visuals. */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-o")) { /* opacity, pass -o 0 to disable alpha */ + opacity = atoi(argv[++i]); + } else { + continue; + } + argv[i][0] = '\0'; // mark as used + } + xinitvisual(); + drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); + #else + drw = drw_create(dpy, screen, root, wa.width, wa.height); + #endif // ALPHA_PATCH + readxresources(); + #endif // XRESOURCES_PATCH + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '\0') + continue; + + /* 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; + #if CENTER_PATCH + } else if (!strcmp(argv[i], "-c")) { /* toggles centering of dmenu window on screen */ + center = !center; + #endif // CENTER_PATCH + #if !NON_BLOCKING_STDIN_PATCH + } else if (!strcmp(argv[i], "-f")) { /* grabs keyboard before reading stdin */ + fast = 1; + #endif // NON_BLOCKING_STDIN_PATCH + #if INCREMENTAL_PATCH + } else if (!strcmp(argv[i], "-r")) { /* incremental */ + incremental = !incremental; + #endif // INCREMENTAL_PATCH + #if CASEINSENSITIVE_PATCH + } else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */ + fstrncmp = strncmp; + fstrstr = strstr; + #else + } else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + #endif // CASEINSENSITIVE_PATCH + #if MANAGED_PATCH + } else if (!strcmp(argv[i], "-wm")) { /* display as managed wm window */ + managed = 1; + #endif // MANAGED_PATCH + #if INSTANT_PATCH + } else if (!strcmp(argv[i], "-n")) { /* instant select only match */ + instant = !instant; + #endif // INSTANT_PATCH + #if PRINTINPUTTEXT_PATCH + } else if (!strcmp(argv[i], "-t")) { /* favors text input over selection */ + use_text_input = 1; + #endif // PRINTINPUTTEXT_PATCH + #if PREFIXCOMPLETION_PATCH + } else if (!strcmp(argv[i], "-x")) { /* invert use_prefix */ + use_prefix = !use_prefix; + #endif // PREFIXCOMPLETION_PATCH + #if FUZZYMATCH_PATCH + } else if (!strcmp(argv[i], "-F")) { /* disable/enable fuzzy matching, depends on default */ + fuzzy = !fuzzy; + #endif // FUZZYMATCH_PATCH + #if PASSWORD_PATCH + } else if (!strcmp(argv[i], "-P")) { /* is the input a password */ + passwd = 1; + #endif // PASSWORD_PATCH + #if FZFEXPECT_PATCH + } else if (!strcmp(argv[i], "-ex")) { /* expect key */ + expected = argv[++i]; + #endif // FZFEXPECT_PATCH + #if REJECTNOMATCH_PATCH + } else if (!strcmp(argv[i], "-R")) { /* reject input which results in no match */ + reject_no_match = 1; + #endif // REJECTNOMATCH_PATCH + #if NO_SORT_PATCH + } else if (!strcmp(argv[i], "-S")) { /* do not sort matches */ + sortmatches = 0; + #endif // NO_SORT_PATCH + #if PRINTINDEX_PATCH + } else if (!strcmp(argv[i], "-ix")) { /* adds ability to return index in list */ + print_index = 1; + #endif // PRINTINDEX_PATCH + #if RESTRICT_RETURN_PATCH + } else if (!strcmp(argv[i], "-1")) { + restrict_return = 1; + #endif // RESTRICT_RETURN_PATCH + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ + #if NAVHISTORY_PATCH + else if (!strcmp(argv[i], "-H")) + histfile = argv[++i]; + #endif // NAVHISTORY_PATCH + #if GRID_PATCH + else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */ + columns = atoi(argv[++i]); + if (columns && lines == 0) + lines = 1; + } + #endif // GRID_PATCH + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + #if XYW_PATCH + else if (!strcmp(argv[i], "-X")) /* window x offset */ + dmx = atoi(argv[++i]); + else if (!strcmp(argv[i], "-Y")) /* window y offset (from bottom up if -b) */ + dmy = atoi(argv[++i]); + else if (!strcmp(argv[i], "-W")) /* make dmenu this wide */ + dmw = atoi(argv[++i]); + #endif // XYW_PATCH + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + #if ALPHA_PATCH + else if (!strcmp(argv[i], "-o")) /* opacity, pass -o 0 to disable alpha */ + opacity = atoi(argv[++i]); + #endif // ALPHA_PATCH + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + #if PANGO_PATCH + strcpy(font, argv[++i]); + #else + fonts[0] = argv[++i]; + #endif // PANGO_PATCH + #if LINE_HEIGHT_PATCH + else if(!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ + lineheight = atoi(argv[++i]); + lineheight = MAX(lineheight, min_lineheight); /* reasonable default in case of value too small/negative */ + } + #endif // LINE_HEIGHT_PATCH + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + 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]; + #if HIGHPRIORITY_PATCH + else if (!strcmp(argv[i], "-hb")) /* high priority background color */ + colors[SchemeHp][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-hf")) /* low priority background color */ + colors[SchemeHp][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-hp")) + hpitems = tokenize(argv[++i], ",", &hplength); + #endif // HIGHPRIORITY_PATCH + #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ + colors[SchemeNormHighlight][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ + colors[SchemeNormHighlight][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ + colors[SchemeSelHighlight][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ + colors[SchemeSelHighlight][ColFg] = argv[++i]; + #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #if CARET_WIDTH_PATCH + else if (!strcmp(argv[i], "-cw")) /* sets caret witdth */ + caret_width = atoi(argv[++i]); + #endif // CARET_WIDTH_PATCH + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + #if SEPARATOR_PATCH + else if (!strcmp(argv[i], "-d") || /* field separator */ + (separator_greedy = !strcmp(argv[i], "-D"))) { + separator = argv[++i][0]; + separator_reverse = argv[i][1] == '|'; + } + #endif // SEPARATOR_PATCH + #if PRESELECT_PATCH + else if (!strcmp(argv[i], "-ps")) /* preselected item */ + preselected = atoi(argv[++i]); + #endif // PRESELECT_PATCH + #if DYNAMIC_OPTIONS_PATCH + else if (!strcmp(argv[i], "-dy")) /* dynamic command to run */ + dynamic = argv[++i]; + #endif // DYNAMIC_OPTIONS_PATCH + #if BORDER_PATCH + else if (!strcmp(argv[i], "-bw")) /* border width around dmenu */ + border_width = atoi(argv[++i]); + #endif // BORDER_PATCH + #if INITIALTEXT_PATCH + else if (!strcmp(argv[i], "-it")) { /* adds initial text */ + const char * text = argv[++i]; + insert(text, strlen(text)); + } + #endif // INITIALTEXT_PATCH + else { + usage(); + } + } + + #if XRESOURCES_PATCH + #if PANGO_PATCH + if (!drw_font_create(drw, font)) + die("no fonts could be loaded."); + #else + if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + #endif // PANGO_PATCH + #else // !XRESOURCES_PATCH + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(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); + + #if ALPHA_PATCH + xinitvisual(); + drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); + #else + drw = drw_create(dpy, screen, root, wa.width, wa.height); + #endif // ALPHA_PATCH + + #if PANGO_PATCH + if (!drw_font_create(drw, font)) + die("no fonts could be loaded."); + #else + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + #endif // PANGO_PATCH + #endif // XRESOURCES_PATCH + + #if PANGO_PATCH + lrpad = drw->font->h; + #else + lrpad = drw->fonts->h; + #endif // PANGO_PATCH + + #if BARPADDING_PATCH + sp = sidepad; + vp = (topbar ? vertpad : - vertpad); + #endif // BARPADDING_PATCH + + #if LINE_HEIGHT_PATCH + if (lineheight == -1) + #if PANGO_PATCH + lineheight = drw->font->h * 2.5; + #else + lineheight = drw->fonts->h * 2.5; + #endif // PANGO_PATCH + #endif // LINE_HEIGHT_PATCH + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", NULL) == -1) + die("pledge"); +#endif + #if NAVHISTORY_PATCH + loadhistory(); + #endif // NAVHISTORY_PATCH + + #if NON_BLOCKING_STDIN_PATCH + grabkeyboard(); + #else + if (fast && !isatty(0)) { + grabkeyboard(); + #if DYNAMIC_OPTIONS_PATCH + if (!(dynamic && *dynamic)) + readstdin(); + #else + readstdin(); + #endif // DYNAMIC_OPTIONS_PATCH + } else { + #if DYNAMIC_OPTIONS_PATCH + if (!(dynamic && *dynamic)) + readstdin(); + #else + readstdin(); + #endif // DYNAMIC_OPTIONS_PATCH + grabkeyboard(); + } + #endif // NON_BLOCKING_STDIN_PATCH + setup(); + run(); + + return 1; /* unreachable */ +} diff --git a/dmenu/dmenu_path b/dmenu-flexipatch/dmenu_path similarity index 100% rename from dmenu/dmenu_path rename to dmenu-flexipatch/dmenu_path diff --git a/dmenu-flexipatch/dmenu_run b/dmenu-flexipatch/dmenu_run new file mode 100755 index 0000000..e6d1355 --- /dev/null +++ b/dmenu-flexipatch/dmenu_run @@ -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"} & \ No newline at end of file diff --git a/dmenu/drw.c b/dmenu-flexipatch/drw.c similarity index 54% rename from dmenu/drw.c rename to dmenu-flexipatch/drw.c index fe3aadd..62096d1 100644 --- a/dmenu/drw.c +++ b/dmenu-flexipatch/drw.c @@ -5,9 +5,11 @@ #include #include +#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 diff --git a/dmenu/drw.h b/dmenu-flexipatch/drw.h similarity index 57% rename from dmenu/drw.h rename to dmenu-flexipatch/drw.h index 4f66f0d..af4af4d 100644 --- a/dmenu/drw.h +++ b/dmenu-flexipatch/drw.h @@ -1,5 +1,10 @@ /* See LICENSE file for copyright and license details. */ +#if PANGO_PATCH +#include +#include +#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 \ No newline at end of file diff --git a/dmenu-flexipatch/patch/center.c b/dmenu-flexipatch/patch/center.c new file mode 100644 index 0000000..9682bfb --- /dev/null +++ b/dmenu-flexipatch/patch/center.c @@ -0,0 +1,8 @@ +static int +max_textw(void) +{ + int len = 0; + for (struct item *item = items; item && item->text; item++) + len = MAX(TEXTW(item->text), len); + return len; +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/dynamicoptions.c b/dmenu-flexipatch/patch/dynamicoptions.c new file mode 100644 index 0000000..0102cfb --- /dev/null +++ b/dmenu-flexipatch/patch/dynamicoptions.c @@ -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; + } +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/dynamicoptions.h b/dmenu-flexipatch/patch/dynamicoptions.h new file mode 100644 index 0000000..eba9fb0 --- /dev/null +++ b/dmenu-flexipatch/patch/dynamicoptions.h @@ -0,0 +1,2 @@ +static void refreshoptions(); +static void readstream(FILE* stream); \ No newline at end of file diff --git a/dmenu-flexipatch/patch/fuzzyhighlight.c b/dmenu-flexipatch/patch/fuzzyhighlight.c new file mode 100644 index 0000000..d31a150 --- /dev/null +++ b/dmenu-flexipatch/patch/fuzzyhighlight.c @@ -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++; + } +} diff --git a/dmenu-flexipatch/patch/fuzzymatch.c b/dmenu-flexipatch/patch/fuzzymatch.c new file mode 100644 index 0000000..9407afe --- /dev/null +++ b/dmenu-flexipatch/patch/fuzzymatch.c @@ -0,0 +1,115 @@ +#include + +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(); +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/fzfexpect.c b/dmenu-flexipatch/patch/fzfexpect.c new file mode 100644 index 0000000..1563362 --- /dev/null +++ b/dmenu-flexipatch/patch/fzfexpect.c @@ -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 \ No newline at end of file diff --git a/dmenu-flexipatch/patch/fzfexpect.h b/dmenu-flexipatch/patch/fzfexpect.h new file mode 100644 index 0000000..95c1dad --- /dev/null +++ b/dmenu-flexipatch/patch/fzfexpect.h @@ -0,0 +1 @@ +static void expect(char *expect, XKeyEvent *ev); diff --git a/dmenu-flexipatch/patch/highlight.c b/dmenu-flexipatch/patch/highlight.c new file mode 100644 index 0000000..d2d55d0 --- /dev/null +++ b/dmenu-flexipatch/patch/highlight.c @@ -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); + } + } +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/highpriority.c b/dmenu-flexipatch/patch/highpriority.c new file mode 100644 index 0000000..2b3577a --- /dev/null +++ b/dmenu-flexipatch/patch/highpriority.c @@ -0,0 +1,35 @@ +static char **hpitems = NULL; +static int hplength = 0; + +static char ** +tokenize(char *source, const char *delim, int *llen) +{ + int listlength = 0, list_size = 0; + char **list = NULL, *token; + + token = strtok(source, delim); + while (token) { + if (listlength + 1 >= list_size) { + if (!(list = realloc(list, (list_size += 8) * sizeof(*list)))) + die("Unable to realloc %zu bytes\n", list_size * sizeof(*list)); + } + if (!(list[listlength] = strdup(token))) + die("Unable to strdup %zu bytes\n", strlen(token) + 1); + token = strtok(NULL, delim); + listlength++; + } + + *llen = listlength; + return list; +} + +static int +arrayhas(char **list, int length, char *item) { + for (int i = 0; i < length; i++) { + int len1 = strlen(list[i]); + int len2 = strlen(item); + if (fstrncmp(list[i], item, len1 > len2 ? len2 : len1) == 0) + return 1; + } + return 0; +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/highpriority.h b/dmenu-flexipatch/patch/highpriority.h new file mode 100644 index 0000000..8fa65bf --- /dev/null +++ b/dmenu-flexipatch/patch/highpriority.h @@ -0,0 +1,2 @@ +static int arrayhas(char **list, int length, char *item); + diff --git a/dmenu-flexipatch/patch/include.c b/dmenu-flexipatch/patch/include.c new file mode 100644 index 0000000..4240212 --- /dev/null +++ b/dmenu-flexipatch/patch/include.c @@ -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 \ No newline at end of file diff --git a/dmenu-flexipatch/patch/include.h b/dmenu-flexipatch/patch/include.h new file mode 100644 index 0000000..c3cb75b --- /dev/null +++ b/dmenu-flexipatch/patch/include.h @@ -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 diff --git a/dmenu-flexipatch/patch/mousesupport.c b/dmenu-flexipatch/patch/mousesupport.c new file mode 100644 index 0000000..d040e86 --- /dev/null +++ b/dmenu-flexipatch/patch/mousesupport.c @@ -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; + } + } +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/multiselect.c b/dmenu-flexipatch/patch/multiselect.c new file mode 100644 index 0000000..ad78db3 --- /dev/null +++ b/dmenu-flexipatch/patch/multiselect.c @@ -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; + } +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/multiselect.h b/dmenu-flexipatch/patch/multiselect.h new file mode 100644 index 0000000..03aa328 --- /dev/null +++ b/dmenu-flexipatch/patch/multiselect.h @@ -0,0 +1 @@ +static int issel(size_t id); diff --git a/dmenu-flexipatch/patch/navhistory.c b/dmenu-flexipatch/patch/navhistory.c new file mode 100644 index 0000000..7d10ee4 --- /dev/null +++ b/dmenu-flexipatch/patch/navhistory.c @@ -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); +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/nonblockingstdin.c b/dmenu-flexipatch/patch/nonblockingstdin.c new file mode 100644 index 0000000..eb7013f --- /dev/null +++ b/dmenu-flexipatch/patch/nonblockingstdin.c @@ -0,0 +1,68 @@ +#include +#include +#include + +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(); + } +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/nonblockingstdin.h b/dmenu-flexipatch/patch/nonblockingstdin.h new file mode 100644 index 0000000..a0c4dfe --- /dev/null +++ b/dmenu-flexipatch/patch/nonblockingstdin.h @@ -0,0 +1 @@ +static void readevent(); \ No newline at end of file diff --git a/dmenu-flexipatch/patch/numbers.c b/dmenu-flexipatch/patch/numbers.c new file mode 100644 index 0000000..9f9557a --- /dev/null +++ b/dmenu-flexipatch/patch/numbers.c @@ -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); +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/numbers.h b/dmenu-flexipatch/patch/numbers.h new file mode 100644 index 0000000..34d3dbc --- /dev/null +++ b/dmenu-flexipatch/patch/numbers.h @@ -0,0 +1,4 @@ +#define NUMBERSMAXDIGITS 100 +#define NUMBERSBUFSIZE (NUMBERSMAXDIGITS * 2) + 1 + +static void recalculatenumbers(); \ No newline at end of file diff --git a/dmenu-flexipatch/patch/scroll.c b/dmenu-flexipatch/patch/scroll.c new file mode 100644 index 0000000..9021edf --- /dev/null +++ b/dmenu-flexipatch/patch/scroll.c @@ -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; +} \ No newline at end of file diff --git a/dmenu-flexipatch/patch/scroll.h b/dmenu-flexipatch/patch/scroll.h new file mode 100644 index 0000000..927df9a --- /dev/null +++ b/dmenu-flexipatch/patch/scroll.h @@ -0,0 +1,3 @@ +enum { AlignL, AlignR }; + +int drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align); \ No newline at end of file diff --git a/dmenu-flexipatch/patch/xresources.c b/dmenu-flexipatch/patch/xresources.c new file mode 100644 index 0000000..cdb1631 --- /dev/null +++ b/dmenu-flexipatch/patch/xresources.c @@ -0,0 +1,90 @@ +#include + +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); + } +} \ No newline at end of file diff --git a/dmenu-flexipatch/patches.def.h b/dmenu-flexipatch/patches.def.h new file mode 100644 index 0000000..3c1651e --- /dev/null +++ b/dmenu-flexipatch/patches.def.h @@ -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 diff --git a/dmenu/stest.1 b/dmenu-flexipatch/stest.1 similarity index 100% rename from dmenu/stest.1 rename to dmenu-flexipatch/stest.1 diff --git a/dmenu/stest.c b/dmenu-flexipatch/stest.c similarity index 100% rename from dmenu/stest.c rename to dmenu-flexipatch/stest.c diff --git a/dmenu/util.c b/dmenu-flexipatch/util.c similarity index 94% rename from dmenu/util.c rename to dmenu-flexipatch/util.c index fe044fc..96b82c9 100644 --- a/dmenu/util.c +++ b/dmenu-flexipatch/util.c @@ -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; +} diff --git a/dmenu/util.h b/dmenu-flexipatch/util.h similarity index 88% rename from dmenu/util.h rename to dmenu-flexipatch/util.h index f633b51..531ab25 100644 --- a/dmenu/util.h +++ b/dmenu-flexipatch/util.h @@ -1,7 +1,11 @@ /* See LICENSE file for copyright and license details. */ +#ifndef MAX #define MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif +#ifndef MIN #define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) void die(const char *fmt, ...); diff --git a/dmenu/config.def.h b/dmenu/config.def.h deleted file mode 100644 index 4790187..0000000 --- a/dmenu/config.def.h +++ /dev/null @@ -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[] = " "; diff --git a/dmenu/dmenu.c b/dmenu/dmenu.c deleted file mode 100644 index 59f7076..0000000 --- a/dmenu/dmenu.c +++ /dev/null @@ -1,847 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#ifdef XINERAMA -#include -#endif -#include - -#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 */ -} diff --git a/dmenu/dmenu_run b/dmenu/dmenu_run deleted file mode 100755 index 834ede5..0000000 --- a/dmenu/dmenu_run +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & diff --git a/dmenu/patches/dmenu-alpha.diff b/dmenu/patches/dmenu-alpha.diff deleted file mode 100644 index ad32fc8..0000000 --- a/dmenu/patches/dmenu-alpha.diff +++ /dev/null @@ -1,281 +0,0 @@ -From 5e232dc79ba3c0b791f3d9ddfab2fcecca19f18d Mon Sep 17 00:00:00 2001 -From: bakkeby -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 diff --git a/dmenu/patches/dmenu-caseinsensitive-20200523-db6093f.diff b/dmenu/patches/dmenu-caseinsensitive-20200523-db6093f.diff deleted file mode 100644 index 939a2e8..0000000 --- a/dmenu/patches/dmenu-caseinsensitive-20200523-db6093f.diff +++ /dev/null @@ -1,42 +0,0 @@ -From 54acbdf72083a5eae5783eed42e162424ab2cec2 Mon Sep 17 00:00:00 2001 -From: Kim Torgersen -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 - diff --git a/dmenu/patches/dmenu-grid-4.9.diff b/dmenu/patches/dmenu-grid-4.9.diff deleted file mode 100644 index c27689b..0000000 --- a/dmenu/patches/dmenu-grid-4.9.diff +++ /dev/null @@ -1,107 +0,0 @@ -From 39ab9676914bd0d8105d0f96bbd7611a53077438 Mon Sep 17 00:00:00 2001 -From: Miles Alan -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 - diff --git a/dmenu/patches/dmenu-morecolor-20190922-4bf895b.diff b/dmenu/patches/dmenu-morecolor-20190922-4bf895b.diff deleted file mode 100644 index 539e9c1..0000000 --- a/dmenu/patches/dmenu-morecolor-20190922-4bf895b.diff +++ /dev/null @@ -1,47 +0,0 @@ -From 4bf895be219ae00394a5cde901dc43ec6dcb3759 Mon Sep 17 00:00:00 2001 -From: Tanner Babcock -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 - diff --git a/dmenu/patches/dmenu-mousesupport.diff b/dmenu/patches/dmenu-mousesupport.diff deleted file mode 100644 index 49824ba..0000000 --- a/dmenu/patches/dmenu-mousesupport.diff +++ /dev/null @@ -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); diff --git a/dmenu/patches/dmenu-scroll.diff b/dmenu/patches/dmenu-scroll.diff deleted file mode 100644 index 7a7386a..0000000 --- a/dmenu/patches/dmenu-scroll.diff +++ /dev/null @@ -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); diff --git a/dmenu/scripts/dfmpeg b/dmenu/scripts/dfmpeg deleted file mode 100755 index 96a73b7..0000000 --- a/dmenu/scripts/dfmpeg +++ /dev/null @@ -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. diff --git a/dwm-flexipatch/.github/FUNDING.yml b/dwm-flexipatch/.github/FUNDING.yml deleted file mode 100644 index 870c946..0000000 --- a/dwm-flexipatch/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -custom: ["https://suckless.org/donations/", "https://paypal.me/dwmflexipatch"] diff --git a/dwm-flexipatch/config.def.h b/dwm-flexipatch/config.def.h index fc5cb07..d40ac22 100644 --- a/dwm-flexipatch/config.def.h +++ b/dwm-flexipatch/config.def.h @@ -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]} }, diff --git a/dwm-flexipatch/config.mk b/dwm-flexipatch/config.mk index 4fe92fc..f782f5d 100644 --- a/dwm-flexipatch/config.mk +++ b/dwm-flexipatch/config.mk @@ -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 diff --git a/dwm-flexipatch/dwm.desktop b/dwm-flexipatch/dwm.desktop index b0c3354..0249693 100644 --- a/dwm-flexipatch/dwm.desktop +++ b/dwm-flexipatch/dwm.desktop @@ -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 diff --git a/dwm-flexipatch/patch/xrdb.c b/dwm-flexipatch/patch/xrdb.c index 435da73..4eef92d 100644 --- a/dwm-flexipatch/patch/xrdb.c +++ b/dwm-flexipatch/patch/xrdb.c @@ -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); diff --git a/dwm-flexipatch/patches.def.h b/dwm-flexipatch/patches.def.h index 85065d5..065759b 100644 --- a/dwm-flexipatch/patches.def.h +++ b/dwm-flexipatch/patches.def.h @@ -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 diff --git a/dwm-flexipatch/startdwm b/dwm-flexipatch/startdwm deleted file mode 100755 index 1af269b..0000000 --- a/dwm-flexipatch/startdwm +++ /dev/null @@ -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 diff --git a/dwmblocks-async/.clang-format b/dwmblocks-async/.clang-format new file mode 100644 index 0000000..d9be3e2 --- /dev/null +++ b/dwmblocks-async/.clang-format @@ -0,0 +1,6 @@ +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 79 +AlignArrayOfStructures: Left +AlignConsecutiveMacros: true +AllowShortFunctionsOnASingleLine: None diff --git a/dwmblocks-async/.clangd b/dwmblocks-async/.clangd new file mode 100644 index 0000000..6c32dd2 --- /dev/null +++ b/dwmblocks-async/.clangd @@ -0,0 +1,6 @@ +CompileFlags: + Add: + - "-I." + - "-I./inc" + - "-I.." + - "-I../inc" diff --git a/dwmblocks-async/.gitignore b/dwmblocks-async/.gitignore new file mode 100644 index 0000000..9056e50 --- /dev/null +++ b/dwmblocks-async/.gitignore @@ -0,0 +1,3 @@ +build/ +.cache/ +dwmblocks diff --git a/dwmblocks-async/LICENSE b/dwmblocks-async/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/dwmblocks-async/LICENSE @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/dwmblocks-async/Makefile b/dwmblocks-async/Makefile new file mode 100644 index 0000000..0d8d60e --- /dev/null +++ b/dwmblocks-async/Makefile @@ -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 diff --git a/dwmblocks-async/README.md b/dwmblocks-async/README.md new file mode 100644 index 0000000..2006457 --- /dev/null +++ b/dwmblocks-async/README.md @@ -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/). diff --git a/dwmblocks-async/config.c b/dwmblocks-async/config.c new file mode 100644 index 0000000..bf0da3f --- /dev/null +++ b/dwmblocks-async/config.c @@ -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); diff --git a/dwmblocks-async/config.h b/dwmblocks-async/config.h new file mode 100644 index 0000000..235ee71 --- /dev/null +++ b/dwmblocks-async/config.h @@ -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 diff --git a/dwmblocks-async/inc/bar.h b/dwmblocks-async/inc/bar.h new file mode 100644 index 0000000..377886e --- /dev/null +++ b/dwmblocks-async/inc/bar.h @@ -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 *); diff --git a/dwmblocks-async/inc/block.h b/dwmblocks-async/inc/block.h new file mode 100644 index 0000000..f0d264c --- /dev/null +++ b/dwmblocks-async/inc/block.h @@ -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 *); diff --git a/dwmblocks-async/inc/util.h b/dwmblocks-async/inc/util.h new file mode 100644 index 0000000..bfc030d --- /dev/null +++ b/dwmblocks-async/inc/util.h @@ -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); diff --git a/dwmblocks-async/inc/x11.h b/dwmblocks-async/inc/x11.h new file mode 100644 index 0000000..abb4f0f --- /dev/null +++ b/dwmblocks-async/inc/x11.h @@ -0,0 +1,5 @@ +#pragma once + +int setupX(); +int closeX(); +void setXRootName(char *); diff --git a/dwmblocks-async/preview.png b/dwmblocks-async/preview.png new file mode 100644 index 0000000..5c00274 Binary files /dev/null and b/dwmblocks-async/preview.png differ diff --git a/dwmblocks-async/src/bar.c b/dwmblocks-async/src/bar.c new file mode 100644 index 0000000..9f8261c --- /dev/null +++ b/dwmblocks-async/src/bar.c @@ -0,0 +1,62 @@ +#include "bar.h" + +#include +#include +#include + +#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); +} diff --git a/dwmblocks-async/src/block.c b/dwmblocks-async/src/block.c new file mode 100644 index 0000000..ae50ea5 --- /dev/null +++ b/dwmblocks-async/src/block.c @@ -0,0 +1,72 @@ +#include "block.h" + +#define _GNU_SOURCE +#include +#include +#include +#include + +#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)); +} diff --git a/dwmblocks-async/src/main.c b/dwmblocks-async/src/main.c new file mode 100644 index 0000000..8b87b63 --- /dev/null +++ b/dwmblocks-async/src/main.c @@ -0,0 +1,157 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#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; +} diff --git a/dwmblocks-async/src/util.c b/dwmblocks-async/src/util.c new file mode 100644 index 0000000..157e626 --- /dev/null +++ b/dwmblocks-async/src/util.c @@ -0,0 +1,41 @@ +#include "util.h" + +#include + +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'; +} diff --git a/dwmblocks-async/src/x11.c b/dwmblocks-async/src/x11.c new file mode 100644 index 0000000..63c5d14 --- /dev/null +++ b/dwmblocks-async/src/x11.c @@ -0,0 +1,25 @@ +#include "x11.h" + +#include + +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); +} diff --git a/pics/neofetch.png b/pics/neofetch.png deleted file mode 100644 index 35cae46..0000000 Binary files a/pics/neofetch.png and /dev/null differ diff --git a/pics/random.png b/pics/random.png deleted file mode 100644 index 9065273..0000000 Binary files a/pics/random.png and /dev/null differ diff --git a/dmenu/scripts/dboard b/scripts/dmenu/dboard similarity index 100% rename from dmenu/scripts/dboard rename to scripts/dmenu/dboard diff --git a/dmenu/scripts/dcalc b/scripts/dmenu/dcalc similarity index 100% rename from dmenu/scripts/dcalc rename to scripts/dmenu/dcalc diff --git a/dmenu/scripts/shutdown.sh b/scripts/dmenu/shutdown.sh similarity index 100% rename from dmenu/scripts/shutdown.sh rename to scripts/dmenu/shutdown.sh diff --git a/scripts/dwmblocks/musicup b/scripts/dwmblocks/musicup new file mode 100755 index 0000000..55e9585 --- /dev/null +++ b/scripts/dwmblocks/musicup @@ -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 diff --git a/scripts/dwmblocks/sb-battery b/scripts/dwmblocks/sb-battery new file mode 100755 index 0000000..bf4a07b --- /dev/null +++ b/scripts/dwmblocks/sb-battery @@ -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 diff --git a/scripts/dwmblocks/sb-date b/scripts/dwmblocks/sb-date new file mode 100755 index 0000000..0d4aca5 --- /dev/null +++ b/scripts/dwmblocks/sb-date @@ -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||;s|..0m||")" + appointments="$(calcurse -d1 \ + --format-apt "• %S - %E\n %m\n" \ + --format-event "• %m\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')" diff --git a/scripts/dwmblocks/sb-disk b/scripts/dwmblocks/sb-disk new file mode 100755 index 0000000..8f4b688 --- /dev/null +++ b/scripts/dwmblocks/sb-disk @@ -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)" diff --git a/scripts/dwmblocks/sb-loadavg b/scripts/dwmblocks/sb-loadavg new file mode 100755 index 0000000..ca242ab --- /dev/null +++ b/scripts/dwmblocks/sb-loadavg @@ -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" diff --git a/scripts/dwmblocks/sb-mail b/scripts/dwmblocks/sb-mail new file mode 100755 index 0000000..327b182 --- /dev/null +++ b/scripts/dwmblocks/sb-mail @@ -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" diff --git a/scripts/dwmblocks/sb-memory b/scripts/dwmblocks/sb-memory new file mode 100755 index 0000000..1a0cbd5 --- /dev/null +++ b/scripts/dwmblocks/sb-memory @@ -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))}')" diff --git a/scripts/dwmblocks/sb-mic b/scripts/dwmblocks/sb-mic new file mode 100755 index 0000000..3c2dbfd --- /dev/null +++ b/scripts/dwmblocks/sb-mic @@ -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 diff --git a/scripts/dwmblocks/sb-music b/scripts/dwmblocks/sb-music new file mode 100755 index 0000000..22c0b21 --- /dev/null +++ b/scripts/dwmblocks/sb-music @@ -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" diff --git a/scripts/dwmblocks/sb-record b/scripts/dwmblocks/sb-record new file mode 100755 index 0000000..a079b1d --- /dev/null +++ b/scripts/dwmblocks/sb-record @@ -0,0 +1,7 @@ +#!/bin/sh +# Displays an indicator during screen records. + +if [ -f /tmp/record ]; then + . sb-theme + display "" +fi diff --git a/scripts/dwmblocks/sb-theme b/scripts/dwmblocks/sb-theme new file mode 100755 index 0000000..e82001e --- /dev/null +++ b/scripts/dwmblocks/sb-theme @@ -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 +} diff --git a/scripts/dwmblocks/sb-volume b/scripts/dwmblocks/sb-volume new file mode 100755 index 0000000..bd8c046 --- /dev/null +++ b/scripts/dwmblocks/sb-volume @@ -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%" diff --git a/slock-flexipatch/.gitignore b/slock-flexipatch/.gitignore new file mode 100644 index 0000000..d79332d --- /dev/null +++ b/slock-flexipatch/.gitignore @@ -0,0 +1,5 @@ +slock +*.o +dwm +config.h +patches.h diff --git a/slock-flexipatch/LICENSE b/slock-flexipatch/LICENSE new file mode 100644 index 0000000..2e4419b --- /dev/null +++ b/slock-flexipatch/LICENSE @@ -0,0 +1,24 @@ +MIT/X Consortium License + +© 2015-2016 Markus Teich +© 2014 Dimitris Papastamos +© 2006-2014 Anselm R Garbe +© 2014-2016 Laslo Hunhold + +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. diff --git a/slock-flexipatch/Makefile b/slock-flexipatch/Makefile new file mode 100644 index 0000000..0caaef0 --- /dev/null +++ b/slock-flexipatch/Makefile @@ -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" ${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 diff --git a/slock-flexipatch/README b/slock-flexipatch/README new file mode 100644 index 0000000..dcacd01 --- /dev/null +++ b/slock-flexipatch/README @@ -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. diff --git a/slock-flexipatch/README.md b/slock-flexipatch/README.md new file mode 100644 index 0000000..1eeb0d9 --- /dev/null +++ b/slock-flexipatch/README.md @@ -0,0 +1,117 @@ +Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this slock 1.5 (4f04554, +2022-10-04) 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 `capscolor` patch then you would only need to flip this setting from 0 +to 1 in [patches.h](https://github.com/bakkeby/slock-flexipatch/blob/master/patches.h): +```c +#define CAPSCOLOR_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/slock/](https://tools.suckless.org/slock/) for details on the +slock tool, how to install it and how it works. + +--- + +### Changelog: + +2022-03-28 - Added the background image patch + +2021-09-13 - Added the dwm logo patch + +2021-09-09 - Added the auto-timeout, failure-command and secret-password patches + +2021-06-08 - Added the color message patch + +2020-08-03 - Added alpha, keypress_feedback and blur_pixelated_screen patches + +2019-11-27 - Added xresources patch + +2019-10-17 - Added capscolor, control clear, dpms, mediakeys, message, pam auth, quickcancel patches + +2019-10-16 - Introduced [flexipatch-finalizer](https://github.com/bakkeby/flexipatch-finalizer) + +### Patches included: + + - [alpha](https://github.com/khuedoan/slock) + - enables transparency for slock + - intended to be combined with a compositor that can blur the transparent background + + - [auto-timeout](https://tools.suckless.org/slock/patches/auto-timeout/) + - allows for a command to be executed after a specified time of inactivity + + - [background_image](https://tools.suckless.org/slock/patches/background-image/) + - sets the lockscreen picture to a background image + + - [blur_pixelated_screen](https://tools.suckless.org/slock/patches/blur-pixelated-screen/) + - sets the lockscreen picture to a blured or pixelated screenshot + + - [capscolor](https://tools.suckless.org/slock/patches/capscolor/) + - adds an additional color to indicate the state of Caps Lock + + - [color-message](https://tools.suckless.org/slock/patches/colormessage/) + - based on the message patch this patch lets you add a message to your lock screen using + 24-bit color ANSI escape codes + + - [control-clear](https://tools.suckless.org/slock/patches/control-clear/) + - with this patch slock will no longer change to the failure color if a control key is pressed + while the buffer is empty + - this may be useful if, for example, you wake your monitor up by pressing a control key and + don't want to spoil the detection of failed unlocking attempts + + - [dpms](https://tools.suckless.org/slock/patches/dpms/) + - interacts with the Display Power Signaling and automatically shuts down the monitor after a + configurable amount of seconds + - the monitor will automatically be activated by pressing a key or moving the mouse and the + password can be entered then + + - [dwmlogo](https://tools.suckless.org/slock/patches/dwmlogo/) + - draws the dwm logo which changes color based on the state + + - [failure-command](https://tools.suckless.org/slock/patches/failure-command/) + - allows for a command to be run after a specified number of incorrect attempts + + - [keypress_feedback](https://tools.suckless.org/slock/patches/keypress-feedback/) + - draws random blocks on the screen to display keypress feedback + + - [mediakeys](https://tools.suckless.org/slock/patches/mediakeys/) + - allows media keys to be used while the screen is locked, e.g. adjust volume or skip to the + next song without having to unlock the screen first + + - [message](https://tools.suckless.org/slock/patches/message/) + - this patch lets you add a custom message to your lock screen + + - [pam-auth](https://tools.suckless.org/slock/patches/pam_auth/) + - replaces shadow support with PAM authentication support + + - [quickcancel](https://tools.suckless.org/slock/patches/quickcancel/) + - cancel slock by moving the mouse within a certain time-period after slock started + - the time-period can be defined in seconds with the setting timetocancel in the config.h + - this can be useful if you forgot to disable xautolock during an activity that requires no + input (e.g. reading text, watching video, etc.) + + - [secret-password](https://tools.suckless.org/slock/patches/secret-password/) + - allows for commands to be executed when the user enters special passwords + + - [terminalkeys](https://tools.suckless.org/slock/patches/terminalkeys/) + - adds key commands that are commonly used in terminal applications (in particular the login + prompt) + + - [unlockscreen](https://tools.suckless.org/slock/patches/unlock_screen/) + - this patch keeps the screen unlocked, but keeps the input locked + - that is, the screen is not affected by slock, but users will not be able to interact with + the X session unless they enter the correct password + + - [xresources](https://tools.suckless.org/slock/patches/xresources/) + - this patch adds the ability to get colors via Xresources diff --git a/slock-flexipatch/arg.h b/slock-flexipatch/arg.h new file mode 100644 index 0000000..0b23c53 --- /dev/null +++ b/slock-flexipatch/arg.h @@ -0,0 +1,65 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/slock-flexipatch/config.def.h b/slock-flexipatch/config.def.h new file mode 100644 index 0000000..faf3d02 --- /dev/null +++ b/slock-flexipatch/config.def.h @@ -0,0 +1,164 @@ +/* user and group to drop privileges to */ +static const char *user = "nobody"; +static const char *group = "nogroup"; // use "nobody" for arch + +static const char *colorname[NUMCOLS] = { + #if DWM_LOGO_PATCH && !BLUR_PIXELATED_SCREEN_PATCH + [BACKGROUND] = "#2d2d2d", /* after initialization */ + #endif // DWM_LOGO_PATCH + [INIT] = "black", /* after initialization */ + [INPUT] = "#005577", /* during input */ + [FAILED] = "#CC3333", /* wrong password */ + #if CAPSCOLOR_PATCH + [CAPS] = "red", /* CapsLock on */ + #endif // CAPSCOLOR_PATCH + #if PAMAUTH_PATCH + [PAM] = "#9400D3", /* waiting for PAM */ + #endif // PAMAUTH_PATCH + #if KEYPRESS_FEEDBACK_PATCH + [BLOCKS] = "#ffffff", /* key feedback block */ + #endif // KEYPRESS_FEEDBACK_PATCH +}; + +#if MESSAGE_PATCH || COLOR_MESSAGE_PATCH +/* default message */ +static const char * message = "Enter your password."; + +/* text color */ +static const char * text_color = "#ffffff"; + +/* text size (must be a valid size) */ +static const char * font_name = "-misc-hack-medium-r-normal-*-*-120-*-100-m-0-iso10646-1"; +#endif // MESSAGE_PATCH | COLOR_MESSAGE_PATCH + +#if BACKGROUND_IMAGE_PATCH +/* Background image path, should be available to the user above */ +static const char * background_image = ""; +#endif // BACKGROUND_IMAGE_PATCH + +#if DWM_LOGO_PATCH +/* insert grid pattern with scale 1:1, the size can be changed with logosize */ +static const int logosize = 75; +static const int logow = 12; /* grid width and height for right center alignment*/ +static const int logoh = 6; + +static XRectangle rectangles[] = { + /* x y w h */ + { 0, 3, 1, 3 }, + { 1, 3, 2, 1 }, + { 0, 5, 8, 1 }, + { 3, 0, 1, 5 }, + { 5, 3, 1, 2 }, + { 7, 3, 1, 2 }, + { 8, 3, 4, 1 }, + { 9, 4, 1, 2 }, + { 11, 4, 1, 2 }, +}; +#endif // DWM_LOGO_PATCH + +#if XRESOURCES_PATCH +/* + * Xresources preferences to load at startup + */ +ResourcePref resources[] = { + #if DWM_LOGO_PATCH && !BLUR_PIXELATED_SCREEN_PATCH + { "background", STRING, &colorname[BACKGROUND] }, + #endif //DWM_LOGO_PATCH + #if BACKGROUND_IMAGE_PATCH + { "bg_image", STRING, &background_image }, + #endif // BACKGROUND_IMAGE_PATCH + { "locked", STRING, &colorname[INIT] }, + { "input", STRING, &colorname[INPUT] }, + { "failed", STRING, &colorname[FAILED] }, + #if CAPSCOLOR_PATCH + { "capslock", STRING, &colorname[CAPS] }, + #endif // CAPSCOLOR_PATCH + #if PAMAUTH_PATCH + { "pamauth", STRING, &colorname[PAM] }, + #endif // PAMAUTH_PATCH + #if MESSAGE_PATCH || COLOR_MESSAGE_PATCH + { "message", STRING, &message }, + { "text_color", STRING, &text_color }, + { "font_name", STRING, &font_name }, + #endif // MESSAGE_PATCH | COLOR_MESSAGE_PATCH +}; +#endif // XRESOURCES_PATCH + +#if ALPHA_PATCH +/* lock screen opacity */ +static const float alpha = 0.9; +#endif // ALPHA_PATCH + +/* treat a cleared input like a wrong password (color) */ +static const int failonclear = 1; + +#if AUTO_TIMEOUT_PATCH +/* length of time (seconds) until */ +static const int timeoffset = 60; + +/* should [command] be run only once? */ +static const int runonce = 0; + +/* command to be run after [time] has passed */ +static const char *command = "doas poweroff"; +#endif // AUTO_TIMEOUT_PATCH + +#if FAILURE_COMMAND_PATCH +/* number of failed password attempts until failcommand is executed. + Set to 0 to disable */ +static const int failcount = 0; + +/* command to be executed after [failcount] failed password attempts */ +static const char *failcommand = "shutdown"; +#endif // FAILURE_COMMAND_PATCH + +#if SECRET_PASSWORD_PATCH +static const secretpass scom[] = { + /* Password command */ + { "shutdown", "doas poweroff"}, +}; +#endif // SECRET_PASSWORD_PATCH + +#if BLUR_PIXELATED_SCREEN_PATCH +/* Enable blur */ +#define BLUR +/* Set blur radius */ +static const int blurRadius = 5; +/* Enable Pixelation */ +//#define PIXELATION +/* Set pixelation radius */ +static const int pixelSize = 10; +#endif // BLUR_PIXELATED_SCREEN_PATCH + +#if CONTROLCLEAR_PATCH +/* allow control key to trigger fail on clear */ +static const int controlkeyclear = 0; +#endif // CONTROLCLEAR_PATCH + +#if DPMS_PATCH +/* time in seconds before the monitor shuts down */ +static const int monitortime = 5; +#endif // DPMS_PATCH + +#if KEYPRESS_FEEDBACK_PATCH +static short int blocks_enabled = 1; // 0 = don't show blocks +static const int blocks_width = 0; // 0 = full width +static const int blocks_height = 16; + +// position +static const int blocks_x = 0; +static const int blocks_y = 0; + +// Number of blocks +static const int blocks_count = 10; +#endif // KEYPRESS_FEEDBACK_PATCH + +#if PAMAUTH_PATCH +/* PAM service that's used for authentication */ +static const char* pam_service = "login"; +#endif // PAMAUTH_PATCH + +#if QUICKCANCEL_PATCH +/* time in seconds to cancel lock with mouse movement */ +static const int timetocancel = 4; +#endif // QUICKCANCEL_PATCH diff --git a/slock-flexipatch/config.mk b/slock-flexipatch/config.mk new file mode 100644 index 0000000..b314836 --- /dev/null +++ b/slock-flexipatch/config.mk @@ -0,0 +1,48 @@ +# slock version +VERSION = 1.4 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Uncomment for BSD +#BSD=-D_BSD_SOURCE + +# Uncomment for NetBSD +#NETBSD=-D_NETBSD_SOURCE + +# Uncomment for message patch / MESSAGE_PATCH / COLORMESSAGE_PATCH / DWM_LOGO_PATCH +XINERAMA=-lXinerama +XINERAMAFLAGS = -DXINERAMA + +# Uncomment for pam auth patch / PAMAUTH_PATCH +#PAM=-lpam + +# Uncomment for blur pixelated screen and background image patches / BLUR_PIXELATED_SCREEN_PATCH, BACKGROUND_IMAGE_PATCH +IMLIB=-lImlib2 + +# includes and libs +INCS = -I. -I/usr/include -I${X11INC} +LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr ${XINERAMA} ${PAM} ${IMLIB} + +# flags +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H ${XINERAMAFLAGS} ${BSD} ${NETBSD} +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} +COMPATSRC = explicit_bzero.c + +# On OpenBSD and Darwin remove -lcrypt from LIBS +#LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr +# On *BSD remove -DHAVE_SHADOW_H from CPPFLAGS +# On NetBSD add -D_NETBSD_SOURCE to CPPFLAGS +#CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_NETBSD_SOURCE +# On OpenBSD set COMPATSRC to empty +#COMPATSRC = + +# compiler and linker +CC = gcc diff --git a/slock-flexipatch/explicit_bzero.c b/slock-flexipatch/explicit_bzero.c new file mode 100644 index 0000000..3e33ca8 --- /dev/null +++ b/slock-flexipatch/explicit_bzero.c @@ -0,0 +1,19 @@ +/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */ +/* + * Public domain. + * Written by Matthew Dempsky. + */ + +#include + +__attribute__((weak)) void +__explicit_bzero_hook(void *buf, size_t len) +{ +} + +void +explicit_bzero(void *buf, size_t len) +{ + memset(buf, 0, len); + __explicit_bzero_hook(buf, len); +} diff --git a/slock-flexipatch/patch/background_image.c b/slock-flexipatch/patch/background_image.c new file mode 100644 index 0000000..4f64c1f --- /dev/null +++ b/slock-flexipatch/patch/background_image.c @@ -0,0 +1,47 @@ +#include + +Imlib_Image image; + +void +render_lock_image(Display *dpy, struct lock *lock, Imlib_Image image) +{ + if (image) { + lock->bgmap = XCreatePixmap(dpy, lock->root, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen), DefaultDepth(dpy, lock->screen)); + imlib_context_set_display(dpy); + imlib_context_set_visual(DefaultVisual(dpy, lock->screen)); + imlib_context_set_colormap(DefaultColormap(dpy, lock->screen)); + imlib_context_set_drawable(lock->bgmap); + imlib_render_image_on_drawable(0, 0); + imlib_free_image(); + } +} + +void +create_lock_image(Display *dpy) +{ + /* Load picture */ + Imlib_Image buffer = imlib_load_image(background_image); + imlib_context_set_image(buffer); + int background_image_width = imlib_image_get_width(); + int background_image_height = imlib_image_get_height(); + + /* Create an image to be rendered */ + Screen *scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); + image = imlib_create_image(scr->width, scr->height); + imlib_context_set_image(image); + + /* Fill the image for every X monitor */ + XRRMonitorInfo *monitors; + int number_of_monitors; + monitors = XRRGetMonitors(dpy, RootWindow(dpy, XScreenNumberOfScreen(scr)), True, &number_of_monitors); + + int i; + for (i = 0; i < number_of_monitors; i++) { + imlib_blend_image_onto_image(buffer, 0, 0, 0, background_image_width, background_image_height, monitors[i].x, monitors[i].y, monitors[i].width, monitors[i].height); + } + + /* Clean up */ + imlib_context_set_image(buffer); + imlib_free_image(); + imlib_context_set_image(image); +} \ No newline at end of file diff --git a/slock-flexipatch/patch/background_image.h b/slock-flexipatch/patch/background_image.h new file mode 100644 index 0000000..2a9050f --- /dev/null +++ b/slock-flexipatch/patch/background_image.h @@ -0,0 +1,4 @@ +#include + +static void create_lock_image(Display *dpy); +static void render_lock_image(Display *dpy, struct lock *lock, Imlib_Image image); \ No newline at end of file diff --git a/slock-flexipatch/patch/blur_pixelated_screen.c b/slock-flexipatch/patch/blur_pixelated_screen.c new file mode 100644 index 0000000..3b6d21f --- /dev/null +++ b/slock-flexipatch/patch/blur_pixelated_screen.c @@ -0,0 +1,70 @@ +#include + +Imlib_Image image; + +void +render_lock_image(Display *dpy, struct lock *lock, Imlib_Image image) +{ + if (image) { + lock->bgmap = XCreatePixmap(dpy, lock->root, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen), DefaultDepth(dpy, lock->screen)); + imlib_context_set_image(image); + imlib_context_set_display(dpy); + imlib_context_set_visual(DefaultVisual(dpy, lock->screen)); + imlib_context_set_colormap(DefaultColormap(dpy, lock->screen)); + imlib_context_set_drawable(lock->bgmap); + imlib_render_image_on_drawable(0, 0); + imlib_free_image(); + } +} + +void +create_lock_image(Display *dpy) +{ + /* Create screenshot Image */ + Screen *scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); + image = imlib_create_image(scr->width,scr->height); + imlib_context_set_image(image); + imlib_context_set_display(dpy); + imlib_context_set_visual(DefaultVisual(dpy,0)); + imlib_context_set_drawable(RootWindow(dpy,XScreenNumberOfScreen(scr))); + imlib_copy_drawable_to_image(0,0,0,scr->width,scr->height,0,0,1); + + #ifdef BLUR + /* Blur function */ + imlib_image_blur(blurRadius); + #endif // BLUR + + #ifdef PIXELATION + /* Pixelation */ + int width = scr->width; + int height = scr->height; + + for (int y = 0; y < height; y += pixelSize) { + for (int x = 0; x < width; x += pixelSize) { + int red = 0; + int green = 0; + int blue = 0; + + Imlib_Color pixel; + Imlib_Color* pp; + pp = &pixel; + for (int j = 0; j < pixelSize && j < height; j++) { + for (int i = 0; i < pixelSize && i < width; i++) { + imlib_image_query_pixel(x + i, y + j, pp); + red += pixel.red; + green += pixel.green; + blue += pixel.blue; + } + } + red /= (pixelSize * pixelSize); + green /= (pixelSize * pixelSize); + blue /= (pixelSize * pixelSize); + imlib_context_set_color(red, green, blue, pixel.alpha); + imlib_image_fill_rectangle(x, y, pixelSize, pixelSize); + red = 0; + green = 0; + blue = 0; + } + } + #endif +} \ No newline at end of file diff --git a/slock-flexipatch/patch/blur_pixelated_screen.h b/slock-flexipatch/patch/blur_pixelated_screen.h new file mode 100644 index 0000000..2a9050f --- /dev/null +++ b/slock-flexipatch/patch/blur_pixelated_screen.h @@ -0,0 +1,4 @@ +#include + +static void create_lock_image(Display *dpy); +static void render_lock_image(Display *dpy, struct lock *lock, Imlib_Image image); \ No newline at end of file diff --git a/slock-flexipatch/patch/colormessage.c b/slock-flexipatch/patch/colormessage.c new file mode 100644 index 0000000..0d53537 --- /dev/null +++ b/slock-flexipatch/patch/colormessage.c @@ -0,0 +1,128 @@ +#include + +/* global count to prevent repeated error messages */ +int count_error = 0; + +static int +readescapedint(const char *str, int *i) { + int n = 0; + if (str[*i]) + ++*i; + while(str[*i] && str[*i] != ';' && str[*i] != 'm') { + n = 10 * n + str[*i] - '0'; + ++*i; + } + return n; +} + +static void +writemessage(Display *dpy, Window win, int screen) +{ + int len, line_len, width, height, s_width, s_height, i, k, tab_size, r, g, b, escaped_int, curr_line_len; + XGCValues gr_values; + XFontStruct *fontinfo; + XColor color, dummy; + XineramaScreenInfo *xsi; + GC gc; + fontinfo = XLoadQueryFont(dpy, font_name); + + if (fontinfo == NULL) { + if (count_error == 0) { + fprintf(stderr, "slock: Unable to load font \"%s\"\n", font_name); + fprintf(stderr, "slock: Try listing fonts with 'slock -f'\n"); + count_error++; + } + return; + } + + tab_size = 8 * XTextWidth(fontinfo, " ", 1); + + XAllocNamedColor(dpy, DefaultColormap(dpy, screen), + text_color, &color, &dummy); + + gr_values.font = fontinfo->fid; + gr_values.foreground = color.pixel; + gc=XCreateGC(dpy,win,GCFont+GCForeground, &gr_values); + + /* To prevent "Uninitialized" warnings. */ + xsi = NULL; + + /* + * Start formatting and drawing text + */ + + len = strlen(message); + + /* Max max line length (cut at '\n') */ + line_len = curr_line_len = 0; + k = 0; + for (i = 0; i < len; i++) { + if (message[i] == '\n') { + curr_line_len = 0; + k++; + } else if (message[i] == 0x1b) { + while (i < len && message[i] != 'm') { + i++; + } + if (i == len) + die("slock: unclosed escape sequence\n"); + } else { + curr_line_len += XTextWidth(fontinfo, message + i, 1); + if (curr_line_len > line_len) + line_len = curr_line_len; + } + } + /* If there is only one line */ + if (line_len == 0) + line_len = len; + + if (XineramaIsActive(dpy)) { + xsi = XineramaQueryScreens(dpy, &i); + s_width = xsi[0].width; + s_height = xsi[0].height; + } else { + s_width = DisplayWidth(dpy, screen); + s_height = DisplayHeight(dpy, screen); + } + height = s_height*3/7 - (k*20)/3; + width = (s_width - line_len)/2; + + line_len = 0; + /* print the text while parsing 24 bit color ANSI escape codes*/ + for (i = k = 0; i < len; i++) { + switch (message[i]) { + case '\n': + line_len = 0; + while (message[i + 1] == '\t') { + line_len += tab_size; + i++; + } + k++; + break; + case 0x1b: + i++; + if (message[i] == '[') { + escaped_int = readescapedint(message, &i); + if (escaped_int == 39) + continue; + if (escaped_int != 38) + die("slock: unknown escape sequence%d\n", escaped_int); + if (readescapedint(message, &i) != 2) + die("slock: only 24 bit color supported\n"); + r = readescapedint(message, &i) & 0xff; + g = readescapedint(message, &i) & 0xff; + b = readescapedint(message, &i) & 0xff; + XSetForeground(dpy, gc, r << 16 | g << 8 | b); + } else + die("slock: unknown escape sequence\n"); + break; + default: + XDrawString(dpy, win, gc, width + line_len, height + 20 * k, message + i, 1); + line_len += XTextWidth(fontinfo, message + i, 1); + } + } + + /* xsi should not be NULL anyway if Xinerama is active, but to be safe */ + if (XineramaIsActive(dpy) && xsi != NULL) + XFree(xsi); +} \ No newline at end of file diff --git a/slock-flexipatch/patch/dwmlogo.c b/slock-flexipatch/patch/dwmlogo.c new file mode 100644 index 0000000..9feb024 --- /dev/null +++ b/slock-flexipatch/patch/dwmlogo.c @@ -0,0 +1,29 @@ +static void +resizerectangles(struct lock *lock) +{ + int i; + + for (i = 0; i < LENGTH(rectangles); i++){ + lock->rectangles[i].x = (rectangles[i].x * logosize) + + lock->xoff + ((lock->mw) / 2) - (logow / 2 * logosize); + lock->rectangles[i].y = (rectangles[i].y * logosize) + + lock->yoff + ((lock->mh) / 2) - (logoh / 2 * logosize); + lock->rectangles[i].width = rectangles[i].width * logosize; + lock->rectangles[i].height = rectangles[i].height * logosize; + } +} + +static void +drawlogo(Display *dpy, struct lock *lock, int color) +{ + #if BLUR_PIXELATED_SCREEN_PATCH + lock->drawable = lock->bgmap; + #else + XSetForeground(dpy, lock->gc, lock->colors[BACKGROUND]); + XFillRectangle(dpy, lock->drawable, lock->gc, 0, 0, lock->x, lock->y); + #endif // BLUR_PIXELATED_SCREEN_PATCH + XSetForeground(dpy, lock->gc, lock->colors[color]); + XFillRectangles(dpy, lock->drawable, lock->gc, lock->rectangles, LENGTH(rectangles)); + XCopyArea(dpy, lock->drawable, lock->win, lock->gc, 0, 0, lock->x, lock->y, 0, 0); + XSync(dpy, False); +} \ No newline at end of file diff --git a/slock-flexipatch/patch/dwmlogo.h b/slock-flexipatch/patch/dwmlogo.h new file mode 100644 index 0000000..4d84ff4 --- /dev/null +++ b/slock-flexipatch/patch/dwmlogo.h @@ -0,0 +1,2 @@ +static void resizerectangles(struct lock *lock); +static void drawlogo(Display *dpy, struct lock *lock, int color); \ No newline at end of file diff --git a/slock-flexipatch/patch/include.c b/slock-flexipatch/patch/include.c new file mode 100644 index 0000000..b932cf8 --- /dev/null +++ b/slock-flexipatch/patch/include.c @@ -0,0 +1,28 @@ +/* Patches */ +#if BACKGROUND_IMAGE_PATCH +#include "background_image.c" +#elif BLUR_PIXELATED_SCREEN_PATCH +#include "blur_pixelated_screen.c" +#endif + +#if COLOR_MESSAGE_PATCH +#include "colormessage.c" +#elif MESSAGE_PATCH +#include "message.c" +#endif + +#if DWM_LOGO_PATCH +#include "dwmlogo.c" +#endif + +#if KEYPRESS_FEEDBACK_PATCH +#include "keypress_feedback.c" +#endif + +#if PAMAUTH_PATCH +#include "pamauth.c" +#endif + +#if XRESOURCES_PATCH +#include "xresources.c" +#endif \ No newline at end of file diff --git a/slock-flexipatch/patch/include.h b/slock-flexipatch/patch/include.h new file mode 100644 index 0000000..c50a402 --- /dev/null +++ b/slock-flexipatch/patch/include.h @@ -0,0 +1,18 @@ +/* Patches */ +#if BACKGROUND_IMAGE_PATCH +#include "background_image.h" +#elif BLUR_PIXELATED_SCREEN_PATCH +#include "blur_pixelated_screen.h" +#endif + +#if DWM_LOGO_PATCH +#include "dwmlogo.h" +#endif + +#if PAMAUTH_PATCH +#include "pamauth.h" +#endif + +#if KEYPRESS_FEEDBACK_PATCH +#include "keypress_feedback.h" +#endif \ No newline at end of file diff --git a/slock-flexipatch/patch/keypress_feedback.c b/slock-flexipatch/patch/keypress_feedback.c new file mode 100644 index 0000000..09cfc0a --- /dev/null +++ b/slock-flexipatch/patch/keypress_feedback.c @@ -0,0 +1,28 @@ +static void +draw_key_feedback(Display *dpy, struct lock **locks, int screen) +{ + XGCValues gr_values; + + Window win = locks[screen]->win; + Window root_win; + + gr_values.foreground = locks[screen]->colors[BLOCKS]; + GC gc = XCreateGC(dpy, win, GCForeground, &gr_values); + + int width = blocks_width, height = blocks_height; + if (blocks_height == 0 || blocks_width == 0) { + int _x, _y; + unsigned int screen_width, screen_height, _b, _d; + XGetGeometry(dpy, win, &root_win, &_x, &_y, &screen_width, &screen_height, &_b, &_d); + width = blocks_width ? blocks_width : screen_width; + height = blocks_height ? blocks_height : screen_height; + } + + unsigned int block_width = width / blocks_count; + unsigned int position = rand() % blocks_count; + + XClearWindow(dpy, win); + XFillRectangle(dpy, win, gc, blocks_x + position*block_width, blocks_y, width, height); + + XFreeGC(dpy, gc); +} \ No newline at end of file diff --git a/slock-flexipatch/patch/keypress_feedback.h b/slock-flexipatch/patch/keypress_feedback.h new file mode 100644 index 0000000..be20690 --- /dev/null +++ b/slock-flexipatch/patch/keypress_feedback.h @@ -0,0 +1 @@ +static void draw_key_feedback(Display *dpy, struct lock **locks, int screen); \ No newline at end of file diff --git a/slock-flexipatch/patch/message.c b/slock-flexipatch/patch/message.c new file mode 100644 index 0000000..43bb2e0 --- /dev/null +++ b/slock-flexipatch/patch/message.c @@ -0,0 +1,94 @@ +#include + +/* global count to prevent repeated error messages */ +int count_error = 0; + +static void +writemessage(Display *dpy, Window win, int screen) +{ + int len, line_len, width, height, s_width, s_height, i, j, k, tab_replace, tab_size; + XGCValues gr_values; + XFontStruct *fontinfo; + XColor color, dummy; + XineramaScreenInfo *xsi; + GC gc; + fontinfo = XLoadQueryFont(dpy, font_name); + + if (fontinfo == NULL) { + if (count_error == 0) { + fprintf(stderr, "slock: Unable to load font \"%s\"\n", font_name); + fprintf(stderr, "slock: Try listing fonts with 'slock -f'\n"); + count_error++; + } + return; + } + + tab_size = 8 * XTextWidth(fontinfo, " ", 1); + + XAllocNamedColor(dpy, DefaultColormap(dpy, screen), + text_color, &color, &dummy); + + gr_values.font = fontinfo->fid; + gr_values.foreground = color.pixel; + gc=XCreateGC(dpy,win,GCFont+GCForeground, &gr_values); + + /* To prevent "Uninitialized" warnings. */ + xsi = NULL; + + /* + * Start formatting and drawing text + */ + + len = strlen(message); + + /* Max max line length (cut at '\n') */ + line_len = 0; + k = 0; + for (i = j = 0; i < len; i++) { + if (message[i] == '\n') { + if (i - j > line_len) + line_len = i - j; + k++; + i++; + j = i; + } + } + /* If there is only one line */ + if (line_len == 0) + line_len = len; + + if (XineramaIsActive(dpy)) { + xsi = XineramaQueryScreens(dpy, &i); + s_width = xsi[0].width; + s_height = xsi[0].height; + } else { + s_width = DisplayWidth(dpy, screen); + s_height = DisplayHeight(dpy, screen); + } + + height = s_height*3/7 - (k*20)/3; + width = (s_width - XTextWidth(fontinfo, message, line_len))/2; + + /* Look for '\n' and print the text between them. */ + for (i = j = k = 0; i <= len; i++) { + /* i == len is the special case for the last line */ + if (i == len || message[i] == '\n') { + tab_replace = 0; + while (message[j] == '\t' && j < i) { + tab_replace++; + j++; + } + + XDrawString(dpy, win, gc, width + tab_size*tab_replace, height + 20*k, message + j, i - j); + while (i < len && message[i] == '\n') { + i++; + j = i; + k++; + } + } + } + + /* xsi should not be NULL anyway if Xinerama is active, but to be safe */ + if (XineramaIsActive(dpy) && xsi != NULL) + XFree(xsi); +} \ No newline at end of file diff --git a/slock-flexipatch/patch/pamauth.c b/slock-flexipatch/patch/pamauth.c new file mode 100644 index 0000000..ccd76d5 --- /dev/null +++ b/slock-flexipatch/patch/pamauth.c @@ -0,0 +1,27 @@ +char passwd[256]; + +static int +pam_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + int retval = PAM_CONV_ERR; + for (int i=0; imsg_style == PAM_PROMPT_ECHO_OFF && + strncmp(msg[i]->msg, "Password: ", 10) == 0) { + struct pam_response *resp_msg = malloc(sizeof(struct pam_response)); + if (!resp_msg) + die("malloc failed\n"); + char *password = malloc(strlen(passwd) + 1); + if (!password) + die("malloc failed\n"); + memset(password, 0, strlen(passwd) + 1); + strcpy(password, passwd); + resp_msg->resp_retcode = 0; + resp_msg->resp = password; + resp[i] = resp_msg; + retval = PAM_SUCCESS; + } + } + return retval; +} + diff --git a/slock-flexipatch/patch/pamauth.h b/slock-flexipatch/patch/pamauth.h new file mode 100644 index 0000000..b87b240 --- /dev/null +++ b/slock-flexipatch/patch/pamauth.h @@ -0,0 +1,5 @@ +#include +#include + +static int pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr); +struct pam_conv pamc = {pam_conv, NULL}; \ No newline at end of file diff --git a/slock-flexipatch/patch/xresources.c b/slock-flexipatch/patch/xresources.c new file mode 100644 index 0000000..c2b9b1c --- /dev/null +++ b/slock-flexipatch/patch/xresources.c @@ -0,0 +1,53 @@ +#include +#include + +int +resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) +{ + char **sdst = dst; + int *idst = dst; + float *fdst = dst; + + char fullname[256]; + char fullclass[256]; + char *type; + XrmValue ret; + + snprintf(fullname, sizeof(fullname), "%s.%s", "slock", name); + snprintf(fullclass, sizeof(fullclass), "%s.%s", "Slock", name); + fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0'; + + XrmGetResource(db, fullname, fullclass, &type, &ret); + if (ret.addr == NULL || strncmp("String", type, 64)) + return 1; + + switch (rtype) { + case STRING: + *sdst = ret.addr; + break; + case INTEGER: + *idst = strtoul(ret.addr, NULL, 10); + break; + case FLOAT: + *fdst = strtof(ret.addr, NULL); + break; + } + return 0; +} + +void +config_init(Display *dpy) +{ + char *resm; + XrmDatabase db; + ResourcePref *p; + + XrmInitialize(); + resm = XResourceManagerString(dpy); + if (!resm) + return; + + db = XrmGetStringDatabase(resm); + for (p = resources; p < resources + LENGTH(resources); p++) + resource_load(db, p->name, p->type, p->dst); +} diff --git a/slock-flexipatch/patches.def.h b/slock-flexipatch/patches.def.h new file mode 100644 index 0000000..9e68401 --- /dev/null +++ b/slock-flexipatch/patches.def.h @@ -0,0 +1,145 @@ +/* + * This file contains patch control flags. + * + * In principle you should be able to mix and match any patches + * you may want. In cases where patches are logically incompatible + * one patch may take precedence over the other as noted in the + * relevant descriptions. + */ + +/* Patches */ + +/* This patch enables transparency for slock. This is intended to be combined + * with a compositor that can blur the transparent background. + * Extrapolated from https://github.com/khuedoan/slock + * https://github.com/khuedoan/slock/commit/5e7a95b50fd72efcf2a40d487278749a17cbb146 + */ +#define ALPHA_PATCH 1 + +/* This patch allows for a command to be executed after a specified time of inactivity. + * https://tools.suckless.org/slock/patches/auto-timeout/ + */ +#define AUTO_TIMEOUT_PATCH 0 + +/* This patch adds a background image for slock. + * This patch depends on the Imlib2 library, uncomment the relevant line in + * config.mk when enabling this patch. + * This patch is a variant of the blur pixelated screen patch and takes precedence over that. + * https://tools.suckless.org/slock/patches/background-image/ + */ +#define BACKGROUND_IMAGE_PATCH 0 + +/* This patch sets the lockscreen picture to a blured or pixelated screenshot. + * This patch depends on the Imlib2 library, uncomment the relevant line in + * config.mk when enabling this patch. + * The background image patch takes precedence over this patch. + * https://tools.suckless.org/slock/patches/blur-pixelated-screen/ + */ +#define BLUR_PIXELATED_SCREEN_PATCH 1 + +/* This patch introduces an additional color to indicate the state of Caps Lock. + * https://tools.suckless.org/slock/patches/capscolor/ + */ +#define CAPSCOLOR_PATCH 1 + +/* Based on the message patch this patch lets you add a message to your lock screen using 24 bit + * color ANSI escape codes. + * + * You can place a default message in config.h and you can also pass a message with the -m command + * line option. + * + * Practical example: + * slock -m "$(printf "text colored \x1b[38;2;0;255;0m green\x1b[39m\n")" + * + * If you enable this then you also need to add the -lXinerama library to the LIBS + * configuration in config.mk. Look for and uncomment the XINERAMA placeholder. + * + * https://tools.suckless.org/slock/patches/colormessage/ + */ +#define COLOR_MESSAGE_PATCH 1 + +/* Adds an additional configuration parameter, controlkeyclear. When set to 1, slock will no + * longer change to the failure color if a control key is pressed while the buffer is empty. + * This may be useful if, for example, you wake your monitor up by pressing a control key + * and don't want to spoil the detection of failed unlocking attempts. + * https://tools.suckless.org/slock/patches/control-clear/ + */ +#define CONTROLCLEAR_PATCH 0 + +/* This patch interacts with the Display Power Signaling and automatically shuts down + * the monitor after a configurable amount of seconds. + * The monitor will automatically be activated by pressing a key or moving the mouse + * and the password can be entered then. + * https://tools.suckless.org/slock/patches/dpms/ + */ +#define DPMS_PATCH 1 + +/* This patch draws the dwm logo which changes color based on the state. + * https://tools.suckless.org/slock/patches/dwmlogo/ + */ +#define DWM_LOGO_PATCH 0 + +/* This patch allows for a command to be run after a specified number of incorrect attempts. + * https://tools.suckless.org/slock/patches/failure-command/ + */ +#define FAILURE_COMMAND_PATCH 0 + +/* Draws random blocks on the screen to display keypress feedback. + * https://tools.suckless.org/slock/patches/keypress-feedback/ + * https://patch-diff.githubusercontent.com/raw/phenax/bslock/pull/2.diff + */ +#define KEYPRESS_FEEDBACK_PATCH 0 + +/* This patch allows media keys to be used while the screen is locked. Allows for volume + * to be adjusted or to skip to the next song without having to unlock the screen first. + * https://tools.suckless.org/slock/patches/mediakeys/ + */ +#define MEDIAKEYS_PATCH 1 + +/* This patch lets you add a message to your lock screen. You can place a default message + * in config.h and you can also pass a message with the -m command line option. + * If you enable this then you also need to add the -lXinerama library to the LIBS + * configuration in config.mk. Look for and uncomment the XINERAMA placeholder. + * https://tools.suckless.org/slock/patches/message/ + */ +#define MESSAGE_PATCH 1 + +/* Replaces shadow support with PAM authentication support. + * Change variable pam_service in config.def.h to the corresponding PAM service. + * The default configuration is for ArchLinux's login service. + * If you enable this then you also need to add the -lpam library to the LIBS configuration + * in config.mk. Look for and uncomment the PAM placeholder. + * https://tools.suckless.org/slock/patches/pam_auth/ + */ +#define PAMAUTH_PATCH 0 + +/* Cancel slock by moving the mouse within a certain time-period after slock started. + * The time-period can be defined in seconds with the setting timetocancel in the config.h. + * This can be useful if you forgot to disable xautolock during an activity that requires + * no input (e.g. reading text, watching video, etc.). + * https://tools.suckless.org/slock/patches/quickcancel/ + */ +#define QUICKCANCEL_PATCH 0 + +/* This patch allows for commands to be executed when the user enters special passwords. + * https://tools.suckless.org/slock/patches/secret-password/ + */ +#define SECRET_PASSWORD_PATCH 0 + +/* Adds key commands that are commonly used in terminal applications (in particular the + * login prompt) to slock. + * https://tools.suckless.org/slock/patches/terminalkeys/ + */ +#define TERMINALKEYS_PATCH 1 + +/* This patch keeps the screen unlocked but keeps the input locked. That is, the screen + * is not affected by slock, but users will not be able to interact with the X session + * unless they enter the correct password. + * https://tools.suckless.org/slock/patches/unlock_screen/ + */ +#define UNLOCKSCREEN_PATCH 0 + +/* This patch adds the ability to get colors via Xresources. + * https://tools.suckless.org/slock/patches/xresources/ + */ +#define XRESOURCES_PATCH 1 diff --git a/slock-flexipatch/slock.1 b/slock-flexipatch/slock.1 new file mode 100644 index 0000000..82cdcd6 --- /dev/null +++ b/slock-flexipatch/slock.1 @@ -0,0 +1,39 @@ +.Dd 2016-08-23 +.Dt SLOCK 1 +.Sh NAME +.Nm slock +.Nd simple X screen locker +.Sh SYNOPSIS +.Nm +.Op Fl v +.Op Ar cmd Op Ar arg ... +.Sh DESCRIPTION +.Nm +is a simple X screen locker. If provided, +.Ar cmd Op Ar arg ... +is executed after the screen has been locked. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl v +Print version information to stdout and exit. +.El +.Sh SECURITY CONSIDERATIONS +To make sure a locked screen can not be bypassed by switching VTs +or killing the X server with Ctrl+Alt+Backspace, it is recommended +to disable both in +.Xr xorg.conf 5 +for maximum security: +.Bd -literal -offset left +Section "ServerFlags" + Option "DontVTSwitch" "True" + Option "DontZap" "True" +EndSection +.Ed +.Sh EXAMPLES +$ +.Nm +/usr/sbin/s2ram +.Sh CUSTOMIZATION +.Nm +can be customized by creating a custom config.h from config.def.h and +(re)compiling the source code. This keeps it fast, secure and simple. diff --git a/slock-flexipatch/slock.c b/slock-flexipatch/slock.c new file mode 100644 index 0000000..8130954 --- /dev/null +++ b/slock-flexipatch/slock.c @@ -0,0 +1,797 @@ +/* See LICENSE file for license details. */ +#define _XOPEN_SOURCE 500 +#define LENGTH(X) (sizeof X / sizeof X[0]) +#if HAVE_SHADOW_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "patches.h" +#if ALPHA_PATCH +#include +#endif // ALPHA_PATCH +#if KEYPRESS_FEEDBACK_PATCH +#include +#endif // KEYPRESS_FEEDBACK_PATCH +#if CAPSCOLOR_PATCH +#include +#endif // CAPSCOLOR_PATCH +#if MEDIAKEYS_PATCH +#include +#endif // MEDIAKEYS_PATCH +#if QUICKCANCEL_PATCH || AUTO_TIMEOUT_PATCH +#include +#endif // QUICKCANCEL_PATCH / AUTO_TIMEOUT_PATCH +#if DPMS_PATCH +#include +#endif // DPMS_PATCH +#ifdef XINERAMA +#include +#endif + +#include "arg.h" +#include "util.h" + +char *argv0; +#if FAILURE_COMMAND_PATCH +int failtrack = 0; +#endif // FAILURE_COMMAND_PATCH + +#if AUTO_TIMEOUT_PATCH +static time_t lasttouched; +int runflag = 0; +#endif // AUTO_TIMEOUT_PATCH +#if QUICKCANCEL_PATCH +static time_t locktime; +#endif // QUICKCANCEL_PATCH + +enum { + #if DWM_LOGO_PATCH && !BLUR_PIXELATED_SCREEN_PATCH + BACKGROUND, + #endif // DWM_LOGO_PATCH + INIT, + INPUT, + FAILED, + #if CAPSCOLOR_PATCH + CAPS, + #endif // CAPSCOLOR_PATCH + #if PAMAUTH_PATCH + PAM, + #endif // PAMAUTH_PATCH + #if KEYPRESS_FEEDBACK_PATCH + BLOCKS, + #endif // KEYPRESS_FEEDBACK_PATCH + NUMCOLS +}; + +#if XRESOURCES_PATCH +/* Xresources preferences */ +enum resource_type { + STRING = 0, + INTEGER = 1, + FLOAT = 2 +}; + +typedef struct { + char *name; + enum resource_type type; + void *dst; +} ResourcePref; +#endif // XRESOURCES_PATCH + +#if SECRET_PASSWORD_PATCH +typedef struct secretpass secretpass; +struct secretpass { + char *pass; + char *command; +}; +#endif // SECRET_PASSWORD_PATCH + +#include "config.h" + +struct lock { + int screen; + Window root, win; + Pixmap pmap; + #if BLUR_PIXELATED_SCREEN_PATCH || BACKGROUND_IMAGE_PATCH + Pixmap bgmap; + #endif // BLUR_PIXELATED_SCREEN_PATCH | BACKGROUND_IMAGE_PATCH + unsigned long colors[NUMCOLS]; + #if DWM_LOGO_PATCH + unsigned int x, y; + unsigned int xoff, yoff, mw, mh; + Drawable drawable; + GC gc; + XRectangle rectangles[LENGTH(rectangles)]; + #endif // DWM_LOGO_PATCH +}; + +struct xrandr { + int active; + int evbase; + int errbase; +}; + +#include "patch/include.h" + +static void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +#include "patch/include.c" + +#ifdef __linux__ +#include +#include + +static void +dontkillme(void) +{ + FILE *f; + const char oomfile[] = "/proc/self/oom_score_adj"; + + if (!(f = fopen(oomfile, "w"))) { + if (errno == ENOENT) + return; + die("slock: fopen %s: %s\n", oomfile, strerror(errno)); + } + fprintf(f, "%d", OOM_SCORE_ADJ_MIN); + if (fclose(f)) { + if (errno == EACCES) + die("slock: unable to disable OOM killer. " + "Make sure to suid or sgid slock.\n"); + else + die("slock: fclose %s: %s\n", oomfile, strerror(errno)); + } +} +#endif + +static const char * +gethash(void) +{ + const char *hash; + struct passwd *pw; + + /* Check if the current user has a password entry */ + errno = 0; + if (!(pw = getpwuid(getuid()))) { + if (errno) + die("slock: getpwuid: %s\n", strerror(errno)); + else + die("slock: cannot retrieve password entry\n"); + } + hash = pw->pw_passwd; + +#if HAVE_SHADOW_H + if (!strcmp(hash, "x")) { + struct spwd *sp; + if (!(sp = getspnam(pw->pw_name))) + die("slock: getspnam: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = sp->sp_pwdp; + } +#else + if (!strcmp(hash, "*")) { +#ifdef __OpenBSD__ + if (!(pw = getpwuid_shadow(getuid()))) + die("slock: getpwnam_shadow: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = pw->pw_passwd; +#else + die("slock: getpwuid: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); +#endif /* __OpenBSD__ */ + } +#endif /* HAVE_SHADOW_H */ + + #if PAMAUTH_PATCH + /* pam, store user name */ + hash = pw->pw_name; + #endif // PAMAUTH_PATCH + return hash; +} + +static void +readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + const char *hash) +{ + XRRScreenChangeNotifyEvent *rre; + #if PAMAUTH_PATCH + char buf[32]; + int retval; + pam_handle_t *pamh; + #else + char buf[32], passwd[256], *inputhash; + #endif // PAMAUTH_PATCH + int num, screen, running, failure, oldc; + unsigned int len, color; + #if AUTO_TIMEOUT_PATCH + time_t currenttime; + #endif // AUTO_TIMEOUT_PATCH + #if CAPSCOLOR_PATCH + int caps; + unsigned int indicators; + #endif // CAPSCOLOR_PATCH + KeySym ksym; + XEvent ev; + + len = 0; + #if CAPSCOLOR_PATCH + caps = 0; + #endif // CAPSCOLOR_PATCH + running = 1; + failure = 0; + oldc = INIT; + + #if CAPSCOLOR_PATCH + if (!XkbGetIndicatorState(dpy, XkbUseCoreKbd, &indicators)) + caps = indicators & 1; + + #endif // CAPSCOLOR_PATCH + #if AUTO_TIMEOUT_PATCH + while (running) + #else + while (running && !XNextEvent(dpy, &ev)) + #endif // AUTO_TIMEOUT_PATCH + { + #if AUTO_TIMEOUT_PATCH + while (XPending(dpy)) { + XNextEvent(dpy, &ev); + #endif // AUTO_TIMEOUT_PATCH + #if QUICKCANCEL_PATCH + running = !((time(NULL) - locktime < timetocancel) && (ev.type == MotionNotify)); + #endif // QUICKCANCEL_PATCH + if (ev.type == KeyPress) { + #if AUTO_TIMEOUT_PATCH + time(&lasttouched); + #endif // AUTO_TIMEOUT_PATCH + explicit_bzero(&buf, sizeof(buf)); + num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0); + if (IsKeypadKey(ksym)) { + if (ksym == XK_KP_Enter) + ksym = XK_Return; + else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) + ksym = (ksym - XK_KP_0) + XK_0; + } + if (IsFunctionKey(ksym) || + IsKeypadKey(ksym) || + IsMiscFunctionKey(ksym) || + IsPFKey(ksym) || + IsPrivateKeypadKey(ksym)) + continue; + #if TERMINALKEYS_PATCH + if (ev.xkey.state & ControlMask) { + switch (ksym) { + case XK_u: + ksym = XK_Escape; + break; + case XK_m: + ksym = XK_Return; + break; + case XK_j: + ksym = XK_Return; + break; + case XK_h: + ksym = XK_BackSpace; + break; + } + } + #endif // TERMINALKEYS_PATCH + switch (ksym) { + case XK_Return: + passwd[len] = '\0'; + errno = 0; + + #if SECRET_PASSWORD_PATCH + for (int i = 0; i < LENGTH(scom); i++) { + if (strcmp(scom[i].pass, passwd) == 0) { + if (system(scom[i].command)); + #if FAILURE_COMMAND_PATCH + failtrack = -1; + #endif // FAILURE_COMMAND_PATCH + } + } + #endif // SECRET_PASSWORD_PATCH + + #if PAMAUTH_PATCH + retval = pam_start(pam_service, hash, &pamc, &pamh); + color = PAM; + for (screen = 0; screen < nscreens; screen++) { + #if DWM_LOGO_PATCH + drawlogo(dpy, locks[screen], color); + #elif BLUR_PIXELATED_SCREEN_PATCH || BACKGROUND_IMAGE_PATCH + if (locks[screen]->bgmap) + XSetWindowBackgroundPixmap(dpy, locks[screen]->win, locks[screen]->bgmap); + else + XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[0]); + XClearWindow(dpy, locks[screen]->win); + #else + XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); + XRaiseWindow(dpy, locks[screen]->win); + #endif // BLUR_PIXELATED_SCREEN_PATCH + + } + XSync(dpy, False); + + if (retval == PAM_SUCCESS) + retval = pam_authenticate(pamh, 0); + if (retval == PAM_SUCCESS) + retval = pam_acct_mgmt(pamh, 0); + + running = 1; + if (retval == PAM_SUCCESS) + running = 0; + else + fprintf(stderr, "slock: %s\n", pam_strerror(pamh, retval)); + pam_end(pamh, retval); + #else + if (!(inputhash = crypt(passwd, hash))) + fprintf(stderr, "slock: crypt: %s\n", strerror(errno)); + else + running = !!strcmp(inputhash, hash); + #endif // PAMAUTH_PATCH + if (running) { + XBell(dpy, 100); + failure = 1; + #if FAILURE_COMMAND_PATCH + failtrack++; + + if (failtrack >= failcount && failcount != 0) { + system(failcommand); + } + #endif // FAILURE_COMMAND_PATCH + } + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_Escape: + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_BackSpace: + if (len) + passwd[--len] = '\0'; + break; + #if CAPSCOLOR_PATCH + case XK_Caps_Lock: + caps = !caps; + break; + #endif // CAPSCOLOR_PATCH + #if MEDIAKEYS_PATCH + case XF86XK_AudioLowerVolume: + case XF86XK_AudioMute: + case XF86XK_AudioRaiseVolume: + case XF86XK_AudioPlay: + case XF86XK_AudioStop: + case XF86XK_AudioPrev: + case XF86XK_AudioNext: + XSendEvent(dpy, DefaultRootWindow(dpy), True, KeyPressMask, &ev); + break; + #endif // MEDIAKEYS_PATCH + default: + #if CONTROLCLEAR_PATCH + if (controlkeyclear && iscntrl((int)buf[0])) + continue; + if (num && (len + num < sizeof(passwd))) + #else + if (num && !iscntrl((int)buf[0]) && + (len + num < sizeof(passwd))) + #endif // CONTROLCLEAR_PATCH + { + memcpy(passwd + len, buf, num); + len += num; + } + #if KEYPRESS_FEEDBACK_PATCH + if (blocks_enabled) + for (screen = 0; screen < nscreens; screen++) + draw_key_feedback(dpy, locks, screen); + #endif // KEYPRESS_FEEDBACK_PATCH + break; + } + #if CAPSCOLOR_PATCH + color = len ? (caps ? CAPS : INPUT) : (failure || failonclear ? FAILED : INIT); + #else + color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); + #endif // CAPSCOLOR_PATCH + if (running && oldc != color) { + for (screen = 0; screen < nscreens; screen++) { + #if DWM_LOGO_PATCH + drawlogo(dpy, locks[screen], color); + #elif BLUR_PIXELATED_SCREEN_PATCH || BACKGROUND_IMAGE_PATCH + if (locks[screen]->bgmap) + XSetWindowBackgroundPixmap(dpy, locks[screen]->win, locks[screen]->bgmap); + else + XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[0]); + XClearWindow(dpy, locks[screen]->win); + #else + XSetWindowBackground(dpy, + locks[screen]->win, + locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); + #endif // BLUR_PIXELATED_SCREEN_PATCH + #if MESSAGE_PATCH || COLOR_MESSAGE_PATCH + writemessage(dpy, locks[screen]->win, screen); + #endif // MESSAGE_PATCH | COLOR_MESSAGE_PATCH + } + oldc = color; + } + } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { + rre = (XRRScreenChangeNotifyEvent*)&ev; + for (screen = 0; screen < nscreens; screen++) { + if (locks[screen]->win == rre->window) { + if (rre->rotation == RR_Rotate_90 || + rre->rotation == RR_Rotate_270) + XResizeWindow(dpy, locks[screen]->win, + rre->height, rre->width); + else + XResizeWindow(dpy, locks[screen]->win, + rre->width, rre->height); + XClearWindow(dpy, locks[screen]->win); + break; + } + } + } else { + for (screen = 0; screen < nscreens; screen++) + XRaiseWindow(dpy, locks[screen]->win); + } + + #if AUTO_TIMEOUT_PATCH + } + + time(¤ttime); + + if (currenttime >= lasttouched + timeoffset) { + if (!runonce || !runflag) { + runflag = 1; + system(command); + } + lasttouched = currenttime; + } + usleep(50); // artificial sleep for 50ms + #endif // AUTO_TIMEOUT_PATCH + } +} + +static struct lock * +lockscreen(Display *dpy, struct xrandr *rr, int screen) +{ + char curs[] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, ptgrab, kbgrab; + struct lock *lock; + XColor color, dummy; + XSetWindowAttributes wa; + Cursor invisible; + #if DWM_LOGO_PATCH + #ifdef XINERAMA + XineramaScreenInfo *info; + int n; + #endif + #endif // DWM_LOGO_PATCH + #if AUTO_TIMEOUT_PATCH + time(&lasttouched); + #endif // AUTO_TIMEOUT_PATCH + + if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock)))) + return NULL; + + lock->screen = screen; + lock->root = RootWindow(dpy, lock->screen); + + #if BLUR_PIXELATED_SCREEN_PATCH || BACKGROUND_IMAGE_PATCH + render_lock_image(dpy, lock, image); + #endif // BLUR_PIXELATED_SCREEN_PATCH | BACKGROUND_IMAGE_PATCH + + for (i = 0; i < NUMCOLS; i++) { + XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), + colorname[i], &color, &dummy); + lock->colors[i] = color.pixel; + } + + #if DWM_LOGO_PATCH + lock->x = DisplayWidth(dpy, lock->screen); + lock->y = DisplayHeight(dpy, lock->screen); + #ifdef XINERAMA + if ((info = XineramaQueryScreens(dpy, &n))) { + lock->xoff = info[0].x_org; + lock->yoff = info[0].y_org; + lock->mw = info[0].width; + lock->mh = info[0].height; + } else + #endif // XINERAMA + { + lock->xoff = lock->yoff = 0; + lock->mw = lock->x; + lock->mh = lock->y; + } + lock->drawable = XCreatePixmap(dpy, lock->root, lock->x, lock->y, DefaultDepth(dpy, screen)); + lock->gc = XCreateGC(dpy, lock->root, 0, NULL); + XSetLineAttributes(dpy, lock->gc, 1, LineSolid, CapButt, JoinMiter); + #endif // DWM_LOGO_PATCH + + /* init */ + wa.override_redirect = 1; + #if DWM_LOGO_PATCH && BLUR_PIXELATED_SCREEN_PATCH + #elif DWM_LOGO_PATCH + wa.background_pixel = lock->colors[BACKGROUND]; + #else + wa.background_pixel = lock->colors[INIT]; + #endif // DWM_LOGO_PATCH + lock->win = XCreateWindow(dpy, lock->root, 0, 0, + #if DWM_LOGO_PATCH + lock->x, + lock->y, + #else + DisplayWidth(dpy, lock->screen), + DisplayHeight(dpy, lock->screen), + #endif // DWM_LOGO_PATCH + 0, DefaultDepth(dpy, lock->screen), + CopyFromParent, + DefaultVisual(dpy, lock->screen), + CWOverrideRedirect | CWBackPixel, &wa); + #if BLUR_PIXELATED_SCREEN_PATCH || BACKGROUND_IMAGE_PATCH + if (lock->bgmap) + XSetWindowBackgroundPixmap(dpy, lock->win, lock->bgmap); + #endif // BLUR_PIXELATED_SCREEN_PATCH | BACKGROUND_IMAGE_PATCH + lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8); + invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, + &color, &color, 0, 0); + XDefineCursor(dpy, lock->win, invisible); + + #if DWM_LOGO_PATCH + resizerectangles(lock); + #endif // DWM_LOGO_PATCH + + /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */ + for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) { + if (ptgrab != GrabSuccess) { + ptgrab = XGrabPointer(dpy, lock->root, False, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, + GrabModeAsync, None, + #if UNLOCKSCREEN_PATCH + None, + #else + invisible, + #endif // UNLOCKSCREEN_PATCH + CurrentTime); + } + if (kbgrab != GrabSuccess) { + kbgrab = XGrabKeyboard(dpy, lock->root, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + } + + /* input is grabbed: we can lock the screen */ + if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) { + #if !UNLOCKSCREEN_PATCH + XMapRaised(dpy, lock->win); + #endif // UNLOCKSCREEN_PATCH + if (rr->active) + XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask); + + XSelectInput(dpy, lock->root, SubstructureNotifyMask); + #if QUICKCANCEL_PATCH + locktime = time(NULL); + #endif // QUICKCANCEL_PATCH + #if DWM_LOGO_PATCH + drawlogo(dpy, lock, INIT); + #endif // DWM_LOGO_PATCH + #if ALPHA_PATCH + unsigned int opacity = (unsigned int)(alpha * 0xffffffff); + XChangeProperty(dpy, lock->win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&opacity, 1L); + XSync(dpy, False); + #endif // ALPHA_PATCH + return lock; + } + + /* retry on AlreadyGrabbed but fail on other errors */ + if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) || + (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess)) + break; + + usleep(100000); + } + + /* we couldn't grab all input: fail out */ + if (ptgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", + screen); + if (kbgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", + screen); + return NULL; +} + +static void +usage(void) +{ + #if MESSAGE_PATCH || COLOR_MESSAGE_PATCH + die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n"); + #else + die("usage: slock [-v] [cmd [arg ...]]\n"); + #endif // MESSAGE_PATCH | COLOR_MESSAGE_PATCH +} + +int +main(int argc, char **argv) { + struct xrandr rr; + struct lock **locks; + struct passwd *pwd; + struct group *grp; + uid_t duid; + gid_t dgid; + const char *hash; + Display *dpy; + int s, nlocks, nscreens; + #if DPMS_PATCH + CARD16 standby, suspend, off; + #endif // DPMS_PATCH + #if MESSAGE_PATCH || COLOR_MESSAGE_PATCH + int i, count_fonts; + char **font_names; + #endif // MESSAGE_PATCH | COLOR_MESSAGE_PATCH + ARGBEGIN { + case 'v': + fprintf(stderr, "slock-"VERSION"\n"); + return 0; + #if MESSAGE_PATCH || COLOR_MESSAGE_PATCH + case 'm': + message = EARGF(usage()); + break; + case 'f': + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); + font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts); + for (i=0; ipw_uid; + errno = 0; + if (!(grp = getgrnam(group))) + die("slock: getgrnam %s: %s\n", group, + errno ? strerror(errno) : "group entry not found"); + dgid = grp->gr_gid; + +#ifdef __linux__ + dontkillme(); +#endif + + #if PAMAUTH_PATCH + /* the contents of hash are used to transport the current user name */ + #endif // PAMAUTH_PATCH + hash = gethash(); + errno = 0; + #if !PAMAUTH_PATCH + if (!crypt("", hash)) + die("slock: crypt: %s\n", strerror(errno)); + #endif // PAMAUTH_PATCH + + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); + + /* drop privileges */ + if (setgroups(0, NULL) < 0) + die("slock: setgroups: %s\n", strerror(errno)); + if (setgid(dgid) < 0) + die("slock: setgid: %s\n", strerror(errno)); + if (setuid(duid) < 0) + die("slock: setuid: %s\n", strerror(errno)); + + #if XRESOURCES_PATCH + config_init(dpy); + #endif // XRESOURCES_PATCH + + #if BLUR_PIXELATED_SCREEN_PATCH || BACKGROUND_IMAGE_PATCH + create_lock_image(dpy); + #endif // BLUR_PIXELATED_SCREEN_PATCH | BACKGROUND_IMAGE_PATCH + + #if KEYPRESS_FEEDBACK_PATCH + time_t t; + srand((unsigned) time(&t)); + #endif // KEYPRESS_FEEDBACK_PATCH + + /* check for Xrandr support */ + rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase); + + /* get number of screens in display "dpy" and blank them */ + nscreens = ScreenCount(dpy); + if (!(locks = calloc(nscreens, sizeof(struct lock *)))) + die("slock: out of memory\n"); + for (nlocks = 0, s = 0; s < nscreens; s++) { + if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) { + #if MESSAGE_PATCH || COLOR_MESSAGE_PATCH + writemessage(dpy, locks[s]->win, s); + #endif // MESSAGE_PATCH | COLOR_MESSAGE_PATCH + nlocks++; + } else { + break; + } + } + XSync(dpy, 0); + + /* did we manage to lock everything? */ + if (nlocks != nscreens) + return 1; + + #if DPMS_PATCH + /* DPMS magic to disable the monitor */ + if (!DPMSCapable(dpy)) + die("slock: DPMSCapable failed\n"); + if (!DPMSEnable(dpy)) + die("slock: DPMSEnable failed\n"); + if (!DPMSGetTimeouts(dpy, &standby, &suspend, &off)) + die("slock: DPMSGetTimeouts failed\n"); + if (!standby || !suspend || !off) + die("slock: at least one DPMS variable is zero\n"); + if (!DPMSSetTimeouts(dpy, monitortime, monitortime, monitortime)) + die("slock: DPMSSetTimeouts failed\n"); + + XSync(dpy, 0); + #endif // DPMS_PATCH + + /* run post-lock command */ + if (argc > 0) { + switch (fork()) { + case -1: + die("slock: fork failed: %s\n", strerror(errno)); + case 0: + if (close(ConnectionNumber(dpy)) < 0) + die("slock: close: %s\n", strerror(errno)); + execvp(argv[0], argv); + fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno)); + _exit(1); + } + } + + /* everything is now blank. Wait for the correct password */ + readpw(dpy, &rr, locks, nscreens, hash); + #if DPMS_PATCH + /* reset DPMS values to inital ones */ + DPMSSetTimeouts(dpy, standby, suspend, off); + XSync(dpy, 0); + #endif // DPMS_PATCH + + #if DWM_LOGO_PATCH + for (nlocks = 0, s = 0; s < nscreens; s++) { + XFreePixmap(dpy, locks[s]->drawable); + XFreeGC(dpy, locks[s]->gc); + } + + XSync(dpy, 0); + XCloseDisplay(dpy); + #endif // DWM_LOGO_PATCH + + return 0; +} diff --git a/slock-flexipatch/util.h b/slock-flexipatch/util.h new file mode 100644 index 0000000..6f748b8 --- /dev/null +++ b/slock-flexipatch/util.h @@ -0,0 +1,2 @@ +#undef explicit_bzero +void explicit_bzero(void *, size_t); diff --git a/st-flexipatch/config.mk b/st-flexipatch/config.mk index b0fe199..051ae2a 100644 --- a/st-flexipatch/config.mk +++ b/st-flexipatch/config.mk @@ -13,7 +13,7 @@ X11LIB = /usr/X11R6/lib PKG_CONFIG = pkg-config # Uncomment this for the alpha patch / ALPHA_PATCH -#XRENDER = -lXrender +XRENDER = -lXrender # Uncomment this for the themed cursor patch / THEMED_CURSOR_PATCH #XCURSOR = -lXcursor diff --git a/st-flexipatch/patches.def.h b/st-flexipatch/patches.def.h index 3444df7..60b0a88 100644 --- a/st-flexipatch/patches.def.h +++ b/st-flexipatch/patches.def.h @@ -22,7 +22,7 @@ * https://github.com/juliusHuelsmann/st-focus/ * https://st.suckless.org/patches/alpha_focus_highlight/ */ -#define ALPHA_FOCUS_HIGHLIGHT_PATCH 1 +#define ALPHA_FOCUS_HIGHLIGHT_PATCH 0 /* Adds gradient transparency to st, depends on the alpha patch. * https://st.suckless.org/patches/gradient/ @@ -274,7 +274,7 @@ * program of choice, e.g. open a file, view an image, open a URL. * https://st.suckless.org/patches/right_click_to_plumb/ */ -#define RIGHTCLICKTOPLUMB_PATCH 1 +#define RIGHTCLICKTOPLUMB_PATCH 0 /* Scroll back through terminal output using Shift+{PageUp, PageDown}. * https://st.suckless.org/patches/scrollback/ @@ -378,7 +378,7 @@ * scroll program. * https://st.suckless.org/patches/universcroll/ */ -#define UNIVERSCROLL_PATCH 0 +#define UNIVERSCROLL_PATCH 1 /* Use XftFontMatch in place of FcFontMatch. * @@ -407,7 +407,7 @@ * https://github.com/juliusHuelsmann/st-history-vim * https://st.suckless.org/patches/vim_browse/ */ -#define VIM_BROWSE_PATCH 1 +#define VIM_BROWSE_PATCH 0 /* Briefly inverts window content on terminal bell event. * https://st.suckless.org/patches/visualbell/ diff --git a/tabbed/.gitignore b/tabbed-flexipatch/.gitignore similarity index 52% rename from tabbed/.gitignore rename to tabbed-flexipatch/.gitignore index 7f45e15..d3d9bd9 100644 --- a/tabbed/.gitignore +++ b/tabbed-flexipatch/.gitignore @@ -1,3 +1,6 @@ *.o tabbed +xembed config.h +patches.h + diff --git a/tabbed/LICENSE b/tabbed-flexipatch/LICENSE similarity index 100% rename from tabbed/LICENSE rename to tabbed-flexipatch/LICENSE diff --git a/tabbed/Makefile b/tabbed-flexipatch/Makefile similarity index 94% rename from tabbed/Makefile rename to tabbed-flexipatch/Makefile index 1b95d15..2db479f 100644 --- a/tabbed/Makefile +++ b/tabbed-flexipatch/Makefile @@ -19,12 +19,16 @@ options: @echo CC $< @${CC} -c ${CFLAGS} $< -${OBJ}: config.h config.mk +${OBJ}: config.h config.mk 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 $@ + .o: @echo CC -o $@ @${CC} -o $@ $< ${LDFLAGS} diff --git a/tabbed/README b/tabbed-flexipatch/README similarity index 100% rename from tabbed/README rename to tabbed-flexipatch/README diff --git a/tabbed-flexipatch/README.md b/tabbed-flexipatch/README.md new file mode 100644 index 0000000..bb9ca57 --- /dev/null +++ b/tabbed-flexipatch/README.md @@ -0,0 +1,66 @@ +Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this tabbed 0.7 (5ddbc73, 2022-10-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/tabbed-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/tabbed/](https://tools.suckless.org/tabbed/) for details on tabbed, how to install it and how it works. + +--- + +### Changelog: + +2022-03-14 - Added the awesomebar patch + +2021-07-29 - Added the bar-height and xresources patches + +2021-07-26 - Added the center patch and the bottom tabs patch + +2020-09-11 - Added icon patch + +2020-04-03 - Added alpha, autohide, clientnumber, hidetabs, keycode and keyrelease patches + +### Patches included: + + - [alpha](https://tools.suckless.org/tabbed/patches/alpha/) + - the alpha patch allows tabbed to handle windows with transparency + + - [autohide](https://tools.suckless.org/tabbed/patches/autohide/) + - hides the tab bar if only one tab is open + + - awesomebar + - evenly divides tab bar space between the tabbed windows + + - [bar-height](https://tools.suckless.org/tabbed/patches/bar-height/) + - allows the height of the bar to be manually specified + + - [bottomtabs](https://github.com/bakkeby/patches/blob/master/tabbed/tabbed-bottomtabs-0.6-20200512-dabf6a2.diff) + - moves the tabs / bar to the bottom of the tabbed window + + - [center](https://github.com/bakkeby/patches/blob/master/tabbed/tabbed-center-0.6-20200512-dabf6a2.diff) + - centers window titles in tabs + + - [clientnumber](https://tools.suckless.org/tabbed/patches/clientnumber/) + - prints the position number of the client before the window title + + - [hidetabs](https://tools.suckless.org/tabbed/patches/hidetabs/) + - this patch hides all the tabs and only shows them when Mod+Shift is pressed + + - [icon](https://tools.suckless.org/tabbed/patches/icon/) + - this patch gives tabbed an icon + - the icon is the currently selected tab's icon + - if the selected tab has no icon (or no tab is selected), use a default icon + + - [keycode](https://tools.suckless.org/tabbed/patches/keycode/) + - with this patch, handling key input is done with keycodes instead of keysyms making the keyboard layout independent + + - [keyrelease](https://tools.suckless.org/tabbed/patches/keyrelease/) + - this patch enables for function handling on KeyRelease events + + - [xresources](https://tools.suckless.org/tabbed/patches/xresources/) + - allows tabbed colors to be defined via Xresources diff --git a/tabbed/TODO b/tabbed-flexipatch/TODO similarity index 100% rename from tabbed/TODO rename to tabbed-flexipatch/TODO diff --git a/tabbed/arg.h b/tabbed-flexipatch/arg.h similarity index 100% rename from tabbed/arg.h rename to tabbed-flexipatch/arg.h diff --git a/tabbed-flexipatch/config.def.h b/tabbed-flexipatch/config.def.h new file mode 100644 index 0000000..1134f61 --- /dev/null +++ b/tabbed-flexipatch/config.def.h @@ -0,0 +1,138 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static char font[] = "Hack Nerd Font:size=12"; +static char* normbgcolor = "#222222"; +static char* normfgcolor = "#cccccc"; +static char* selbgcolor = "#555555"; +static char* selfgcolor = "#ffffff"; +static char* urgbgcolor = "#111111"; +static char* urgfgcolor = "#cc0000"; +static const char before[] = "<"; +static const char after[] = ">"; +static const char titletrim[] = "..."; +#if AWESOMEBAR_PATCH +static int tabwidth = 200; +#else +static const int tabwidth = 200; +#endif // AWESOMEBAR_PATCH +static const Bool foreground = True; +static Bool urgentswitch = False; + +#if BAR_HEIGHT_PATCH +static const int barheight = 0; /* 0 means derive by font (default), otherwise absolute height */ +#endif // BAR_HEIGHT_PATCH + +/* + * Where to place a new tab when it is opened. When npisrelative is True, + * then the current position is changed + newposition. If npisrelative + * is False, then newposition is an absolute position. + */ +static int newposition = 0; +static Bool npisrelative = False; + +#define SETPROP(p) { \ + .v = (char *[]){ "/bin/sh", "-c", \ + "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ + "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ + "xargs -0 printf %b | dmenu -l 10 -w $1`\" &&" \ + "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ + p, winid, NULL \ + } \ +} + +#if XRESOURCES_PATCH +/* + * Xresources preferences to load at startup + */ +ResourcePref resources[] = { + { "font", STRING, &font }, + { "color0", STRING, &normbgcolor }, + { "color4", STRING, &normfgcolor }, + { "color4", STRING, &selbgcolor }, + { "color7", STRING, &selfgcolor }, + { "color2", STRING, &urgbgcolor }, + { "color3", STRING, &urgfgcolor }, +}; +#endif // XRESOURCES_PATCH + +#define MODKEY ControlMask +#if KEYCODE_PATCH +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY|ShiftMask, 36, focusonce, { 0 } }, + { MODKEY|ShiftMask, 36, spawn, { 0 } }, + { MODKEY|ShiftMask, 46, rotate, { .i = +1 } }, + { MODKEY|ShiftMask, 43, rotate, { .i = -1 } }, + { MODKEY|ShiftMask, 44, movetab, { .i = -1 } }, + { MODKEY|ShiftMask, 45, movetab, { .i = +1 } }, + { MODKEY, 23, rotate, { .i = 0 } }, + { MODKEY, 49, spawn, SETPROP("_TABBED_SELECT_TAB") }, + { MODKEY, 10, move, { .i = 0 } }, + { MODKEY, 11, move, { .i = 1 } }, + { MODKEY, 12, move, { .i = 2 } }, + { MODKEY, 13, move, { .i = 3 } }, + { MODKEY, 14, move, { .i = 4 } }, + { MODKEY, 15, move, { .i = 5 } }, + { MODKEY, 16, move, { .i = 6 } }, + { MODKEY, 17, move, { .i = 7 } }, + { MODKEY, 18, move, { .i = 8 } }, + { MODKEY, 19, move, { .i = 9 } }, + { MODKEY, 24, killclient, { 0 } }, + { MODKEY, 30, focusurgent, { .v = NULL } }, + { MODKEY|ShiftMask, 30, toggle, { .v = (void*) &urgentswitch } }, + { 0, 95, fullscreen, { 0 } }, + #if HIDETABS_PATCH + { MODKEY, 50, showbar, { .i = 1 } }, + { ShiftMask, 37, showbar, { .i = 1 } }, + #endif // HIDETABS_PATCH +}; +#else +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY|ShiftMask, XK_Return, focusonce, { 0 } }, + { MODKEY|ShiftMask, XK_Return, spawn, { 0 } }, + + { MODKEY|ShiftMask, XK_l, rotate, { .i = +1 } }, + { MODKEY|ShiftMask, XK_h, rotate, { .i = -1 } }, + { MODKEY|ShiftMask, XK_j, movetab, { .i = -1 } }, + { MODKEY|ShiftMask, XK_k, movetab, { .i = +1 } }, + { MODKEY, XK_Tab, rotate, { .i = 0 } }, + + { MODKEY, XK_grave, spawn, SETPROP("_TABBED_SELECT_TAB") }, + { MODKEY, XK_1, move, { .i = 0 } }, + { MODKEY, XK_2, move, { .i = 1 } }, + { MODKEY, XK_3, move, { .i = 2 } }, + { MODKEY, XK_4, move, { .i = 3 } }, + { MODKEY, XK_5, move, { .i = 4 } }, + { MODKEY, XK_6, move, { .i = 5 } }, + { MODKEY, XK_7, move, { .i = 6 } }, + { MODKEY, XK_8, move, { .i = 7 } }, + { MODKEY, XK_9, move, { .i = 8 } }, + { MODKEY, XK_0, move, { .i = 9 } }, + + { MODKEY, XK_q, killclient, { 0 } }, + + { MODKEY, XK_u, focusurgent, { 0 } }, + { MODKEY|ShiftMask, XK_u, toggle, { .v = (void*) &urgentswitch } }, + + { 0, XK_F11, fullscreen, { 0 } }, + #if HIDETABS_PATCH + { MODKEY, XK_Shift_L, showbar, { .i = 1 } }, + { ShiftMask, XK_Control_L, showbar, { .i = 1 } }, + #endif // HIDETABS_PATCH +}; +#endif // KEYCODE_PATCH + +#if KEYRELEASE_PATCH +static const Key keyreleases[] = { + /* modifier key function argument */ + #if HIDETABS_PATCH + { MODKEY|ShiftMask, XK_Shift_L, showbar, { .i = 0 } }, + { MODKEY|ShiftMask, XK_Control_L, showbar, { .i = 0 } }, + #else + { 0, XK_Shift_L, NULL, { 0 } }, + #endif // HIDETABS_PATCH + +}; +#endif // KEYRELEASE_PATCH diff --git a/tabbed/config.mk b/tabbed-flexipatch/config.mk similarity index 80% rename from tabbed/config.mk rename to tabbed-flexipatch/config.mk index 5477af4..8bd9dab 100644 --- a/tabbed/config.mk +++ b/tabbed-flexipatch/config.mk @@ -1,5 +1,5 @@ # tabbed version -VERSION = 0.6 +VERSION = 0.7 # Customize below to fit your system @@ -16,9 +16,12 @@ FREETYPEINC = /usr/include/freetype2 # OpenBSD (uncomment) #FREETYPEINC = ${X11INC}/freetype2 +# Uncomment this for the alpha patch / ALPHA_PATCH +XRENDER = -lXrender + # includes and libs INCS = -I. -I/usr/include -I$(X11INC) -I${FREETYPEINC} -LIBS = -L/usr/lib -lc -lX11 -lfontconfig -lXft -lXrender +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${FREETYPELIBS} ${XRENDER} # flags CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE diff --git a/tabbed-flexipatch/patch/hidebar.c b/tabbed-flexipatch/patch/hidebar.c new file mode 100644 index 0000000..fccfc2d --- /dev/null +++ b/tabbed-flexipatch/patch/hidebar.c @@ -0,0 +1,6 @@ +void +showbar(const Arg *arg) +{ + barvisibility = arg->i; + drawbar(); +} \ No newline at end of file diff --git a/tabbed-flexipatch/patch/hidebar.h b/tabbed-flexipatch/patch/hidebar.h new file mode 100644 index 0000000..b5b4ef0 --- /dev/null +++ b/tabbed-flexipatch/patch/hidebar.h @@ -0,0 +1 @@ +static void showbar(const Arg *arg); \ No newline at end of file diff --git a/tabbed-flexipatch/patch/icon.c b/tabbed-flexipatch/patch/icon.c new file mode 100644 index 0000000..4562c5e --- /dev/null +++ b/tabbed-flexipatch/patch/icon.c @@ -0,0 +1,41 @@ +static unsigned long icon[ICON_WIDTH * ICON_HEIGHT + 2]; + +void +xseticon(void) +{ + Atom ret_type; + XWMHints *wmh, *cwmh; + int ret_format; + unsigned long ret_nitems, ret_nleft; + long offset = 0L; + unsigned char *data; + + wmh = XGetWMHints(dpy, win); + wmh->flags &= ~(IconPixmapHint | IconMaskHint); + wmh->icon_pixmap = wmh->icon_mask = None; + + + if (XGetWindowProperty(dpy, clients[sel]->win, wmatom[WMIcon], offset, LONG_MAX, False, + XA_CARDINAL, &ret_type, &ret_format, &ret_nitems, + &ret_nleft, &data) == Success && + ret_type == XA_CARDINAL && ret_format == 32) + { + XChangeProperty(dpy, win, wmatom[WMIcon], XA_CARDINAL, 32, + PropModeReplace, data, ret_nitems); + } else if ((cwmh = XGetWMHints(dpy, clients[sel]->win)) && cwmh->flags & IconPixmapHint) { + XDeleteProperty(dpy, win, wmatom[WMIcon]); + wmh->flags |= IconPixmapHint; + wmh->icon_pixmap = cwmh->icon_pixmap; + if (cwmh->flags & IconMaskHint) { + wmh->flags |= IconMaskHint; + wmh->icon_mask = cwmh->icon_mask; + } + XFree(cwmh); + } else { + XChangeProperty(dpy, win, wmatom[WMIcon], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) icon, ICON_WIDTH * ICON_HEIGHT + 2); + } + XSetWMHints(dpy, win, wmh); + XFree(wmh); + XFree(data); +} \ No newline at end of file diff --git a/tabbed-flexipatch/patch/icon.h b/tabbed-flexipatch/patch/icon.h new file mode 100644 index 0000000..7a740e5 --- /dev/null +++ b/tabbed-flexipatch/patch/icon.h @@ -0,0 +1,43 @@ +/* GIMP RGBA C-Source image dump (icon.c) */ + +#define ICON_WIDTH (16) +#define ICON_HEIGHT (16) +#define ICON_BYTES_PER_PIXEL (4) /* 2:RGB16, 3:RGB, 4:RGBA */ +#define ICON_COMMENT \ + "GIMP -> Export -> C-Source -> Prefixed name = ICON, Use macros, Save alpha" +#define ICON_PIXEL_DATA ((unsigned char*) ICON_pixel_data) +static const unsigned char ICON_pixel_data[16 * 16 * 4 + 1] = + "\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000" + "\000\377\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000" + "\000\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000" + "\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" + "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" + "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" + "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" + "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" + "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" + "\000\377\000\000\000\377\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000" + "\000\377\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + "\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377" + "\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000" + "\000\000\377\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" + "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" + "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" + "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" + "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377" + "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000" + "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + "\000\000\377\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\001\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000" + "\000\000\377\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\377\000\000\000\377"; + +static void xseticon(void); \ No newline at end of file diff --git a/tabbed-flexipatch/patch/include.c b/tabbed-flexipatch/patch/include.c new file mode 100644 index 0000000..59a24eb --- /dev/null +++ b/tabbed-flexipatch/patch/include.c @@ -0,0 +1,13 @@ +/* Patches */ +#if HIDETABS_PATCH +#include "hidebar.c" +#endif +#if ICON_PATCH +#include "icon.c" +#endif +#if KEYRELEASE_PATCH +#include "keyrelease.c" +#endif +#if XRESOURCES_PATCH +#include "xresources.c" +#endif \ No newline at end of file diff --git a/tabbed-flexipatch/patch/include.h b/tabbed-flexipatch/patch/include.h new file mode 100644 index 0000000..1ef3327 --- /dev/null +++ b/tabbed-flexipatch/patch/include.h @@ -0,0 +1,13 @@ +/* Patches */ +#if HIDETABS_PATCH +#include "hidebar.h" +#endif +#if ICON_PATCH +#include "icon.h" +#endif +#if KEYRELEASE_PATCH +#include "keyrelease.h" +#endif +#if XRESOURCES_PATCH +#include "xresources.h" +#endif \ No newline at end of file diff --git a/tabbed-flexipatch/patch/keyrelease.c b/tabbed-flexipatch/patch/keyrelease.c new file mode 100644 index 0000000..4cf422c --- /dev/null +++ b/tabbed-flexipatch/patch/keyrelease.c @@ -0,0 +1,15 @@ +void +keyrelease(const XEvent *e) +{ + const XKeyEvent *ev = &e->xkey; + unsigned int i; + KeySym keysym; + + keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); + for (i = 0; i < LENGTH(keyreleases); i++) { + if (keysym == keyreleases[i].keysym && + CLEANMASK(keyreleases[i].mod) == CLEANMASK(ev->state) && + keyreleases[i].func) + keyreleases[i].func(&(keyreleases[i].arg)); + } +} \ No newline at end of file diff --git a/tabbed-flexipatch/patch/keyrelease.h b/tabbed-flexipatch/patch/keyrelease.h new file mode 100644 index 0000000..f58d2a3 --- /dev/null +++ b/tabbed-flexipatch/patch/keyrelease.h @@ -0,0 +1 @@ +static void keyrelease(const XEvent *e); \ No newline at end of file diff --git a/tabbed-flexipatch/patch/xresources.c b/tabbed-flexipatch/patch/xresources.c new file mode 100644 index 0000000..5316869 --- /dev/null +++ b/tabbed-flexipatch/patch/xresources.c @@ -0,0 +1,50 @@ +void +config_init(void) +{ + char *resm; + XrmDatabase db; + ResourcePref *p; + + XrmInitialize(); + resm = XResourceManagerString(dpy); + if (!resm) + return; + + db = XrmGetStringDatabase(resm); + for (p = resources; p < resources + LENGTH(resources); p++) + resource_load(db, p->name, p->type, p->dst); +} + +int +resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) +{ + char **sdst = dst; + int *idst = dst; + float *fdst = dst; + + char fullname[256]; + char fullclass[256]; + char *type; + XrmValue ret; + + snprintf(fullname, sizeof(fullname), "%s.%s", "tabbed", name); + snprintf(fullclass, sizeof(fullclass), "%s.%s", "tabbed", name); + fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0'; + + XrmGetResource(db, fullname, fullclass, &type, &ret); + if (ret.addr == NULL || strncmp("String", type, 64)) + return 1; + + switch (rtype) { + case STRING: + *sdst = ret.addr; + break; + case INTEGER: + *idst = strtoul(ret.addr, NULL, 10); + break; + case FLOAT: + *fdst = strtof(ret.addr, NULL); + break; + } + return 0; +} \ No newline at end of file diff --git a/tabbed-flexipatch/patch/xresources.h b/tabbed-flexipatch/patch/xresources.h new file mode 100644 index 0000000..35c094b --- /dev/null +++ b/tabbed-flexipatch/patch/xresources.h @@ -0,0 +1,17 @@ +#include + +/* Xresources preferences */ +enum resource_type { + STRING = 0, + INTEGER = 1, + FLOAT = 2 +}; + +typedef struct { + char *name; + enum resource_type type; + void *dst; +} ResourcePref; + +static void config_init(void); +static int resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst); \ No newline at end of file diff --git a/tabbed-flexipatch/patches.def.h b/tabbed-flexipatch/patches.def.h new file mode 100644 index 0000000..bf0a7fa --- /dev/null +++ b/tabbed-flexipatch/patches.def.h @@ -0,0 +1,83 @@ +/* + * This file contains patch control flags. + * + * In principle you should be able to mix and match any patches + * you may want. In cases where patches are logically incompatible + * one patch may take precedence over the other as noted in the + * relevant descriptions. + */ + +/* Patches */ + +/* This patch allows tabbed to handle windows with transparency. + * You need to uncomment the corresponding line in config.mk to use the -lXrender library + * when including this patch. + * https://tools.suckless.org/tabbed/patches/alpha/ + */ +#define ALPHA_PATCH 1 + +/* This patch hides the tab bar if only one tab is open. + * https://tools.suckless.org/tabbed/patches/autohide/ + */ +#define AUTOHIDE_PATCH 1 + +/* Named after the window title bar patch for dwm this patch divides the + * tab bar space evenly between the tabbed windows. + */ +#define AWESOMEBAR_PATCH 1 + +/* This patch allows the height of the bar to be manually specified. + * https://tools.suckless.org/tabbed/patches/bar-height/ + */ +#define BAR_HEIGHT_PATCH 1 + +/* This patch moves the tabs / bar to the bottom of the tabbed window. + * https://github.com/bakkeby/patches/blob/master/tabbed/tabbed-bottomtabs-0.6-20200512-dabf6a2.diff + */ +#define BOTTOM_TABS_PATCH 0 + +/* This patch centers the tab text. + * https://www.reddit.com/r/suckless/comments/oi4zjl/tabbed_text_alignment/ + * https://github.com/bakkeby/patches/blob/master/tabbed/tabbed-center-0.6-20200512-dabf6a2.diff + */ +#define CENTER_PATCH 1 + +/* This patch prints the position number of the client before the window title. + * https://tools.suckless.org/tabbed/patches/clientnumber/ + */ +#define CLIENTNUMBER_PATCH 1 + +/* This patch hides all the tabs and only shows them when Mod+Shift is pressed. All functions + * with switching, rotating, and creating tabs involve Mod+Shift. When not doing one of these + * functions, visibility of the tabs is not needed. + * This patch relies on the keyrelease patch to support show/hide on keypress/keyrelease. + * https://tools.suckless.org/tabbed/patches/hidetabs/ + */ +#define HIDETABS_PATCH 1 + +/* This patch gives tabbed an icon. This icon is the currently selected tab's icon. + * If the selected tab has no icon (or no tab is selected), use a (admittedly ugly) default icon. + * + * This patch supports both the new EWMH (_NET_WM_ICON) and + * legacy ICCCM (WM_ICON) ways of setting a window's icon. + * + * https://tools.suckless.org/tabbed/patches/icon/ + */ +#define ICON_PATCH 1 + +/* With this patch, handling key input is done with keycodes instead of keysyms making + * the keyboard layout independent. + * https://tools.suckless.org/tabbed/patches/keycode/ + */ +#define KEYCODE_PATCH 0 + +/* This patch enables for function handling on KeyRelease events. + * For example usage see: hidetabs + * https://tools.suckless.org/tabbed/patches/keyrelease/ + */ +#define KEYRELEASE_PATCH 1 + +/* This patch allows tabbed colors to be defined via Xresources. + * https://tools.suckless.org/tabbed/patches/xresources/ + */ +#define XRESOURCES_PATCH 1 diff --git a/tabbed/tabbed.1 b/tabbed-flexipatch/tabbed.1 similarity index 100% rename from tabbed/tabbed.1 rename to tabbed-flexipatch/tabbed.1 diff --git a/tabbed/tabbed.c b/tabbed-flexipatch/tabbed.c similarity index 84% rename from tabbed/tabbed.c rename to tabbed-flexipatch/tabbed.c index c01e0fc..8f15629 100644 --- a/tabbed/tabbed.c +++ b/tabbed-flexipatch/tabbed.c @@ -17,6 +17,7 @@ #include #include +#include "patches.h" #include "arg.h" /* XEMBED messages */ @@ -48,8 +49,19 @@ #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) enum { ColFG, ColBG, ColLast }; /* color */ -enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, - XEmbed, WMSelectTab, WMLast }; /* default atoms */ +enum { + WMProtocols, + WMDelete, + WMName, + WMState, + WMFullscreen, + XEmbed, + WMSelectTab, + #if ICON_PATCH + WMIcon, + #endif // ICON_PATCH + WMLast +}; /* default atoms */ typedef union { int i; @@ -58,7 +70,11 @@ typedef union { typedef struct { unsigned int mod; + #if KEYCODE_PATCH + KeyCode keycode; + #else KeySym keysym; + #endif // KEYCODE_PATCH void (*func)(const Arg *); const Arg arg; } Key; @@ -136,6 +152,8 @@ static void updatetitle(int c); static int xerror(Display *dpy, XErrorEvent *ee); static void xsettitle(Window w, const char *str); +#include "patch/include.h" + /* variables */ static int screen; static void (*handler[LASTEvent]) (const XEvent *) = { @@ -149,10 +167,16 @@ static void (*handler[LASTEvent]) (const XEvent *) = { [Expose] = expose, [FocusIn] = focusin, [KeyPress] = keypress, + #if KEYRELEASE_PATCH + [KeyRelease] = keyrelease, + #endif // KEYRELEASE_PATCH [MapRequest] = maprequest, [PropertyNotify] = propertynotify, }; -static int bh, obh, wx, wy, ww, wh, vbh; +static int bh, obh, wx, wy, ww, wh; +#if AUTOHIDE_PATCH || HIDETABS_PATCH +static int vbh; +#endif // AUTOHIDE_PATCH | HIDETABS_PATCH static unsigned int numlockmask; static Bool running = True, nextfocus, doinitspawn = True, fillagain = False, closelastclient = False, @@ -169,15 +193,22 @@ static char winid[64]; static char **cmd; static char *wmname = "tabbed"; static const char *geometry; +#if HIDETABS_PATCH +static Bool barvisibility = False; +#endif // HIDETABS_PATCH +#if ALPHA_PATCH static Colormap cmap; static Visual *visual = NULL; +#endif // ALPHA_PATCH char *argv0; /* configuration, allows nested code to access above variables */ #include "config.h" +#include "patch/include.c" + void buttonpress(const XEvent *e) { @@ -185,7 +216,11 @@ buttonpress(const XEvent *e) int i, fc; Arg arg; + #if BOTTOM_TABS_PATCH + if (ev->y < wh - bh) + #else if (ev->y < 0 || ev->y > bh) + #endif // BOTTOM_TABS_PATCH return; if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x < 0) @@ -257,8 +292,14 @@ configurenotify(const XEvent *e) ww = ev->width; wh = ev->height; XFreePixmap(dpy, dc.drawable); + #if ALPHA_PATCH dc.drawable = XCreatePixmap(dpy, win, ww, wh, 32); + #else + dc.drawable = XCreatePixmap(dpy, root, ww, wh, + DefaultDepth(dpy, screen)); + #endif // ALPHA_PATCH + if (!obh && (wh <= bh)) { obh = bh; bh = 0; @@ -326,31 +367,67 @@ void drawbar(void) { XftColor *col; - int c, cc, fc, width, nbh, i; + int c, cc, fc, width; + #if AUTOHIDE_PATCH || HIDETABS_PATCH + int nbh; + #endif // AUTOHIDE_PATCH | HIDETABS_PATCH char *name = NULL; - char tabtitle[256]; + #if CLIENTNUMBER_PATCH + char tabtitle[312]; + #endif // CLIENTNUMBER_PATCH + #if BOTTOM_TABS_PATCH + int by = wh - bh; + #else + int by = 0; + #endif // BOTTOM_TABS_PATCH + + #if AUTOHIDE_PATCH || HIDETABS_PATCH + #if AUTOHIDE_PATCH && HIDETABS_PATCH + nbh = barvisibility && nclients > 1 ? vbh : 0; + #elif HIDETABS_PATCH + nbh = barvisibility ? vbh : 0; + #elif AUTOHIDE_PATCH + nbh = nclients > 1 ? vbh : 0; + #endif + if (nbh != bh) { + bh = nbh; + #if BOTTOM_TABS_PATCH + by = wh - bh; + #endif // BOTTOM_TABS_PATCH + for (c = 0; c < nclients; c++) + #if BOTTOM_TABS_PATCH + XMoveResizeWindow(dpy, clients[c]->win, 0, 0, ww, wh - bh); + #else + XMoveResizeWindow(dpy, clients[c]->win, 0, bh, ww, wh - bh); + #endif // BOTTOM_TABS_PATCH + } + #endif // AUTOHIDE_PATCH | HIDETABS_PATCH if (nclients == 0) { dc.x = 0; dc.w = ww; XFetchName(dpy, win, &name); drawtext(name ? name : "", dc.norm); - XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, vbh, 0, 0); + #if AUTOHIDE_PATCH + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, vbh, 0, by); + #else + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, by); + #endif // AUTOHIDE_PATCH XSync(dpy, False); return; } - nbh = nclients > 1 ? vbh : 0; - if (bh != nbh) { - bh = nbh; - for (i = 0; i < nclients; i++) - XMoveResizeWindow(dpy, clients[i]->win, 0, bh, ww, wh - bh); - } + #if AUTOHIDE_PATCH || HIDETABS_PATCH if (bh == 0) return; + #endif // AUTOHIDE_PATCH | HIDETABS_PATCH width = ww; + + #if AWESOMEBAR_PATCH + tabwidth = ww / nclients; + #endif // AWESOMEBAR_PATCH cc = ww / tabwidth; if (nclients > cc) cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; @@ -379,13 +456,17 @@ drawbar(void) } else { col = clients[c]->urgent ? dc.urg : dc.norm; } + #if CLIENTNUMBER_PATCH snprintf(tabtitle, sizeof(tabtitle), "%d: %s", c + 1, clients[c]->name); drawtext(tabtitle, col); + #else + drawtext(clients[c]->name, col); + #endif // CLIENTNUMBER_PATCH dc.x += dc.w; clients[c]->tabx = dc.x; } - XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, by); XSync(dpy, False); } @@ -416,12 +497,18 @@ drawtext(const char *text, XftColor col[ColLast]) memcpy(buf, text, len); if (len < olen) { - for (i = len, j = strlen(titletrim); j && i; - buf[--i] = titletrim[--j]) - ; + for (i = len, j = strlen(titletrim); j && i; buf[--i] = titletrim[--j]); } + #if CENTER_PATCH + else + x += (dc.w - TEXTW(buf)) / 2; // center text + #endif // CENTER_PATCH + #if ALPHA_PATCH d = XftDrawCreate(dpy, dc.drawable, visual, cmap); + #else + d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen)); + #endif // ALPHA_PATCH XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len); XftDrawDestroy(d); } @@ -469,6 +556,10 @@ focus(int c) n += snprintf(&buf[n], sizeof(buf) - n, " %s", cmd[i]); xsettitle(win, buf); + #if ICON_PATCH + XChangeProperty(dpy, win, wmatom[WMIcon], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) icon, ICON_WIDTH * ICON_HEIGHT + 2); + #endif // ICON_PATCH XRaiseWindow(dpy, win); return; @@ -488,6 +579,9 @@ focus(int c) lastsel = sel; sel = c; } + #if ICON_PATCH + xseticon(); + #endif // ICON_PATCH if (clients[c]->urgent && (wmh = XGetWMHints(dpy, clients[c]->win))) { wmh->flags &= ~XUrgencyHint; @@ -589,7 +683,11 @@ getcolor(const char *colstr) { XftColor color; - if (!XftColorAllocName(dpy, visual, cmap, colstr, &color)) + #if ALPHA_PATCH + if (!XftColorAllocName(dpy, visual, cmap, colstr, &color)) + #else + if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), colstr, &color)) + #endif // ALPHA_PATCH die("%s: cannot allocate color '%s'\n", argv0, colstr); return color; @@ -676,11 +774,18 @@ keypress(const XEvent *e) { const XKeyEvent *ev = &e->xkey; unsigned int i; + #if !KEYCODE_PATCH KeySym keysym; keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); + #endif // KEYCODE_PATCH for (i = 0; i < LENGTH(keys); i++) { - if (keysym == keys[i].keysym && + if ( + #if KEYCODE_PATCH + ev->keycode == keys[i].keycode && + #else + keysym == keys[i].keysym && + #endif // KEYCODE_PATCH CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && keys[i].func) keys[i].func(&(keys[i].arg)); @@ -717,7 +822,9 @@ manage(Window w) int i, j, nextpos; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask | LockMask }; + #if !KEYCODE_PATCH KeyCode code; + #endif // KEYCODE_PATCH Client *c; XEvent e; @@ -728,6 +835,13 @@ manage(Window w) XSync(dpy, False); for (i = 0; i < LENGTH(keys); i++) { + #if KEYCODE_PATCH + for (j = 0; j < LENGTH(modifiers); ++j) { + XGrabKey(dpy, keys[i].keycode, + keys[i].mod | modifiers[j], w, + True, GrabModeAsync, GrabModeAsync); + } + #else if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) { for (j = 0; j < LENGTH(modifiers); j++) { XGrabKey(dpy, code, keys[i].mod | @@ -735,8 +849,21 @@ manage(Window w) GrabModeAsync, GrabModeAsync); } } + #endif // KEYCODE_PATCH } + #if KEYRELEASE_PATCH + for (i = 0; i < LENGTH(keyreleases); i++) { + if ((code = XKeysymToKeycode(dpy, keyreleases[i].keysym))) { + for (j = 0; j < LENGTH(modifiers); j++) { + XGrabKey(dpy, code, keyreleases[i].mod | + modifiers[j], w, True, + GrabModeAsync, GrabModeAsync); + } + } + } + #endif // KEYRELEASE_PATCH + c = ecalloc(1, sizeof *c); c->win = w; @@ -882,9 +1009,17 @@ propertynotify(const XEvent *e) } } XFree(wmh); + #if ICON_PATCH + if (c == sel) + xseticon(); + #endif // ICON_PATCH } else if (ev->state != PropertyDelete && ev->atom == XA_WM_NAME && (c = getclient(ev->window)) > -1) { updatetitle(c); + #if ICON_PATCH + } else if (ev->atom == wmatom[WMIcon] && (c = getclient(ev->window)) > -1 && c == sel) { + xseticon(); + #endif // ICON_PATCH } } @@ -895,7 +1030,11 @@ resize(int c, int w, int h) XWindowChanges wc; ce.x = 0; + #if BOTTOM_TABS_PATCH + ce.y = wc.y = 0; + #else ce.y = wc.y = bh; + #endif // BOTTOM_TABS_PATCH ce.width = wc.width = w; ce.height = wc.height = h; ce.type = ConfigureNotify; @@ -998,7 +1137,16 @@ setup(void) screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); initfont(font); - vbh = dc.h = dc.font.height + 2; + #if BAR_HEIGHT_PATCH + dc.h = (barheight ? barheight : dc.font.height + 2); + #else + dc.h = dc.font.height + 2; + #endif // BAR_HEIGHT_PATCH + #if AUTOHIDE_PATCH || HIDETABS_PATCH + vbh = dc.h; + #else + bh = dc.h; + #endif // AUTOHIDE_PATCH | HIDETABS_PATCH /* init atoms */ wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); @@ -1009,6 +1157,9 @@ setup(void) wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", False); wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False); wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False); + #if ICON_PATCH + wmatom[WMIcon] = XInternAtom(dpy, "_NET_WM_ICON", False); + #endif // ICON_PATCH /* init appearance */ wx = 0; @@ -1044,6 +1195,7 @@ setup(void) wy = dh + wy - wh - 1; } + #if ALPHA_PATCH XVisualInfo *vis; XRenderPictFormat *fmt; int nvi; @@ -1072,13 +1224,15 @@ setup(void) } cmap = XCreateColormap( dpy, root, visual, None); + #endif // ALPHA_PATCH + dc.norm[ColBG] = getcolor(normbgcolor); dc.norm[ColFG] = getcolor(normfgcolor); dc.sel[ColBG] = getcolor(selbgcolor); dc.sel[ColFG] = getcolor(selfgcolor); dc.urg[ColBG] = getcolor(urgbgcolor); dc.urg[ColFG] = getcolor(urgfgcolor); - + #if ALPHA_PATCH XSetWindowAttributes attrs; attrs.background_pixel = dc.norm[ColBG].pixel; attrs.border_pixel = dc.norm[ColFG].pixel; @@ -1086,21 +1240,29 @@ setup(void) attrs.event_mask = FocusChangeMask | KeyPressMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; - attrs.background_pixmap = None ; + attrs.background_pixmap = None; attrs.colormap = cmap; - win = XCreateWindow(dpy, root, wx, wy, - ww, wh, 0, 32, InputOutput, - visual, CWBackPixmap | CWBorderPixel | CWBitGravity - | CWEventMask | CWColormap, &attrs); + win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0, 32, InputOutput, + visual, CWBackPixmap | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &attrs); - dc.drawable = XCreatePixmap(dpy, win, ww, wh, - 32); + dc.drawable = XCreatePixmap(dpy, win, ww, wh, 32); dc.gc = XCreateGC(dpy, dc.drawable, 0, 0); + #else + dc.drawable = XCreatePixmap(dpy, root, ww, wh, + DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, + dc.norm[ColFG].pixel, dc.norm[ColBG].pixel); + #endif // ALPHA_PATCH XMapRaised(dpy, win); XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask | ButtonPressMask | ExposureMask | KeyPressMask | + #if KEYRELEASE_PATCH + KeyReleaseMask | + #endif // KEYRELEASE_PATCH PropertyChangeMask | StructureNotifyMask | SubstructureRedirectMask); xerrorxlib = XSetErrorHandler(xerror); @@ -1130,6 +1292,19 @@ setup(void) snprintf(winid, sizeof(winid), "%lu", win); setenv("XEMBED", winid, 1); + #if ICON_PATCH + /* change icon from RGBA to ARGB */ + icon[0] = ICON_WIDTH; + icon[1] = ICON_HEIGHT; + for (int i = 0; i < ICON_WIDTH * ICON_HEIGHT; ++i) { + icon[i + 2] = + ICON_PIXEL_DATA[i * 4 + 3] << 24 | + ICON_PIXEL_DATA[i * 4 + 0] << 0 | + ICON_PIXEL_DATA[i * 4 + 1] << 8 | + ICON_PIXEL_DATA[i * 4 + 2] << 16 ; + } + #endif // ICON_PATCH + nextfocus = foreground; focus(-1); } @@ -1410,6 +1585,9 @@ main(int argc, char *argv[]) if (!(dpy = XOpenDisplay(NULL))) die("%s: cannot open display\n", argv0); + #if XRESOURCES_PATCH + config_init(); + #endif // XRESOURCES_PATCH setup(); printf("0x%lx\n", win); fflush(NULL); diff --git a/tabbed/xembed.1 b/tabbed-flexipatch/xembed.1 similarity index 100% rename from tabbed/xembed.1 rename to tabbed-flexipatch/xembed.1 diff --git a/tabbed/xembed.c b/tabbed-flexipatch/xembed.c similarity index 100% rename from tabbed/xembed.c rename to tabbed-flexipatch/xembed.c diff --git a/tabbed/config.def.h b/tabbed/config.def.h deleted file mode 100644 index 69aea65..0000000 --- a/tabbed/config.def.h +++ /dev/null @@ -1,66 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -/* appearance */ -static const char font[] = "terminus-font:size=12"; -static const char* normbgcolor = "#222222"; -static const char* normfgcolor = "#cccccc"; -static const char* selbgcolor = "#555555"; -static const char* selfgcolor = "#ffffff"; -static const char* urgbgcolor = "#111111"; -static const char* urgfgcolor = "#cc0000"; -static const char before[] = "<"; -static const char after[] = ">"; -static const char titletrim[] = "..."; -static const int tabwidth = 200; -static const Bool foreground = True; -static Bool urgentswitch = False; - -/* - * Where to place a new tab when it is opened. When npisrelative is True, - * then the current position is changed + newposition. If npisrelative - * is False, then newposition is an absolute position. - */ -static int newposition = 0; -static Bool npisrelative = False; - -#define SETPROP(p) { \ - .v = (char *[]){ "/bin/sh", "-c", \ - "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ - "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ - "xargs -0 printf %b | dmenu -l 10 -w $1`\" &&" \ - "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ - p, winid, NULL \ - } \ -} - -#define MODKEY ControlMask -static Key keys[] = { - /* modifier key function argument */ - { MODKEY, XK_Return, focusonce, { 0 } }, - { MODKEY|ShiftMask, XK_Return, spawn, { 0 } }, - - { MODKEY|ShiftMask, XK_l, rotate, { .i = +1 } }, - { MODKEY|ShiftMask, XK_h, rotate, { .i = -1 } }, - { MODKEY|ShiftMask, XK_j, movetab, { .i = -1 } }, - { MODKEY|ShiftMask, XK_k, movetab, { .i = +1 } }, - { MODKEY, XK_Tab, rotate, { .i = 0 } }, - - { MODKEY, XK_grave, spawn, SETPROP("_TABBED_SELECT_TAB") }, - { MODKEY, XK_1, move, { .i = 0 } }, - { MODKEY, XK_2, move, { .i = 1 } }, - { MODKEY, XK_3, move, { .i = 2 } }, - { MODKEY, XK_4, move, { .i = 3 } }, - { MODKEY, XK_5, move, { .i = 4 } }, - { MODKEY, XK_6, move, { .i = 5 } }, - { MODKEY, XK_7, move, { .i = 6 } }, - { MODKEY, XK_8, move, { .i = 7 } }, - { MODKEY, XK_9, move, { .i = 8 } }, - { MODKEY, XK_0, move, { .i = 9 } }, - - { MODKEY, XK_q, killclient, { 0 } }, - - { MODKEY, XK_u, focusurgent, { 0 } }, - { MODKEY|ShiftMask, XK_u, toggle, { .v = (void*) &urgentswitch } }, - - { 0, XK_F11, fullscreen, { 0 } }, -}; diff --git a/tabbed/patches/alpha.diff b/tabbed/patches/alpha.diff deleted file mode 100644 index 3ce77a7..0000000 --- a/tabbed/patches/alpha.diff +++ /dev/null @@ -1,122 +0,0 @@ -diff --git a/config.mk b/config.mk -index 3a71529..095cead 100644 ---- a/config.mk -+++ b/config.mk -@@ -9,7 +9,7 @@ MANPREFIX = ${PREFIX}/share/man - - # includes and libs - INCS = -I. -I/usr/include -I/usr/include/freetype2 --LIBS = -L/usr/lib -lc -lX11 -lfontconfig -lXft -+LIBS = -L/usr/lib -lc -lX11 -lfontconfig -lXft -lXrender - - # flags - CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -diff --git a/tabbed.c b/tabbed.c -index 9a44795..b4d47d1 100644 ---- a/tabbed.c -+++ b/tabbed.c -@@ -170,6 +170,9 @@ static char **cmd; - static char *wmname = "tabbed"; - static const char *geometry; - -+static Colormap cmap; -+static Visual *visual = NULL; -+ - char *argv0; - - /* configuration, allows nested code to access above variables */ -@@ -255,8 +258,8 @@ configurenotify(const XEvent *e) - ww = ev->width; - wh = ev->height; - XFreePixmap(dpy, dc.drawable); -- dc.drawable = XCreatePixmap(dpy, root, ww, wh, -- DefaultDepth(dpy, screen)); -+ dc.drawable = XCreatePixmap(dpy, win, ww, wh, -+ 32); - if (sel > -1) - resize(sel, ww, wh - bh); - XSync(dpy, False); -@@ -399,7 +402,7 @@ drawtext(const char *text, XftColor col[ColLast]) - ; - } - -- d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen)); -+ d = XftDrawCreate(dpy, dc.drawable, visual, cmap); - XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len); - XftDrawDestroy(d); - } -@@ -564,7 +567,7 @@ getcolor(const char *colstr) - { - XftColor color; - -- if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), colstr, &color)) -+ if (!XftColorAllocName(dpy, visual, cmap, colstr, &color)) - die("%s: cannot allocate color '%s'\n", argv0, colstr); - - return color; -@@ -1016,18 +1019,60 @@ setup(void) - wy = dh + wy - wh - 1; - } - -+ XVisualInfo *vis; -+ XRenderPictFormat *fmt; -+ int nvi; -+ int i; -+ -+ XVisualInfo tpl = { -+ .screen = screen, -+ .depth = 32, -+ .class = TrueColor -+ }; -+ -+ vis = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &tpl, &nvi); -+ for(i = 0; i < nvi; i ++) { -+ fmt = XRenderFindVisualFormat(dpy, vis[i].visual); -+ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { -+ visual = vis[i].visual; -+ break; -+ } -+ } -+ -+ XFree(vis); -+ -+ if (! visual) { -+ fprintf(stderr, "Couldn't find ARGB visual.\n"); -+ exit(1); -+ } -+ -+ cmap = XCreateColormap( dpy, root, visual, None); - dc.norm[ColBG] = getcolor(normbgcolor); - dc.norm[ColFG] = getcolor(normfgcolor); - dc.sel[ColBG] = getcolor(selbgcolor); - dc.sel[ColFG] = getcolor(selfgcolor); - dc.urg[ColBG] = getcolor(urgbgcolor); - dc.urg[ColFG] = getcolor(urgfgcolor); -- dc.drawable = XCreatePixmap(dpy, root, ww, wh, -- DefaultDepth(dpy, screen)); -- dc.gc = XCreateGC(dpy, root, 0, 0); - -- win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, -- dc.norm[ColFG].pixel, dc.norm[ColBG].pixel); -+ XSetWindowAttributes attrs; -+ attrs.background_pixel = dc.norm[ColBG].pixel; -+ attrs.border_pixel = dc.norm[ColFG].pixel; -+ attrs.bit_gravity = NorthWestGravity; -+ attrs.event_mask = FocusChangeMask | KeyPressMask -+ | ExposureMask | VisibilityChangeMask | StructureNotifyMask -+ | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; -+ attrs.background_pixmap = None ; -+ attrs.colormap = cmap; -+ -+ win = XCreateWindow(dpy, root, wx, wy, -+ ww, wh, 0, 32, InputOutput, -+ visual, CWBackPixmap | CWBorderPixel | CWBitGravity -+ | CWEventMask | CWColormap, &attrs); -+ -+ dc.drawable = XCreatePixmap(dpy, win, ww, wh, -+ 32); -+ dc.gc = XCreateGC(dpy, dc.drawable, 0, 0); -+ - XMapRaised(dpy, win); - XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask | - ButtonPressMask | ExposureMask | KeyPressMask | diff --git a/tabbed/patches/tabbed-autohide.diff b/tabbed/patches/tabbed-autohide.diff deleted file mode 100644 index d9f71ba..0000000 --- a/tabbed/patches/tabbed-autohide.diff +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/tabbed.c b/tabbed.c -index eafe28a..b0b9662 100644 ---- a/tabbed.c -+++ b/tabbed.c -@@ -152,7 +152,7 @@ static void (*handler[LASTEvent]) (const XEvent *) = { - [MapRequest] = maprequest, - [PropertyNotify] = propertynotify, - }; --static int bh, obh, wx, wy, ww, wh; -+static int bh, obh, wx, wy, ww, wh, vbh; - static unsigned int numlockmask; - static Bool running = True, nextfocus, doinitspawn = True, - fillagain = False, closelastclient = False, -@@ -324,7 +324,7 @@ void - drawbar(void) - { - XftColor *col; -- int c, cc, fc, width; -+ int c, cc, fc, width, nbh, i; - char *name = NULL; - - if (nclients == 0) { -@@ -332,12 +332,21 @@ drawbar(void) - dc.w = ww; - XFetchName(dpy, win, &name); - drawtext(name ? name : "", dc.norm); -- XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); -+ XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, vbh, 0, 0); - XSync(dpy, False); - - return; - } - -+ nbh = nclients > 1 ? vbh : 0; -+ if (bh != nbh) { -+ bh = nbh; -+ for (i = 0; i < nclients; i++) -+ XMoveResizeWindow(dpy, clients[i]->win, 0, bh, ww, wh - bh); -+ } -+ if (bh == 0) -+ return; -+ - width = ww; - cc = ww / tabwidth; - if (nclients > cc) -@@ -984,7 +993,7 @@ setup(void) - screen = DefaultScreen(dpy); - root = RootWindow(dpy, screen); - initfont(font); -- bh = dc.h = dc.font.height + 2; -+ vbh = dc.h = dc.font.height + 2; - - /* init atoms */ - wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); diff --git a/tabbed/patches/tabbed-clientnumber.diff b/tabbed/patches/tabbed-clientnumber.diff deleted file mode 100644 index 430245c..0000000 --- a/tabbed/patches/tabbed-clientnumber.diff +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/tabbed.c b/tabbed.c -index d30206b..70642cb 100644 ---- a/tabbed.c -+++ b/tabbed.c -@@ -308,6 +308,7 @@ drawbar(void) { - unsigned long *col; - int c, fc, width, n = 0; - char *name = NULL; -+ char tabtitle[256]; - - if(nclients == 0) { - dc.x = 0; -@@ -353,7 +354,9 @@ drawbar(void) { - } else { - col = dc.norm; - } -- drawtext(clients[c]->name, col); -+ snprintf(tabtitle, sizeof(tabtitle), "%d: %s", -+ c + 1, clients[c]->name); -+ drawtext(tabtitle, col); - dc.x += dc.w; - clients[c]->tabx = dc.x; - } diff --git a/tabbed/tabbed b/tabbed/tabbed deleted file mode 100755 index 94422af..0000000 Binary files a/tabbed/tabbed and /dev/null differ diff --git a/tabbed/xembed b/tabbed/xembed deleted file mode 100755 index 0bfa27a..0000000 Binary files a/tabbed/xembed and /dev/null differ