main
xl.c
1#include <X11/Xlib.h>
2#include <X11/keysym.h>
3
4#include <stdbool.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include <getopt.h>
10
11#include "config.h"
12#include "rd.h"
13
14/*
15 * a remake of the late `lnd`.
16 * since X is less secure,
17 * i can actually find the mouse position
18 * and make it really dropdown.
19 */
20
21int main(int ac, char **av) {
22 int wi = 80, hi = 150, x = 0, y = 0;
23
24 char path[128];
25 snprintf(path, sizeof path, "%s/%s", getenv("HOME"), ".xlrc");
26
27 char *fnt = NULL;
28
29 int opt;
30 while ((opt = getopt(ac, av, "c:g:f:h ")) != -1) {
31 switch (opt) {
32 case 'c':
33 snprintf(path, sizeof path, "%s", optarg);
34 break;
35 case 'g': {
36 const char *p = strchr(optarg, 'x');
37 if (!p) {
38 fprintf(stderr, "invalid geom (default: 80x150)\n");
39 return 1;
40 }
41
42 wi = atoi(optarg);
43 hi = atoi(p + 1);
44 break;
45 }
46 case 'f':
47 fnt = optarg;
48 break;
49 case 'h':
50 fprintf(stderr,
51 "usage: %s [-c file] [-g WxH] [-f fontname]\nusage: %s "
52 "[-h]\nusage: %s\n",
53 av[0], av[0], av[0]);
54 return 0;
55 break;
56 case ':':
57 fprintf(stderr, "-%c needs a argument!\n", optopt);
58 return 1;
59 break;
60 case '?':
61 fprintf(stderr, "unknown flag: -%c\n", optopt);
62 return 1;
63 break;
64 }
65 }
66
67 Display *d = XOpenDisplay(NULL);
68 if (!d) {
69 fprintf(stderr, "couldnt open display\n");
70 return 1;
71 }
72
73 int s = DefaultScreen(d);
74 Window r = DefaultRootWindow(d);
75 Window w_r;
76 unsigned int m_r;
77 int d1, d2;
78
79 if (XQueryPointer(d, r, &w_r, &w_r, &x, &y, &d1, &d2, &m_r)) {
80 }
81
82 XColor col[6];
83 for (size_t c = 0; cols[c] != NULL; c++) {
84 XParseColor(d, DefaultColormap(d, s), cols[c], &col[c]);
85 XAllocColor(d, DefaultColormap(d, s), &col[c]);
86 }
87
88 Window w = XCreateSimpleWindow(d, RootWindow(d, s), x, y, wi, hi, 1,
89 col[0].pixel, WhitePixel(d, s));
90 XStoreName(d, w, "xlnd");
91 XSelectInput(d, w,
92 ExposureMask | KeyPressMask | StructureNotifyMask |
93 ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
94 LeaveWindowMask);
95 /*
96 * dont make it a regular window
97 * kinda like rofi or layer-shell
98 */
99 XSetWindowAttributes at;
100 at.override_redirect = True;
101 XChangeWindowAttributes(d, w, CWOverrideRedirect, &at);
102
103 XMapWindow(d, w);
104 XMapRaised(d, w);
105 XSetInputFocus(d, w, RevertToPointerRoot, CurrentTime);
106 XGrabKeyboard(d, w, True, GrabModeAsync, GrabModeAsync, CurrentTime);
107 XGrabPointer(d, w, True, ButtonPressMask | PointerMotionMask, GrabModeAsync,
108 GrabModeAsync, None, None, CurrentTime);
109
110 GC gc = XCreateGC(d, w, 0, NULL);
111 XSetForeground(d, gc, WhitePixel(d, s));
112 XFillRectangle(d, w, gc, 0, 0, wi, hi);
113 XSetForeground(d, gc, BlackPixel(d, s));
114
115 XFontStruct *font = XLoadQueryFont(d, fnt ? fnt : "fixed");
116 if (!font)
117 font = XLoadQueryFont(d, "fixed");
118 XSetFont(d, gc, font->fid);
119
120 int32_t sel = 0;
121
122 /* file reading */
123 char *prg = fbuf(path);
124 if (!prg) {
125 fprintf(stderr, "could not read from %s\n", path);
126 free(prg);
127 goto q;
128 }
129
130 /* init prg list */
131 char *prgs[64];
132
133 size_t p_i = 0;
134 char p_c[1024];
135 strncpy(p_c, prg, sizeof p_c - 1);
136 p_c[sizeof p_c - 1] = '\0';
137
138 char *t = strtok(p_c, "\n");
139 while (t != NULL && p_i < 64) {
140 prgs[p_i++] = t;
141 t = strtok(NULL, "\n");
142 }
143
144 while (1) {
145 XEvent ev;
146 XNextEvent(d, &ev);
147
148 switch (ev.type) {
149 case Expose:
150 if (ev.xexpose.count != 0)
151 break;
152
153 XSetForeground(d, gc, col[1].pixel);
154 XFillRectangle(d, w, gc, 0, 0, wi, hi);
155
156 for (int j = 0; j < (int)p_i; ++j) {
157 if (sel == j && strcmp(prgs[j], "EMPTY") != 0) {
158 XSetForeground(d, gc, col[3].pixel);
159 } else {
160 XSetForeground(d, gc, col[1].pixel);
161 }
162 if (prgs[j][0] == '!')
163 XSetForeground(d, gc, col[5].pixel);
164
165 XFillRectangle(d, w, gc, 0, (hi * j) / (int)p_i, wi, hi / (int)p_i + 1);
166 }
167
168 for (int i = 0; i < (int)p_i; ++i) {
169 int direction, as, des;
170 XCharStruct ov;
171
172 XTextExtents(font, (prgs[i][0] == '!') ? prgs[i] + 1 : prgs[i],
173 (prgs[i][0] == '!') ? strlen(prgs[i]) - 1
174 : strlen(prgs[i]),
175 &direction, &as, &des, &ov);
176
177 if (sel == i) {
178 XSetForeground(d, gc, col[4].pixel);
179 } else {
180 XSetForeground(d, gc, col[2].pixel);
181 }
182 if (prgs[i][0] == '!')
183 XSetForeground(d, gc, col[2].pixel);
184
185 if (strcmp(prgs[i], "EMPTY") != 0) {
186 XDrawString(d, w, gc, (wi - ov.width) / 2,
187 (hi * i) / (int)p_i + (hi / (int)p_i + (as + des)) / 2,
188 (prgs[i][0] == '!') ? prgs[i] + 1 : prgs[i],
189 (prgs[i][0] == '!') ? strlen(prgs[i]) - 1
190 : strlen(prgs[i]));
191 }
192 }
193 break;
194 case ConfigureNotify:
195 break;
196 case LeaveNotify:
197 goto q;
198 break;
199 case ButtonPress:
200 if (strcmp(prgs[sel], "EMPTY") != 0) {
201 if (prgs[sel][0] == '!')
202 break;
203 printf("%s\n", prgs[sel]);
204 goto q;
205 }
206 break;
207 case ButtonRelease:
208 break;
209 case MotionNotify: {
210 int ssel = ev.xmotion.y / (hi / (int)p_i);
211 if (ssel >= (int)p_i)
212 ssel = (int)p_i - 1;
213
214 if (ssel != sel) {
215 sel = ssel;
216 XClearArea(d, w, 0, 0, 0, 0, True);
217 }
218 break;
219 }
220 case KeyPress: {
221 KeySym k = XLookupKeysym(&ev.xkey, 0);
222 if (k == XK_Escape)
223 goto q;
224
225 break;
226 }
227 }
228 }
229
230q:
231 free(prg);
232 XFreeFont(d, font);
233 XUngrabKeyboard(d, CurrentTime);
234 XUngrabPointer(d, CurrentTime);
235 XFreeGC(d, gc);
236 XDestroyWindow(d, w);
237 XCloseDisplay(d);
238 return 0;
239}