cmdtree.c (7373B)
1 #include <stdarg.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <time.h> 6 7 #include <X11/Xlib.h> 8 #include <X11/Xutil.h> 9 #include <X11/Xresource.h> 10 #include <X11/Xft/Xft.h> 11 12 #include <unistd.h> 13 #include <assert.h> 14 15 #include "drw.h" 16 #include "util.h" 17 #include "command.h" 18 19 enum { 20 SchemeNorm, 21 SchemePrefix, 22 SchemeLast, 23 }; /* color schemes */ 24 25 26 struct cmdstack { 27 struct command *children; 28 int nchildren; 29 }; 30 31 #define CMDSTACK_SIZE 32 32 static struct cmdstack cmdstack[CMDSTACK_SIZE]; 33 static int cmdstack_ptr = 0; 34 35 static Window root, parentwin, win; 36 static int screen; 37 static Display *display; 38 static int sep_width; 39 static int bh, mw, mh; 40 static XIC xic; 41 42 #define DEFCMD(b, nme, cmd) \ 43 { .bind = (b), \ 44 .name = (nme), \ 45 .command = (cmd), \ 46 .nchildren = 0, \ 47 .children = NULL, \ 48 }, 49 50 #define DEFPREFIX(b, nme, cs) \ 51 { .bind = b, \ 52 .name = nme, \ 53 .command = 0, \ 54 .nchildren = LENGTH(cs), \ 55 .children = (cs), \ 56 }, 57 58 #include "cfg.h" 59 60 static int 61 cmdstack_push_(struct command *children, int nchildren) { 62 if (cmdstack_ptr + 1 >= CMDSTACK_SIZE) 63 return 0; 64 cmdstack[cmdstack_ptr].children = children; 65 cmdstack[cmdstack_ptr].nchildren = nchildren; 66 cmdstack_ptr++; 67 return 1; 68 } 69 70 static int 71 cmdstack_push(struct command *cmd) { 72 return cmdstack_push_(cmd->children, cmd->nchildren); 73 } 74 75 struct cmdstack * 76 cmdstack_top() { 77 assert(cmdstack_ptr >= 1); 78 return &cmdstack[cmdstack_ptr-1]; 79 } 80 81 struct cmdstack * 82 cmdstack_pop() { 83 if (cmdstack_ptr == 1) 84 return 0; 85 86 return &cmdstack[cmdstack_ptr--]; 87 } 88 89 static void 90 grabkeyboard(void) 91 { 92 struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; 93 int i; 94 95 // XXXembed 96 /* if (embed) */ 97 /* return; */ 98 /* try to grab keyboard, we may have to wait for another process to ungrab */ 99 for (i = 0; i < 1000; i++) { 100 if (XGrabKeyboard(display, DefaultRootWindow(display), True, 101 GrabModeAsync, GrabModeAsync, CurrentTime) 102 == GrabSuccess) 103 return; 104 nanosleep(&ts, NULL); 105 } 106 die("cannot grab keyboard"); 107 } 108 109 static void 110 setup(Drw *drw) 111 { 112 int x, y; 113 XSetWindowAttributes swa; 114 XWindowAttributes wa; 115 XIM xim; 116 XClassHint ch = {"cmdtree", "cmdtree"}; 117 118 bh = drw->fonts->h + 2; 119 /* init appearance */ 120 drw_scm_create(drw, schemes, LENGTH(schemes)); 121 122 if (!XGetWindowAttributes(display, parentwin, &wa)) 123 die("could not get embedding window attributes: 0x%lx", 124 parentwin); 125 int vertwidth = 200; 126 mw = wa.width; 127 mh = 1 * bh; 128 switch (position) { 129 case POSITION_BOTTOM: 130 y = wa.height - mh; 131 x = 0; 132 break; 133 case POSITION_TOP: 134 x = 0; 135 y = 0; 136 break; 137 case POSITION_LEFT: 138 x = 0; 139 y = 0; 140 // TODO: calc vertwidth = max(textwidth(nodes))) 141 mw = vertwidth; 142 mh = wa.height; 143 break; 144 case POSITION_RIGHT: 145 x = wa.width - vertwidth; 146 y = 0; 147 mw = vertwidth; 148 mh = wa.height; 149 } 150 sep_width = drw_fontset_getwidth(drw, separator); 151 152 swa.override_redirect = True; 153 swa.background_pixel = schemes[SchemeNorm].bg_clr.pixel; 154 swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; 155 156 win = XCreateWindow(display, parentwin, x, y, mw, mh, 0, 157 CopyFromParent, CopyFromParent, CopyFromParent, 158 CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); 159 160 XSetClassHint(display, win, &ch); 161 162 // what do? 163 XMapRaised(display, win); 164 165 xim = XOpenIM(display, NULL, NULL, NULL); 166 xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 167 XNClientWindow, win, XNFocusWindow, win, NULL); 168 XSelectInput(display, win, ExposureMask | KeyPressMask); 169 XMapWindow(display, win); 170 } 171 172 static const char * 173 bind_name(const char *bind) { 174 switch (*bind) { 175 case ' ': return "SPC"; 176 default: return bind; 177 } 178 } 179 180 static int 181 draw_command(Drw *drw, int x, int y, struct command *cmd) { 182 char buf[256]; 183 int lpad = 0; 184 int pad = 0; 185 unsigned int w; 186 int invert = 0; 187 int isprefix = 0; 188 char *name = cmd->name; 189 struct scheme *scheme = NULL; 190 191 //drw_fontset_getwidth(drw, binding) + 1; 192 193 drw_setscheme(drw, &schemes[SchemeNorm].bind_clr, 194 &schemes[SchemeNorm].bg_clr); 195 196 const char *bindname = bind_name(cmd->bind); 197 198 w = drw_fontset_getwidth(drw, bindname); 199 x = drw_text(drw, x+pad, y, w, bh, lpad, bindname, invert); 200 201 w = sep_width; 202 drw_setscheme(drw, &schemes[SchemeNorm].arrow_clr, 203 &schemes[SchemeNorm].bg_clr); 204 x = drw_text(drw, x+pad, y, w, bh, lpad, separator, invert); 205 206 isprefix = command_is_prefix(cmd); 207 208 if (isprefix) 209 scheme = &schemes[SchemePrefix]; 210 else 211 scheme = &schemes[SchemeNorm]; 212 213 drw_setscheme(drw, &scheme->name_clr, &scheme->bg_clr); 214 215 if (scheme->prefix && strlen(scheme->prefix) != 0) { 216 snprintf(buf, 256, "%s%s", scheme->prefix, cmd->name); 217 name = buf; 218 } 219 220 w = drw_fontset_getwidth(drw, name); 221 x = drw_text(drw, x+pad, y, w, bh, lpad, name, invert); 222 223 return x; 224 } 225 226 227 /* static void */ 228 /* calc_tree_exts(struct node *nodes, int num_nodes, int *rows, int *cols) { */ 229 /* } */ 230 static void 231 draw_tree_vertical(Drw *drw, struct command *cmds, int ncmds, int x, int y, int w, int h) { 232 int i; 233 int colw = 0; 234 235 x += xpad; 236 y += ypad; 237 238 drw_setscheme(drw, &schemes[SchemeNorm].bg_clr, 239 &schemes[SchemeNorm].bg_clr); 240 drw_rect(drw, 0, 0, w, h, 1, 1); 241 242 int c = '0'; 243 244 for (i = 0; i < ncmds; ++i, ++c, y += bh) { 245 struct command *cmd = &cmds[i]; 246 if (y >= mh) { 247 x = colw; 248 y = 0; 249 colw = 0; 250 } 251 colw = MAX(draw_command(drw, x, y, cmd) + 20, colw); 252 } 253 } 254 255 256 static void 257 draw_tree(Drw *drw, int x, int y, int w, int h) { 258 if (!drw) 259 return; 260 261 struct cmdstack *cmdstack = cmdstack_top(); 262 263 draw_tree_vertical(drw, cmdstack->children, cmdstack->nchildren, x, y, 264 w, h); 265 266 XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 267 XSync(drw->dpy, False); 268 } 269 270 271 static void 272 cleanup(Drw *drw, int code) { 273 drw_free(drw); 274 exit(code); 275 } 276 277 static void 278 run(Drw *drw) { 279 XEvent e; 280 int done = 0; 281 char buf[32]; 282 KeySym ksym = NoSymbol; 283 Status status; 284 int code = 0; 285 struct cmdstack *cmdstack = NULL; 286 struct command *cmd = NULL; 287 288 while (!done) { 289 cmd = NULL; 290 XNextEvent(display, &e); 291 switch (e.type) { 292 case Expose: 293 draw_tree(drw, 0, 0, mw, mh); 294 break; 295 case KeyPress: 296 XmbLookupString(xic, (XKeyEvent*)&e, buf, sizeof buf, 297 &ksym, &status); 298 299 if (ksym == XK_Escape) { 300 code = 1; 301 done = 1; 302 break; 303 } 304 else if (ksym == XK_BackSpace) { 305 if (cmdstack_pop()) 306 draw_tree(drw, 0, 0, mw, mh); 307 } 308 309 cmdstack = cmdstack_top(); 310 cmd = command_lookup(cmdstack->children, 311 cmdstack->nchildren, buf); 312 313 if (cmd) { 314 if (command_is_prefix(cmd)) { 315 cmdstack_push(cmd); 316 draw_tree(drw, 0, 0, mw, mh); 317 } 318 else { 319 command_exec(cmd); 320 } 321 } 322 323 break; 324 } 325 } 326 327 cleanup(drw, code); 328 } 329 330 331 int main(void) { 332 XWindowAttributes wa; 333 Drw *drw; 334 335 display = XOpenDisplay(NULL); 336 if (display == NULL) { 337 fprintf(stderr, "Cannot open display\n"); 338 exit(1); 339 } 340 341 screen = DefaultScreen(display); 342 root = RootWindow(display, screen); 343 344 parentwin = root; 345 346 int res = cmdstack_push_(commands, LENGTH(commands)); 347 assert(res); 348 349 if (!XGetWindowAttributes(display, parentwin, &wa)) 350 die("could not get embedding window attributes: 0x%lx", 351 parentwin); 352 353 drw = drw_create(display, screen, root, wa.width, wa.height); 354 355 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 356 die("no fonts could be loaded."); 357 358 grabkeyboard(); 359 setup(drw); 360 run(drw); 361 362 XCloseDisplay(display); 363 return 0; 364 }