shellex/0000755000175000017500000000000013033775355013021 5ustar pseyfertpseyfertshellex/CHANGELOG.md0000644000175000017500000000125513033775355014635 0ustar pseyfertpseyfertChangelog ========= 0.2 - 2016-12-27 ---------------- • Added optional sudo configuration • Bugfix: flashing window with large completion lists • Moved config handling from perl to shell • Disable beeping • Improved history file handling • Avoid using temporary files for configuration parsing • Avoid using temporary files for command execution 0.1 - 2013-09-03 ---------------- • History support (default) • Pass command-line to urxvt (support colors) • Bugfix: Correct pointer-based positioning • Bugfix: Correct focus-based positioning • Bugfix: Correctly predict terminal size, when adding strings 0.0 - 2013-08-31 ---------------- • Initial release shellex/shellex.in0000644000175000017500000000075513033775355015024 0ustar pseyfertpseyfert#!/bin/sh # shellex - shell based launcher # This is a short shellscript to set some needed variables and defaults. # See shellex(1) for information on invocation. # © 2013 Axel Wagner and contributors (see also: LICENSE) SHELLEX_PRELOAD="@PREFIX@@LIBDIR@/shellex/shellex_preload.so" export SHELLEX_SIZE_FILE=$(mktemp -t shellex-size-XXXXXXXX) exec urxvt -perl-lib @PREFIX@@LIBDIR@/shellex/urxvt -pe shellex -override-redirect -name shellex $* -e env LD_PRELOAD=$SHELLEX_PRELOAD zsh -f shellex/conf/0000755000175000017500000000000013033775355013746 5ustar pseyfertpseyfertshellex/conf/20-nobeep0000644000175000017500000000021513033775355015356 0ustar pseyfertpseyfert# disable beeping of the shellex shell. e.g. when tab-completing # © 2016 Paul Seyfert and contributors (see also: LICENSE) setopt NO_BEEP shellex/conf/10-autoexec0000644000175000017500000000157113033775355015730 0ustar pseyfertpseyfert# vim:ft=zsh # Make zsh automatically execute a command, when enter is hit # © 2013 Axel Wagner and contributors (see also: LICENSE) zmodload zsh/regex function shellex_preexec () { # In $1 the command-line is given # In $3 the command-line with expanded aliases and function-bodies is given # Execute the tempfile, then exit zsh -c $3 > /dev/null 2>&1 & disown exit } # We use preexec_functions, so that the user can decide to overwrite our choice # or ammend it by own functions preexec_functions=(shellex_preexec) # preexec doesn't get executed on empty lines, so employ zle to exit on empty lines # https://www.reddit.com/r/zsh/comments/s6t6d/is_there_an_alias_for_an_empty_line/ function empty-buffer-to-exit() { if [[ $#BUFFER == 0 ]]; then BUFFER="exit" fi } # set special widget, see man zshzle zle -N zle-line-finish empty-buffer-to-exit shellex/conf/40-sudo0000644000175000017500000000037213033775322015060 0ustar pseyfertpseyfert# vim:ft=zsh # use gksudo (or kdesudo if gksudo not exists) instead of sudo # © 2013 Johannes Visintini and contributors (see also: LICENSE) which kdesudo > /dev/null 2>&1 && alias sudo='kdesudo' which gksudo > /dev/null 2>&1 && alias sudo='gksudo' shellex/conf/shellexrc.in0000644000175000017500000000130013033775355016261 0ustar pseyfertpseyfert# vim:ft=zsh # Source all conf files # © 2016 Paul Seyfert and contributors (see also: LICENSE) # get array of all relevant files # http://stackoverflow.com/a/10981499 # http://unix.stackexchange.com/a/26825 thefiles=(@SYSCONFDIR@/shellex/* $HOME/.shellex/*(.N)) # get the basenames of all files and make unique list # http://stackoverflow.com/a/9516801 uniquified=( $( for f in "${thefiles[@]}" ; do basename $f ; done | sort -u ) ) # source each file from $HOME/.shellex if it exists there, otherwise from /etc for f in $uniquified do # -r checks if file exists and is readable if [[ -r $HOME/.shellex/$f ]] then source $HOME/.shellex/$f else source @SYSCONFDIR@/shellex/$f fi done shellex/conf/40-home_end0000644000175000017500000000030613033775322015661 0ustar pseyfertpseyfert# vim:ft=zsh # Make home and end buttons usable # © 2014 Axel Wagner and contributors (see also: LICENSE) bindkey '^[[7~' beginning-of-line bindkey '^[[8~' end-of-line bindkey '^[[3~' delete-char shellex/conf/40-sigint0000644000175000017500000000015713033775322015404 0ustar pseyfertpseyfert# vim:ft=zsh # Make zsh exit on ^C # © 2013 Axel Wagner and contributors (see also: LICENSE) trap exit SIGINT shellex/conf/99-clear0000644000175000017500000000016313033775322015210 0ustar pseyfertpseyfert# vim:ft=zsh # Clear the terminal-window at start # © 2013 Axel Wagner and contributors (see also: LICENSE) clear shellex/conf/40-escape0000644000175000017500000000030113033775355015344 0ustar pseyfertpseyfert# vim:ft=zsh # Make zsh exit on escape # © 2013 Axel Wagner and contributors (see also: LICENSE) function _shellex_exit { fc -P exit } zle -N _shellex_exit bindkey '^[' _shellex_exit shellex/conf/90-hist0000644000175000017500000000073013033775355015066 0ustar pseyfertpseyfert# vim:ft=zsh # Avoid configuration sourcing in history # © 2013 Paul Seyfert and contributors (see also: LICENSE) # ensure history gets actually written (shell gets closed before history gets # written otherwise) setopt inc_append_history HISTSIZE=100 SAVEHIST=100 # Push current history to stack (make e.g. sourcing of configuration # inaccessible to user). The stack won't be poped. Set HISTFILE to ~/.shellex # and read history from there. fc -p ~/.shellex_history shellex/conf/40-setprompt0000644000175000017500000000015413033775322016141 0ustar pseyfertpseyfert# vim:ft=zsh # Set the prompt # © 2013 Axel Wagner and contributors (see also: LICENSE) PROMPT="shellex> " shellex/doc/0000755000175000017500000000000013033775355013566 5ustar pseyfertpseyfertshellex/doc/man/0000755000175000017500000000000013033775355014341 5ustar pseyfertpseyfertshellex/doc/man/Makefile0000644000175000017500000000012413033775322015770 0ustar pseyfertpseyfertall: $(MAKE) -C ../.. mans clean: $(MAKE) -C ../.. clean-mans .PHONY: all clean shellex/doc/man/shellex.man.in0000644000175000017500000000464713033775355017122 0ustar pseyfertpseyfertshellex(1) ========== Paul Seyfert v0.2, December 2016 == NAME shellex - shell-based launcher == SYNOPSIS *shellex* [...] == OPTIONS All command-line parameters (together with some shellex-specific) are passed on to urxvt. This means, you can you e.g. '-bg grey20' for a lighter background. Using it for more than just customizing the appearance (for example adding own extensions) might stop shellex from working, so be careful. See urxvt(1) for a full list of options. == DESCRIPTION *shellex* is a shell-based launcher with a lot more features and a lot simpler design. It launches a shell (currently zsh(1)) and shows it in a small terminal (currently urxvt(1)), wrapping every command with a little bit of extra magic (redirecting stdout, stderr, disowning and closing the shell) to get more typical launcher-behaviour. This gives you a simple launcher with tab-completion and other shell-features, configurable in shell. == RESOURCES *shellex* uses two X-Resources at the moment, to manipulate its behaviour: shellex.pos:: If pointer, shellex shows the window on the window, the mousepointer is on. If focus, it uses the output, where most of the currently focused window is. Defaults to focus. shellex.edge:: On what screen edge to show the launcher (top or bottom). Defaults to top. Additionally all resources defined by urxvt (with class 'shellex' instead of 'urxvt') can be used to customize the appearance or behaviour of the used terminal window. == CONFIGURATION *shellex* configuration snippets can be found in *@PREFIX@@LIBDIR@/shellex/*. On start, *shellex* looks into @SYSCONFDIR@/shellex for default-snippets to source (usually this will be symlinks to *@PREFIX@@LIBDIR@/shellex/*) as well as into *$HOME/.shellex/* for any user-configuration. If a file of the same name exists in both locations, it will only use the one in *$HOME/.shellex/*. To customize shellex, you can do the following things in *$HOME/.shellex/*: 1. Overwrite a default by creating a new snippet of the same name 2. Not include a default by creating a symlink to */dev/null* of the same same 3. Include an example-snippet not used by default, by creating a symlink to *@PREFIX@@LIBDIR@/shellex/snippet* 4. Write you own snippets with a currently unused name To avoid naming-conflicts in the future, you should add a common suffix to all your own snippets. == AUTHORS Axel Wagner and contributors shellex/doc/man/man.mk0000644000175000017500000000060413033775322015437 0ustar pseyfertpseyfertDISTCLEAN_TARGETS += clean-mans A2X = a2x A2X_MAN_CALL = $(V_A2X)$(A2X) -f manpage --asciidoc-opts="-f doc/man/asciidoc.conf" $(A2X_FLAGS) $< MANS = \ doc/man/shellex.1 mans: $(MANS) %.1: %.man doc/man/asciidoc.conf $(A2X_MAN_CALL) %.man: %.man.in $(SED) $(sed_replace_vars) $< > $@ clean-mans: for file in $(basename $(MANS)); \ do \ rm -f $${file}.1 $${file}.man; \ done shellex/doc/man/asciidoc.conf0000644000175000017500000000076613033775355016777 0ustar pseyfertpseyfertifdef::doctype-manpage[] ifdef::backend-docbook[] [header] template::[header-declarations] {mantitle} {manvolnum} shellex 0.2 shellex Manual {manname} {manpurpose} endif::backend-docbook[] endif::doctype-manpage[] shellex/doc/autoresize.txt0000644000175000017500000000320013033775355016514 0ustar pseyfertpseyfertThe process of automatically resizing the window to match the shell-output is surprisingly complex. Normally the way the shell and terminal orchestrate themselves to do the output is the following: The terminal gets resized and does a TIOCSWINSZ ioctl on the pty-fd over which the two processes communicate, giving the new dimenions. This prompts the terminal to send the shell a SIGWINCH. The shell handles this by doing a TIOCGWINSZ ioctl on the pty which returns the data the terminal gave. zsh now uses this to determine, wether or not e.g. a tabcompletion-suggestion fits on the terminal and if not, handles it differently. This is a problem for shellex, because when is starting it's output, there is not enough space, for the tabcompletion, so even if we immediately resize the terminalwindow, it will be too late and the shell-output is screwed up. We rectify this, by injecting a custom ioctl-function into zsh via LD_PRELOAD, which rewrites all TIOCGWINSZ-requests to have a constant size. This fakes to the shell that there is more space available, then there actually is. The actual number of rows is calculated on start of the urxvt and put into a temporary file. The size is chosen a bit smaller than the screen, such that if zsh needs even more space for the tabcompletion than fits on the screen, the zsh handeling to deal with less space gets active. The filename is generated with mktemp before either zsh or urxvt start. The file gets unlinked by preload/main.c once a successful read happened. Shrinking after tab completions mostly works fine: Depending on the tabcompletion settings, shrinking breaks once one hit the maximum size limit. shellex/LICENSE0000644000175000017500000000270113033775355014026 0ustar pseyfertpseyfertCopyright © 2016 Axel Wagner and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Axel Wagner nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY Axel Wagner ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Axel Wagner BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. shellex/README.md0000644000175000017500000001630313033775355014303 0ustar pseyfertpseyfertshellex - Shell-based launcher ============================== `shellex` is supposed to be a dmenu-style launcher with a lot more features and a lot simpler design. It launches a shell (currently `zsh`) and shows it in a small terminal, wrapping every command with a little bit of extra magic (redirecting stdout, stderr, disowning and closing the shell) to get more typical launcher-behaviour. This gives you a simple launcher with tab-completion and other shell-features, configurable in shell. See this video for a short demonstration and comparison to another app starter: ![demo](http://virgilio.mib.infn.it/~seyfert/images/shellexdemo.gif) I tried to do this a few years back, then using C and implementing the terminal-operations myself. This turned out to be a very bad idea, it made the design overly complex and the state I left it in had regular segfaults and was far from working. After not much seem to happen in that direction, I decided to start again, this time using `urxvt` to do the terminal-part, which turned out to be really easy. So, this is the early prototype. It is usable and should already work and be useful, but much is not working yet. I hope this time I will continue the work for longer ;) Architecture ============ `shellex` has three parts: * [A small shell-script](shellex.in) that just starts a `urxvt` with some extra parameters * [An urxvt-extension](urxvt/shellex.in) that manages the terminal/displaying part. * [configfile](conf) that do all stuff relating to the functional behaviour (Planned) Features ================== Working: * Launching Applications (yay) * Commandline parameters * Basic Tab-completion * Starting on the right output (configurable, either the output containing the currently focused window or the output containing the mousepointer) * Dynamic resizing of the launcher-window e.g. for multiple lines of suggestions for tab-completions (see [doc/autoresize.txt](doc/autoresize.txt)) Planned, but not Implemented yet: * Buffering/showing some output, for errors etc. We have to think about some magic way to determine, wether output is helpful or the launcher should be hidden immediately * dmenu-like completion, typing part of a command still completing (maybe zsh has something to do that?) * .desktop-file integration * Your ticket here Installation ============ There are packages in * [debian](http://packages.debian.org/search?keywords=shellex&searchon=names&suite=all§ion=all&sourceid=mozilla-search) * Arch Linux: [Arch User Repository](https://aur.archlinux.org/packages/?SeB=n&K=shellex) ([latest Release](https://aur.archlinux.org/packages/shellex/) and [git-Repository](https://aur.archlinux.org/packages/shellex-git/)) * Gentoo: there is an [Overlay](https://github.com/proxypoke/gentoo-overlay) which contains shellex If you are on one of these distributions, we encourage you to install `shellex` via your package manager. Else, or if you want to help developing, just do ```sh $ git clone git://github.com/Merovius/shellex.git $ cd shellex $ make $ make install ``` for installing in `$(HOME).local` ```sh $ git clone git://github.com/Merovius/shellex.git $ cd shellex $ PREFIX=$HOME/.local make $ PREFIX=$HOME/.local make install ``` Usage ===== After installing `shellex` you probably want to bind it to a shortcut - most likely Alt+F2. How to do this depends on your desktop environment and/or window manager. ## xbindkeys If you're running [xbindkeys](http://www.nongnu.org/xbindkeys/xbindkeys.html), the entry for your `~/.xbindkeysrc` file might look like: ``` "shellex" alt + c:68 ``` ## i3 [i3](i3wm.org) shortcuts can be modified as described in the [official documentation](https://i3wm.org/docs/userguide.html). ``` bindsym Mod1+F2 exec shellex ``` Contributing ============ `shellex` is a very young project, it would highly profit from your help. The following is a not comprehensive list of highly appreciated ways to contribute: 1. Test it and make [tickets](https://github.com/Merovius/shellex/issues) for *any* problems you stumble upon or ideas you have to make it better. 2. Have a look at a [list of issues](https://github.com/Merovius/shellex/issues) and find one to fix. But please announce your intention to fix it, so that we can be sure that it will be merged and there is no duplication of effort. 3. Have a look at the [list of packaged dirstributions](https://github.com/Merovius/shellex#installation). If your favourite distribution is not on it, please package it. Please announce your intent to do so (in a ticket) and treat it as at least a mid-term commitment to maintain the package. 4. Customize `shellex` in self-contained, side-effect free config-snippets and add them - if you consider them useful to more then just yourself - in a pull-request to the conf-dir. 5. Contribute comments and documentation. Consider translating the manpage. Again, please announce your intention and again, if you translate to a language, that none of the core-developers speak (currently everything but English and German) please consider it to be at least a mid-term commitment to maintain the translation. Configuration ============= Configuration of `shellex` has two parts: The first one are X-resources. Additionally to the urxvt-class, instances run by shellex will also look for the shellex-class. This makes it possible to customize the appearance of shellex without interfering with your usual terminals. There are also two additional resources defined by shellex: Resource | Values | Default | Description ----------- | -------------- | ------- | --- shellex.pos | pointer|focus | focus | If pointer, shellex shows the window on the window, the mousepointer is on, else it uses the output, where most of the currently focused window is (falling back to the pointer-method, if the root-window is focused). shellex.edge | bottom|top | top | On what screenedge to show shellex The other source of configuration are small shell-script-snippets. When starting, `shellex` will look into `$HOME/.shellex` and into `/etc/shellex`. It will then source all the snippets in either location. If there is an identically named file in both directories, the one in your home will be preferred. This makes for a pretty flexible configuration process: Usually there will be a lot of snippets in `/usr/lib/shellex/conf`, which should be self-contained and without a lot of side-effects. In `/etc/shellex` there then are some symlinks to those snippets, making up the default-configuration on this system, together with administrator-provided additional defaults. Whenever you don't want a snippet form `/etc/shellex` to be used, just create a symlink of the same name to `/dev/null` in `$HOME/.shellex`. If you want to create your own snippets, just put them in `$HOME/.shellex` under a name not used yet and it will be automatically sourced. Command-line ============ All command-line parameters given to `shellex` are passed directly to `urxvt`, so if you want to change colors or font, you can do it through the appropriate `urxvt`-parameters (or by using resources, for persistent configuration). For example, to get a dark grey background with a slightly yellow font you might start shellex with ```sh $ shellex -bg grey15 -fg linen ``` shellex/preload/0000755000175000017500000000000013033775355014447 5ustar pseyfertpseyfertshellex/preload/preload.mk0000644000175000017500000000127013033775322016420 0ustar pseyfertpseyfertALL_TARGETS += preload/shellex_preload.so INSTALL_TARGETS += install-shellex_preload CLEAN_TARGETS += clean-shellex_preload SHELLEX_PRELOAD_LDFLAGS += -shared SHELLEX_PRELOAD_CFLAGS += -fPIC preload/shellex_preload.so: preload/main.c echo "[CC] $@" $(CC) $(SHELLEX_CPPFLAGS) $(CPPFLAGS) $(SHELLEX_CFLAGS) $(CFLAGS) $(SHELLEX_PRELOAD_CFLAGS) $(LDFLAGS) $(SHELLEX_LDFLAGS) $(SHELLEX_PRELOAD_LDFLAGS) -o $@ $< install-shellex_preload: preload/shellex_preload.so echo "[INSTALL] $<" $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)$(LIBDIR)/shellex $(INSTALL) -m 0755 $< $(DESTDIR)$(PREFIX)$(LIBDIR)/shellex/ clean-shellex_preload: echo "[CLEAN] shellex_preload" rm -f preload/shellex_preload.so shellex/preload/main.c0000644000175000017500000000364513033775355015547 0ustar pseyfertpseyfert/* * shellex - shell based launcher * This is a small LD_PRELOAD library to work around some issues * © 2013 Axel Wagner and contributors (see also: LICENSE) */ #define _GNU_SOURCE #include #include #include #include /* We can not take this from , because it would define the * ioctl-function itself */ struct winsize { unsigned short ws_row; unsigned short ws_col; unsigned short ws_xpixel; unsigned short ws_ypixel; }; int ioctl (int d, int request, char *argp) { static int (*orig_ioctl)(int, int, char *); if (orig_ioctl == NULL) { orig_ioctl = dlsym(RTLD_NEXT, "ioctl"); } /* We only care for TIOCGWINSZ ioctls */ if (request != 0x5413) { return orig_ioctl(d, request, argp); } static int max_rows = -1; /* ioctl gets called once before the perl module had the time to determine * the right size! Leave max_rows negative to indicat that it still needs to * be read from the SHELLEX_SIZE_FILE */ if (max_rows < 0 ) { char *fname = getenv("SHELLEX_SIZE_FILE"); if (fname != NULL && fname[0] != '\0') { FILE *stream = fopen(fname,"r"); char str[5] = "-500"; if (stream != NULL) { char *ret = fgets(str,5,stream); fclose(stream); if (ret != NULL) { /* this may be -500 */ max_rows = atoi(str); if (max_rows > 0 ) { unlink(fname); } } } } } int retval = orig_ioctl(d, request, (char *)argp); struct winsize *ws = (struct winsize *)argp; /* max_rows is still negative at first invocation */ int fheight = ws->ws_ypixel / ws->ws_row; ws->ws_row = (max_rows > 0) ? max_rows : 25; ws->ws_ypixel = ws->ws_row * fheight; return retval; } shellex/Makefile0000644000175000017500000000056113033775322014455 0ustar pseyfertpseyfertTOPDIR=$(shell pwd) include $(TOPDIR)/common.mk ALL_TARGETS = INSTALL_TARGETS = CLEAN_TARGETS = DISTCLEAN_TARGETS = all: real-all include preload/preload.mk include shellex.mk include urxvt/urxvt_shellex.mk include conf.mk include doc/man/man.mk real-all: $(ALL_TARGETS) install: $(INSTALL_TARGETS) clean: $(CLEAN_TARGETS) distclean: clean $(DISTCLEAN_TARGETS) shellex/common.mk0000644000175000017500000000117313033775322014636 0ustar pseyfertpseyfertINSTALL=install SED=sed ifndef PREFIX PREFIX=/usr endif ifndef SYSCONFDIR ifeq ($(PREFIX),/usr) SYSCONFDIR=/etc else SYSCONFDIR=$(PREFIX)/etc endif endif LIBDIR ?= /lib SHELLEX_CFLAGS = -std=c99 SHELLEX_CFLAGS += -Wall SHELLEX_CFLAGS += -Wunused-value sed_replace_vars := -e 's,@DESTDIR@,$(DESTDIR),g' \ -e 's,@PREFIX@,$(PREFIX),g' \ -e 's,@LIBDIR@,$(LIBDIR),g' \ -e 's,@SYSCONFDIR@,$(SYSCONFDIR),g' V ?= 0 ifeq ($(V),0) # Don't print command lines which are run .SILENT: endif # always remake the following targets .PHONY: install clean dist distclean shellex/conf.mk0000644000175000017500000000210213033775355014272 0ustar pseyfertpseyfertINSTALL_TARGETS += install-conf install-rc CLEAN_TARGETS += clean-shellexrc ALL_TARGETS += conf/shellexrc default_confs := 10-autoexec 20-nobeep 40-escape 40-home_end 40-setprompt 40-sigint 90-hist 99-clear install-conf: echo "[INSTALL] $@" $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)$(LIBDIR)/shellex/conf for file in $(wildcard conf/[0-9][0-9]-*); \ do \ $(INSTALL) -m 0644 $${file} $(DESTDIR)$(PREFIX)$(LIBDIR)/shellex/conf/; \ done $(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/shellex for link in $(default_confs); \ do \ [ -e $(DESTDIR)$(SYSCONFDIR)/shellex/$${link} ] || ln -s $(PREFIX)$(LIBDIR)/shellex/conf/$${link} $(DESTDIR)$(SYSCONFDIR)/shellex; \ done conf/shellexrc: conf/shellexrc.in echo "[SED] $@" $(SED) $(sed_replace_vars) $< > $@ install-rc: conf/shellexrc echo "[INSTALL] $@" $(INSTALL) -m 0644 conf/shellexrc $(DESTDIR)$(PREFIX)$(LIBDIR)/shellex/shellexrc [ -e $(DESTDIR)$(SYSCONFDIR)/shellexrc ] || ln -s $(PREFIX)$(LIBDIR)/shellex/shellexrc $(DESTDIR)$(SYSCONFDIR)/shellexrc clean-shellexrc: echo "[CLEAN] conf/shellexrc" rm -f conf/shellexrc shellex/urxvt/0000755000175000017500000000000013033775355014211 5ustar pseyfertpseyfertshellex/urxvt/shellex.in0000644000175000017500000002621213033775355016210 0ustar pseyfertpseyfert# vim:ft=perl #line 3 # shellex - shell based launcher # This is the urxvt extension part of shellex. # © 2013 Axel Wagner and contributors (see also: LICENSE) use X11::Protocol; use File::Temp qw|tempfile|; use File::Basename qw|basename|; use POSIX qw|ceil|; use strict; # The existing Randr-modules on CPAN seem to work only barely, so instead we # just parse the output of xrandr -q. This is an uglyness, that should go away # some time in the feature. sub get_outputs { my @outputs = (); for my $line (qx(xrandr -q)) { next unless $line =~ /\sconnected/; my ($w, $h, $x, $y) = ($line =~ /(\d+)x(\d+)\+(\d+)\+(\d+)/); push @outputs, { w => $w, h => $h, x => $x, y => $y }; } return @outputs; } # This takes a list of outputs and looks up the one, the mouse pointer # currently is on. sub geometry_from_ptr { my ($self) = @_; my @outputs = get_outputs(); my $ptr = { $self->{X}->QueryPointer($self->DefaultRootWindow) }; for my $output (@outputs) { if ($output->{x} <= $ptr->{root_x} && $ptr->{root_x} < $output->{x} + $output->{w}) { $self->{x} = $output->{x}; if ($self->{bottom}) { # The real y-coordinate will change during execution, when the window grows $self->{y} = $output->{y} + $output->{h}; } else { $self->{y} = $output->{y}; } $self->{w} = $output->{w}; $self->{h} = $output->{h}; } } } # Helper, that take a list of numbers and return the max resp. min sub max { my $max = shift; while (my $n = shift) { $max = $n > $max ? $n : $max; } return $max; } sub min { my $min = shift; while (my $n = shift) { $min = $n < $min ? $n : $min; } return $min; } # This takes a list of outputs and looks up the one, that contains most of the # window having the input focus currently sub geometry_from_focus { my ($self) = @_; my @outputs = get_outputs(); # Look up the window that currently has the input focus my ($focus, $revert) = $self->{X}->GetInputFocus(); # If the root-window is focused, we fall back to using the pointer-position if ($focus == $self->DefaultRootWindow) { return $self->geometry_from_ptr(); } my $geom = { $self->{X}->GetGeometry($focus) }; my ($fw, $fh) = ($geom->{width}, $geom->{height}); print "Focus $focus (${fw}x${fh})\n"; # The (x,y) coordinates we get are relative to the parent not the # root-window. So we just translate the coordinates of the upper-left # corner into the coordinate-system of the root-window my (undef, undef, $fx, $fy) = $self->{X}->TranslateCoordinates($focus, $self->DefaultRootWindow, 0, 0); # Returns the area (in pixel²) of the intersection of two rectangles. # To understand how it works, best draw a picture. my $intersection = sub { my ($x, $y, $w, $h) = @_; my $dx; if ($x < $fx) { $dx = $x + $w - $fx; } else { $dx = $fx + $fw - $x; } $dx = max(0, min($dx, $fw, $w)); my $dy; if ($y < $fy) { $dy = $y + $h - $fy; } else { $dy = $fy + $fh - $y; } $dy = max(0, min($dy, $fh, $h)); return $dx * $dy; }; my $max_area = 0; for my $output (@outputs) { my $area = $intersection->($output->{x}, $output->{y}, $output->{w}, $output->{h}); if ($area >= $max_area) { $max_area = $area; $self->{x} = $output->{x}; if ($self->{bottom}) { # The real y-coordinate will change during execution, when the window grows $self->{y} = $output->{y} + $output->{h}; } else { $self->{y} = $output->{y}; } $self->{w} = $output->{w}; $self->{h} = $output->{h}; } } } # This hook is run when the extension is first initialized, before any windows # are created or mapped. There is not much work we can do here. sub on_init { my ($self) = @_; $self->{X} = X11::Protocol->new($self->display_id); # Some reasonably sane values in case all our methods to determine a # geometry fails. $self->{x} = 0; $self->{y} = 0; $self->{w} = 1024; $self->{h} = 768; (); } # This hook is run after the window is created, but before it is mapped, so # this is the place to set the geometry to what we want sub on_start { my ($self) = @_; # TODO: Remove compatibility code in future version if (defined $self->x_resource("%.edge") || defined $self->x_resource("%.pos")) { print "WARNING: URxvt.shellex.* resources are deprecated and will be removed in the future. Use shellex.*\n" } if ($self->x_resource("edge") eq 'bottom' || $self->x_resource("%.edge") eq 'bottom') { print "position should be at the bottom\n"; $self->{bottom} = 1; $self->{y} = $self->{h}; } else { print "position should be at the top\n"; } if ($self->x_resource("pos") eq 'pointer' || $self->x_resource("%.pos") eq 'pointer') { print "Getting shellex-position from pointer\n"; $self->geometry_from_ptr(); } else { print "Getting shellex-position from focused window\n"; $self->geometry_from_focus(); } # This environment variable is used by the LD_PRELOAD ioctl-override to # determine the values to send to the shell # TODO revisit communication protocol (from file to pipe?) # TODO check if user defined their own SHELLEX_MAX_ROWS, which should be used like #$ENV{SHELLEX_MAX_ROWS} = $sane_max_rows < $ENV{SHELLEX_MAX_ROWS} ? $sane_max_rows : $ENV{SHELLEX_MAX_ROWS} ; my $sane_max_rows = int($self->{h} / $self->fheight) - 10; $ENV{SHELLEX_MAX_ROWS} = $sane_max_rows; my $filename = $ENV{SHELLEX_SIZE_FILE}; open(my $fh, '>', $filename); print $fh "$ENV{SHELLEX_MAX_ROWS}\n"; close $fh; print "writing max rows file done\n"; $self->{border} = $self->x_resource('internalBorder'); # the compiled-in default if the resource is not set $self->{border} //= 2; $self->{border} += $self->x_resource('externalBorder'); $self->{row_height} = $self->fheight + $self->x_resource('lineSpace'); my $height = $self->{row_height} + 2 * $self->{border}; my $y = $self->{y}; # Our initial position is different, if we have to be at the bottom $y -= $height if $self->{bottom}; $self->XMoveResizeWindow($self->parent, $self->{x}, $y, $self->{w}, $height); print "loading config\n"; $self->tt_write($self->locale_encode("unset LD_PRELOAD\n")); $self->tt_write($self->locale_encode(". @SYSCONFDIR@/shellexrc\n")); (); } # This hook is run every time a line was changed. We do some resizing here, # because this catches most cases where we would want to shrink our window. sub on_line_update { my ($self, $row) = @_; print "line_update(row = $row)\n"; # Determine the last row, that is not empty. # TODO: Does this work as intended, if there is an empty line in the # middle? my $nrow = 0; for my $i ($self->top_row .. $self->nrow-1) { if ($self->ROW_l($i) > 0) { $nrow++; } } $nrow = $nrow > 0 ? $nrow : 1; $nrow = $nrow > $ENV{SHELLEX_MAX_ROWS} ? $ENV{SHELLEX_MAX_ROWS} : $nrow; print "resizing to $nrow\n"; # If the window is supposed to be at the bottom, we have to move the # window up a little bit my $y = $self->{y}; if ($self->{bottom}) { $y -= 2+$nrow*$self->fheight; } $self->cmd_parse("\e[8;$nrow;t\e[3;$self->{x};${y}t"); (); } # Predict the number of rows the terminal will have, after adding $string at # the current position sub predict_term_size { my ($self, $string) = @_; my ($row, $col) = $self->screen_cur(); my $i = $self->top_row; my $n = 0; # We iterate over all lines and accumulate the number of rows. If the # curser is not at the current line, we can just add its number of rows to # the total, else we test, if it grows when adding the string and add an # according number to the total while ($i < $self->nrow) { my $line = $self->line($i); $i += $line->end - $line->beg + 1; unless ($line->beg <= $row && $row <= $line->end) { $n += $line->end - $line->beg + 1; next } my $len = ($row - $line->beg) * $self->ncol + $col; # Because there might be control-sequences in $string, affecting the # number of lines, we need to manually walk it for (my $j = 0; $j < length($string); $j++) { # Linebreaks mean the creating of a new line, finishing the old one if (substr($string, $j, 1) eq "\n") { $len = ($len == 0 ? 1 : $len); $n += ceil(($len * 1.0) / $self->ncol); $len = 0; next; } # Carriage-returns mean starting from the beginning. Though the new # len does not really have to be 0 (because the text is not # actually erased) it is a good enough estimate for now if (substr($string, $j, 1) eq "\r") { $len = 0; next; } # We just add one per other char. This actually might not work # correctly with wide-chars, but it is a good enough estimate for # now $len++; } $n += ceil(($len + 1.0) / $self->ncol); } return $n; } # This hook is run every time before there is text output. We resize here, # immediately before new lines would be added, which would create scrolling sub on_add_lines { my ($self, $string) = @_; my $str = $string; $str =~ s/\n/\\n/g; $str =~ s/\r/\\r/g; print "add_lines(string = \"$str\")\n"; my $nrow = $self->predict_term_size($string); $nrow = $nrow > 0 ? $nrow : 1; $nrow = $nrow > $ENV{SHELLEX_MAX_ROWS} ? $ENV{SHELLEX_MAX_ROWS} : $nrow; print "resizing to $nrow\n"; # If the window is supposed to be at the bottom, we have to move the # window up a little bit my $y = $self->{y}; if ($self->{bottom}) { $y -= 2+$nrow*$self->fheight; } $self->cmd_parse("\e[8;$nrow;t\e[3;$self->{x};${y}t"); (); } # Just for debugging sub on_size_change { my ($self, $nw, $nh) = @_; print "size_change($nw, $nh)\n"; (); } sub on_view_change { my ($self, $offset) = @_; print "view_change(offset = $offset)\n"; (); } sub on_scroll_back { my ($self, $lines, $saved) = @_; print "scroll_back(lines = $lines, saved = $saved)\n"; (); } sub on_x_event { my ($self, $event) = @_; if ($event->{type} == urxvt::EnterNotify) { $self->{X}->SetInputFocus($self->parent, 2, $self->{data}{event}{time}); $self->{X}->GetInputFocus(); } (); } # This hook is run directly after the window was mapped (= displayed on # screen). We grab the keyboard here. sub on_map_notify { my ($self, $ev) = @_; $self->{X}->SetInputFocus($self->parent, 2, $self->{data}{event}{time}); # We use GetInputFocus as a syncing-mechanism $self->{X}->GetInputFocus(); $self->vt_emask_add(urxvt::EnterWindowMask); (); } shellex/urxvt/urxvt_shellex.mk0000644000175000017500000000072513033775322017454 0ustar pseyfertpseyfertALL_TARGETS += urxvt/shellex INSTALL_TARGETS += install-urxvt_shellex CLEAN_TARGETS += clean-urxvt_shellex urxvt/shellex: urxvt/shellex.in echo "[SED] $@" $(SED) $(sed_replace_vars) $< > $@ install-urxvt_shellex: urxvt/shellex echo "[INSTALL] $<" $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)$(LIBDIR)/shellex/urxvt $(INSTALL) -m 0644 urxvt/shellex $(DESTDIR)$(PREFIX)$(LIBDIR)/shellex/urxvt/ clean-urxvt_shellex: echo "[CLEAN] urxvt/shellex" rm -f urxvt/shellex shellex/shellex.mk0000644000175000017500000000055513033775322015015 0ustar pseyfertpseyfertALL_TARGETS += shellex INSTALL_TARGETS += install-shellex CLEAN_TARGETS += clean-shellex shellex: shellex.in echo "[SED] $@" $(SED) $(sed_replace_vars) $< > $@ install-shellex: shellex echo "[INSTALL] $<" $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 0755 shellex $(DESTDIR)$(PREFIX)/bin/ clean-shellex: echo "[CLEAN] shellex" rm -f shellex