working, added scripts from selecttext and showmessage, fixed -a
This commit is contained in:
commit
dda3160389
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015, Mytchel Hammond
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
|
@ -0,0 +1,33 @@
|
|||
DESTDIR?=
|
||||
PREFIX?=/usr
|
||||
|
||||
all: nenu
|
||||
|
||||
nenu: nenu.c config.h
|
||||
gcc -o nenu nenu.c \
|
||||
-lX11 \
|
||||
-lXft \
|
||||
-lXrender \
|
||||
`pkg-config --cflags freetype2` \
|
||||
`pkg-config --cflags fontconfig` \
|
||||
`pkg-config --libs fontconfig` \
|
||||
`pkg-config --libs freetype2`
|
||||
|
||||
.PHONY:
|
||||
clean:
|
||||
rm nenu
|
||||
|
||||
.PHONY:
|
||||
install: nenu nexec nwindow ntime
|
||||
install -Dm 755 nenu ${DESTDIR}${PREFIX}/nenu
|
||||
install -Dm 755 nexec ${DESTDIR}${PREFIX}/nexec
|
||||
install -Dm 755 nwindow ${DESTDIR}${PREFIX}/nwindow
|
||||
install -Dm 755 ntime ${DESTDIR}${PREFIX}/ntime
|
||||
|
||||
.PHONY:
|
||||
uninstall:
|
||||
rm ${DESTDIR}${PREFIX}/bin/nenu
|
||||
rm ${DESTDIR}${PREFIX}/bin/nexec
|
||||
rm ${DESTDIR}${PREFIX}/bin/nwindow
|
||||
rm ${DESTDIR}${PREFIX}/bin/ntime
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
nenu
|
||||
==========
|
||||
|
||||
Nenu is a X11 utility similar to dmenu (but more like the menu's in cwm) that
|
||||
takes input from stdin and presents it to the user in a way they they can
|
||||
select what to return in a number of different ways.
|
||||
|
||||
A remake and combination of [selecttext](http://github.com/mytch444/selecttext)
|
||||
and [showmessage](http://github.com/mytch444/showmessage) that is used to
|
||||
display messages, get choices and get input from the user.
|
||||
|
||||
By default nenu takes input from stdin and display it to the user in a vertical
|
||||
list. The user then types which reduces the list to those that match. Once the
|
||||
user presses return nenu exits and prints what was typed to stdout.
|
||||
|
||||
The last non-flag argument given is used as the header.
|
||||
Avaliable flags are -a, -c, -n and -e.
|
||||
|
||||
-a: exit as soon as there is only one match.
|
||||
|
||||
-c: on exit return the match at the begining of the list. If there is no match
|
||||
nenu returns the user input.
|
||||
|
||||
-n: no input is taken and the -c flag is set. This is useful if you want to use
|
||||
nenu to show the user something but take no input.
|
||||
|
||||
-e: takes no input from stdin, nenu become a text input box.
|
||||
|
||||
-p x,y: opens the window at x,y rather than mouse coords.
|
||||
|
||||
-ap: don't shift the window so it stays inside the screen.
|
||||
|
||||
Look in nexec, ntime and nwindow for examples of use. These are useable scripts
|
||||
in their own right.
|
|
@ -0,0 +1,9 @@
|
|||
#define PADDING 4
|
||||
#define BORDER_WIDTH 2
|
||||
|
||||
#define MAX_LENGTH 512
|
||||
|
||||
char bg_name[] = "#000000";
|
||||
char fg_name[] = "#cccccc";
|
||||
|
||||
char fontstr[] = "DejaVuSansMono:pixelsize=12:antialias=true:autohint=false";
|
|
@ -0,0 +1,585 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xft/Xft.h>
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
#include <ctype.h>
|
||||
#include <strings.h>
|
||||
|
||||
#define MAX(A, B) (A > B ? A : B)
|
||||
|
||||
#include "nenu.h"
|
||||
#include "config.h"
|
||||
|
||||
Window root, win;
|
||||
int screen;
|
||||
Display *display;
|
||||
GC gc;
|
||||
Drawable buf;
|
||||
XftDraw *draw;
|
||||
Pixmap nullpixmap;
|
||||
|
||||
XIM xim;
|
||||
XIC xic;
|
||||
|
||||
int x = -1, y = -1;
|
||||
int w = 0, h = 0;
|
||||
int maxh;
|
||||
|
||||
XftColor fg, bg;
|
||||
|
||||
XftFont *font;
|
||||
int ascent, descent;
|
||||
int message_width, cursor_width;
|
||||
|
||||
int quit;
|
||||
int exit_on_one, complete_on_exit, input_bar, read_options, absolute_position;
|
||||
size_t cursor;
|
||||
char text[MAX_LENGTH];
|
||||
char *message;
|
||||
|
||||
option *options;
|
||||
option *current, *valid;
|
||||
|
||||
void usage() {
|
||||
printf(
|
||||
"usage: nenu [-a|-c|-n|-e|-p x,y|-ap] [header]\n\n"
|
||||
"nenu takes options from stdin and display them, allowing the user to input \n"
|
||||
"their desired option\n\n"
|
||||
" -a : exits as soon as there is only one match.\n"
|
||||
" -c : on exit return the match at the begining of the list. if there is\n"
|
||||
" no match nenu returns the user input\n"
|
||||
" -n : no input bar is displayed and -c is put on.\n"
|
||||
" -e : takes no input from stdin\n"
|
||||
" -p x,y: set's window position\n"
|
||||
" -ap : don't shift the window so it stays inside the screen\n"
|
||||
);
|
||||
}
|
||||
|
||||
void die(char *msg) {
|
||||
fprintf(stderr, "nenu [ERROR]: %s\n", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void finish() {
|
||||
if (complete_on_exit) {
|
||||
if (current) printf("%s\n", current->text);
|
||||
else if (valid) printf("%s\n", valid->text);
|
||||
} else if (exit_on_one) {
|
||||
if (valid) printf("%s\n", valid->text);
|
||||
} else {
|
||||
printf("%s\n", text);
|
||||
}
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
int text_width(char *str) {
|
||||
XGlyphInfo g;
|
||||
int len = strlen(str);
|
||||
|
||||
if (len == 0) return 0;
|
||||
|
||||
// If last char is space TextExtents seems to ignore it.
|
||||
if (str[len - 1] == ' ') {
|
||||
char *s = malloc(sizeof(char) * len);
|
||||
strcpy(s, str);
|
||||
s[len - 1] = '-';
|
||||
str = s;
|
||||
}
|
||||
|
||||
XftTextExtentsUtf8(display, font, str, len, &g);
|
||||
return g.width;
|
||||
}
|
||||
|
||||
void draw_string(char *str, int x, int y) {
|
||||
XftDrawStringUtf8(draw, &fg, font, x, y, (FcChar8 *) str, strlen(str));
|
||||
}
|
||||
|
||||
void render_options(int oy) {
|
||||
option *o;
|
||||
int flipped;
|
||||
if (current)
|
||||
o = current;
|
||||
else
|
||||
o = valid;
|
||||
|
||||
for (flipped = 0; o; o = !o->next && !(flipped++) ? valid : o->next) {
|
||||
if (flipped && o == current)
|
||||
break;
|
||||
draw_string(o->text, PADDING, oy + ascent);
|
||||
oy += ascent + descent;
|
||||
|
||||
if (oy > maxh)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void render() {
|
||||
char *cursor_text;
|
||||
int cursor_pos;
|
||||
|
||||
update_size();
|
||||
XftDrawRect(draw, &bg, 0, 0, w, h);
|
||||
|
||||
if (input_bar) {
|
||||
cursor_text = malloc(sizeof(char) * (cursor + 1));
|
||||
strncpy(cursor_text, text, cursor);
|
||||
cursor_text[cursor] = '\0';
|
||||
cursor_pos = text_width(cursor_text);
|
||||
free(cursor_text);
|
||||
|
||||
draw_string(message, PADDING, PADDING + ascent);
|
||||
draw_string(text, PADDING + message_width, PADDING + ascent);
|
||||
draw_string("_", PADDING + message_width + cursor_pos,
|
||||
PADDING + ascent);
|
||||
|
||||
render_options(PADDING + ascent + 5);
|
||||
} else
|
||||
render_options(PADDING);
|
||||
|
||||
XCopyArea(display, buf, win, gc, 0, 0, w, h, 0, 0);
|
||||
}
|
||||
|
||||
void insert(char *str, ssize_t n) {
|
||||
int i, j;
|
||||
|
||||
if (!input_bar)
|
||||
return;
|
||||
if (strlen(text) + n > MAX_LENGTH)
|
||||
return;
|
||||
|
||||
memmove(&text[cursor + n], &text[cursor], MAX_LENGTH - cursor - MAX(n, 0));
|
||||
if (n > 0)
|
||||
memcpy(&text[cursor], str, n);
|
||||
cursor += n;
|
||||
}
|
||||
|
||||
// return location of next utf8 rune in the given direction -- thanks dmenu
|
||||
size_t nextrune(int inc) {
|
||||
ssize_t n;
|
||||
|
||||
for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc);
|
||||
return n;
|
||||
}
|
||||
|
||||
void handle_button(XButtonEvent be) {
|
||||
option *o;
|
||||
switch(be.button) {
|
||||
case Button4:
|
||||
if (current && current->prev)
|
||||
current = current->prev;
|
||||
else {
|
||||
for (o = valid; o && o->next; o = o->next);
|
||||
current = o;
|
||||
}
|
||||
break;
|
||||
case Button5:
|
||||
if (current && current->next)
|
||||
current = current->next;
|
||||
else
|
||||
current = valid;
|
||||
break;
|
||||
case Button2:
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
void handle_key(XKeyEvent ke) {
|
||||
char buf[32];
|
||||
int len, n;
|
||||
KeySym keysym = NoSymbol;
|
||||
Status status;
|
||||
option *o;
|
||||
|
||||
len = XmbLookupString(xic, &ke, buf, sizeof(buf), &keysym, &status);
|
||||
if (status == XBufferOverflow)
|
||||
return;
|
||||
|
||||
if (ke.state & ControlMask) {
|
||||
switch(keysym) {
|
||||
// Emacs
|
||||
case XK_p:
|
||||
keysym = XK_Up;
|
||||
break;
|
||||
case XK_n:
|
||||
keysym = XK_Down;
|
||||
break;
|
||||
case XK_f:
|
||||
keysym = XK_Right;
|
||||
break;
|
||||
case XK_b:
|
||||
keysym = XK_Left;
|
||||
break;
|
||||
case XK_d:
|
||||
keysym = XK_Delete;
|
||||
break;
|
||||
// Vim
|
||||
case XK_j:
|
||||
keysym = XK_Down;
|
||||
break;
|
||||
case XK_k:
|
||||
keysym = XK_Up;
|
||||
break;
|
||||
case XK_h:
|
||||
keysym = XK_Left;
|
||||
break;
|
||||
case XK_l:
|
||||
keysym = XK_Right;
|
||||
break;
|
||||
}
|
||||
} else if (ke.state & Mod1Mask) {
|
||||
|
||||
}
|
||||
|
||||
switch(keysym) {
|
||||
default:
|
||||
if (input_bar) {
|
||||
if (!iscntrl(*buf))
|
||||
insert(buf, len);
|
||||
update_valid_options();
|
||||
}
|
||||
break;
|
||||
case XK_Delete: // How the fuck does this work?
|
||||
if (text[cursor] == '\0')
|
||||
return;
|
||||
cursor = nextrune(+1);
|
||||
update_valid_options();
|
||||
case XK_BackSpace:
|
||||
if (input_bar) {
|
||||
if (cursor == 0)
|
||||
exit(0);
|
||||
insert(NULL, nextrune(-1) - cursor);
|
||||
update_valid_options();
|
||||
}
|
||||
break;
|
||||
case XK_Tab:
|
||||
select_forward_match();
|
||||
update_valid_options();
|
||||
break;
|
||||
|
||||
case XK_Left:
|
||||
if (cursor != 0)
|
||||
cursor = nextrune(-1);
|
||||
break;
|
||||
case XK_Right:
|
||||
if (text[cursor] != '\0' && cursor < MAX_LENGTH - 1)
|
||||
cursor = nextrune(+1);
|
||||
break;
|
||||
case XK_Up:
|
||||
if (current->prev) {
|
||||
current = current->prev;
|
||||
} else {
|
||||
for (o = valid; o && o->next; o = o->next);
|
||||
current = o;
|
||||
}
|
||||
break;
|
||||
case XK_Down:
|
||||
if (current->next)
|
||||
current = current->next;
|
||||
else
|
||||
current = valid;
|
||||
break;
|
||||
case XK_Return:
|
||||
finish();
|
||||
break;
|
||||
case XK_Escape:
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (exit_on_one) {
|
||||
for (n = 0, o = valid; o; o = o->next) n++;
|
||||
if (n == 1) {
|
||||
current = valid;
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
void update_valid_options() {
|
||||
option *head = malloc(sizeof(option));
|
||||
head->next = NULL;
|
||||
option *v = head;
|
||||
option *o;
|
||||
|
||||
for (o = options; o; o = o->next) {
|
||||
if (strncmp(text, o->text, strlen(text)) == 0) {
|
||||
v->next = malloc(sizeof(option));
|
||||
v->next->prev = v;
|
||||
v = v->next;
|
||||
v->text = o->text;
|
||||
v->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
valid = head->next;
|
||||
if (valid)
|
||||
valid->prev = NULL;
|
||||
free(head);
|
||||
current = valid;
|
||||
}
|
||||
|
||||
void select_forward_match() {
|
||||
option *o;
|
||||
int i, can_move = -1, good = 1;
|
||||
char c;
|
||||
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
while (good) {
|
||||
c = valid->text[++can_move];
|
||||
if (!c) break;
|
||||
for (o = valid; o && good; o = o->next)
|
||||
if (o->text[can_move] != c)
|
||||
good = 0;
|
||||
}
|
||||
|
||||
if (can_move > 0)
|
||||
for (i = 0; i < can_move; i++)
|
||||
text[i] = valid->text[i];
|
||||
cursor = can_move;
|
||||
}
|
||||
|
||||
void load_font() {
|
||||
FcPattern *pattern, *match;
|
||||
FcResult result;
|
||||
|
||||
if (fontstr[0] == '-')
|
||||
pattern = XftXlfdParse(fontstr, False, False);
|
||||
else
|
||||
pattern = FcNameParse((FcChar8 *) fontstr);
|
||||
|
||||
if (!pattern)
|
||||
die("Failed to get font pattern");
|
||||
|
||||
match = FcFontMatch(NULL, pattern, &result);
|
||||
if (!match)
|
||||
die("Failed to get font match");
|
||||
|
||||
if (!(font = XftFontOpenPattern(display, match))) {
|
||||
FcPatternDestroy(match);
|
||||
die("Failed to open font");
|
||||
}
|
||||
|
||||
ascent = font->ascent;
|
||||
descent = font->descent;
|
||||
}
|
||||
|
||||
void grab_keyboard_pointer() {
|
||||
int i, p = 1, k = 1;
|
||||
XColor dummycolor;
|
||||
Cursor nullcursor;
|
||||
|
||||
nullpixmap = XCreatePixmap(display, root, 1, 1, 1);
|
||||
nullcursor = XCreatePixmapCursor(display, nullpixmap, nullpixmap,
|
||||
&dummycolor, &dummycolor, 0, 0);
|
||||
|
||||
for (i = 0; (k || p) && i < 1000; i++) {
|
||||
|
||||
if (p && XGrabPointer(display, root, False, ButtonPressMask,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
win, nullcursor, CurrentTime)
|
||||
== GrabSuccess) p = 0;
|
||||
|
||||
if (k && XGrabKeyboard(display, root, True,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
CurrentTime) == GrabSuccess) k = 0;
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
if (p) die("Failed to grab pointer!");
|
||||
if (k) die("Failed to grab keyboard!");
|
||||
}
|
||||
|
||||
void update_size() {
|
||||
int tw;
|
||||
int ow = w, oh = h;
|
||||
option *o;
|
||||
|
||||
w = message_width + cursor_width + text_width(text);
|
||||
h = input_bar ? ascent + descent : 0;
|
||||
|
||||
for (o = valid; o; o = o->next) {
|
||||
tw = text_width(o->text);
|
||||
if (tw > w)
|
||||
w = tw;
|
||||
h += ascent + descent;
|
||||
|
||||
if (h >= maxh)
|
||||
break;
|
||||
}
|
||||
|
||||
w += PADDING * 2;
|
||||
h += PADDING * 2;
|
||||
|
||||
if (ow == w && oh == h)
|
||||
return;
|
||||
|
||||
XResizeWindow(display, win, w, h);
|
||||
|
||||
if (buf)
|
||||
XFreePixmap(display, buf);
|
||||
buf = XCreatePixmap(display, win, w, h, DefaultDepth(display, screen));
|
||||
XftDrawChange(draw, buf);
|
||||
}
|
||||
|
||||
void set_position() {
|
||||
Window ww;
|
||||
int c, v;
|
||||
|
||||
/* if x,y not set then set to cursor position. */
|
||||
if (x == -1 && y == -1)
|
||||
XQueryPointer(display, root, &ww, &ww, &x, &y, &c, &c, &v);
|
||||
|
||||
XMoveWindow(display, win, x, y);
|
||||
}
|
||||
|
||||
void read_input() {
|
||||
char line[512];
|
||||
option *o, *head;
|
||||
head = malloc(sizeof(option));
|
||||
head->next = NULL;
|
||||
o = head;
|
||||
|
||||
while (fgets(line, sizeof(line), stdin)) {
|
||||
o->next = malloc(sizeof(option));
|
||||
o->next->prev = o;
|
||||
o = o->next;
|
||||
|
||||
o->text = malloc(sizeof(char) * (strlen(line) + 1));
|
||||
strcpy(o->text, line);
|
||||
o->text[strlen(o->text) - 1] = '\0';
|
||||
o->next = NULL;
|
||||
}
|
||||
|
||||
options = head->next;
|
||||
options->prev = NULL;
|
||||
free(head);
|
||||
update_valid_options();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
XSetWindowAttributes attributes;
|
||||
XWindowAttributes window_attributes;
|
||||
Visual *vis;
|
||||
Colormap cmap;
|
||||
XEvent ev;
|
||||
int i;
|
||||
|
||||
exit_on_one = 0;
|
||||
complete_on_exit = 0;
|
||||
input_bar = 1;
|
||||
read_options = 1;
|
||||
absolute_position = 0;
|
||||
|
||||
cursor = 0;
|
||||
message = "";
|
||||
for (i = 0; i < MAX_LENGTH; i++)
|
||||
text[i] = '\0';
|
||||
valid = NULL;
|
||||
current = NULL;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-h") == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
} else if (strcmp(argv[i], "-a") == 0) {
|
||||
exit_on_one = 1;
|
||||
} else if (strcmp(argv[i], "-c") == 0) {
|
||||
complete_on_exit = 1;
|
||||
} else if (strcmp(argv[i], "-n") == 0) {
|
||||
complete_on_exit = 1;
|
||||
input_bar = 0;
|
||||
} else if (strcmp(argv[i], "-e") == 0) {
|
||||
read_options = 0;
|
||||
} else if (strcmp(argv[i], "-p") == 0) {
|
||||
i++;
|
||||
x = atoi(strsep(&argv[i], ","));
|
||||
y = atoi(argv[i]);
|
||||
} else if (strcmp(argv[i], "-ap") == 0) {
|
||||
absolute_position = 1;
|
||||
} else {
|
||||
message = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (read_options)
|
||||
read_input();
|
||||
|
||||
display = XOpenDisplay(NULL);
|
||||
root = RootWindow(display, 0);
|
||||
screen = DefaultScreen(display);
|
||||
vis = XDefaultVisual(display, screen);
|
||||
cmap = DefaultColormap(display, screen);
|
||||
|
||||
maxh = DisplayHeight(display, 0) - y;
|
||||
|
||||
if (!XftColorAllocName(display, vis, cmap, fg_name, &fg))
|
||||
die("Failed to allocate foreground color");
|
||||
if (!XftColorAllocName(display, vis, cmap, bg_name, &bg))
|
||||
die("Failed to allocate background color");
|
||||
|
||||
/* Setup and map window */
|
||||
attributes.border_pixel = fg.pixel;
|
||||
attributes.override_redirect = True;
|
||||
attributes.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
|
||||
|
||||
win = XCreateWindow(display, root,
|
||||
0, 0, 10, 10, BORDER_WIDTH,
|
||||
DefaultDepth(display, 0),
|
||||
CopyFromParent, CopyFromParent,
|
||||
CWBackPixel | CWOverrideRedirect | CWEventMask | CWBorderPixel,
|
||||
&attributes);
|
||||
|
||||
xim = XOpenIM(display, NULL, NULL, NULL);
|
||||
xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
|
||||
XNClientWindow, win, XNFocusWindow, win, NULL);
|
||||
|
||||
gc = XCreateGC(display, win, 0, 0);
|
||||
|
||||
buf = XCreatePixmap(display, win, 10, 10, DefaultDepth(display, screen));
|
||||
draw = XftDrawCreate(display, buf, vis, cmap);
|
||||
|
||||
load_font();
|
||||
|
||||
message_width = text_width(message);
|
||||
cursor_width = text_width("_");
|
||||
|
||||
update_size();
|
||||
set_position();
|
||||
|
||||
XMapWindow(display, win);
|
||||
render();
|
||||
|
||||
grab_keyboard_pointer();
|
||||
|
||||
quit = 0;
|
||||
while (!quit && !XNextEvent(display, &ev)) {
|
||||
if (ev.type == KeyPress)
|
||||
handle_key(ev.xkey);
|
||||
else if (ev.type == ButtonPress)
|
||||
handle_button(ev.xbutton);
|
||||
else if (ev.type == Expose) {
|
||||
XRaiseWindow(display, win);
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
XUngrabKeyboard(display, CurrentTime);
|
||||
XUngrabPointer(display, CurrentTime);
|
||||
XFreeGC(display, gc);
|
||||
XFreePixmap(display, buf);
|
||||
XFreePixmap(display, nullpixmap);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
typedef struct option option;
|
||||
struct option {
|
||||
char *text;
|
||||
option *next, *prev;
|
||||
};
|
||||
|
||||
void usage();
|
||||
void die(char *msg);
|
||||
void finish();
|
||||
|
||||
int text_width(char *str);
|
||||
void draw_string(char *str, int x, int y);
|
||||
|
||||
void render_options(int oy);
|
||||
void render();
|
||||
|
||||
void insert(char *str, ssize_t n);
|
||||
size_t nextrune(int inc);
|
||||
|
||||
void handle_button(XButtonEvent be);
|
||||
void handle_key(XKeyEvent ke);
|
||||
|
||||
void update_valid_options();
|
||||
void select_forward_match();
|
||||
|
||||
void make_cursor();
|
||||
void load_font();
|
||||
void grab_keyboard_pointer();
|
||||
void update_size();
|
||||
void set_position();
|
||||
|
||||
void read_input();
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
lsx `echo $PATH | sed "s/:/\n/g"` | sort | nenu -c "exec: " | ${SHELL:-"/bin/sh"}
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
# use with suckless's lsw and wmutils's chwso to show menu of mapped windows,
|
||||
# select one then raise it.
|
||||
|
||||
LSW=/usr/local/bin/lsw
|
||||
|
||||
WINDOWS=`$LSW | cut -d' ' -f2- | nl -n rz -w $((\`$LSW | wc -l | wc -c\` - 1)) -s ' '`
|
||||
N=`echo $WINDOWS | nenu -n | cut -d' ' -f1`
|
||||
[ ! -z $N ] && $LSW | head -$N | tail -1 | cut -d' ' -f1 | xargs chwso -r
|
Reference in New Issue