spmenu/libs/arg.c
Jacob 3f456f3a37 Feature: Add the ability to take "screenshots" of spmenu.
It doesn't actually capture your screen, but rather saves the Cairo
surface to an image. The path to the image and some other options
can also be configured in the config file.

By default, Print Screen can be pressed in Normal mode with no modifier
to take a screenshot. The default location is the user's home directory,
and the file has a date attached to it. Of course, this can be changed
as well.
2023-06-30 02:44:29 +02:00

558 lines
12 KiB
C

/* See LICENSE file for copyright and license details. */
void moveleft(Arg *arg) {
struct item *tmpsel;
int i, offscreen = 0;
int argu = arg->i ? arg->i : 1;
if (columns > 1) {
if (!sel)
return;
tmpsel = sel;
for (i = 0; i < lines; i++) {
if (!tmpsel->left || tmpsel->left->right != tmpsel) {
if (offscreen)
drawmenu();
return;
}
if (tmpsel == curr)
offscreen = 1;
tmpsel = tmpsel->left;
}
sel = tmpsel;
if (offscreen) {
for (int j = 0; j < argu; j++) {
curr = prev;
}
}
drawmenu();
calcoffsets();
}
}
void moveright(Arg *arg) {
struct item *tmpsel;
int i, offscreen = 0;
int argu = arg->i ? arg->i : 1;
if (columns > 1) {
if (!sel)
return;
tmpsel = sel;
for (i = 0; i < lines; i++) {
if (!tmpsel->right || tmpsel->right->left != tmpsel) {
if (offscreen)
drawmenu();
return;
}
tmpsel = tmpsel->right;
if (tmpsel == next)
offscreen = 1;
}
sel = tmpsel;
if (offscreen) {
for (int j = 0; j < argu; j++)
curr = next;
}
calcoffsets();
}
drawmenu();
}
void movedown(Arg *arg) {
int argu = arg->i ? arg->i : 1;
for (int j = 0; j < argu; j++) {
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
}
}
calcoffsets();
drawmenu();
}
void moveup(Arg *arg) {
int argu = arg->i ? arg->i : 1;
for (int j = 0; j < argu; j++) {
if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
}
}
calcoffsets();
drawmenu();
}
void complete(Arg *arg) {
if (hideitem) return;
strncpy(tx.text, sel->clntext, sizeof tx.text - 1);
tx.text[sizeof tx.text - 1] = '\0';
sp.cursor = strlen(tx.text);
match();
drawmenu();
}
void movenext(Arg *arg) {
if (!next)
return;
sel = curr = next;
drawmenu();
}
void moveprev(Arg *arg) {
if (!prev)
return;
sel = curr = prev;
calcoffsets();
drawmenu();
}
void moveitem(Arg *arg) {
for (int i = 0; i < arg->i; i++) {
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
calcoffsets();
}
}
drawmenu();
}
void movestart(Arg *arg) {
if (sel == matches) {
sp.cursor = 0;
drawmenu();
return;
}
sel = curr = matches;
calcoffsets();
drawmenu();
}
void moveend(Arg *arg) {
if (tx.text[sp.cursor] != '\0') {
sp.cursor = strlen(tx.text);
drawmenu();
return;
}
if (next) {
curr = matchend;
calcoffsets();
curr = prev;
calcoffsets();
while (next && (curr = curr->right))
calcoffsets();
}
sel = matchend;
drawmenu();
}
void paste(Arg *arg) {
#if USEX
if (!protocol) {
paste_x11(arg->i);
}
#endif
}
void viewhist(Arg *arg) {
int i;
if (histfile) {
if (!backup_items) {
backup_items = items;
items = calloc(histsz + 1, sizeof(struct item));
if (!items) {
die("spmenu: cannot allocate memory");
}
for (i = 0; i < histsz; i++) {
items[i].text = history[i];
}
} else {
free(items);
items = backup_items;
backup_items = NULL;
}
}
match();
drawmenu();
}
void deleteword(Arg *arg) {
if (sp.cursor == 0) return;
while (sp.cursor > 0 && strchr(worddelimiters, tx.text[nextrune(-1)])) {
insert(NULL, nextrune(-1) - sp.cursor);
} while (sp.cursor > 0 && !strchr(worddelimiters, tx.text[nextrune(-1)])) {
insert(NULL, nextrune(-1) - sp.cursor);
}
drawmenu();
}
void moveword(Arg *arg) {
if (arg->i < 0) { // move sp.cursor to the start of the word
while (sp.cursor > 0 && strchr(worddelimiters, tx.text[nextrune(-1)])) {
sp.cursor = nextrune(-1);
} while (sp.cursor > 0 && !strchr(worddelimiters, tx.text[nextrune(-1)])) {
sp.cursor = nextrune(-1);
}
} else { // move sp.cursor to the end of the word
while (tx.text[sp.cursor] && strchr(worddelimiters, tx.text[sp.cursor])) {
sp.cursor = nextrune(+1);
} while (tx.text[sp.cursor] && !strchr(worddelimiters, tx.text[sp.cursor])) {
sp.cursor = nextrune(+1);
}
}
drawmenu();
}
void movecursor(Arg *arg) {
if (arg->i < 0) {
if (sp.cursor > 0) {
sp.cursor = nextrune(-1);
}
} else {
if (tx.text[sp.cursor]) {
sp.cursor = nextrune(+1);
}
}
drawmenu();
}
void backspace(Arg *arg) {
if (sp.cursor == 0)
return;
insert(NULL, nextrune(-1) - sp.cursor);
drawmenu();
}
void markitem(Arg *arg) {
if (!mark) return;
if (sel && is_selected(sel->index)) {
for (int i = 0; i < sel_size; i++) {
if (sel_index[i] == sel->index) {
sel_index[i] = -1;
}
}
} else {
for (int i = 0; i < sel_size; i++) {
if (sel_index[i] == -1) {
sel_index[i] = sel->index;
return;
}
}
sel_size++;
sel_index = realloc(sel_index, (sel_size + 1) * sizeof(int));
sel_index[sel_size - 1] = sel->index;
}
}
void selectitem(Arg *arg) {
char *selection;
// print index
if (printindex && sel && arg->i) {
fprintf(stdout, "%d\n", sel->index);
cleanup();
exit(0);
}
// selected item or input?
if (sel && arg->i && !hideitem) {
selection = sel->text;
} else {
selection = tx.text;
}
for (int i = 0; i < sel_size; i++) {
if (sel_index[i] != -1 && (!sel || sel->index != sel_index[i])) {
puts(items[sel_index[i]].text);
}
}
if (!selection)
return;
puts(selection);
savehistory(selection);
cleanup();
exit(0);
}
void navhistory(Arg *arg) {
navigatehistfile(arg->i);
drawmenu();
}
void restoresel(Arg *arg) {
tx.text[sp.cursor] = '\0';
match();
drawmenu();
}
void clear(Arg *arg) {
insert(NULL, 0 - sp.cursor);
drawmenu();
}
void clearins(Arg *arg) {
insert(NULL, 0 - sp.cursor);
sp.mode = 1;
sp.allowkeys = 0;
strncpy(tx.modetext, instext, 15);
calcoffsets();
drawmenu();
}
void quit(Arg *arg) {
cleanup();
exit(0);
}
void savehistory(char *input) {
unsigned int i;
FILE *fp;
if (!histfile ||
0 == maxhist ||
0 == strlen(input)) {
goto out;
}
fp = fopen(histfile, "w");
if (!fp) {
die("spmenu: failed to open %s", histfile);
}
for (i = histsz < maxhist ? 0 : histsz - maxhist; i < histsz; i++) {
if (0 >= fprintf(fp, "%s\n", history[i])) {
die("spmenu: failed to write to %s", histfile);
}
}
if (histsz == 0 || histdup || (histsz > 0 && strcmp(input, history[histsz-1]) != 0)) {
if (0 >= fputs(input, fp)) {
die("spmenu: failed to write to %s", histfile);
}
}
if (fclose(fp)) {
die("spmenu: failed to close file %s", histfile);
}
out:
for (i = 0; i < histsz; i++) {
free(history[i]);
}
free(history);
}
void setimgsize(Arg *arg) {
#if USEIMAGE
setimagesize(img.imagewidth + arg->i, img.imageheight + arg->i);
drawmenu();
#endif
}
void flipimg(Arg *arg) {
#if USEIMAGE
if (!image) return;
img.flip = img.flip ? 0 : arg->i ? 1 : 2;
drawmenu();
#endif
}
void setimgpos(Arg *arg) {
#if USEIMAGE
if (!image || hideimage) return;
if (imageposition < 3) {
imageposition += arg->i;
} else {
imageposition = 0;
}
drawmenu();
#endif
}
void setimggaps(Arg *arg) {
#if USEIMAGE
img.imagegaps += arg->i;
if (!image || hideimage) return;
if (img.imagegaps < 0)
img.imagegaps = 0;
// limitation to make sure we have a reasonable gap size
if (img.imagegaps > img.imagewidth / 2)
img.imagegaps -= arg->i;
drawmenu();
#endif
}
void toggleimg(Arg *arg) {
#if USEIMAGE
hideimage = !hideimage;
drawmenu();
#endif
}
void defaultimg(Arg *arg) {
#if USEIMAGE
if (hideimage || !image) return;
img.imagewidth = imagewidth;
img.imageheight = imageheight;
img.imagegaps = imagegaps;
drawmenu();
#endif
}
void setlines(Arg *arg) {
if (!overridelines) return;
sel = curr = matches;
lines += arg->i;
if (lines < 0) {
lines = 0;
}
if (lines == 0) {
match();
}
resizeclient();
calcoffsets();
drawmenu();
}
void setcolumns(Arg *arg) {
if (!overridecolumns) return;
columns += arg->i;
if (columns < 1) {
columns = 1;
}
if (lines == 0) {
match();
}
resizeclient();
calcoffsets();
drawmenu();
}
void spawn(Arg *arg) {
if (!system(arg->c))
die("spmenu: failed to execute command '%s'", arg->c);
else
exit(0);
}
void togglehighlight(Arg *arg) {
hidehighlight = !hidehighlight;
drawmenu();
}
void setprofile(Arg *arg) {
if (!system("command -v spmenu_profile > /dev/null && spmenu_profile --spmenu-set-profile")) {
die("spmenu: failed to run profile menu\n");
} else {
exit(0);
}
}
void switchmode(Arg *arg) {
sp.mode = !sp.mode;
if (!type) sp.mode = 0; // only normal mode allowed
sp.allowkeys = !sp.mode;
strncpy(tx.modetext, sp.mode ? instext : normtext, 15);
drawmenu();
}
void screenshot(Arg *arg) {
char *file = NULL;
char *home = NULL;
time_t time_ = time(NULL);
struct tm t = *localtime(&time_);
if (!screenshotfile) {
if (!(home = getenv("HOME"))) {
fprintf(stderr, "spmenu: failed to determine home directory\n");
return;
}
if (!screenshotdir && !screenshotname) { // default
if (!(file = malloc(snprintf(NULL, 0, "%s/%s-%02d-%02d-%02d%s", home, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png") + 1))) {
die("spmenu: failed to malloc screenshot file");
}
sprintf(file, "%s/%s-%02d-%02d-%02d%s", home, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png");
} else if (!screenshotdir && screenshotname) { // no dir but name
if (!(file = malloc(snprintf(NULL, 0, "%s/%s", home, screenshotname) + 1))) {
die("spmenu: failed to malloc screenshot file");
}
sprintf(file, "%s/%s", home, screenshotname);
} else if (screenshotdir && !screenshotname) { // dir but no name
if (!(file = malloc(snprintf(NULL, 0, "%s/%s-%02d-%02d-%02d%s", screenshotdir, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png") + 1))) {
die("spmenu: failed to malloc screenshot file");
}
sprintf(file, "%s/%s-%02d-%02d-%02d%s", screenshotdir, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png");
} else { // dir and name
if (!(file = malloc(snprintf(NULL, 0, "%s/%s", screenshotdir, screenshotname) + 1))) {
die("spmenu: failed to malloc screenshot file");
}
sprintf(file, "%s/%s", screenshotdir, screenshotname);
}
} else { // custom file
if (!(file = malloc(snprintf(NULL, 0, "%s", screenshotfile) + 1))) {
die("spmenu: failed to malloc screenshot file");
}
sprintf(file, "%s", screenshotfile);
}
draw_save_screen(draw, file);
}