Skip to content

Commit 59cc5ff

Browse files
committed
add support for tags and string output
1 parent 99187d5 commit 59cc5ff

File tree

3 files changed

+141
-55
lines changed

3 files changed

+141
-55
lines changed

xnotify.1

+18
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ xnotify \- popup a notification on the screen
66
.RB [ \-o ]
77
.RB [ \-G
88
.IR gravity ]
9+
.RB [ \-b
10+
.IR button ]
911
.RB [ \-g
1012
.IR geometry ]
1113
.RB [ \-m
@@ -31,6 +33,14 @@ can be "NE" for NorthEastGravity (display on the top right corner of the screen)
3133
"C" for CenterGravity (display on the center of the screen);
3234
etc.
3335
.TP
36+
.BI "\-b " button
37+
Specifies a number between 1 and 5 to be the mouse action button.
38+
When clicking over a notification with the action button,
39+
the notification will close,
40+
and xnotify will output to its stdout the value of the notification's
41+
.B CMD:
42+
option.
43+
.TP
3444
.BI "\-g " geometry
3545
Specifies the geometry in the form
3646
.B [<WIDTH>x<HEIGHT>][{-+}<XOFFSET>{-+}<YOFFSET>].
@@ -95,6 +105,14 @@ The following names are currently accepted for the NAME:VALUE pairs:
95105
.B IMG:
96106
Specify the path of a image to be displayed on the notification.
97107
.TP
108+
.B TAG:
109+
Specify a string to be the notification's tag.
110+
When a notification with a given tag spawns,
111+
all other notifications with the same tag disappears.
112+
.TP
113+
.B CMD:
114+
Specify a string to be output when clicking on the notification with the action mouse button.
115+
.TP
98116
.B BG:
99117
Specify the color of the notification background.
100118
.TP

xnotify.c

+106-54
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static int oflag = 0; /* whether only one notification must exist at a time */
4848
void
4949
usage(void)
5050
{
51-
(void)fprintf(stderr, "usage: xnotify [-o] [-G gravity] [-g geometry] [-m monitor] [-s seconds]\n");
51+
(void)fprintf(stderr, "usage: xnotify [-o] [-G gravity] [-b button] [-g geometry] [-m monitor] [-s seconds]\n");
5252
exit(1);
5353
}
5454

@@ -111,11 +111,34 @@ getoptions(int argc, char *argv[])
111111
unsigned long n;
112112
int ch;
113113

114-
while ((ch = getopt(argc, argv, "G:g:m:os:")) != -1) {
114+
while ((ch = getopt(argc, argv, "G:b:g:m:os:")) != -1) {
115115
switch (ch) {
116116
case 'G':
117117
config.gravityspec = optarg;
118118
break;
119+
case 'b':
120+
if (*(optarg+1) != '\0')
121+
break;
122+
switch (*optarg) {
123+
case '1':
124+
config.actionbutton = Button1;
125+
break;
126+
case '2':
127+
config.actionbutton = Button2;
128+
break;
129+
case '3':
130+
config.actionbutton = Button3;
131+
break;
132+
case '4':
133+
config.actionbutton = Button4;
134+
break;
135+
case '5':
136+
config.actionbutton = Button5;
137+
break;
138+
default:
139+
break;
140+
}
141+
break;
119142
case 'g':
120143
config.geometryspec = optarg;
121144
break;
@@ -731,7 +754,7 @@ resettime(struct Item *item)
731754

732755
/* add item notification item and set its window and contents */
733756
static void
734-
additem(struct Queue *queue, const char *title, const char *body, const char *file, const char *background, const char *foreground, const char *border)
757+
additem(struct Queue *queue, struct Itemspec *itemspec)
735758
{
736759
struct Item *item;
737760
int titlew, bodyw;
@@ -740,9 +763,11 @@ additem(struct Queue *queue, const char *title, const char *body, const char *fi
740763
if ((item = malloc(sizeof *item)) == NULL)
741764
err(1, "malloc");
742765
item->next = NULL;
743-
item->title = strdup(title);
744-
item->body = (body) ? strdup(body) : NULL;
745-
item->image = (file) ? loadimage(file) : NULL;
766+
item->title = strdup(itemspec->title);
767+
item->body = (itemspec->body) ? strdup(itemspec->body) : NULL;
768+
item->image = (itemspec->file) ? loadimage(itemspec->file) : NULL;
769+
item->tag = (itemspec->tag) ? strdup(itemspec->tag) : NULL;
770+
item->cmd = (itemspec->cmd) ? strdup(itemspec->cmd) : NULL;
746771
if (!queue->head)
747772
queue->head = item;
748773
else
@@ -751,11 +776,11 @@ additem(struct Queue *queue, const char *title, const char *body, const char *fi
751776
queue->tail = item;
752777

753778
/* allocate colors */
754-
if (!background || ealloccolor(background, &item->background, 0) == -1)
779+
if (!itemspec->background || ealloccolor(itemspec->background, &item->background, 0) == -1)
755780
item->background = dc.background;
756-
if (!foreground || ealloccolor(foreground, &item->foreground, 0) == -1)
781+
if (!itemspec->foreground || ealloccolor(itemspec->foreground, &item->foreground, 0) == -1)
757782
item->foreground = dc.foreground;
758-
if (!border || ealloccolor(border, &item->border, 0) == -1)
783+
if (!itemspec->border || ealloccolor(itemspec->border, &item->border, 0) == -1)
759784
item->border = dc.border;
760785

761786
/* compute notification height */
@@ -824,73 +849,72 @@ optiontype(const char *s)
824849
return FG;
825850
if (strncmp(s, "BRD:", 4) == 0)
826851
return BRD;
852+
if (strncmp(s, "TAG:", 4) == 0)
853+
return TAG;
854+
if (strncmp(s, "CMD:", 4) == 0)
855+
return CMD;
827856
return UNKNOWN;
828857
}
829858

830-
/* destroy all notification items */
831-
static void
832-
cleanitems(struct Queue *queue)
833-
{
834-
struct Item *item;
835-
struct Item *tmp;
836-
837-
item = queue->head;
838-
while (item) {
839-
tmp = item;
840-
item = item->next;
841-
delitem(queue, tmp);
842-
}
843-
}
844-
845-
/* read stdin */
846-
static void
847-
parseinput(struct Queue *queue, char *s)
859+
/* parse notification line */
860+
static struct Itemspec *
861+
parseline(char *s)
848862
{
849863
enum ItemOption option;
850-
char *title, *body, *file, *fg, *bg, *brd;
864+
struct Itemspec *itemspec;
865+
866+
if ((itemspec = malloc(sizeof *itemspec)) == NULL)
867+
err(1, "malloc");
851868

852869
/* get the title */
853-
title = strtok(s, "\t\n");
870+
itemspec->title = strtok(s, "\t\n");
854871

855872
/* get the filename */
856-
file = NULL;
857-
fg = bg = brd = NULL;
858-
while (title && (option = optiontype(title)) != UNKNOWN) {
873+
itemspec->file = NULL;
874+
itemspec->foreground = NULL;
875+
itemspec->background = NULL;
876+
itemspec->border = NULL;
877+
itemspec->tag = NULL;
878+
itemspec->cmd = NULL;
879+
while (itemspec->title && (option = optiontype(itemspec->title)) != UNKNOWN) {
859880
switch (option) {
860881
case IMG:
861-
file = title + 4;
862-
title = strtok(NULL, "\t\n");
882+
itemspec->file = itemspec->title + 4;
883+
itemspec->title = strtok(NULL, "\t\n");
863884
break;
864885
case BG:
865-
bg = title + 3;
866-
title = strtok(NULL, "\t\n");
886+
itemspec->background = itemspec->title + 3;
887+
itemspec->title = strtok(NULL, "\t\n");
867888
break;
868889
case FG:
869-
fg = title + 3;
870-
title = strtok(NULL, "\t\n");
890+
itemspec->foreground = itemspec->title + 3;
891+
itemspec->title = strtok(NULL, "\t\n");
871892
break;
872893
case BRD:
873-
brd = title + 4;
874-
title = strtok(NULL, "\t\n");
894+
itemspec->border = itemspec->title + 4;
895+
itemspec->title = strtok(NULL, "\t\n");
875896
break;
897+
case TAG:
898+
itemspec->tag = itemspec->title + 4;
899+
itemspec->title = strtok(NULL, "\t\n");
900+
case CMD:
901+
itemspec->cmd = itemspec->title + 4;
902+
itemspec->title = strtok(NULL, "\t\n");
876903
default:
877904
break;
878905
}
879906
}
880907

881908
/* get the body */
882-
body = strtok(NULL, "\n");
883-
if (body)
884-
while (*body == '\t')
885-
body++;
909+
itemspec->body = strtok(NULL, "\n");
910+
if (itemspec->body)
911+
while (*itemspec->body == '\t')
912+
itemspec->body++;
886913

887-
if (!title)
888-
return;
889-
890-
if (oflag)
891-
cleanitems(queue);
914+
if (!itemspec->title)
915+
return NULL;
892916

893-
additem(queue, title, body, file, bg, fg, brd);
917+
return itemspec;
894918
}
895919

896920
/* read x events */
@@ -907,8 +931,11 @@ readevent(struct Queue *queue)
907931
copypixmap(item);
908932
break;
909933
case ButtonPress:
910-
if ((item = getitem(queue, ev.xexpose.window)) != NULL)
911-
delitem(queue, item);
934+
if ((item = getitem(queue, ev.xbutton.window)) == NULL)
935+
break;
936+
if ((ev.xbutton.button == config.actionbutton) && item->cmd)
937+
printf("%s\n", item->cmd);
938+
delitem(queue, item);
912939
break;
913940
case MotionNotify:
914941
if ((item = getitem(queue, ev.xmotion.window)) != NULL)
@@ -998,6 +1025,23 @@ moveitems(struct Queue *queue)
9981025
queue->change = 0;
9991026
}
10001027

1028+
/* destroy all notification items of the given tag, or all items if tag is NULL */
1029+
static void
1030+
cleanitems(struct Queue *queue, const char *tag)
1031+
{
1032+
struct Item *item;
1033+
struct Item *tmp;
1034+
1035+
item = queue->head;
1036+
while (item) {
1037+
tmp = item;
1038+
item = item->next;
1039+
if (tag == NULL || (tmp->tag && strcmp(tmp->tag, tag) == 0)) {
1040+
delitem(queue, tmp);
1041+
}
1042+
}
1043+
}
1044+
10011045
/* clean up dc elements */
10021046
static void
10031047
cleandc(void)
@@ -1015,6 +1059,7 @@ cleandc(void)
10151059
int
10161060
main(int argc, char *argv[])
10171061
{
1062+
struct Itemspec *itemspec;
10181063
struct Queue *queue; /* it contains the queue of notifications and their geometry */
10191064
struct pollfd pfd[2]; /* [2] for stdin and xfd, see poll(2) */
10201065
char buf[BUFSIZ]; /* buffer for stdin */
@@ -1076,7 +1121,14 @@ main(int argc, char *argv[])
10761121
if (pfd[0].revents & POLLIN) {
10771122
if (fgets(buf, sizeof buf, stdin) == NULL)
10781123
break;
1079-
parseinput(queue, buf);
1124+
if ((itemspec = parseline(buf)) != NULL) {
1125+
if (oflag) {
1126+
cleanitems(queue, NULL);
1127+
} else if (itemspec->tag) {
1128+
cleanitems(queue, itemspec->tag);
1129+
}
1130+
additem(queue, itemspec);
1131+
}
10801132
}
10811133
if (pfd[1].revents & POLLIN) {
10821134
readevent(queue);
@@ -1093,7 +1145,7 @@ main(int argc, char *argv[])
10931145
}
10941146

10951147
/* clean up stuff */
1096-
cleanitems(queue);
1148+
cleanitems(queue, NULL);
10971149
cleandc();
10981150
free(queue);
10991151

xnotify.h

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
enum ItemOption {IMG, BG, FG, BRD, UNKNOWN};
1+
enum ItemOption {IMG, BG, FG, BRD, TAG, CMD, UNKNOWN};
22
enum {DownWards, UpWards};
33
enum {LeftAlignment, CenterAlignment, RightAlignment};
44
enum {
@@ -32,6 +32,8 @@ struct Config {
3232
int shrink;
3333

3434
int sec;
35+
36+
unsigned int actionbutton;
3537
};
3638

3739
/* monitor geometry structure */
@@ -57,12 +59,26 @@ struct Fonts {
5759
int texth; /* text height, also used for padding */
5860
};
5961

62+
/* notification item specification structure */
63+
struct Itemspec {
64+
char *title;
65+
char *body;
66+
char *file;
67+
char *background;
68+
char *foreground;
69+
char *border;
70+
char *tag;
71+
char *cmd;
72+
};
73+
6074
/* notification item structure */
6175
struct Item {
6276
struct Item *prev, *next;
6377

6478
char *title;
6579
char *body;
80+
char *tag;
81+
char *cmd;
6682

6783
time_t time;
6884

0 commit comments

Comments
 (0)