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}