forked from speedie/spmenu
add plenty of comments to spmenu
This commit is contained in:
parent
4e01d87818
commit
6ac2afb240
1
TODO
1
TODO
|
@ -7,3 +7,4 @@
|
|||
- Use cairo for text drawing over Xft
|
||||
- MAYBE wayland support, but only if it doesn't require writing any extra code which as of now seems unlikely
|
||||
- Write documentation about profiles
|
||||
- FIFO, used to dynamically refresh
|
||||
|
|
|
@ -509,6 +509,6 @@ setcolumns(const Arg *arg)
|
|||
void
|
||||
setprofile(const Arg *arg)
|
||||
{
|
||||
int ax = system("command -v spmenu_profile > /dev/null && spmenu_profile --spmenu-set-profile > /dev/null");
|
||||
exit(ax);
|
||||
// this just runs an external shell script to set the profile
|
||||
exit(system("command -v spmenu_profile > /dev/null && spmenu_profile --spmenu-set-profile > /dev/null"));
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
typedef union {
|
||||
int i;
|
||||
unsigned int ui;
|
||||
float f;
|
||||
const void *v;
|
||||
int i; // integer
|
||||
unsigned int ui; // unsigned int
|
||||
float f; // float
|
||||
const void *v; // void
|
||||
} Arg;
|
||||
|
||||
// declare keybind functions
|
||||
|
|
10
libs/argv.c
10
libs/argv.c
|
@ -6,7 +6,8 @@ readargs(int argc, char *argv[])
|
|||
|
||||
int cxrdb = 0;
|
||||
|
||||
// check if we should load the xrdb/config, because it needs to be loaded before arguments are checked (internal -> xresources -> arguments)
|
||||
// check if we should load the xrdb/config, because it needs to be loaded before arguments are checked
|
||||
// priority: internal -> xresources -> arguments
|
||||
for (j = 1; j < argc; j++) {
|
||||
if (!strcmp(argv[j], "-xrdb") || (!strcmp(argv[j], "--xrdb"))) {
|
||||
xresources = 1;
|
||||
|
@ -23,16 +24,19 @@ readargs(int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: improve this function significantly
|
||||
|
||||
// init/read xrdb
|
||||
if (xresources) {
|
||||
XrmInitialize();
|
||||
|
||||
// also load config/profile if .Xresources
|
||||
if (loadconfig) {
|
||||
cxrdb = system("command -v spmenu_profile > /dev/null && spmenu_profile --spmenu-load-default-profile > /dev/null");
|
||||
}
|
||||
|
||||
// avoid an annoying warning gcc will spit out when you don't care about the result
|
||||
if (!cxrdb || cxrdb || xresources) load_xresources();
|
||||
if (!cxrdb||cxrdb) // load .Xresources, cxrdb is only here to avoid an annoying gcc warning
|
||||
load_xresources();
|
||||
}
|
||||
|
||||
// no arguments
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
void
|
||||
prepare_window_size(void)
|
||||
{
|
||||
// set horizontal and vertical padding
|
||||
sp = menupaddingh;
|
||||
vp = (menuposition == 1) ? menupaddingv : - menupaddingv;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -13,7 +16,12 @@ create_window(int x, int y, int w, int h)
|
|||
swa.override_redirect = managed ? False : True;
|
||||
swa.background_pixel = 0;
|
||||
swa.colormap = cmap;
|
||||
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonPressMask | PointerMotionMask;
|
||||
swa.event_mask =
|
||||
ExposureMask | // mapping the drawing
|
||||
KeyPressMask | // keypresses
|
||||
VisibilityChangeMask | // whether or not client is visible
|
||||
ButtonPressMask | // see buttonpress in libs/mouse.c for usage
|
||||
PointerMotionMask; // we need pointer for selecting entries using the mouse
|
||||
|
||||
// create client
|
||||
win = XCreateWindow(dpy, parentwin, x, y, w, h, borderwidth,
|
||||
|
@ -29,17 +37,17 @@ set_window(void)
|
|||
{
|
||||
XClassHint ch = { class, class };
|
||||
|
||||
// set border and class
|
||||
XSetWindowBorder(dpy, win, scheme[SchemeBorder][ColBg].pixel);
|
||||
XSetClassHint(dpy, win, &ch);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
set_prop(void)
|
||||
{
|
||||
XChangeProperty(dpy, win, types, XA_ATOM, 32, PropModeReplace, (unsigned char *) &dock, 1);
|
||||
|
||||
// TODO: add toggle for this
|
||||
XChangeProperty(dpy, win, types, XA_ATOM, 32, PropModeReplace, (unsigned char *) &dock, 1); // set dock property
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -58,17 +66,18 @@ resizeclient(void)
|
|||
lines = MIN(itemCount, MAX(lines, 0));
|
||||
reallines = lines;
|
||||
|
||||
// resize client to image height
|
||||
#if USEIMAGE
|
||||
if (image)
|
||||
resizetoimageheight(imageheight);
|
||||
if (image) resizetoimageheight(imageheight);
|
||||
#endif
|
||||
|
||||
mh = (lines + 1) * bh;
|
||||
|
||||
// why have an empty line?
|
||||
// why have an empty line? when there's nothing to draw there anyway?
|
||||
if (hideprompt && hideinput && hidemode && hidematchcount)
|
||||
mh += bh;
|
||||
|
||||
// no window/invalid window or menu height we had before is the same as the current window height
|
||||
if (!win || omh == mh) return;
|
||||
|
||||
XResizeWindow(dpy, win, mw, mh);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// color scheme arrays
|
||||
// alpha array
|
||||
static unsigned int alphas[][3] = {
|
||||
// fg bg border
|
||||
[SchemeLArrow] = { fgalpha, bgalpha, borderalpha },
|
||||
|
@ -18,6 +18,7 @@ static unsigned int alphas[][3] = {
|
|||
[SchemeBorder] = { fgalpha, bgalpha, borderalpha },
|
||||
};
|
||||
|
||||
// colorscheme array
|
||||
static char *colors[SchemeLast][2] = {
|
||||
// fg bg
|
||||
[SchemeLArrow] = { col_larrowfg, col_larrowbg },
|
||||
|
@ -37,7 +38,7 @@ static char *colors[SchemeLast][2] = {
|
|||
[SchemeBorder] = { NULL, col_bordercolor },
|
||||
};
|
||||
|
||||
// sgr colors
|
||||
// sgr color array
|
||||
// to enable 256 color support, append to this.
|
||||
static char *textcolors[] = {
|
||||
col_sgrcolor0,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// item
|
||||
#define MAXITEMLENGTH 1024
|
||||
|
||||
// user friendly names for all the modifiers
|
||||
// user friendly names for all the modifiers we're using, but there are many more
|
||||
#define CONTROL ControlMask
|
||||
#define SHIFT ShiftMask
|
||||
#define ALT Mod1Mask
|
||||
|
|
|
@ -389,6 +389,8 @@ drawmenu(void)
|
|||
if (!hideinput) x = drawinput(x, y, w);
|
||||
if (!hidemode) modeWidth = pango_mode ? TEXTWM(modetext) : TEXTW(modetext);
|
||||
|
||||
// draw the items, this function also calls drawrarrow() and drawlarrow()
|
||||
// TODO: Allow hiding items, without setting columns to 0
|
||||
drawitem(x, y, w);
|
||||
|
||||
if (!hidematchcount) drawnumber(x, y, w, numberWidth, modeWidth);
|
||||
|
|
|
@ -36,10 +36,10 @@ eventloop(void)
|
|||
if (ev.xfocus.window != win)
|
||||
grabfocus();
|
||||
break;
|
||||
case KeyPress:
|
||||
case KeyPress: // read key array and call functions
|
||||
keypress(&ev);
|
||||
break;
|
||||
case SelectionNotify:
|
||||
case SelectionNotify: // paste selection
|
||||
if (ev.xselection.property == utf8)
|
||||
pastesel();
|
||||
break;
|
||||
|
|
92
libs/history.c
Normal file
92
libs/history.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
void
|
||||
loadhistory(void)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
static size_t cap = 0;
|
||||
size_t llen;
|
||||
char *line;
|
||||
|
||||
// no history file, give up like i do with all things in life
|
||||
if (!histfile) {
|
||||
return;
|
||||
}
|
||||
|
||||
// open history file, return if it failed
|
||||
fp = fopen(histfile, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "spmenu: failed to open history file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
line = NULL;
|
||||
llen = 0;
|
||||
|
||||
if (-1 == getline(&line, &llen, fp)) {
|
||||
if (ferror(fp)) {
|
||||
die("spmenu: failed to read history");
|
||||
}
|
||||
|
||||
free(line);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cap == histsz) {
|
||||
cap += 64 * sizeof(char*);
|
||||
history = realloc(history, cap);
|
||||
if (!history) {
|
||||
die("spmenu: failed to realloc memory");
|
||||
}
|
||||
}
|
||||
strtok(line, "\n");
|
||||
history[histsz] = line;
|
||||
histsz++;
|
||||
}
|
||||
|
||||
histpos = histsz;
|
||||
|
||||
if (fclose(fp)) {
|
||||
die("spmenu: failed to close file %s", histfile);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
navigatehistfile(int dir)
|
||||
{
|
||||
static char def[BUFSIZ];
|
||||
char *p = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
if (!history || histpos + 1 == 0)
|
||||
return;
|
||||
|
||||
if (histsz == histpos) {
|
||||
strncpy(def, text, sizeof(def));
|
||||
}
|
||||
|
||||
switch (dir) {
|
||||
case 1:
|
||||
if (histpos < histsz - 1) {
|
||||
p = history[++histpos];
|
||||
} else if (histpos == histsz - 1) {
|
||||
p = def;
|
||||
histpos++;
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
if (histpos > 0) {
|
||||
p = history[--histpos];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
len = MIN(strlen(p), BUFSIZ - 1);
|
||||
strcpy(text, p);
|
||||
text[len] = '\0';
|
||||
cursor = len;
|
||||
match();
|
||||
}
|
3
libs/history.h
Normal file
3
libs/history.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
static char *histfile;
|
||||
static char **history;
|
||||
static size_t histsz, histpos;
|
49
libs/img-c.c
49
libs/img-c.c
|
@ -1,6 +1,7 @@
|
|||
void
|
||||
setimagesize(int width, int height)
|
||||
{
|
||||
// don't even bother if we don't have image support
|
||||
#if !USEIMAGE
|
||||
return;
|
||||
#endif
|
||||
|
@ -11,6 +12,7 @@ setimagesize(int width, int height)
|
|||
// this makes sure we cannot scale the image up or down too much
|
||||
if ((!image && height < imageheight) || (!image && width < imagewidth) || width > mw || hideimage) return;
|
||||
|
||||
// original width/height
|
||||
oih = imageheight;
|
||||
oiw = imagewidth;
|
||||
|
||||
|
@ -33,6 +35,7 @@ setimagesize(int width, int height)
|
|||
void
|
||||
flipimage(void)
|
||||
{
|
||||
// flip image
|
||||
switch (flip) {
|
||||
case 1: // horizontal
|
||||
imlib_image_flip_horizontal();
|
||||
|
@ -59,7 +62,7 @@ rotateimage(void)
|
|||
void
|
||||
cleanupimage(void)
|
||||
{
|
||||
if (image) {
|
||||
if (image) { // free image using imlib2
|
||||
imlib_free_image();
|
||||
image = NULL;
|
||||
}
|
||||
|
@ -73,29 +76,30 @@ drawimage(void)
|
|||
|
||||
if (!lines || !columns || hideimage) return;
|
||||
|
||||
// to prevent the image from being drawn multiple times
|
||||
// to prevent the image from being drawn multiple times wasting resources
|
||||
if (!needredraw) {
|
||||
needredraw = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// load image cache
|
||||
if (sel && sel->image && strcmp(sel->image, limg ? limg : "")) {
|
||||
if (longestedge)
|
||||
loadimagecache(sel->image, &width, &height);
|
||||
} else if ((!sel || !sel->image) && image) {
|
||||
imlib_free_image();
|
||||
image = NULL;
|
||||
} if (image && longestedge) {
|
||||
|
||||
} else if ((!sel || !sel->image) && image) { // free image
|
||||
cleanupimage();
|
||||
} if (image && longestedge) { // render the image
|
||||
// rotate and flip, will return if we don't need to
|
||||
rotateimage();
|
||||
flipimage();
|
||||
|
||||
int leftmargin = imagegaps;
|
||||
|
||||
if(mh != bh + height + imagegaps * 2) {
|
||||
if (mh != bh + height + imagegaps * 2) { // menu height cannot be smaller than image height
|
||||
resizetoimageheight(height);
|
||||
}
|
||||
|
||||
// render image
|
||||
if (!imageposition) { // top mode = 0
|
||||
if (height > width)
|
||||
width = height;
|
||||
|
@ -110,9 +114,9 @@ drawimage(void)
|
|||
int minh = MIN(height, mh-bh-imagegaps*2);
|
||||
imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, (minh-height)/2+bh+imagegaps);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} if (sel) {
|
||||
if (sel) {
|
||||
limg = sel->image;
|
||||
} else {
|
||||
limg = NULL;
|
||||
|
@ -135,13 +139,16 @@ setimageopts(void)
|
|||
void
|
||||
createifnexist(const char *dir)
|
||||
{
|
||||
if(access(dir, F_OK) == 0)
|
||||
// exists, so return
|
||||
if (access(dir, F_OK) == 0)
|
||||
return;
|
||||
|
||||
if(errno == EACCES)
|
||||
// fatal: permission denied
|
||||
if (errno == EACCES)
|
||||
fprintf(stderr, "spmenu: no access to create directory: %s\n", dir);
|
||||
|
||||
if(mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1)
|
||||
// mkdir() failure
|
||||
if (mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1)
|
||||
fprintf(stderr, "spmenu: failed to create directory: %s\n", dir);
|
||||
}
|
||||
|
||||
|
@ -181,21 +188,22 @@ loadimage(const char *file, int *width, int *height)
|
|||
void
|
||||
scaleimage(int *width, int *height)
|
||||
{
|
||||
int nwidth, nheight;
|
||||
int new_width, new_height;
|
||||
float aspect = 1.0f;
|
||||
|
||||
// depending on what size, we determine aspect ratio
|
||||
if (imagewidth > *width)
|
||||
aspect = (float)(*width)/imagewidth;
|
||||
else
|
||||
aspect = (float)imagewidth/(*width);
|
||||
|
||||
nwidth = *width * aspect;
|
||||
nheight = *height * aspect;
|
||||
new_width = *width * aspect;
|
||||
new_height = *height * aspect;
|
||||
|
||||
if(nwidth == *width && nheight == *height)
|
||||
if (new_width == *width && new_height == *height)
|
||||
return;
|
||||
|
||||
image = imlib_create_cropped_scaled_image(0,0,*width,*height,nwidth,nheight);
|
||||
image = imlib_create_cropped_scaled_image(0,0,*width,*height,new_width,new_height);
|
||||
|
||||
imlib_free_image();
|
||||
|
||||
|
@ -204,8 +212,8 @@ scaleimage(int *width, int *height)
|
|||
|
||||
imlib_context_set_image(image);
|
||||
|
||||
*width = nwidth;
|
||||
*height = nheight;
|
||||
*width = new_width;
|
||||
*height = new_height;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -343,6 +351,7 @@ resizetoimageheight(int imageheight)
|
|||
struct item *item;
|
||||
unsigned int i = 1;
|
||||
|
||||
// walk through all matches
|
||||
for (item = matches; item && item != sel; item = item->right)
|
||||
++i;
|
||||
|
||||
|
|
|
@ -91,13 +91,13 @@ readstdin(void)
|
|||
// spmenu:about
|
||||
if (!strncmp("about", items[i].ex, strlen("about"))) {
|
||||
int i = system("printf \"spmenu $([ -f '/usr/share/spmenu/version' ] && cat /usr/share/spmenu/version || printf unknown)\\nBased on dmenu 5.2 from https://tools.suckless.org/dmenu\\nCompiled $([ -f '/usr/share/spmenu/compile-date' ] && cat /usr/share/spmenu/compile-date || printf Unknown)\\nCFLAGS: $([ -f '/usr/share/spmenu/cflags' ] && cat /usr/share/spmenu/cflags || printf unknown)\\nCC: $([ -f '/usr/share/spmenu/cc' ] && cat /usr/share/spmenu/cc || printf unknown)\\nDistro: $([ -f '/usr/share/spmenu/pkg_arch' ] && echo Arch || echo Installed manually)\\n\" | spmenu --columns 1 --lines 10 --hide-cursor --no-allow-typing --hide-mode --hide-match-count --hide-prompt --hide-powerline --hide-input --no-indent --no-color-items > /dev/null");
|
||||
if (i||!i) exit(0);
|
||||
if (i||!i) exit(i);
|
||||
}
|
||||
|
||||
// spmenu:test
|
||||
if (!strncmp("test", items[i].ex, strlen("test"))) {
|
||||
int i = system("command -v spmenu_test > /dev/null && spmenu_test");
|
||||
if (i||!i) exit(0);
|
||||
if (i||!i) exit(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,8 @@ resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst)
|
|||
snprintf(fullname, sizeof(fullname), "%s.%s", "spmenu", name);
|
||||
fullname[sizeof(fullname) - 1] = '\0';
|
||||
XrmGetResource(db, fullname, "*", &type, &ret);
|
||||
if (!(ret.addr == NULL || strncmp("String", type, 64)))
|
||||
{
|
||||
switch (rtype) {
|
||||
if (!(ret.addr == NULL || strncmp("String", type, 64))) {
|
||||
switch (rtype) { // type
|
||||
case STRING:
|
||||
strcpy(sdst, ret.addr);
|
||||
break;
|
||||
|
|
187
spmenu.c
187
spmenu.c
|
@ -99,6 +99,7 @@
|
|||
#include "libs/key.h"
|
||||
#include "libs/mouse.h"
|
||||
#include "libs/sort.h"
|
||||
#include "libs/history.h"
|
||||
|
||||
// misc
|
||||
#include "libs/key_struct.c"
|
||||
|
@ -182,11 +183,6 @@ static Drw *drw;
|
|||
static Clr *scheme[SchemeLast];
|
||||
static Clr textclrs[256];
|
||||
|
||||
// history
|
||||
static char *histfile;
|
||||
static char **history;
|
||||
static size_t histsz, histpos;
|
||||
|
||||
// declare functions
|
||||
static void calcoffsets(void);
|
||||
static void recalculatenumbers(void);
|
||||
|
@ -235,6 +231,7 @@ static char *(*fstrstr)(const char *, const char *) = cistrstr;
|
|||
#include "libs/client.c"
|
||||
#include "libs/match.h"
|
||||
#include "libs/match.c"
|
||||
#include "libs/history.c"
|
||||
#include "libs/arg.c"
|
||||
#include "libs/stream.c"
|
||||
|
||||
|
@ -258,11 +255,16 @@ recalculatenumbers(void)
|
|||
struct item *item;
|
||||
if (matchend) {
|
||||
numer++;
|
||||
|
||||
// walk through items that match and add to numer
|
||||
for (item = matchend; item && item->left; item = item->left)
|
||||
numer++;
|
||||
}
|
||||
|
||||
// walk through all items, matching or not and add to denom
|
||||
for (item = items; item && item->text; item++)
|
||||
denom++;
|
||||
|
||||
snprintf(numbers, NUMBERSBUFSIZE, "%d/%d", numer, denom);
|
||||
}
|
||||
|
||||
|
@ -273,7 +275,7 @@ calcoffsets(void)
|
|||
|
||||
if (lines > 0)
|
||||
n = lines * columns * bh;
|
||||
else {
|
||||
else { // no lines, therefore the size of items must be decreased to fit the menu elements
|
||||
int numberWidth = 0;
|
||||
int modeWidth = 0;
|
||||
int larrowWidth = 0;
|
||||
|
@ -287,10 +289,12 @@ calcoffsets(void)
|
|||
n = mw - (promptw + inputw + larrowWidth + rarrowWidth + modeWidth + numberWidth);
|
||||
}
|
||||
|
||||
// calculate which items will begin the next page and previous page
|
||||
// calculate which items will begin the next page
|
||||
for (i = 0, next = curr; next; next = next->right)
|
||||
if ((i += (lines > 0) ? bh : MIN(TEXTWM(next->text), n)) > n)
|
||||
break;
|
||||
|
||||
// calculate which items will begin the previous page
|
||||
for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
|
||||
if ((i += (lines > 0) ? bh : MIN(TEXTWM(prev->left->text), n)) > n)
|
||||
break;
|
||||
|
@ -300,8 +304,10 @@ int
|
|||
max_textw(void)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
for (struct item *item = items; item && item->text; item++)
|
||||
len = MAX(TEXTW(item->text), len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -311,17 +317,20 @@ cleanup(void)
|
|||
size_t i;
|
||||
|
||||
#if USEIMAGE
|
||||
cleanupimage();
|
||||
cleanupimage(); // function frees images
|
||||
#endif
|
||||
|
||||
XUngrabKey(dpy, AnyKey, AnyModifier, root);
|
||||
XUngrabKey(dpy, AnyKey, AnyModifier, root); // ungrab keys
|
||||
|
||||
// free color scheme
|
||||
for (i = 0; i < SchemeLast; i++)
|
||||
free(scheme[i]);
|
||||
|
||||
// free high priority items
|
||||
for (i = 0; i < hplength; ++i)
|
||||
free(hpitems[i]);
|
||||
|
||||
// free drawing and close the display
|
||||
drw_free(drw);
|
||||
XSync(dpy, False);
|
||||
XCloseDisplay(dpy);
|
||||
|
@ -337,11 +346,12 @@ cistrstr(const char *h, const char *n)
|
|||
|
||||
for (; *h; ++h) {
|
||||
for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
|
||||
tolower((unsigned char)h[i]); ++i)
|
||||
;
|
||||
tolower((unsigned char)h[i]); ++i);
|
||||
|
||||
if (n[i] == '\0')
|
||||
return (char *)h;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -356,6 +366,8 @@ grabfocus(void)
|
|||
XGetInputFocus(dpy, &focuswin, &revertwin);
|
||||
if (focuswin == win)
|
||||
return;
|
||||
|
||||
// if it's a client, we can't just steal all the input for ourselves
|
||||
if (managed) {
|
||||
XTextProperty prop;
|
||||
char *windowtitle = prompt != NULL ? prompt : "spmenu";
|
||||
|
@ -363,13 +375,14 @@ grabfocus(void)
|
|||
XSetWMName(dpy, win, &prop);
|
||||
XSetTextProperty(dpy, win, &prop, XInternAtom(dpy, "_NET_WM_NAME", False));
|
||||
XFree(prop.value);
|
||||
} else {
|
||||
} else { // spmenu is not managed, and is very greedy
|
||||
XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
|
||||
}
|
||||
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
die("cannot grab focus");
|
||||
|
||||
die("spmenu: cannot grab focus"); // not possible to grab focus, abort immediately
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -377,10 +390,15 @@ 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));
|
||||
|
||||
// update cursor
|
||||
if (n > 0)
|
||||
memcpy(&text[cursor], str, n);
|
||||
|
||||
// add to cursor position and continue matching
|
||||
cursor += n;
|
||||
match();
|
||||
}
|
||||
|
@ -396,93 +414,6 @@ nextrune(int inc)
|
|||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
loadhistory(void)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
static size_t cap = 0;
|
||||
size_t llen;
|
||||
char *line;
|
||||
|
||||
if (!histfile) {
|
||||
return;
|
||||
}
|
||||
|
||||
fp = fopen(histfile, "r");
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
line = NULL;
|
||||
llen = 0;
|
||||
if (-1 == getline(&line, &llen, fp)) {
|
||||
if (ferror(fp)) {
|
||||
die("failed to read history");
|
||||
}
|
||||
free(line);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cap == histsz) {
|
||||
cap += 64 * sizeof(char*);
|
||||
history = realloc(history, cap);
|
||||
if (!history) {
|
||||
die("failed to realloc memory");
|
||||
}
|
||||
}
|
||||
strtok(line, "\n");
|
||||
history[histsz] = line;
|
||||
histsz++;
|
||||
}
|
||||
histpos = histsz;
|
||||
|
||||
if (fclose(fp)) {
|
||||
die("failed to close file %s", histfile);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
navigatehistfile(int dir)
|
||||
{
|
||||
static char def[BUFSIZ];
|
||||
char *p = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
if (!history || histpos + 1 == 0)
|
||||
return;
|
||||
|
||||
if (histsz == histpos) {
|
||||
strncpy(def, text, sizeof(def));
|
||||
}
|
||||
|
||||
switch (dir) {
|
||||
case 1:
|
||||
if (histpos < histsz - 1) {
|
||||
p = history[++histpos];
|
||||
} else if (histpos == histsz - 1) {
|
||||
p = def;
|
||||
histpos++;
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
if (histpos > 0) {
|
||||
p = history[--histpos];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
len = MIN(strlen(p), BUFSIZ - 1);
|
||||
strcpy(text, p);
|
||||
text[len] = '\0';
|
||||
cursor = len;
|
||||
match();
|
||||
}
|
||||
|
||||
void
|
||||
pastesel(void)
|
||||
{
|
||||
|
@ -495,9 +426,11 @@ pastesel(void)
|
|||
if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
|
||||
utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
|
||||
== Success && p) {
|
||||
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
|
||||
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); // insert selection
|
||||
XFree(p);
|
||||
}
|
||||
|
||||
// draw the menu
|
||||
drawmenu();
|
||||
}
|
||||
|
||||
|
@ -509,7 +442,7 @@ xinitvisual()
|
|||
int nitems;
|
||||
int i;
|
||||
|
||||
// properties
|
||||
// visual properties
|
||||
XVisualInfo tpl = {
|
||||
.screen = screen,
|
||||
.depth = 32,
|
||||
|
@ -520,6 +453,8 @@ xinitvisual()
|
|||
|
||||
infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
|
||||
visual = NULL;
|
||||
|
||||
// create colormap
|
||||
for(i = 0; i < nitems; i ++) {
|
||||
fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
|
||||
if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
|
||||
|
@ -542,7 +477,7 @@ xinitvisual()
|
|||
}
|
||||
|
||||
void
|
||||
setup(void)
|
||||
setupdisplay(void)
|
||||
{
|
||||
int x, y, i;
|
||||
#if USEXINERAMA
|
||||
|
@ -563,7 +498,7 @@ setup(void)
|
|||
|
||||
init_appearance(); // init colorschemes by reading arrays
|
||||
|
||||
// properties
|
||||
// set properties indicating what spmenu handles
|
||||
clip = XInternAtom(dpy, "CLIPBOARD", False);
|
||||
utf8 = XInternAtom(dpy, "UTF8_STRING", False);
|
||||
types = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
|
||||
|
@ -581,6 +516,7 @@ setup(void)
|
|||
|
||||
mh = (lines + 1) * bh; // lines + 1 * bh is the menu height
|
||||
|
||||
// set prompt width based on prompt size
|
||||
promptw = (prompt && *prompt)
|
||||
? pango_prompt ? TEXTWM(prompt) : TEXTW(prompt) - lrpad / 4 : 0; // prompt width
|
||||
|
||||
|
@ -628,11 +564,11 @@ setup(void)
|
|||
break;
|
||||
|
||||
// calculate x/y position
|
||||
if (menuposition == 2) {
|
||||
if (menuposition == 2) { // centered
|
||||
mw = MIN(MAX(max_textw() + promptw, minwidth), info[i].width);
|
||||
x = info[i].x_org + ((info[i].width - mw) / 2);
|
||||
y = info[i].y_org + ((info[i].height - mh) / 2);
|
||||
} else {
|
||||
} else { // top or bottom
|
||||
x = info[i].x_org + dmx;
|
||||
y = info[i].y_org + (menuposition ? 0 : info[i].height - mh - dmy);
|
||||
mw = (dmw>0 ? dmw : info[i].width);
|
||||
|
@ -643,14 +579,14 @@ setup(void)
|
|||
#endif
|
||||
{
|
||||
if (!XGetWindowAttributes(dpy, parentwin, &wa))
|
||||
die("could not get embedding window attributes: 0x%lx",
|
||||
parentwin);
|
||||
die("spmenu: could not get embedding window attributes: 0x%lx",
|
||||
parentwin); // die because unable to get attributes for the parent window
|
||||
|
||||
if (menuposition == 2) {
|
||||
if (menuposition == 2) { // centered
|
||||
mw = MIN(MAX(max_textw() + promptw, minwidth), wa.width);
|
||||
x = (wa.width - mw) / 2;
|
||||
y = (wa.height - mh) / 2;
|
||||
} else {
|
||||
} else { // top or bottom
|
||||
x = 0;
|
||||
y = 0;
|
||||
mw = wa.width;
|
||||
|
@ -660,9 +596,9 @@ setup(void)
|
|||
// might be faster in some instances, most of the time unnecessary
|
||||
if (!accuratewidth) inputw = MIN(inputw, mw/3);
|
||||
|
||||
match();
|
||||
match(); // match entries
|
||||
|
||||
// create menu window
|
||||
// create menu window and set properties for it
|
||||
create_window(x + sp, y + vp - (menuposition == 2 ? 0 : borderwidth * 2), mw - 2 * sp - borderwidth * 2, mh);
|
||||
set_window();
|
||||
set_prop();
|
||||
|
@ -679,6 +615,8 @@ setup(void)
|
|||
XNClientWindow, win, XNFocusWindow, win, NULL);
|
||||
|
||||
XMapRaised(dpy, win);
|
||||
|
||||
// embed spmenu inside parent window
|
||||
if (embed) {
|
||||
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
|
||||
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
|
||||
|
@ -699,12 +637,13 @@ main(int argc, char *argv[])
|
|||
{
|
||||
XWindowAttributes wa;
|
||||
|
||||
readargs(argc, argv);
|
||||
readargs(argc, argv); // start by reading arguments
|
||||
|
||||
#if USEIMAGE
|
||||
longestedge = MAX(imagewidth, imageheight);
|
||||
#endif
|
||||
|
||||
// set default mode, must be done before the event loop or keybindings will not work
|
||||
if (mode) {
|
||||
curMode = 1;
|
||||
allowkeys = 1;
|
||||
|
@ -718,36 +657,40 @@ main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
|
||||
fputs("warning: no locale support\n", stderr);
|
||||
fputs("warning: no locale support\n", stderr); // invalid locale, so notify the user about it
|
||||
|
||||
if (!(dpy = XOpenDisplay(NULL)))
|
||||
die("spmenu: cannot open display");
|
||||
die("spmenu: cannot open display"); // failed to open display
|
||||
|
||||
// set screen and root window
|
||||
screen = DefaultScreen(dpy);
|
||||
root = RootWindow(dpy, screen);
|
||||
|
||||
// parent window is the root window (ie. window manager) because we're not embedding
|
||||
if (!embed || !(parentwin = strtol(embed, NULL, 0)))
|
||||
parentwin = root;
|
||||
|
||||
if (!XGetWindowAttributes(dpy, parentwin, &wa))
|
||||
die("spmenu: could not get embedding window attributes: 0x%lx", parentwin);
|
||||
|
||||
xinitvisual();
|
||||
drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
|
||||
xinitvisual(); // init visual and create drawable after
|
||||
drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); // wrapper function creating a drawable
|
||||
|
||||
// load fonts
|
||||
if (!drw_font_create(drw, fonts, LENGTH(fonts)))
|
||||
die("no fonts could be loaded.");
|
||||
|
||||
// resize window
|
||||
lrpad = drw->font->h + textpadding;
|
||||
prepare_window_size();
|
||||
prepare_window_size(); // this function sets padding size
|
||||
|
||||
// openbsd specifics, i use gnu/linux so i have no idea why this is here
|
||||
#ifdef __OpenBSD__
|
||||
if (pledge("stdio rpath", NULL) == -1)
|
||||
die("pledge");
|
||||
#endif
|
||||
|
||||
loadhistory();
|
||||
loadhistory(); // read history entries
|
||||
|
||||
// fast (-f) means we grab keyboard before reading standard input
|
||||
if (fast && !isatty(0)) {
|
||||
|
@ -767,8 +710,8 @@ main(int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
setup();
|
||||
eventloop();
|
||||
setupdisplay(); // set up display and create window
|
||||
eventloop(); // function is a loop which checks X11 events and calls other functions accordingly
|
||||
|
||||
return 1;
|
||||
return 1; // should be unreachable
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue