diff --git a/Makefile.in b/Makefile.in index 0dfe6fe..59e41b9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -11,6 +11,7 @@ SRCS= \ src/ewmh.c \ src/infobar.c \ src/layout.c \ + src/launcher.c \ src/parse_api.c \ src/parse.c \ src/screen.c \ diff --git a/src/config.c b/src/config.c index 691b1ed..f5b7c27 100644 --- a/src/config.c +++ b/src/config.c @@ -267,6 +267,38 @@ config_rule(void) free(ks); } +static void +config_launcher(void) +{ + struct conf_sec *sec, **ks; + struct launcher *l; + int n, i; + + /* [launchers] */ + sec = fetch_section_first(NULL, "launchers"); + ks = fetch_section(sec, "launcher"); + n = fetch_section_count(ks); + + SLIST_INIT(&W->h.launcher); + + /* [launcher] */ + for(i = 0; i < n; ++i) + { + l = xcalloc(1, sizeof(struct launcher)); + + l->name = xstrdup(fetch_opt_first(ks[i], "default", "name").str); + l->prompt = xstrdup(fetch_opt_first(ks[i], ":", "prompt").str); + l->command = xstrdup(fetch_opt_first(ks[i], "spawn", "command").str); + + if((l->width = fetch_opt_first(ks[i], "150", "width").num) <= 0) + l->width = 150; + + SLIST_INSERT_HEAD(&W->h.launcher, l, next); + } + + free(ks); +} + static void config_keybind(void) { @@ -338,6 +370,7 @@ config_init(void) config_client(); config_bars(); config_rule(); + config_launcher(); free_conf(); } diff --git a/src/config.h b/src/config.h index 4070ecd..36220aa 100644 --- a/src/config.h +++ b/src/config.h @@ -18,6 +18,7 @@ #include "mouse.h" #include "screen.h" #include "infobar.h" +#include "launcher.h" #define THEME_DEFAULT (SLIST_FIRST(&W->h.theme)) @@ -88,6 +89,9 @@ static const struct { char *name; void (*func)(Uicb cmd); } uicb_list[] = { "screen_move_client_next", uicb_screen_move_client_next }, { "screen_move_client_prev", uicb_screen_move_client_prev }, + /* Launcher */ + { "launcher", uicb_launcher }, + { NULL, NULL } }; diff --git a/src/infobar.c b/src/infobar.c index b893ca6..049d5e4 100644 --- a/src/infobar.c +++ b/src/infobar.c @@ -18,6 +18,8 @@ static void infobar_elem_status_init(struct element *e); static void infobar_elem_status_update(struct element *e); static void infobar_elem_systray_init(struct element *e); static void infobar_elem_systray_update(struct element *e); +static void infobar_elem_launcher_init(struct element *e); +static void infobar_elem_launcher_update(struct element *e); const struct elem_funcs { @@ -26,9 +28,10 @@ const struct elem_funcs void (*func_update)(struct element *e); } elem_funcs[] = { - { 't', infobar_elem_tag_init, infobar_elem_tag_update }, - { 's', infobar_elem_status_init, infobar_elem_status_update }, - { 'y', infobar_elem_systray_init, infobar_elem_systray_update }, + { 't', infobar_elem_tag_init, infobar_elem_tag_update }, + { 's', infobar_elem_status_init, infobar_elem_status_update }, + { 'y', infobar_elem_systray_init, infobar_elem_systray_update }, + { 'l', infobar_elem_launcher_init, infobar_elem_launcher_update }, { '\0', NULL, NULL } }; @@ -218,12 +221,60 @@ infobar_elem_systray_update(struct element *e) systray_update(); } +static void +infobar_elem_launcher_init(struct element *e) +{ + struct barwin *b; + + if(!(W->flags & WMFS_LAUNCHER)) + e->geo.w = 1; + + infobar_elem_placement(e); + + if(!(b = SLIST_FIRST(&e->bars))) + { + b = barwin_new(e->infobar->bar->win, e->geo.x, 0, e->geo.w, e->geo.h, 0, 0, false); + b->fg = e->infobar->theme->bars.fg; + b->bg = e->infobar->theme->bars.bg; + SLIST_INSERT_HEAD(&e->bars, b, enext); + } + else + { + barwin_move(b, e->geo.x, e->geo.y); + barwin_resize(b, e->geo.w, e->geo.h); + } + + barwin_refresh_color(b); + barwin_refresh(b); +} + +static void +infobar_elem_launcher_update(struct element *e) +{ + struct barwin *b = SLIST_FIRST(&e->bars); + int l; + + if(!(W->flags & WMFS_LAUNCHER)) + return; + + barwin_refresh_color(b); + + l = draw_textw(e->infobar->theme, e->data) + 2; + draw_text(b->dr, e->infobar->theme, 1, TEXTY(e->infobar->theme, e->geo.h), b->fg, e->data); + + /* Cursor */ + XDrawLine(W->dpy, b->dr, W->gc, l, 2, l, e->geo.h - 4); + + barwin_refresh(b); +} + #define ELEM_INIT(a) \ do { \ e = xcalloc(1, sizeof(struct element)); \ SLIST_INIT(&e->bars); \ e->infobar = i; \ e->type = j; \ + e->data = NULL; \ e->align = a; \ e->func_init = elem_funcs[j].func_init; \ e->func_update = elem_funcs[j].func_update; \ @@ -336,6 +387,8 @@ infobar_elem_reinit(struct infobar *i) { struct element *e; + barwin_refresh_color(i->bar); + TAILQ_FOREACH(e, &i->elements, next) { /* Status element found, scan from the tail now */ @@ -361,6 +414,8 @@ infobar_elem_reinit(struct infobar *i) e->func_init(e); e->func_update(e); } + + barwin_refresh(i->bar); } struct infobar* @@ -412,7 +467,6 @@ infobar_remove(struct infobar *i) free(i->elemorder); free(i->name); - free(i->status); if(i == W->systray.infobar) systray_freeicons(); diff --git a/src/infobar.h b/src/infobar.h index 7192ade..9e9c82f 100644 --- a/src/infobar.h +++ b/src/infobar.h @@ -10,7 +10,7 @@ #include "util.h" #include "tag.h" -enum { ElemTag = 0, ElemStatus, ElemSystray, ElemCustom, ElemLast }; +enum { ElemTag = 0, ElemStatus, ElemSystray, ElemLauncher, ElemCustom, ElemLast }; struct infobar *infobar_new(struct screen *s, char *name, struct theme *theme, enum barpos pos, const char *elem); void infobar_elem_update(struct infobar *i, int type); diff --git a/src/launcher.c b/src/launcher.c new file mode 100644 index 0000000..3a1e829 --- /dev/null +++ b/src/launcher.c @@ -0,0 +1,196 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include +#include + +#include "wmfs.h" +#include "event.h" +#include "util.h" +#include "infobar.h" +#include "config.h" + +#define LAUNCHER_INIT_ELEM(width) \ + SLIST_FOREACH(ib, &W->screen->infobars, next) \ + { \ + TAILQ_FOREACH(e, &ib->elements, next) \ + if(e->type == ElemLauncher) \ + { \ + e->geo.w = width; \ + e->data = data; \ + } \ + infobar_elem_reinit(ib); \ + } + +static void +launcher_process(struct launcher *l) +{ + struct infobar *ib; + struct element *e; + bool loop = true; + char buf[512] = { 0 }; + char tmp[32] = { 0 }; + char *p, *data, *arg, *cmd = xstrdup(l->command); + int i, pos = 0, histpos = 0; + void (*func)(Uicb); + XEvent ev; + KeySym ks; + + W->flags |= WMFS_LAUNCHER; + + /* Prepare elements */ + xasprintf(&data, "%s ", l->prompt); + LAUNCHER_INIT_ELEM(l->width); + + XGrabKeyboard(W->dpy, W->root, true, GrabModeAsync, GrabModeAsync, CurrentTime); + + while(loop) + { + XNextEvent(W->dpy, &ev); + + if(ev.type != KeyPress) + { + EVENT_HANDLE(&ev); + continue; + } + + /* Get pressed key */ + XLookupString(&ev.xkey, tmp, sizeof(tmp), &ks, 0); + + /* Check Ctrl-c / Ctrl-d */ + if(ev.xkey.state & ControlMask) + { + switch(ks) + { + case XK_c: + case XK_d: + ks = XK_Escape; + break; + case XK_p: + ks = XK_Up; + break; + case XK_n: + ks = XK_Down; + break; + } + } + + /* Check if there is a keypad */ + if(IsKeypadKey(ks) && ks == XK_KP_Enter) + ks = XK_Return; + + /* Manage pressed keys */ + switch(ks) + { + case XK_Up: + if(l->nhisto) + { + if(histpos >= (int)l->nhisto) + histpos = 0; + strncpy(buf, l->histo[l->nhisto - ++histpos], sizeof(buf)); + pos = strlen(buf); + } + break; + + case XK_Down: + if(l->nhisto && histpos > 0 && histpos < (int)l->nhisto) + { + strncpy(buf, l->histo[l->nhisto - --histpos], sizeof(buf)); + pos = strlen(buf); + } + break; + + case XK_Return: + /* Get function name only, if cmds are added in command */ + arg = NULL; + if((p = strchr(cmd, ' '))) + { + *p = '\0'; + xasprintf(&arg, "%s %s", p + 1, buf); + } + + if((func = uicb_name_func(cmd))) + { + if(arg) + { + func(arg); + free(arg); + } + else + func(buf); + } + + /* Histo */ + if(l->nhisto + 1 > HISTOLEN) + { + for(i = l->nhisto - 1; i > 1; --i) + strncpy(l->histo[i], l->histo[i - 1], sizeof(l->histo[i])); + + l->nhisto = 0; + } + /* Store in histo array */ + strncpy(l->histo[l->nhisto++], buf, sizeof(buf)); + + loop = false; + break; + + case XK_Escape: + loop = false; + break; + + /* TODO: Completion */ + case XK_Tab: + break; + + case XK_BackSpace: + if(pos) + buf[--pos] = '\0'; + break; + + default: + strncat(buf, tmp, sizeof(tmp)); + ++pos; + break; + } + + free(data); + xasprintf(&data, "%s %s", l->prompt, buf); + + /* Update EVERY launcher element of the screen */ + SLIST_FOREACH(ib, &W->screen->infobars, next) + { + TAILQ_FOREACH(e, &ib->elements, next) + { + if(e->type != ElemLauncher) + continue; + + e->data = data; + e->func_update(e); + } + } + } + + XUngrabKeyboard(W->dpy, CurrentTime); + + free(cmd); + + /* 'Close' launcher elements */ + W->flags ^= WMFS_LAUNCHER; + data = NULL; + LAUNCHER_INIT_ELEM(1); +} + +void +uicb_launcher(Uicb cmd) +{ + struct launcher *l; + + SLIST_FOREACH(l, &W->h.launcher, next) + if(!strcmp(l->name, cmd)) + { + launcher_process(l); + break; + } +} diff --git a/src/launcher.h b/src/launcher.h new file mode 100644 index 0000000..d89ae5a --- /dev/null +++ b/src/launcher.h @@ -0,0 +1,13 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef LAUNCHER_H +#define LAUNCHER_H + +#include "wmfs.h" + +void uicb_launcher(Uicb cmd); + +#endif /* LAUNCHER_H */ diff --git a/src/wmfs.c b/src/wmfs.c index 0a1a4e3..d3a63ed 100644 --- a/src/wmfs.c +++ b/src/wmfs.c @@ -406,6 +406,7 @@ wmfs_quit(void) struct theme *t; struct client *c; struct mousebind *m; + struct launcher *l; ewmh_update_wmfs_props(); @@ -419,7 +420,6 @@ wmfs_quit(void) client_remove(c); } - /* Will free: * * Screens -> tags @@ -460,6 +460,16 @@ wmfs_quit(void) free(k); } + while(!SLIST_EMPTY(&W->h.launcher)) + { + l = SLIST_FIRST(&W->h.launcher); + SLIST_REMOVE_HEAD(&W->h.launcher, next); + free((void*)l->name); + free((void*)l->prompt); + free((void*)l->command); + free(l); + } + while(!SLIST_EMPTY(&W->h.mousebind)) { m = SLIST_FIRST(&W->h.mousebind); diff --git a/src/wmfs.h b/src/wmfs.h index 7c08516..5c8ed9b 100644 --- a/src/wmfs.h +++ b/src/wmfs.h @@ -123,6 +123,7 @@ struct element struct infobar *infobar; struct status_ctx *statusctx; int type; + char *data; enum position align; void (*func_init)(struct element *e); void (*func_update)(struct element *e); @@ -140,7 +141,6 @@ struct infobar enum barpos pos; char *elemorder; char *name; - char *status; TAILQ_HEAD(esub, element) elements; SLIST_ENTRY(infobar) next; }; @@ -275,6 +275,18 @@ struct rule SLIST_ENTRY(rule) next; }; +struct launcher +{ + char *name; + char *prompt; + char *command; +#define HISTOLEN 64 + char histo[HISTOLEN][256]; + int nhisto; + int width; + SLIST_ENTRY(launcher) next; +}; + struct _systray { struct geo geo; @@ -298,6 +310,7 @@ struct wmfs #define WMFS_RELOAD 0x04 #define WMFS_SYSTRAY 0x08 #define WMFS_LOG 0x10 +#define WMFS_LAUNCHER 0x20 Flags flags; GC gc, rgc; Atom *net_atom; @@ -325,6 +338,7 @@ struct wmfs SLIST_HEAD(, theme) theme; SLIST_HEAD(, rule) rule; SLIST_HEAD(, mousebind) mousebind; + SLIST_HEAD(, launcher) launcher; } h; /* diff --git a/wmfsrc b/wmfsrc index c184cde..282dd45 100644 --- a/wmfsrc +++ b/wmfsrc @@ -65,11 +65,12 @@ # t Tags # s Statustext (will take available space) # y Systray (can be set only ONE time among all element) + # l Launcher (will be expended at launcher use) [bar] position = 0 screen = 0 - elements = "tsy" # element order in bar + elements = "tlsy" # element order in bar theme = "default" [/bar] @@ -135,6 +136,22 @@ [/rules] +[launchers] + + # command can be an uicb function or an uicb function + extension (see example) + [launcher] + name = "exec" + prompt = "Run:" + + # Example of uicb + ext: + # command = "spawn xterm -e" + command = "spawn" + + width = 150 + [/launcher] + +[/launchers] + [keys] [key] mod = {"Super"} key = "Return" func = "spawn" cmd = "urxvt || xterm" [/key] @@ -215,6 +232,9 @@ # Layout set historic travelling function (TESTING) [key] mod = {"Super"} key = "o" func = "layout_prev_set" [/key] - [key] mod = {"Super"} key = "p" func = "layout_next_set" [/key] + [key] mod = {"Super", "Shift"} key = "o" func = "layout_next_set" [/key] + + # Launcher + [key] mod = {"Super"} key = "p" func = "launcher" cmd = "exec" [/key] [/keys]