2023-04-02 01:49:58 +02:00
|
|
|
/* spmenu - fancy dynamic menu
|
2023-03-16 10:09:51 +01:00
|
|
|
* See LICENSE file for copyright and license details.
|
|
|
|
*/
|
2023-08-07 06:27:10 +02:00
|
|
|
|
|
|
|
#include <stdarg.h>
|
2023-01-20 23:17:30 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2023-08-07 06:27:10 +02:00
|
|
|
#include <locale.h>
|
2023-08-07 07:20:16 +02:00
|
|
|
#include <math.h>
|
2023-01-20 23:17:30 +01:00
|
|
|
#include <unistd.h>
|
2023-08-07 06:19:15 +02:00
|
|
|
#include "libs/main.c"
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-08-07 06:46:16 +02:00
|
|
|
#include "libs/draw/draw.h"
|
|
|
|
|
2023-05-22 01:20:54 +02:00
|
|
|
#ifndef VERSION
|
|
|
|
#define VERSION "unknown"
|
|
|
|
#endif
|
|
|
|
|
2023-08-07 06:19:15 +02:00
|
|
|
enum {
|
2023-06-23 03:38:21 +02:00
|
|
|
ClickWindow,
|
|
|
|
ClickPrompt,
|
|
|
|
ClickInput,
|
|
|
|
ClickLArrow,
|
|
|
|
ClickItem,
|
|
|
|
ClickRArrow,
|
|
|
|
ClickNumber,
|
|
|
|
ClickCaps,
|
|
|
|
ClickMode,
|
2023-07-03 23:06:33 +02:00
|
|
|
ClickImage,
|
2023-07-14 21:31:29 +02:00
|
|
|
ClickNone,
|
2023-06-23 03:38:21 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct item {
|
|
|
|
char *text;
|
2023-07-04 23:32:51 +02:00
|
|
|
char *nsgrtext;
|
2023-06-23 03:38:21 +02:00
|
|
|
char *image;
|
|
|
|
char *ex;
|
|
|
|
struct item *left, *right;
|
|
|
|
int hp;
|
|
|
|
int index;
|
|
|
|
double distance;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sp {
|
|
|
|
int bh; // height of each menu item
|
|
|
|
int mw; // width
|
|
|
|
int mh; // height
|
|
|
|
int vp; // vertical padding for bar
|
|
|
|
int sp; // side padding for bar
|
|
|
|
int lrpad; // sum of left and right padding
|
|
|
|
|
|
|
|
int mode; // current mode
|
|
|
|
int allowkeys; // interpret a keypress as an insertion?
|
|
|
|
int capslockstate; // caps lock state
|
2023-07-27 00:39:57 +02:00
|
|
|
int isdrawing;
|
2023-06-23 03:38:21 +02:00
|
|
|
|
|
|
|
int inputw; // input width
|
|
|
|
int promptw; // prompt width
|
|
|
|
int plw; // powerline width
|
|
|
|
|
|
|
|
int itemnumber; // item number
|
2023-07-09 23:56:34 +02:00
|
|
|
int listcount;
|
|
|
|
int listchanged;
|
2023-06-23 03:38:21 +02:00
|
|
|
|
2023-07-28 23:15:05 +02:00
|
|
|
int maxlen; // max length of text
|
|
|
|
|
2023-06-24 04:20:51 +02:00
|
|
|
size_t cursor; // cursor width
|
2023-06-23 03:38:21 +02:00
|
|
|
|
|
|
|
int ignoreconfkeys; // can be set globally if you don't want to override keybinds with config file keys
|
|
|
|
int ignoreglobalkeys; // should be set in the config file, if 1, the Keys keys array is ignored
|
|
|
|
int ignoreconfmouse; // same for mouse
|
|
|
|
int ignoreglobalmouse; // same for mouse
|
|
|
|
};
|
|
|
|
|
2023-06-24 17:13:17 +02:00
|
|
|
struct mo {
|
|
|
|
int output_width; // output width
|
|
|
|
int output_height; // output height
|
|
|
|
};
|
|
|
|
|
2023-08-07 06:37:38 +02:00
|
|
|
#if IMAGE
|
2023-06-23 03:38:21 +02:00
|
|
|
struct img {
|
2023-06-24 04:20:51 +02:00
|
|
|
int setlines; // actual lines
|
|
|
|
int flip; // %=
|
2023-07-16 02:57:38 +02:00
|
|
|
int longestedge; // MAX(imagewidth, imageheight)
|
2023-06-24 04:20:51 +02:00
|
|
|
int imagewidth; // current image width
|
|
|
|
int imageheight; // current image height
|
|
|
|
int imagegaps; // current image gaps
|
2023-06-23 03:38:21 +02:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct tx {
|
|
|
|
char modetext[64]; // mode text
|
|
|
|
char text[BUFSIZ]; // input text
|
|
|
|
char numbers[NUMBERSBUFSIZE]; // number text
|
|
|
|
char capstext[64]; // caps lock text
|
|
|
|
};
|
|
|
|
|
2023-08-07 06:37:38 +02:00
|
|
|
#if X11
|
2023-06-23 03:38:21 +02:00
|
|
|
struct x11 {
|
|
|
|
int numlockmask;
|
|
|
|
int useargb;
|
|
|
|
int depth;
|
|
|
|
char *embed;
|
|
|
|
int screen;
|
|
|
|
Visual *visual;
|
|
|
|
Colormap cmap;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static struct sp sp = {0};
|
|
|
|
static struct tx tx = {0};
|
2023-06-24 17:13:17 +02:00
|
|
|
static struct mo mo = {0};
|
2023-08-07 06:37:38 +02:00
|
|
|
#if IMAGE
|
2023-06-23 03:38:21 +02:00
|
|
|
static struct img img = {0};
|
|
|
|
#endif
|
2023-08-07 06:37:38 +02:00
|
|
|
#if X11
|
2023-06-23 03:38:21 +02:00
|
|
|
static struct x11 x11 = {0};
|
|
|
|
#endif
|
|
|
|
|
2023-07-16 03:16:45 +02:00
|
|
|
static struct item *items = NULL;
|
|
|
|
static struct item *history_items;
|
|
|
|
static struct item *list_items;
|
|
|
|
static struct item *matches;
|
|
|
|
static struct item *matchend;
|
|
|
|
|
2023-08-08 20:13:29 +02:00
|
|
|
static struct item *previousitem;
|
|
|
|
static struct item *currentitem;
|
|
|
|
static struct item *nextitem;
|
|
|
|
static struct item *selecteditem;
|
|
|
|
static struct item *mouseitem;
|
2023-05-06 14:29:45 +02:00
|
|
|
|
2023-08-08 20:13:29 +02:00
|
|
|
static Draw_t *draw;
|
2023-06-23 03:38:21 +02:00
|
|
|
|
|
|
|
static int hplength = 0;
|
|
|
|
static char **hpitems = NULL;
|
2023-03-16 10:09:51 +01:00
|
|
|
|
2023-07-28 02:26:49 +02:00
|
|
|
static int theme_override = 0;
|
|
|
|
static int binds_override = 0;
|
|
|
|
static int protocol_override = 0;
|
|
|
|
|
2023-05-16 18:54:26 +02:00
|
|
|
static int *sel_index = NULL;
|
|
|
|
static unsigned int sel_size = 0;
|
2023-06-23 03:38:21 +02:00
|
|
|
static int itemn = 0;
|
2023-03-03 21:18:25 +01:00
|
|
|
|
2023-08-07 06:37:38 +02:00
|
|
|
#if RTL
|
2023-03-02 17:51:25 +01:00
|
|
|
static int isrtl = 1;
|
|
|
|
#else
|
|
|
|
static int isrtl = 0;
|
|
|
|
#endif
|
|
|
|
|
2023-05-16 18:54:26 +02:00
|
|
|
static int is_selected(size_t index);
|
2023-02-28 23:03:35 +01:00
|
|
|
static void calcoffsets(void);
|
2023-03-02 11:40:52 +01:00
|
|
|
static void recalculatenumbers(void);
|
2023-03-06 21:03:06 +01:00
|
|
|
static void insert(const char *str, ssize_t n);
|
|
|
|
static void cleanup(void);
|
|
|
|
static void navigatehistfile(int dir);
|
2023-06-02 18:37:51 +02:00
|
|
|
static void resizeclient(void);
|
|
|
|
static void get_width(void);
|
2023-06-11 16:46:36 +02:00
|
|
|
static void get_mh(void);
|
2023-06-05 17:16:26 +02:00
|
|
|
static void set_mode(void);
|
2023-06-02 18:37:51 +02:00
|
|
|
static void handle(void);
|
2023-03-13 21:21:40 +01:00
|
|
|
static void appenditem(struct item *item, struct item **list, struct item **last);
|
2023-03-08 17:20:32 +01:00
|
|
|
static int max_textw(void);
|
2023-03-16 14:56:41 +01:00
|
|
|
static size_t nextrune(int inc);
|
2023-07-16 02:57:38 +02:00
|
|
|
|
2023-05-23 22:14:24 +02:00
|
|
|
static char * cistrstr(const char *s, const char *sub);
|
|
|
|
static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
|
|
|
|
static char *(*fstrstr)(const char *, const char *) = cistrstr;
|
2023-02-25 17:44:52 +01:00
|
|
|
|
2023-05-07 01:41:30 +02:00
|
|
|
static char **list;
|
|
|
|
static size_t listsize;
|
|
|
|
|
2023-08-07 06:23:56 +02:00
|
|
|
// various headers
|
2023-06-09 10:23:10 +02:00
|
|
|
#include "libs/options.h"
|
2023-08-07 06:23:56 +02:00
|
|
|
#include "libs/draw.h"
|
|
|
|
#include "libs/arg.h"
|
2023-08-07 06:46:16 +02:00
|
|
|
|
2023-08-07 06:23:56 +02:00
|
|
|
#include "libs/x11/inc.h"
|
|
|
|
#include "libs/wl/inc.h"
|
2023-03-06 16:23:03 +01:00
|
|
|
|
2023-04-04 21:39:38 +02:00
|
|
|
static char *fonts[] = { font };
|
|
|
|
|
2023-08-07 06:19:15 +02:00
|
|
|
#include "libs/img.c"
|
|
|
|
#include "libs/icon.c"
|
|
|
|
#include "libs/rtl.c"
|
|
|
|
#include "libs/sort.c"
|
|
|
|
#include "libs/match.c"
|
|
|
|
#include "libs/schemes.c"
|
2023-08-07 06:46:16 +02:00
|
|
|
#include "libs/stream.c"
|
2023-08-07 06:19:15 +02:00
|
|
|
#include "libs/draw.c"
|
|
|
|
#include "libs/conf/config.c"
|
|
|
|
#include "libs/argv.c"
|
|
|
|
#include "libs/history.c"
|
|
|
|
#include "libs/arg.c"
|
2023-08-08 20:53:56 +02:00
|
|
|
#include "libs/fifo.c"
|
2023-08-07 06:19:15 +02:00
|
|
|
|
2023-08-07 06:37:38 +02:00
|
|
|
#if X11
|
2023-07-29 21:12:35 +02:00
|
|
|
static Key keys[] = {
|
|
|
|
{ -1, 0, XK_Return, selectitem, {.i = +1 } },
|
|
|
|
{ -1, Shift, XK_Return, selectitem, {0} },
|
|
|
|
{ -1, Ctrl, XK_Return, markitem, {0} },
|
|
|
|
{ -1, 0, XK_Tab, complete, {0} },
|
|
|
|
{ -1, Ctrl, XK_v, paste, {.i = 2 } },
|
|
|
|
{ -1, 0, XK_BackSpace, backspace, {0} },
|
|
|
|
{ -1, Ctrl, XK_BackSpace, deleteword, {0} },
|
|
|
|
{ -1, Ctrl|Shift, XK_p, setprofile, {0} },
|
|
|
|
{ -1, 0, XK_Print, screenshot, {0} },
|
|
|
|
{ -1, Ctrl, XK_equal, setimgsize, {.i = +10 } },
|
|
|
|
{ -1, Ctrl, XK_minus, setimgsize, {.i = -10 } },
|
|
|
|
{ -1, 0, XK_Up, moveup, {0} },
|
|
|
|
{ -1, 0, XK_Down, movedown, {0} },
|
|
|
|
{ -1, 0, XK_Left, moveleft, {0} },
|
|
|
|
{ -1, 0, XK_Right, moveright, {0} },
|
|
|
|
{ -1, Ctrl, XK_u, moveup, {.i = 5 } },
|
|
|
|
{ -1, Ctrl, XK_d, movedown, {.i = 5 } },
|
|
|
|
{ -1, Shift, XK_h, viewhist, {0} },
|
|
|
|
{ -1, 0, XK_Escape, quit, {0} },
|
|
|
|
{ -1, Ctrl, XK_p, navhistory, {.i = -1 } },
|
|
|
|
{ -1, Ctrl, XK_n, navhistory, {.i = +1 } },
|
|
|
|
};
|
|
|
|
#endif
|
2023-08-07 06:37:38 +02:00
|
|
|
#if WAYLAND
|
2023-07-29 21:12:35 +02:00
|
|
|
static WlKey wl_keys[] = {
|
|
|
|
{ -1, WL_None, XKB_KEY_Return, selectitem, {.i = +1 } },
|
|
|
|
{ -1, WL_Shift, XKB_KEY_Return, selectitem, {0} },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_Return, markitem, {0} },
|
|
|
|
{ -1, WL_None, XKB_KEY_Tab, complete, {0} },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_v, paste, {.i = 2 } },
|
|
|
|
{ -1, WL_None, XKB_KEY_BackSpace, backspace, {0} },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_BackSpace, deleteword, {0} },
|
|
|
|
{ -1, WL_CtrlShift, XKB_KEY_p, setprofile, {0} },
|
|
|
|
{ -1, WL_None, XKB_KEY_Print, screenshot, {0} },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_equal, setimgsize, {.i = +10 } },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_minus, setimgsize, {.i = -10 } },
|
|
|
|
{ -1, WL_None, XKB_KEY_Up, moveup, {0} },
|
|
|
|
{ -1, WL_None, XKB_KEY_Down, movedown, {0} },
|
|
|
|
{ -1, WL_None, XKB_KEY_Left, moveleft, {0} },
|
|
|
|
{ -1, WL_None, XKB_KEY_Right, moveright, {0} },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_u, moveup, {.i = 5 } },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_d, movedown, {.i = 5 } },
|
|
|
|
{ -1, WL_Shift, XKB_KEY_h, viewhist, {0} },
|
|
|
|
{ -1, WL_None, XKB_KEY_Escape, quit, {0} },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_p, navhistory, {.i = -1 } },
|
|
|
|
{ -1, WL_Ctrl, XKB_KEY_n, navhistory, {.i = +1 } },
|
|
|
|
|
|
|
|
};
|
|
|
|
#endif
|
2023-08-07 06:37:38 +02:00
|
|
|
#if X11
|
2023-07-29 21:12:35 +02:00
|
|
|
static Mouse buttons[] = {
|
|
|
|
{ ClickInput, Button1, clear, {0} },
|
|
|
|
{ ClickPrompt, Button1, clear, {0} },
|
|
|
|
{ ClickMode, Button1, switchmode, {0} },
|
|
|
|
{ ClickNumber, Button1, viewhist, {0} },
|
|
|
|
{ ClickItem, Button1, selecthover, {0} },
|
|
|
|
{ ClickItem, Button2, markhover, {0} },
|
|
|
|
{ ClickNone, Button5, movenext, {0} },
|
|
|
|
{ ClickNone, Button4, moveprev, {0} },
|
|
|
|
};
|
|
|
|
#endif
|
2023-08-07 06:37:38 +02:00
|
|
|
#if WAYLAND
|
2023-07-29 21:12:35 +02:00
|
|
|
static WlMouse wl_buttons[] = {
|
|
|
|
{ ClickInput, WL_Left, clear, {0} },
|
|
|
|
{ ClickPrompt, WL_Left, clear, {0} },
|
|
|
|
{ ClickMode, WL_Left, switchmode, {0} },
|
|
|
|
{ ClickNumber, WL_Left, viewhist, {0} },
|
|
|
|
{ ClickItem, WL_Left, selecthover, {0} },
|
|
|
|
{ ClickItem, WL_Right, markhover, {0} },
|
|
|
|
{ ClickNone, WL_Down, movenext, {0} },
|
|
|
|
{ ClickNone, WL_Up, moveprev, {0} },
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2023-08-07 05:41:42 +02:00
|
|
|
#include "libs/x11/inc.c"
|
|
|
|
#include "libs/wl/inc.c"
|
|
|
|
|
2023-05-16 18:54:26 +02:00
|
|
|
int is_selected(size_t index) {
|
|
|
|
for (int i = 0; i < sel_size; i++) {
|
|
|
|
if (sel_index[i] == index) {
|
2023-08-08 20:13:29 +02:00
|
|
|
return 1;
|
2023-05-16 18:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
void appenditem(struct item *item, struct item **list, struct item **last) {
|
2023-05-08 23:00:45 +02:00
|
|
|
if (*last)
|
|
|
|
(*last)->right = item;
|
|
|
|
else
|
|
|
|
*list = item;
|
|
|
|
|
|
|
|
item->left = *last;
|
|
|
|
item->right = NULL;
|
|
|
|
*last = item;
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
void recalculatenumbers(void) {
|
2023-05-16 21:16:51 +02:00
|
|
|
unsigned int numer = 0, denom = 0, selected = 0;
|
2023-05-08 23:00:45 +02:00
|
|
|
struct item *item;
|
2023-07-16 02:57:38 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
if (matchend) {
|
|
|
|
numer++;
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
for (item = matchend; item && item->left; item = item->left)
|
|
|
|
numer++;
|
|
|
|
}
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-07-16 02:57:38 +02:00
|
|
|
for (item = items; item && item->text; item++) {
|
2023-05-08 23:00:45 +02:00
|
|
|
denom++;
|
2023-07-16 02:57:38 +02:00
|
|
|
}
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-05-16 21:16:51 +02:00
|
|
|
for (int i = 0; i < sel_size; i++) {
|
|
|
|
if (sel_index[i] == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
selected++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selected) {
|
2023-06-23 03:38:21 +02:00
|
|
|
snprintf(tx.numbers, NUMBERSBUFSIZE, "%d/%d/%d", numer, denom, selected);
|
2023-05-16 21:16:51 +02:00
|
|
|
} else {
|
2023-06-23 03:38:21 +02:00
|
|
|
snprintf(tx.numbers, NUMBERSBUFSIZE, "%d/%d", numer, denom);
|
2023-05-16 21:16:51 +02:00
|
|
|
}
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
void calcoffsets(void) {
|
2023-07-16 02:57:38 +02:00
|
|
|
int i, offset;
|
2023-07-28 23:15:05 +02:00
|
|
|
int numberw = 0;
|
|
|
|
int modew = 0;
|
|
|
|
int larroww = 0;
|
|
|
|
int rarroww = 0;
|
|
|
|
int capsw = 0;
|
|
|
|
|
|
|
|
if (!hidematchcount) numberw = pango_numbers ? TEXTWM(tx.numbers) : TEXTW(tx.numbers);
|
|
|
|
if (!hidemode) modew = pango_mode ? TEXTWM(tx.modetext) : TEXTW(tx.modetext);
|
|
|
|
if (!hidelarrow) larroww = pango_leftarrow ? TEXTWM(leftarrow) : TEXTW(leftarrow);
|
|
|
|
if (!hiderarrow) rarroww = pango_rightarrow ? TEXTWM(rightarrow) : TEXTW(rightarrow);
|
|
|
|
if (!hidecaps) capsw = pango_caps ? TEXTWM(tx.capstext) : TEXTW(tx.capstext);
|
|
|
|
|
|
|
|
if (!strcmp(tx.capstext, "")) {
|
|
|
|
capsw = 0;
|
|
|
|
}
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-07-16 02:57:38 +02:00
|
|
|
if (lines > 0) {
|
|
|
|
offset = lines * columns * sp.bh;
|
2023-07-29 00:19:31 +02:00
|
|
|
sp.maxlen = sp.mw - (sp.promptw + modew + numberw + capsw + menumarginh);
|
2023-08-08 20:13:29 +02:00
|
|
|
} else { /* no lines, therefore the size of items must be decreased to fit the menu elements */
|
2023-07-16 02:57:38 +02:00
|
|
|
offset = sp.mw - (sp.promptw + sp.inputw + larroww + rarroww + modew + numberw + capsw + menumarginh);
|
2023-07-29 00:19:31 +02:00
|
|
|
sp.maxlen = selecteditem ? sp.inputw : sp.mw - (sp.promptw + modew + numberw + capsw + (selecteditem ? larroww : 0) + (selecteditem ? rarroww : 0));
|
2023-03-02 17:51:25 +01:00
|
|
|
}
|
2023-03-16 16:54:36 +01:00
|
|
|
|
2023-08-08 20:13:29 +02:00
|
|
|
for (i = 0, nextitem = currentitem; nextitem; nextitem = nextitem->right) { // next page
|
2023-07-16 16:35:25 +02:00
|
|
|
nextitem->nsgrtext = get_text_n_sgr(nextitem);
|
|
|
|
|
|
|
|
if ((i += (lines > 0) ? sp.bh : MIN(TEXTWM(nextitem->nsgrtext) + (powerlineitems ? !lines ? 3 * sp.plw : 0 : 0), offset)) > offset)
|
2023-05-08 23:00:45 +02:00
|
|
|
break;
|
2023-07-16 03:16:45 +02:00
|
|
|
}
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-08-08 20:13:29 +02:00
|
|
|
for (i = 0, previousitem = currentitem; previousitem && previousitem->left; previousitem = previousitem->left) { // previous page
|
2023-07-16 16:35:25 +02:00
|
|
|
previousitem->nsgrtext = get_text_n_sgr(previousitem);
|
|
|
|
|
|
|
|
if ((i += (lines > 0) ? sp.bh : MIN(TEXTWM(previousitem->left->nsgrtext) + (powerlineitems ? !lines ? 3 * sp.plw : 0 : 0), offset)) > offset)
|
2023-05-08 23:00:45 +02:00
|
|
|
break;
|
2023-07-16 03:16:45 +02:00
|
|
|
}
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
int max_textw(void) {
|
2023-05-08 23:00:45 +02:00
|
|
|
int len = 0;
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
for (struct item *item = items; item && item->text; item++)
|
|
|
|
len = MAX(TEXTW(item->text), len);
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
return len;
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
void cleanup(void) {
|
2023-05-08 23:00:45 +02:00
|
|
|
size_t i;
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-08-07 06:37:38 +02:00
|
|
|
#if IMAGE
|
2023-08-08 20:13:29 +02:00
|
|
|
cleanupimage();
|
2023-05-08 23:00:45 +02:00
|
|
|
#endif
|
2023-03-02 11:46:44 +01:00
|
|
|
|
2023-03-09 11:56:44 +01:00
|
|
|
for (i = 0; i < hplength; ++i)
|
2023-05-08 23:00:45 +02:00
|
|
|
free(hpitems[i]);
|
2023-03-09 11:56:44 +01:00
|
|
|
|
2023-06-23 03:49:23 +02:00
|
|
|
draw_free(draw);
|
2023-06-02 18:37:51 +02:00
|
|
|
|
2023-08-07 06:37:38 +02:00
|
|
|
#if X11
|
2023-06-02 18:37:51 +02:00
|
|
|
if (!protocol) {
|
|
|
|
cleanup_x11(dpy);
|
|
|
|
}
|
2023-06-05 17:16:26 +02:00
|
|
|
#endif
|
2023-06-02 18:37:51 +02:00
|
|
|
|
2023-08-12 05:21:31 +02:00
|
|
|
#if FIFO
|
|
|
|
remove(fifofile);
|
|
|
|
#endif
|
|
|
|
|
2023-05-16 18:54:26 +02:00
|
|
|
free(sel_index);
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
char * cistrstr(const char *h, const char *n) {
|
2023-05-08 23:00:45 +02:00
|
|
|
size_t i;
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
if (!n[0])
|
|
|
|
return (char *)h;
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
for (; *h; ++h) {
|
|
|
|
for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
|
|
|
|
tolower((unsigned char)h[i]); ++i);
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
if (n[i] == '\0')
|
|
|
|
return (char *)h;
|
|
|
|
}
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
return NULL;
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
void insert(const char *str, ssize_t n) {
|
2023-06-23 03:38:21 +02:00
|
|
|
if (strlen(tx.text) + n > sizeof tx.text - 1)
|
2023-08-08 20:13:29 +02:00
|
|
|
return;
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-05-09 19:30:01 +02:00
|
|
|
static char l[BUFSIZ] = "";
|
2023-07-16 02:57:38 +02:00
|
|
|
|
|
|
|
if (requirematch) {
|
|
|
|
memcpy(l, tx.text, BUFSIZ);
|
|
|
|
}
|
2023-05-09 19:30:01 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
// move existing text out of the way, insert new text, and update cursor
|
2023-07-16 02:57:38 +02:00
|
|
|
memmove(
|
|
|
|
&tx.text[sp.cursor + n],
|
|
|
|
&tx.text[sp.cursor],
|
|
|
|
sizeof tx.text - sp.cursor - MAX(n, 0)
|
|
|
|
);
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-07-16 02:57:38 +02:00
|
|
|
if (n > 0 && str && n) {
|
2023-06-23 03:38:21 +02:00
|
|
|
memcpy(&tx.text[sp.cursor], str, n);
|
2023-07-16 02:57:38 +02:00
|
|
|
}
|
2023-03-31 12:42:15 +02:00
|
|
|
|
2023-06-23 03:38:21 +02:00
|
|
|
sp.cursor += n;
|
2023-05-08 23:00:45 +02:00
|
|
|
match();
|
2023-05-09 19:30:01 +02:00
|
|
|
|
|
|
|
if (!matches && requirematch) {
|
2023-06-23 03:38:21 +02:00
|
|
|
memcpy(tx.text, l, BUFSIZ);
|
|
|
|
sp.cursor -= n;
|
2023-05-09 19:30:01 +02:00
|
|
|
match();
|
|
|
|
}
|
2023-07-09 15:26:52 +02:00
|
|
|
|
2023-07-16 02:57:38 +02:00
|
|
|
// output on insertion
|
2023-07-09 15:26:52 +02:00
|
|
|
if (incremental) {
|
|
|
|
puts(tx.text);
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
size_t nextrune(int inc) {
|
2023-07-16 02:57:38 +02:00
|
|
|
ssize_t rune;
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
// return location of next utf8 rune in the given direction (+1 or -1)
|
2023-07-16 02:57:38 +02:00
|
|
|
for (rune = sp.cursor + inc; rune + inc >= 0 && (tx.text[rune] & 0xc0) == 0x80; rune += inc)
|
2023-03-31 12:42:15 +02:00
|
|
|
;
|
2023-07-16 02:57:38 +02:00
|
|
|
|
|
|
|
return rune;
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
void resizeclient(void) {
|
2023-08-07 06:37:38 +02:00
|
|
|
#if WAYLAND
|
2023-06-02 18:37:51 +02:00
|
|
|
if (protocol) {
|
|
|
|
resizeclient_wl(&state);
|
|
|
|
} else {
|
2023-08-07 06:37:38 +02:00
|
|
|
#if X11
|
2023-06-02 18:37:51 +02:00
|
|
|
resizeclient_x11();
|
2023-06-05 17:16:26 +02:00
|
|
|
#endif
|
2023-06-02 18:37:51 +02:00
|
|
|
}
|
2023-08-07 06:37:38 +02:00
|
|
|
#elif X11
|
2023-06-02 18:37:51 +02:00
|
|
|
resizeclient_x11();
|
|
|
|
#endif
|
|
|
|
}
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-08-08 20:13:29 +02:00
|
|
|
/* Width reserved for input when !lines is a fixed size of the menu width * inputwidth
|
2023-07-16 02:57:38 +02:00
|
|
|
* This is reasonable, but in rare cases may cause input text to overlap
|
|
|
|
* items.
|
|
|
|
*/
|
2023-06-02 18:37:51 +02:00
|
|
|
void get_width(void) {
|
2023-08-08 17:27:04 +02:00
|
|
|
sp.inputw = sp.mw * inputwidth;
|
2023-06-02 18:37:51 +02:00
|
|
|
}
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-06-11 16:46:36 +02:00
|
|
|
void get_mh(void) {
|
2023-07-14 01:16:35 +02:00
|
|
|
int epad;
|
|
|
|
|
2023-06-23 03:38:21 +02:00
|
|
|
sp.mh = (lines + 1) * sp.bh;
|
|
|
|
sp.mh += 2 * menumarginv;
|
2023-06-11 19:32:02 +02:00
|
|
|
|
2023-07-16 02:57:38 +02:00
|
|
|
// subtract 1 line if there's nothing to draw on the top line
|
2023-06-11 19:32:02 +02:00
|
|
|
if ((hideprompt && hideinput && hidemode && hidematchcount && hidecaps) && lines) {
|
2023-06-23 03:38:21 +02:00
|
|
|
sp.mh -= sp.bh;
|
2023-06-11 19:32:02 +02:00
|
|
|
}
|
2023-07-14 01:16:35 +02:00
|
|
|
|
|
|
|
epad = 2 * menupaddingv;
|
|
|
|
|
2023-07-16 02:57:38 +02:00
|
|
|
// the spmenu window should not exceed the screen resolution height
|
2023-07-14 01:16:35 +02:00
|
|
|
if (mo.output_height && !xpos && !ypos) {
|
|
|
|
sp.mh = MIN(sp.mh, mo.output_height - epad);
|
|
|
|
|
|
|
|
if (sp.mh == mo.output_height - epad) {
|
|
|
|
lines = ((mo.output_height - epad) / sp.bh) - 1;
|
|
|
|
}
|
|
|
|
}
|
2023-06-11 16:46:36 +02:00
|
|
|
}
|
|
|
|
|
2023-06-05 17:16:26 +02:00
|
|
|
void set_mode(void) {
|
2023-08-08 20:05:15 +02:00
|
|
|
if (!type) {
|
2023-07-03 22:19:19 +02:00
|
|
|
sp.mode = 0;
|
2023-06-05 17:16:26 +02:00
|
|
|
}
|
|
|
|
|
2023-08-08 20:05:15 +02:00
|
|
|
// set default mode
|
2023-06-05 17:16:26 +02:00
|
|
|
if (mode) {
|
2023-06-23 03:38:21 +02:00
|
|
|
sp.mode = 1;
|
|
|
|
sp.allowkeys = 1;
|
2023-06-05 17:16:26 +02:00
|
|
|
|
2023-06-23 03:38:21 +02:00
|
|
|
sp_strncpy(tx.modetext, instext, sizeof(tx.modetext));
|
2023-06-05 17:16:26 +02:00
|
|
|
} else {
|
2023-06-23 03:38:21 +02:00
|
|
|
sp.mode = 0;
|
|
|
|
sp.allowkeys = !sp.mode;
|
2023-06-05 17:16:26 +02:00
|
|
|
|
2023-06-23 03:38:21 +02:00
|
|
|
sp_strncpy(tx.modetext, normtext, sizeof(tx.modetext));
|
2023-06-05 17:16:26 +02:00
|
|
|
}
|
2023-07-03 22:07:09 +02:00
|
|
|
|
2023-07-29 04:24:02 +02:00
|
|
|
if (forceinsertmode) {
|
2023-07-03 22:07:09 +02:00
|
|
|
sp.mode = 1;
|
2023-07-03 22:19:19 +02:00
|
|
|
sp.allowkeys = 1;
|
2023-07-03 22:07:09 +02:00
|
|
|
}
|
2023-06-05 17:16:26 +02:00
|
|
|
}
|
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
void handle(void) {
|
|
|
|
if (!protocol) {
|
2023-08-07 06:37:38 +02:00
|
|
|
#if X11
|
2023-06-02 18:37:51 +02:00
|
|
|
handle_x11();
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-06-23 03:49:23 +02:00
|
|
|
if (!draw_font_create(draw, fonts, LENGTH(fonts))) {
|
2023-06-02 18:37:51 +02:00
|
|
|
die("no fonts could be loaded.");
|
|
|
|
}
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-08-08 20:13:29 +02:00
|
|
|
loadhistory();
|
2023-08-07 06:37:38 +02:00
|
|
|
#if IMAGE
|
2023-06-02 18:37:51 +02:00
|
|
|
store_image_vars();
|
2023-06-05 17:16:26 +02:00
|
|
|
#endif
|
2023-08-08 20:13:29 +02:00
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
if (fast && !isatty(0)) {
|
|
|
|
grabkeyboard_x11();
|
|
|
|
readstdin();
|
|
|
|
} else {
|
|
|
|
readstdin();
|
|
|
|
grabkeyboard_x11();
|
|
|
|
}
|
|
|
|
|
|
|
|
set_mode();
|
|
|
|
|
|
|
|
init_appearance(); // init colorschemes by reading arrays
|
2023-07-27 00:39:57 +02:00
|
|
|
|
2023-08-12 05:17:03 +02:00
|
|
|
setupdisplay_x11(); // set up display and create window
|
2023-08-08 20:53:56 +02:00
|
|
|
#if FIFO
|
|
|
|
init_fifo();
|
|
|
|
#endif
|
2023-06-02 18:37:51 +02:00
|
|
|
eventloop_x11(); // function is a loop which checks X11 events and calls other functions accordingly
|
2023-06-05 17:16:26 +02:00
|
|
|
#endif
|
2023-08-07 06:37:38 +02:00
|
|
|
#if WAYLAND
|
2023-05-08 23:00:45 +02:00
|
|
|
} else {
|
2023-06-02 18:37:51 +02:00
|
|
|
loadhistory();
|
2023-08-07 06:37:38 +02:00
|
|
|
#if IMAGE
|
2023-06-02 18:37:51 +02:00
|
|
|
store_image_vars();
|
2023-06-17 15:38:22 +02:00
|
|
|
setimageopts();
|
2023-06-05 17:16:26 +02:00
|
|
|
#endif
|
2023-06-02 18:37:51 +02:00
|
|
|
|
2023-06-03 15:37:48 +02:00
|
|
|
// Disable some X11 only features
|
|
|
|
menupaddingv = menupaddingh = 0;
|
|
|
|
xpos = ypos = 0;
|
|
|
|
borderwidth = 0;
|
|
|
|
managed = 0;
|
|
|
|
|
2023-06-23 03:49:23 +02:00
|
|
|
draw = draw_create_wl(protocol);
|
2023-06-02 18:37:51 +02:00
|
|
|
|
2023-06-23 03:49:23 +02:00
|
|
|
if (!draw_font_create(draw, fonts, LENGTH(fonts))) {
|
2023-06-02 18:37:51 +02:00
|
|
|
die("no fonts could be loaded.");
|
|
|
|
}
|
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
readstdin();
|
2023-06-02 18:37:51 +02:00
|
|
|
set_mode();
|
|
|
|
init_appearance();
|
|
|
|
|
2023-08-08 20:53:56 +02:00
|
|
|
#if FIFO
|
|
|
|
init_fifo();
|
|
|
|
#endif
|
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
handle_wl();
|
|
|
|
#endif
|
2023-05-08 23:00:45 +02:00
|
|
|
}
|
2023-06-02 18:37:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
2023-08-07 06:27:10 +02:00
|
|
|
readargs(argc, argv);
|
2023-03-04 15:21:43 +01:00
|
|
|
|
2023-07-16 02:57:38 +02:00
|
|
|
/* pledge limits what programs can do, so here we specify what spmenu should be allowed to do
|
2023-08-07 06:27:10 +02:00
|
|
|
* TODO: Test this on an actual OpenBSD operating system
|
2023-07-16 02:57:38 +02:00
|
|
|
*/
|
2023-06-02 18:37:51 +02:00
|
|
|
#ifdef __OpenBSD__
|
|
|
|
if (pledge("stdio rpath wpath cpath", NULL) == -1)
|
|
|
|
die("pledge");
|
|
|
|
#endif
|
2023-03-04 15:21:43 +01:00
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
handle();
|
2023-01-20 23:17:30 +01:00
|
|
|
|
2023-08-07 06:27:10 +02:00
|
|
|
return 1;
|
2023-01-20 23:17:30 +01:00
|
|
|
}
|