Instantly share code, notes, and snippets.

@coleifer /LICENSE Secret
Last active May 9, 2017

Embed
What would you like to do?
.TH "DMENU" "1" "2014/09/18" "dmenu3 0\&.1" "dmenu manual"
.SH NAME
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-b ]
.RB [ \-f ]
.RB [ \-r ]
.RB [ \-i ]
.RB [ \-z ]
.RB [ \-t ]
.RB [ \-mask ]
.RB [ \-noinput ]
.RB [ \-s
.IR screen ]
.RB [ \-name
.IR name ]
.RB [ \-class
.IR class ]
.RB [ \-o
.IR opacity ]
.RB [ \-dim
.IR opacity ]
.RB [ \-dc
.IR color ]
.RB [ \-l
.IR lines ]
.RB [ \-h
.IR height ]
.RB [ \-uh
.IR height ]
.RB [ \-w
.IR width ]
.RB [ \-centerx ]
.RB [ \-centery ]
.RB [ \-p
.IR prompt ]
.RB [ \-fn
.IR font ]
.RB [ \-nb
.IR color ]
.RB [ \-nf
.IR color ]
.RB [ \-mb
.IR multiselcolor ]
.RB [ \-mf
.IR multiselcolor ]
.RB [ \-sb
.IR color ]
.RB [ \-sf
.IR color ]
.RC [ \-uc
.IR color ]
.RB [ \-hist
.IR "<filename>" ]
.RB [ \-v ]
.P
.BR dmenu_run " ..."
.SH DESCRIPTION
.B dmenu
is a dynamic menu for X, which reads a list of newline\-separated items from
stdin. When the user selects an item and presses Return, their choice is printed
to stdout and dmenu terminates. Entering text will narrow the items to those
matching the tokens in the input.
.P
.B dmenu_run
is a script used by
.IR dwm (1)
which lists programs in the user's $PATH and runs the result in their $SHELL.
.SH OPTIONS
.TP
.B \-b
dmenu appears at the bottom of the screen.
.TP
.B \-q
dmenu will not show any items if the search string is empty.
.TP
.B \-f
dmenu grabs the keyboard before reading stdin. This is faster, but will lock up
X until stdin reaches end\-of\-file.
.TP
.B \-r
activates filter mode. All matching items currently shown in the list will be
selected, starting with the item that is highlighted and wrapping around to the
beginning of the list.
.TP
.B \-i
dmenu matches menu items case insensitively.
.TP
.B \-z
dmenu uses fuzzy matching. It matches items that have all characters entered, in sequence they are
entered, but there may be any number of characters between matched characters.
For example it takes "txt" makes it to "*t*x*t" glob pattern and checks if it
matches.
.TP
.B \-t
dmenu uses space\-separated tokens to match menu items. Using this overrides -z option.
.TP
.B \-mask
dmenu masks input with asterisk characters (*).
.TP
.B \-noinput
dmenu ignores input from stdin (equivalent to: echo | dmenu).
.TP
.BI \-s " screen"
dmenu apears on the specified screen number. Number given corespondes to screen number in X configuration.
.TP
.BI \-name " name"
defines window name for dmenu. Defaults to "dmenu".
.TP
.BI \-class " class"
defines window class for dmenu. Defaults to "Dmenu".
.TP
.BI \-o " opacity"
defines window opacity for dmenu. Defaults to 1.0.
.TP
.BI \-dim " opacity"
enables screen dimming when dmenu appers. Takes dim opacity as argument.
.TP
.BI \-dc " color"
defines color of screen dimming. Active only when -dim in effect. Defautls to black (#000000)
.TP
.BI \-l " lines"
dmenu lists items vertically, with the given number of lines.
.TP
.BI \-h " height"
defines the height of the bar in pixels.
.TP
.BI \-uh " height"
defines the height of the underline in pixels.
.TP
.BI \-p " prompt"
defines the prompt to be displayed to the left of the input field.
.TP
.BI \-fn " font"
defines the font or font set used. eg. "fixed" or "Monospace-12:normal" (an xft font)
.TP
.BI \-x " xoffset"
defines the offset from the left border of the screen.
.TP
.BI \-y " yoffset"
defines the offset from the top border of the screen.
.TP
.BI \-w " width"
defines the desired menu window width.
.TP
.BI \-centerx
centers the dmenu window horizontally.
.TP
.BI \-centery
centers the dmenu window vertically.
.TP
.BI \-nb " color"
defines the normal background color.
.IR #RGB ,
.IR #RRGGBB ,
and X color names are supported.
.TP
.BI \-nf " color"
defines the normal foreground color.
.TP
.BI \-sb " color"
defines the selected background color.
.TP
.BI \-sf " color"
defines the selected foreground color.
.TP
.BI \-mb " color"
defines the background color for multi-selected items.
.TP
.BI \-mf " color"
defines the foreground color for multi-selected items.
.TP
.BI \-uc " color"
defines the underline color.
.TP
.BI \-hist " <histfile>"
the file to use for history
.TP
.B \-v
prints version information to stdout, then exits.
.SH USAGE
dmenu is completely controlled by the keyboard. Items are selected using the
arrow keys, page up, page down, home, and end.
.TP
.B Tab
Copy the selected item to the input field.
.TP
.B Return
Confirm selection. Prints the selected item to stdout and exits, returning
success.
.TP
.B Shift\-Return
Confirm input. Prints the input text to stdout and exits, returning success.
.TP
.B Escape
Exit without selecting an item, returning failure.
.TP
C\-a
Home
.TP
C\-b
Left
.TP
C\-c
Escape
.TP
C\-d
Delete
.TP
C\-e
End
.TP
C\-f
Right
.TP
C\-g
Escape
.TP
C\-h
Backspace
.TP
C\-i
Tab
.TP
C\-j
Return
.TP
C\-J
Shift-Return
.TP
C\-k
Delete line right
.TP
C\-m
Return
.TP
C\-n
Down
.TP
C\-p
Up
.TP
C\-u
Delete line left
.TP
C\-w
Delete word left
.TP
C\-y
Paste from primary X selection
.TP
C\-Y
Paste from X clipboard
.TP
M\-g
Home
.TP
M\-G
End
.TP
M\-h
Up
.TP
M\-j
Page down
.TP
M\-k
Page up
.TP
M\-l
Down
.SH SEE ALSO
.IR dwm (1),
.IR stest (1)
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#ifdef XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
#include "draw.h"
#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 MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define DEFFONT "PragmataPro-11" /* xft example: "Monospace-11" */
#define HIST_SIZE 20
#define HIST_LINE_LEN 1024
typedef struct Item Item;
struct Item {
char *text;
Item *left, *right;
Bool out;
};
struct item_state {
char buf[BUFSIZ];
char *max_str;
size_t max_len;
size_t size;
size_t items;
};
static void appenditem(Item *item, Item **list, Item **last);
static void calcoffsets(void);
static void cleanup(void);
static char *cistrstr(const char *s, const char *sub);
static void drawmenu(void);
static void grabkeyboard(void);
static void insert(const char *str, ssize_t n);
static void keypress(XKeyEvent *ev);
static void matchstr(void);
static void matchtok(void);
static void matchfuzzy(void);
static char *strchri(const char *s, int c);
static size_t nextrune(int inc);
static size_t utf8length();
static void paste(void);
static void readitems(void);
static void run(void);
static void setup(void);
static void usage(void);
static void read_resourses(void);
static char text[BUFSIZ] = "";
static char originaltext[BUFSIZ] = "";
static int bh, mw, mh;
static int inputw, promptw;
static size_t cursor = 0;
static const char *font = NULL;
static const char *prompt = NULL;
static const char *normbgcolor = NULL;
static const char *normfgcolor = NULL;
static const char *selbgcolor = NULL;
static const char *selfgcolor = NULL;
static const char *mselbgcolor = NULL;
static const char *mselfgcolor = NULL;
static const char *dimcolor = NULL;
static const char *undercolor = NULL;
static const char *bordercolor = NULL;
static char *name = "dmenu";
static char *class = "Dmenu";
static char *dimname = "dimenu";
static unsigned int lines = 0, line_height = 0, under_height = 0;
static int border_width = 0;
static int indentWidth = 0;
static int xoffset = 0;
static int yoffset = 0;
static int width = 0;
#ifdef XINERAMA
static int snum = -1;
#endif
static ColorSet *normcol;
static ColorSet *selcol;
static ColorSet *mselcol;
static ColorSet *dimcol;
static ColorSet *undercol;
static ColorSet *bordercol;
static Atom clip, utf8;
static Bool topbar = True;
static Bool centerX = False;
static Bool running = True;
static Bool filter = False;
static Bool maskin = False;
static Bool noinput = False;
static int ret = 0;
static Bool quiet = False;
static DC *dc;
static Item *items = NULL;
static Item *matches, *matchend;
static Item *prev, *curr, *next, *sel;
static Window win, dim;
static XIC xic;
static double opacity = 1.0, dimopacity = 0.0;
static char hist[HIST_SIZE][HIST_LINE_LEN];
static char *histfile = NULL;
static int hcnt = 0;
#define OPAQUE 0xffffffff
#define OPACITY "_NET_WM_WINDOW_OPACITY"
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;
static void (*match)(void) = matchstr;
static char *(*fstrchr)(const char *, const int) = strchr;
int
main(int argc, char *argv[]) {
Bool fast = False;
int i;
for(i = 1; i < argc; i++) {
if(!strcmp(argv[i], "-v")) {
puts("dmenu3-"VERSION);
exit(EXIT_SUCCESS);
}
else if(!strcmp(argv[i], "-hist"))
histfile = argv[++i];
else if(!strcmp(argv[i], "-b"))
topbar = False;
else if(!strcmp(argv[i], "-q"))
quiet = True;
else if(!strcmp(argv[i], "-f"))
fast = True;
else if(!strcmp(argv[i], "-z"))
match = matchfuzzy;
else if(!strcmp(argv[i], "-r"))
filter = True;
else if(!strcmp(argv[i], "-i")) {
fstrncmp = strncasecmp;
fstrstr = cistrstr;
fstrchr = strchri;
}
else if(!strcmp(argv[i], "-mask"))
maskin = True;
else if(!strcmp(argv[i], "-noinput"))
noinput = True;
else if(!strcmp(argv[i], "-centerx"))
centerX = True;
else if(!strcmp(argv[i], "-t"))
match = matchtok;
else if(i+1 == argc)
usage();
/* The following options accept a single argument. */
else if(!strcmp(argv[i], "-x"))
xoffset = atoi(argv[++i]);
else if(!strcmp(argv[i], "-y"))
yoffset = atoi(argv[++i]);
else if(!strcmp(argv[i], "-w"))
width = atoi(argv[++i]);
else if(!strcmp(argv[i], "-l"))
lines = atoi(argv[++i]);
else if(!strcmp(argv[i], "-h"))
line_height = atoi(argv[++i]);
else if(!strcmp(argv[i], "-uh"))
under_height = atoi(argv[++i]);
#ifdef XINERAMA
else if(!strcmp(argv[i], "-s"))
snum = atoi(argv[++i]);
#endif
else if (!strcmp(argv[i], "-name"))
name = argv[++i];
else if (!strcmp(argv[i], "-class"))
class = argv[++i];
else if (!strcmp(argv[i], "-o"))
opacity = atof(argv[++i]);
else if (!strcmp(argv[i], "-dim"))
dimopacity = atof(argv[++i]);
else if (!strcmp(argv[i], "-dc"))
dimcolor = argv[++i];
else if (!strcmp(argv[i], "-uc"))
undercolor = argv[++i];
else if(!strcmp(argv[i], "-p"))
prompt = argv[++i];
else if(!strcmp(argv[i], "-fn"))
font = argv[++i];
else if(!strcmp(argv[i], "-nb"))
normbgcolor = argv[++i];
else if(!strcmp(argv[i], "-nf"))
normfgcolor = argv[++i];
else if(!strcmp(argv[i], "-sb"))
selbgcolor = argv[++i];
else if(!strcmp(argv[i], "-sf"))
selfgcolor = argv[++i];
else if(!strcmp(argv[i], "-mb"))
mselbgcolor = argv[++i];
else if(!strcmp(argv[i], "-mf"))
mselfgcolor = argv[++i];
else if(!strcmp(argv[i], "-in"))
indentWidth = atoi(argv[++i]);
else if(!strcmp(argv[i], "-bw"))
border_width = atoi(argv[++i]);
else if(!strcmp(argv[i], "-bc"))
bordercolor = argv[++i];
else
usage();
}
dc = initdc();
read_resourses();
initfont(dc, font ? font : DEFFONT);
normcol = initcolor(dc, normfgcolor, normbgcolor);
selcol = initcolor(dc, selfgcolor, selbgcolor);
dimcol = initcolor(dc, dimcolor, dimcolor);
undercol = initcolor(dc, undercolor, undercolor);
mselcol = initcolor(dc, mselfgcolor, mselbgcolor);
bordercol = initcolor(dc, bordercolor, bordercolor);
if (noinput) {
grabkeyboard();
} else if(fast) {
grabkeyboard();
readitems();
} else {
readitems();
grabkeyboard();
}
setup();
run();
cleanup();
return ret;
}
static int writehistory(char *command) {
/* Write command to history file, returning 1 on success, 0 on failure. */
int i = 0;
FILE *f;
if (!histfile || strlen(command) <= 0)
return 0;
if ((f = fopen(histfile, "w"))) {
fputs(command, f);
fputc('\n', f);
for (; i < hcnt; i++) {
if (strcmp(command, hist[i]) != 0) {
fputs(hist[i], f);
fputc('\n', f);
}
}
fclose(f);
return 1;
}
return 0;
}
/* Set font and colors from X resources database if they are not set
* from command line */
void
read_resourses(void) {
XrmDatabase xdb;
char* xrm;
char* datatype[20];
XrmValue xvalue;
XrmInitialize();
xrm = XResourceManagerString(dc->dpy);
if( xrm != NULL ) {
xdb = XrmGetStringDatabase(xrm);
if( font == NULL && XrmGetResource(xdb, "dmenu.font", "*", datatype, &xvalue) == True )
font = strdup(xvalue.addr);
if( normfgcolor == NULL && XrmGetResource(xdb, "dmenu.foreground", "*", datatype, &xvalue) == True )
normfgcolor = strdup(xvalue.addr);
if( normbgcolor == NULL && XrmGetResource(xdb, "dmenu.background", "*", datatype, &xvalue) == True )
normbgcolor = strdup(xvalue.addr);
if( selfgcolor == NULL && XrmGetResource(xdb, "dmenu.selforeground", "*", datatype, &xvalue) == True )
selfgcolor = strdup(xvalue.addr);
if( selbgcolor == NULL && XrmGetResource(xdb, "dmenu.selbackground", "*", datatype, &xvalue) == True )
selbgcolor = strdup(xvalue.addr);
if( mselfgcolor == NULL && XrmGetResource(xdb, "dmenu.mselforeground", "*", datatype, &xvalue) == True )
mselfgcolor = strdup(xvalue.addr);
if( mselbgcolor == NULL && XrmGetResource(xdb, "dmenu.mselbackground", "*", datatype, &xvalue) == True )
mselbgcolor = strdup(xvalue.addr);
if( dimcolor == NULL && XrmGetResource(xdb, "dmenu.dimcolor", "*", datatype, &xvalue) == True )
dimcolor = strdup(xvalue.addr);
if( undercolor == NULL && XrmGetResource(xdb, "dmenu.undercolor", "*", datatype, &xvalue) == True )
undercolor = strdup(xvalue.addr);
if( bordercolor == NULL && XrmGetResource(xdb, "dmenu.bordercolor", "*", datatype, &xvalue) == True )
bordercolor = strdup(xvalue.addr);
if( XrmGetResource(xdb, "dmenu.opacity", "*", datatype, &xvalue) == True )
opacity = atof(strdup(xvalue.addr));
XrmDestroyDatabase(xdb);
}
/* Set default colors if they are not set */
if( normbgcolor == NULL )
normbgcolor = "#222222";
if( normfgcolor == NULL )
normfgcolor = "#bbbbbb";
if( selbgcolor == NULL )
selbgcolor = "#005577";
if( selfgcolor == NULL )
selfgcolor = "#eeeeee";
if( mselbgcolor == NULL )
mselbgcolor = strdup(selbgcolor);
if( mselfgcolor == NULL )
mselfgcolor = strdup(selfgcolor);
if( dimcolor == NULL )
dimcolor = "#000000";
if (undercolor == NULL)
undercolor = selbgcolor;
if( bordercolor == NULL )
bordercolor = "#555555";
if( !opacity )
opacity = 1.0;
}
void
appenditem(Item *item, Item **list, Item **last) {
if(*last)
(*last)->right = item;
else
*list = item;
item->left = *last;
item->right = NULL;
*last = item;
}
void
calcoffsets(void) {
int i, n;
if(lines > 0)
n = lines * bh;
else
n = mw - (promptw + inputw + textw(dc, "<") + textw(dc, ">"));
/* 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(dc, next->text), n)) > n)
break;
for(i = 0, prev = curr; prev && prev->left; prev = prev->left)
if((i += (lines > 0) ? bh : MIN(textw(dc, prev->left->text), n)) > n)
break;
}
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;
}
void
cleanup(void) {
freecol(dc, normcol);
freecol(dc, selcol);
freecol(dc, dimcol);
XDestroyWindow(dc->dpy, win);
if(dimopacity > 0)
XDestroyWindow(dc->dpy, dim);
XUngrabKeyboard(dc->dpy, CurrentTime);
freedc(dc);
}
const char *
createmaskinput(char *maskinput, int length)
{
if (length <= 0) {
*maskinput = '\0';
} else {
memset(maskinput, '*', length);
maskinput[length] = '\0';
}
return (maskinput);
}
void
drawmenu(void) {
int curpos;
char maskinput[sizeof text];
int length = maskin ? utf8length() : cursor;
Item *item;
dc->x = 0;
dc->y = 0;
dc->h = bh;
drawrect(dc, 0, 0, mw, mh, True, normcol->BG);
/* TODO: border */
if(prompt && *prompt) {
dc->w = promptw;
drawtext(dc, prompt, selcol);
drawrect(dc, 0, dc->h-under_height, dc->w, under_height, True, undercol->BG);
dc->x = dc->w;
}
/* draw input field */
dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw;
drawtext(dc, maskin ? createmaskinput(maskinput, length) : text, normcol);
if((curpos = textnw(dc, maskin ? maskinput : text, length) + dc->font.height/2) < dc->w)
drawrect(dc, curpos, (dc->h - dc->font.height)/2 + 1, 1, dc->font.height -1, True, normcol->FG);
if(!quiet || strlen(text) > 0) {
if(lines > 0) {
/* draw vertical list */
dc->w = mw;// - dc->x;
/* negative indent leaves as-is. all others are treated as
* absolute values. */
if (promptw && prompt && indentWidth >= 0)
dc->x = indentWidth;
for(item = curr; item != next; item = item->right) {
dc->y += dc->h;
drawtext(dc, item->text, (item == sel) ? selcol :
(item->out) ? mselcol : normcol);
if (item == sel)
drawrect(dc, 0, dc->h-under_height, mw, under_height, True, undercol->BG);
}
}
else if(matches) {
/* draw horizontal list */
dc->x += inputw;
dc->w = textw(dc, "<");
if(curr->left)
drawtext(dc, "<", normcol);
for(item = curr; item != next; item = item->right) {
dc->x += dc->w;
dc->w = MIN(textw(dc, item->text), mw - dc->x - textw(dc, ">"));
drawtext(dc, item->text, (item == sel) ? selcol :
(item->out) ? mselcol : normcol);
if (item == sel)
drawrect(dc, 0, dc->h-under_height, dc->w, under_height, True, undercol->BG);
}
dc->w = textw(dc, ">");
dc->x = mw - dc->w;
if(next)
drawtext(dc, ">", normcol);
}
}
mapdc(dc, win, mw, mh, border_width);
}
void
grabkeyboard(void) {
int i;
const struct timespec req = {
.tv_sec = 0,
.tv_nsec = 1000000
};
/* try to grab keyboard, we may have to wait for another process to ungrab */
for(i = 0; i < 1000; i++) {
if(XGrabKeyboard(dc->dpy, DefaultRootWindow(dc->dpy), True,
GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
return;
nanosleep(&req, NULL);
}
eprintf("cannot grab keyboard\n");
}
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();
}
void
keypress(XKeyEvent *ev) {
char buf[32];
int len;
KeySym ksym = NoSymbol;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
if(status == XBufferOverflow)
return;
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: ksym = XK_Return; break;
case XK_m: /* fallthrough */
case XK_M: ksym = XK_Return; 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 && text[nextrune(-1)] == ' ')
insert(NULL, nextrune(-1) - cursor);
while(cursor > 0 && text[nextrune(-1)] != ' ' && text[nextrune(-1)] != '/')
insert(NULL, nextrune(-1) - cursor);
break;
case XK_y: /* fallthrough */
case XK_Y: /* paste selection */
XConvertSelection(dc->dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime);
return;
case XK_Return:
case XK_KP_Enter:
break;
default:
return;
}
else if(ev->state & Mod1Mask)
switch(ksym) {
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:
if(!iscntrl(*buf))
insert(buf, len);
break;
case XK_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:
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:
ret = EXIT_FAILURE;
running = False;
case XK_Home:
if(sel == matches) {
cursor = 0;
break;
}
sel = curr = matches;
calcoffsets();
break;
case XK_Left:
if(cursor > 0 && (!sel || !sel->left || lines > 0)) {
cursor = nextrune(-1);
break;
}
if(lines > 0)
return;
/* fallthrough */
case XK_Up:
if(sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
calcoffsets();
}
break;
case XK_Next:
if(!next)
return;
sel = curr = next;
calcoffsets();
break;
case XK_Prior:
if(!prev)
return;
sel = curr = prev;
calcoffsets();
break;
case XK_Return:
case XK_KP_Enter:
running = False;
if((ev->state & ShiftMask) || !sel){
puts(text);
writehistory(text);
}
else if(!filter){
puts(sel->text);
writehistory(sel->text);
if((ev->state & ControlMask)) {
running = True;
sel->out = True;
}
}
else {
for(Item *item = sel; item; item = item->right)
puts(item->text);
for(Item *item = matches; item != sel; item = item->right)
puts(item->text);
}
if (!running) ret = EXIT_SUCCESS;
case XK_Right:
if(text[cursor] != '\0') {
cursor = nextrune(+1);
break;
}
if(lines > 0)
return;
/* fallthrough */
case XK_Down:
if(sel && sel->right && (sel = sel->right) == next) {
curr = next;
calcoffsets();
}
break;
case XK_Tab:
if(!sel)
return;
if(strcmp(text, sel->text)) {
strncpy(originaltext, text, sizeof originaltext);
strncpy(text, sel->text, sizeof text);
cursor = strlen(text);
} else {
if(sel && sel->right) {
sel = sel->right;
strncpy(text, sel->text, sizeof text);
cursor = strlen(text);
}
else {
strncpy(text, originaltext, sizeof text);
cursor = strlen(text);
match();
}
}
break;
case XK_ISO_Left_Tab:
if(!sel)
return;
if(strcmp(text, sel->text)) {
sel = matchend;
strncpy(originaltext, text, sizeof originaltext);
strncpy(text, sel->text, sizeof text);
cursor = strlen(text);
} else {
if(sel && sel->left) {
sel = sel->left;
strncpy(text, sel->text, sizeof text);
cursor = strlen(text);
}
else {
strncpy(text, originaltext, sizeof text);
cursor = strlen(text);
match();
}
}
break;
}
drawmenu();
}
char *
strchri(const char *s, int c) {
int i;
c = tolower(c);
while (*s) {
i = tolower(*s);
if (i == c)
return ((char *) s);
s++;
}
return NULL;
}
void
matchstr(void) {
static char **tokv = NULL;
static int tokn = 0;
char buf[sizeof text], *s;
int i, tokc = 0;
size_t len;
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)))
eprintf("cannot realloc %u bytes\n", tokn * sizeof *tokv);
len = tokc ? strlen(tokv[0]) : 0;
matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
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(tokv[0], item->text, len+1))
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();
}
void
matchtok(void) {
char buf[sizeof text];
char **tokv, *s;
int tokc, i;
Item *item, *end;
tokc = 0;
tokv = NULL;
strcpy(buf, text);
for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " "))
if(!(tokv = realloc(tokv, ++tokc * sizeof *tokv)))
eprintf("cannot realloc %u bytes\n", tokc * sizeof *tokv);
matches = end = NULL;
for(item = items; item->text; item++) {
for(i = 0; i < tokc; i++)
if(!fstrstr(item->text, tokv[i]))
break;
if(i == tokc)
appenditem(item, &matches, &end);
}
free(tokv);
curr = prev = next = sel = matches;
calcoffsets();
}
void
matchfuzzy(void) {
int i;
size_t len;
Item *item;
char *pos;
len = strlen(text);
matches = matchend = NULL;
for(item = items; item && item->text; item++) {
for (i = 0, pos = fstrchr(item->text, text[i]); pos && text[i]; i++)
pos = fstrchr(pos+1, text[i]);
if (((size_t)i) == len)
appenditem(item, &matches, &matchend);
}
curr = sel = matches;
calcoffsets();
}
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;
}
/* UTF-8 length for password */
size_t
utf8length()
{
ssize_t n = cursor - 1, length = 0;
while (n >= 0) {
for (; n - 1 >= 0 && (text[n] & 0xc0) == 0x80; n--)
;
n--;
length++;
}
return (length);
}
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 */
XGetWindowProperty(dc->dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p));
XFree(p);
drawmenu();
}
/* Return either length, or -1 for failure */
size_t
readitem(FILE *fd, struct item_state *s) {
char *p;
size_t len = -1;
if (fgets(s->buf, BUFSIZ, fd)) {
if (s->items + 1 >= s->size / sizeof *items) {
if (!(items = realloc(items, (s->size += BUFSIZ)))) {
eprintf("cannot realloc %u bytes:", s->size);
}
}
if((p = strchr(s->buf, '\n'))) {
*p = '\0';
}
len = strlen(s->buf);
if (!(items[s->items].text = strdup(s->buf))) {
eprintf("cannot strdup %u bytes:", len + 1);
}
items[s->items].out = False;
if(len > s->max_len) {
s->max_len = len;
s->max_str = items[s->items].text;
}
s->items++;
}
return len;
}
void
readitems(void) {
struct item_state s;
FILE *f;
size_t len = 0;
memset(&s, 0, sizeof(s));
if (histfile && (f = fopen(histfile, "r"))) {
while ((len = readitem(f, &s)) != (size_t)-1) {
strncpy(hist[hcnt], s.buf, MIN(len, HIST_LINE_LEN - 1));
hist[hcnt++][HIST_LINE_LEN - 1] = '\0';
}
fclose(f);
}
/* read each line from stdin and add it to the item list */
while(readitem(stdin, &s) != (size_t)-1);
if(items)
items[s.items].text = NULL;
inputw = s.max_str ? textw(dc, s.max_str) : 0;
lines = MIN(lines, s.items);
}
void
run(void) {
XEvent ev;
while(running && !XNextEvent(dc->dpy, &ev)) {
if(XFilterEvent(&ev, win))
continue;
switch(ev.type) {
case Expose:
if(ev.xexpose.count == 0)
mapdc(dc, win, mw, mh, border_width);
break;
case KeyPress:
keypress(&ev.xkey);
break;
case SelectionNotify:
if(ev.xselection.property == utf8)
paste();
break;
case VisibilityNotify:
if(ev.xvisibility.state != VisibilityUnobscured)
XRaiseWindow(dc->dpy, win);
break;
}
}
}
void
setup(void) {
int x, y, screen = DefaultScreen(dc->dpy);
Screen *defScreen = DefaultScreenOfDisplay(dc->dpy);
int dimx, dimy, dimw, dimh;
Window root = RootWindow(dc->dpy, screen);
XSetWindowAttributes swa;
XIM xim;
#ifdef XINERAMA
int n;
XineramaScreenInfo *info;
#endif
clip = XInternAtom(dc->dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False);
/* calculate menu geometry */
bh = (line_height > ((unsigned int)dc->font.height + 2)) ? line_height : ((unsigned int)dc->font.height + 2);
lines = MAX(lines, 0);
mh = (lines + 1) * bh;
#ifdef XINERAMA
if((info = XineramaQueryScreens(dc->dpy, &n))) {
int a, j, di, i = 0, area = 0;
unsigned int du;
Window w, pw, dw, *dws;
XWindowAttributes wa;
if(snum > -1 && snum < n) {
x = info[snum].x_org;
y = info[snum].y_org + (topbar ? yoffset : info[i].height - mh - yoffset);
mw = info[snum].width;
dimx = info[snum].x_org;
dimy = info[snum].y_org;
dimw = info[snum].width;
dimh = info[snum].height;
}
else {
XGetInputFocus(dc->dpy, &w, &di);
if(w != root && w != PointerRoot && w != None) {
/* find top-level window containing current input focus */
do {
if(XQueryTree(dc->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(dc->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(!area && XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
for(i = 0; i < n; i++)
if(INTERSECT(x, y, 1, 1, info[i]))
break;
x = info[i].x_org;
y = info[i].y_org + (topbar ? yoffset : info[i].height - mh - yoffset);
mw = info[i].width;
dimx = info[i].x_org;
dimy = info[i].y_org;
dimw = info[i].width;
dimh = info[i].height;
}
XFree(info);
}
else
#endif
{
x = 0;
y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh - yoffset;
mw = DisplayWidth(dc->dpy, screen);
dimx = 0;
dimy = 0;
dimw = WidthOfScreen(defScreen);
dimh = HeightOfScreen(defScreen);
}
if (xoffset < 0 && width != 0)
x = x + dimw + xoffset; /* Add one screen width and subtract */
else
x += xoffset;
mw = width ? width : mw;
promptw = (prompt && *prompt) ? textw(dc, prompt) : 0;
inputw = MIN(inputw, mw/3);
match();
swa.override_redirect = True;
/* create dim window */
if(dimopacity > 0) {
swa.background_pixel = dimcol->BG;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
dim = XCreateWindow(dc->dpy, root, dimx, dimy, dimw, dimh, 0,
DefaultDepth(dc->dpy, screen), CopyFromParent,
DefaultVisual(dc->dpy, screen),
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
XClassHint dimhint = { .res_name = dimname, .res_class = class };
XSetClassHint(dc->dpy, dim, &dimhint);
dimopacity = MIN(MAX(dimopacity, 0), 1);
unsigned int dimopacity_set = (unsigned int)(dimopacity * OPAQUE);
XChangeProperty(dc->dpy, dim, XInternAtom(dc->dpy, OPACITY, False),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *) &dimopacity_set, 1L);
XMapRaised(dc->dpy, dim);
}
/* if no width was specified, dmenu will span the entire screen, so we don't
* need to center */
if(centerX && width != 0) {
x = x + ((dimw - width) / 2);
}
/* create menu window */
swa.background_pixel = border_width ? bordercol->BG : normcol->BG;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dc->dpy, root, x, y, mw + border_width * 2, mh + border_width * 2, 0,
DefaultDepth(dc->dpy, screen), CopyFromParent,
DefaultVisual(dc->dpy, screen),
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
XClassHint hint = { .res_name = name, .res_class = class };
XSetClassHint(dc->dpy, win, &hint);
opacity = MIN(MAX(opacity, 0), 1);
unsigned int opacity_set = (unsigned int)(opacity * OPAQUE);
XChangeProperty(dc->dpy, win, XInternAtom(dc->dpy, OPACITY, False),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *) &opacity_set, 1L);
/* open input methods */
xim = XOpenIM(dc->dpy, NULL, NULL, NULL);
xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, win, XNFocusWindow, win, NULL);
XMapRaised(dc->dpy, win);
resizedc(dc, mw, mh);
drawmenu();
}
void
usage(void) {
fputs("usage: dmenu [-b] [-q] [-f] [-r] [-i] [-z] [-t] [-mask] [-noinput]\n"
" [-s screen] [-name name] [-class class] [ -o opacity]\n"
" [-dim opcity] [-dc color] [-l lines] [-p prompt] [-fn font]\n"
" [-x xoffset] [-y yoffset] [-h height] [-w width] [-uh height] [-centerx]\n"
" [-nb color] [-nf color] [-sb color] [-sf color] [-uc color] [-hist histfile] [-v]\n", stderr);
exit(EXIT_FAILURE);
}
#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include "draw.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
void
drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) {
XSetForeground(dc->dpy, dc->gc, color);
if(fill)
XFillRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w, h);
else
XDrawRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w-1, h-1);
}
void
drawtext(DC *dc, const char *text, ColorSet *col) {
char buf[BUFSIZ];
size_t mn, n = strlen(text);
/* shorten text if necessary */
for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) + dc->font.height/2 > dc->w; mn--)
if(mn == 0)
return;
memcpy(buf, text, mn);
if(mn < n)
for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.');
drawrect(dc, 0, 0, dc->w, dc->h, True, col->BG);
drawtextn(dc, buf, mn, col);
}
void
drawtextn(DC *dc, const char *text, size_t n, ColorSet *col) {
int x = dc->x + dc->font.height/2;
int y = dc->y + dc->font.ascent + (dc->h - dc->font.height)/2;
XSetForeground(dc->dpy, dc->gc, col->FG);
if(dc->font.xft_font) {
if (!dc->xftdraw)
eprintf("error, xft drawable does not exist");
XftDrawStringUtf8(dc->xftdraw, &col->FG_xft,
dc->font.xft_font, x, y, (unsigned char*)text, n);
} else if(dc->font.set) {
XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n);
} else {
XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid);
XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n);
}
}
void
eprintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if(fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
}
exit(EXIT_FAILURE);
}
void
freecol(DC *dc, ColorSet *col) {
if(col) {
if(&col->FG_xft)
XftColorFree(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)),
DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), &col->FG_xft);
free(col);
}
}
void
freedc(DC *dc) {
if(dc->font.xft_font) {
XftFontClose(dc->dpy, dc->font.xft_font);
XftDrawDestroy(dc->xftdraw);
}
if(dc->font.set)
XFreeFontSet(dc->dpy, dc->font.set);
if(dc->font.xfont)
XFreeFont(dc->dpy, dc->font.xfont);
if(dc->canvas)
XFreePixmap(dc->dpy, dc->canvas);
if(dc->gc)
XFreeGC(dc->dpy, dc->gc);
if(dc->dpy)
XCloseDisplay(dc->dpy);
if(dc)
free(dc);
}
unsigned long
getcolor(DC *dc, const char *colstr) {
Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy));
XColor color;
if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color))
eprintf("cannot allocate color '%s'\n", colstr);
return color.pixel;
}
ColorSet *
initcolor(DC *dc, const char * foreground, const char * background) {
ColorSet * col = (ColorSet *)malloc(sizeof(ColorSet));
if(!col)
eprintf("error, cannot allocate memory for color set");
col->BG = getcolor(dc, background);
col->FG = getcolor(dc, foreground);
if(dc->font.xft_font)
if(!XftColorAllocName(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)),
DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), foreground, &col->FG_xft))
eprintf("error, cannot allocate xft font color '%s'\n", foreground);
return col;
}
DC *
initdc(void) {
DC *dc;
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("no locale support\n", stderr);
if(!(dc = calloc(1, sizeof *dc)))
eprintf("cannot malloc %u bytes:", sizeof *dc);
if(!(dc->dpy = XOpenDisplay(NULL)))
eprintf("cannot open display\n");
dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL);
XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter);
return dc;
}
void
initfont(DC *dc, const char *fontstr) {
char *def, **missing, **names;
int i, n;
XFontStruct **xfonts;
missing = NULL;
if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
dc->font.ascent = dc->font.xfont->ascent;
dc->font.descent = dc->font.xfont->descent;
dc->font.width = dc->font.xfont->max_bounds.width;
} else if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
n = XFontsOfFontSet(dc->font.set, &xfonts, &names);
for(i = 0; i < n; i++) {
dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent);
dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent);
dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width);
}
} else if((dc->font.xft_font = XftFontOpenName(dc->dpy, DefaultScreen(dc->dpy), fontstr))) {
dc->font.ascent = dc->font.xft_font->ascent;
dc->font.descent = dc->font.xft_font->descent;
dc->font.width = dc->font.xft_font->max_advance_width;
} else {
eprintf("cannot load font '%s'\n", fontstr);
}
if(missing)
XFreeStringList(missing);
dc->font.height = dc->font.ascent + dc->font.descent;
return;
}
void
mapdc(DC *dc, Window win, unsigned int w, unsigned int h, int offset) {
XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, offset, offset);
}
void
resizedc(DC *dc, unsigned int w, unsigned int h) {
int screen = DefaultScreen(dc->dpy);
if(dc->canvas)
XFreePixmap(dc->dpy, dc->canvas);
dc->w = w;
dc->h = h;
dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h,
DefaultDepth(dc->dpy, screen));
if(dc->font.xft_font && !(dc->xftdraw)) {
dc->xftdraw = XftDrawCreate(dc->dpy, dc->canvas, DefaultVisual(dc->dpy,screen), DefaultColormap(dc->dpy,screen));
if(!(dc->xftdraw))
eprintf("error, cannot create xft drawable\n");
}
}
int
textnw(DC *dc, const char *text, size_t len) {
if(dc->font.xft_font) {
XGlyphInfo gi;
XftTextExtentsUtf8(dc->dpy, dc->font.xft_font, (const FcChar8*)text, len, &gi);
return gi.width;
} else if(dc->font.set) {
XRectangle r;
XmbTextExtents(dc->font.set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(dc->font.xfont, text, len);
}
int
textw(DC *dc, const char *text) {
return textnw(dc, text, strlen(text)) + dc->font.height;
}
#include <X11/Xft/Xft.h>
typedef struct {
int x, y, w, h;
Bool invert;
Display *dpy;
GC gc;
Pixmap canvas;
XftDraw *xftdraw;
struct {
int ascent;
int descent;
int height;
int width;
XFontSet set;
XFontStruct *xfont;
XftFont *xft_font;
} font;
} DC; /* draw context */
typedef struct {
unsigned long FG;
XftColor FG_xft;
unsigned long BG;
} ColorSet;
void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color);
void drawtext(DC *dc, const char *text, ColorSet *col);
void drawtextn(DC *dc, const char *text, size_t n, ColorSet *col);
void freecol(DC *dc, ColorSet *col);
void eprintf(const char *fmt, ...);
void freedc(DC *dc);
unsigned long getcolor(DC *dc, const char *colstr);
ColorSet *initcolor(DC *dc, const char *foreground, const char *background);
DC *initdc(void);
void initfont(DC *dc, const char *fontstr);
void mapdc(DC *dc, Window win, unsigned int w, unsigned int h, int offset);
void resizedc(DC *dc, unsigned int w, unsigned int h);
int textnw(DC *dc, const char *text, size_t len);
int textw(DC *dc, const char *text);
MIT/X Consortium License
© 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 2009 Gottox <gottox@s01.de>
© 2009 Markus Schnalke <meillo@marmaro.de>
© 2009 Evan Gates <evan.gates@gmail.com>
© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
© 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org>
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.
VERSION = 0.1
CC ?= gcc
LIBS = -lX11 -lXinerama -lXft -lXrender -lfreetype -lz -lfontconfig
CFLAGS += -std=c99 -pedantic -Wall -Wextra -I/usr/include/freetype2
CFLAGS += -DXINERAMA -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\"
LDFLAGS +=
PREFIX ?= /usr/local
BINPREFIX = $(PREFIX)/bin
MANPREFIX = $(PREFIX)/share/man
DM_SRC = dmenu3.c draw.c
DM_OBJ = $(DM_SRC:.c=.o)
all: CFLAGS += -Os
all: LDFLAGS += -s
all: dmenu3
debug: CFLAGS += -g -O0 -DDEBUG
debug: dmenu3
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
dmenu3: $(DM_OBJ)
$(CC) -o $@ $(DM_OBJ) $(LDFLAGS) $(LIBS)
install:
mkdir -p "$(DESTDIR)$(BINPREFIX)"
cp -p dmenu3 "$(DESTDIR)$(BINPREFIX)"
mkdir -p "$(DESTDIR)$(MANPREFIX)"/man1
cp -p dmenu3.1 "$(DESTDIR)$(MANPREFIX)"/man1
uninstall:
rm -f "$(DESTDIR)$(BINPREFIX)"/dmenu3
rm -f "$(DESTDIR)$(MANPREFIX)"/man1/dmenu3.1
clean:
rm -f $(DM_OBJ) dmenu3
.PHONY: all debug install uninstall clean
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment