diff --git a/.gitignore b/.gitignore index ed608a242..50ffec6f7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ widgetgen.h layoutgen.h awesome-version-internal.h awesome-message +awesome-menu diff --git a/Makefile.am b/Makefile.am index 7c154b8e4..670901d65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -121,7 +121,7 @@ awesome_SOURCES = \ awesome.c awesome.h \ tag.c tag.h \ common/util.c common/util.h \ - xutil.c xutil.h \ + common/xutil.c common/xutil.h \ config.c config.h \ screen.c screen.h \ statusbar.c statusbar.h \ @@ -157,6 +157,18 @@ awesome_message_SOURCES = \ awesome_message_LDADD = $(XFT_LIBS) $(X_LIBS) $(CAIRO_LIBS) $(CONFUSE_LIBS) +bin_PROGRAMS += awesome-menu +awesome_menu_SOURCES = \ + common/swindow.c common/swindow.h \ + common/draw.c common/draw.h \ + common/util.h common/util.c \ + common/version.h common/version.c \ + common/configopts.h common/configopts.c \ + common/xutil.h common/xutil.c \ + awesome-menu.c + +awesome_menu_LDADD = $(XFT_LIBS) $(X_LIBS) $(CAIRO_LIBS) $(CONFUSE_LIBS) + if HAVE_XMLTO if HAVE_ASCIIDOC if XMLTO_MAN_WORKS diff --git a/awesome-menu.c b/awesome-menu.c new file mode 100644 index 000000000..30331c4e3 --- /dev/null +++ b/awesome-menu.c @@ -0,0 +1,501 @@ +/* + * awesome-menu.c - menu window for awesome + * + * Copyright © 2008 Julien Danjou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#define _GNU_SOURCE +#include + +#include +#include +#include + +#include + +#include + +#include "common/swindow.h" +#include "common/util.h" +#include "common/version.h" +#include "common/configopts.h" +#include "common/xutil.h" + +#define PROGNAME "awesome-menu" + +#define CLEANMASK(mask) (mask & ~(globalconf.numlockmask | LockMask)) + +typedef enum +{ + STOP = 0, + RUN = 1, + CANCEL = 2 +} status_t; + +static status_t status = RUN; + +/** Import awesome config file format */ +extern cfg_opt_t awesome_opts[]; + +typedef struct item_t item_t; +struct item_t +{ + char *data; + item_t *next; + Bool match; +}; + +DO_SLIST(item_t, item, p_delete); + +/** awesome-run global configuration structure */ +typedef struct +{ + /** Display ref */ + Display *display; + /** The window */ + SimpleWindow *sw; + /** The draw contet */ + DrawCtx *ctx; + /** Font to use */ + XftFont *font; + /** Foreground color */ + XColor fg; + /** Background color */ + XColor bg; + /** List background */ + XColor fg_focus, bg_focus; + /** Numlock mask */ + unsigned int numlockmask; + /** The text */ + char text[PATH_MAX]; + /** Item list */ + item_t *items; + /** Selected item */ + item_t *item_selected; + /** What to do with the result text */ + char *exec; + /** Prompt */ + char *prompt; +} AwesomeMenuConf; + +static AwesomeMenuConf globalconf; + +static void __attribute__ ((noreturn)) +exit_help(int exit_code) +{ + FILE *outfile = (exit_code == EXIT_SUCCESS) ? stdout : stderr; + fprintf(outfile, "Usage: %s [-x xcoord] [-y ycoord] [-e command] \n", + PROGNAME); + exit(exit_code); +} + +static int +config_parse(const char *confpatharg) +{ + int ret; + char *confpath; + cfg_t *cfg, *cfg_screen, *cfg_general, *cfg_colors; + + if(!confpatharg) + confpath = config_file(); + else + confpath = a_strdup(confpatharg); + + cfg = cfg_init(awesome_opts, CFGF_NONE); + + switch((ret = cfg_parse(cfg, confpath))) + { + case CFG_FILE_ERROR: + perror("awesome-message: parsing configuration file failed"); + break; + case CFG_PARSE_ERROR: + cfg_error(cfg, "awesome: parsing configuration file %s failed.\n", confpath); + break; + } + + if(ret) + return ret; + + /* get global screen section */ + cfg_screen = cfg_getsec(cfg, "screen"); + + if(!cfg_screen) + eprint("parsing configuration file failed, no screen section found\n"); + + /* get colors and general section */ + cfg_general = cfg_getsec(cfg_screen, "general"); + cfg_colors = cfg_getsec(cfg_screen, "colors"); + + /* colors */ + draw_color_new(globalconf.display, DefaultScreen(globalconf.display), + cfg_getstr(cfg_colors, "normal_fg"), + &globalconf.fg); + draw_color_new(globalconf.display, DefaultScreen(globalconf.display), + cfg_getstr(cfg_colors, "normal_bg"), + &globalconf.bg); + draw_color_new(globalconf.display, DefaultScreen(globalconf.display), + cfg_getstr(cfg_colors, "focus_fg"), + &globalconf.fg_focus); + draw_color_new(globalconf.display, DefaultScreen(globalconf.display), + cfg_getstr(cfg_colors, "focus_bg"), + &globalconf.bg_focus); + + /* font */ + globalconf.font = XftFontOpenName(globalconf.display, DefaultScreen(globalconf.display), + cfg_getstr(cfg_general, "font")); + + p_delete(&confpath); + + return ret; +} + +static void +complete(void) +{ + item_t *item = NULL; + + if(globalconf.item_selected) + item = globalconf.item_selected->next; + else + for(item = globalconf.items; item; item = item->next) + if(item->match) + { + globalconf.item_selected = item; + break; + } + + for(; item; item = item->next) + if(item->match) + { + a_strcpy(globalconf.text, ssizeof(globalconf.text), item->data); + globalconf.item_selected = item; + return; + } +} + +static void +compute_match(void) +{ + item_t *item; + + /* reset the selected item to NULL */ + globalconf.item_selected = NULL; + + for(item = globalconf.items; item; item = item->next) + if(!a_strncmp(globalconf.text, item->data, a_strlen(globalconf.text))) + item->match = True; + else + item->match = False; +} + +/* Why not? */ +#define MARGIN 10 + +static void +redraw(void) +{ + item_t *item; + Area geometry = globalconf.sw->geometry; + unsigned int len; + + draw_text(globalconf.ctx, geometry, AlignLeft, + MARGIN, globalconf.font, globalconf.prompt, globalconf.fg_focus, globalconf.bg_focus); + + len = MARGIN * 2 + draw_textwidth(globalconf.display, globalconf.font, globalconf.prompt); + geometry.x += len; + geometry.width -= len; + + draw_text(globalconf.ctx, geometry, AlignLeft, + MARGIN, globalconf.font, globalconf.text, globalconf.fg, globalconf.bg); + + len = MARGIN * 2 + MAX(draw_textwidth(globalconf.display, globalconf.font, globalconf.text), + geometry.width / 20); + geometry.x += len; + geometry.width -= len; + + for(item = globalconf.items; item && geometry.width > 0; item = item->next) + if(item->match) + { + if(item == globalconf.item_selected) + draw_text(globalconf.ctx, geometry, AlignLeft, + MARGIN / 2, globalconf.font, item->data, globalconf.fg_focus, globalconf.bg_focus); + else + draw_text(globalconf.ctx, geometry, AlignLeft, + MARGIN / 2, globalconf.font, item->data, globalconf.fg, globalconf.bg); + len = MARGIN + draw_textwidth(globalconf.display, globalconf.font, item->data); + geometry.x += len; + geometry.width -= len; + } + + if(geometry.width) + draw_rectangle(globalconf.ctx, geometry, True, globalconf.bg); + + simplewindow_refresh_drawable(globalconf.sw, DefaultScreen(globalconf.display)); + XSync(globalconf.display, False); +} + +static void +handle_kpress(XKeyEvent *e) +{ + char buf[32]; + KeySym ksym; + int num; + ssize_t len; + + len = a_strlen(globalconf.text); + num = XLookupString(e, buf, sizeof(buf), &ksym, 0); + if(e->state & ControlMask) + switch(ksym) + { + default: + return; + case XK_bracketleft: + ksym = XK_Escape; + break; + case XK_h: + case XK_H: + ksym = XK_BackSpace; + break; + case XK_i: + case XK_I: + ksym = XK_Tab; + break; + case XK_j: + case XK_J: + ksym = XK_Return; + break; + case XK_c: + case XK_C: + status = CANCEL; + break; + case XK_w: + case XK_W: + /* erase word */ + if(len) + { + int i = len - 1; + while(i >= 0 && globalconf.text[i] == ' ') + globalconf.text[i--] = '\0'; + while(i >= 0 && globalconf.text[i] != ' ') + globalconf.text[i--] = '\0'; + compute_match(); + redraw(); + } + return; + } + else if(CLEANMASK(e->state) & Mod1Mask) + switch(ksym) + { + default: + return; + case XK_h: + ksym = XK_Left; + break; + case XK_l: + ksym = XK_Right; + break; + case XK_j: + ksym = XK_Next; + break; + case XK_k: + ksym = XK_Prior; + break; + case XK_g: + ksym = XK_Home; + break; + case XK_G: + ksym = XK_End; + break; + } + + switch(ksym) + { + default: + if(num && !iscntrl((int) buf[0])) + { + buf[num] = '\0'; + a_strncat(globalconf.text, sizeof(globalconf.text), buf, num); + compute_match(); + redraw(); + } + break; + case XK_BackSpace: + if(len) + { + globalconf.text[--len] = '\0'; + compute_match(); + redraw(); + } + break; + case XK_Right: + case XK_Tab: + complete(); + redraw(); + break; + case XK_Escape: + status= CANCEL; + break; + case XK_Return: + status = STOP; + break; + } +} + +static void +item_list_fill(void) +{ + char buf[PATH_MAX]; + item_t *newitem; + + item_list_init(&globalconf.items); + + while(fgets(buf, sizeof(buf), stdin)) + { + buf[a_strlen(buf) - 1] = '\0'; + newitem = p_new(item_t, 1); + newitem->data = a_strdup(buf); + newitem->match = True; + item_list_append(&globalconf.items, newitem); + } +} + +int +main(int argc, char **argv) +{ + Display *disp; + XEvent ev; + int opt, ret; + char *configfile = NULL, *cmd; + ssize_t len; + const char *shell = getenv("SHELL"); + Area geometry = { 0, 0, 0, 0, NULL }; + static struct option long_options[] = + { + {"help", 0, NULL, 'h'}, + {"version", 0, NULL, 'v'}, + {"exec", 0, NULL, 'e'}, + {NULL, 0, NULL, 0} + }; + + if(!(disp = XOpenDisplay(NULL))) + eprint("unable to open display"); + + globalconf.display = disp; + + while((opt = getopt_long(argc, argv, "vhf:b:x:y:n:c:e:", + long_options, NULL)) != -1) + switch(opt) + { + case 'v': + eprint_version(PROGNAME); + break; + case 'x': + geometry.x = atoi(optarg); + break; + case 'y': + geometry.y = atoi(optarg); + break; + case 'h': + exit_help(EXIT_SUCCESS); + break; + case 'c': + configfile = a_strdup(optarg); + break; + case 'e': + globalconf.exec = a_strdup(optarg); + break; + } + + if(argc - optind >= 1) + globalconf.prompt = a_strdup(argv[optind]); + + if((ret = config_parse(configfile))) + return ret; + + /* Get the numlock mask */ + globalconf.numlockmask = get_numlockmask(disp); + + /* Init the geometry */ + geometry.height = globalconf.font->height * 1.5; + geometry.width = DisplayWidth(globalconf.display, DefaultScreen(globalconf.display)); + + /* Create the window */ + globalconf.sw = simplewindow_new(disp, DefaultScreen(disp), + geometry.x, geometry.y, geometry.width, geometry.height, 0); + + XStoreName(disp, globalconf.sw->window, PROGNAME); + XMapRaised(disp, globalconf.sw->window); + + /* Create the drawing context */ + globalconf.ctx = draw_context_new(disp, DefaultScreen(disp), + geometry.width, geometry.height, + globalconf.sw->drawable); + + + + item_list_fill(); + + if(XGrabKeyboard(disp, DefaultRootWindow(disp), True, + GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) + eprint("cannot grab keyboard"); + + redraw(); + + while(status == RUN) + { + XNextEvent(disp, &ev); + switch(ev.type) + { + case ButtonPress: + status = CANCEL; + break; + case KeyPress: + handle_kpress(&ev.xkey); + break; + case Expose: + if(!ev.xexpose.count) + simplewindow_refresh_drawable(globalconf.sw, DefaultScreen(disp)); + break; + default: + break; + } + } + + if(status != CANCEL) + { + if(!globalconf.exec) + printf("%s\n", globalconf.text); + else + { + if(!shell) + shell = a_strdup("/bin/sh"); + len = a_strlen(globalconf.exec) + a_strlen(globalconf.text) + 1; + cmd = p_new(char, len); + a_strcpy(cmd, len, globalconf.exec); + a_strcat(cmd, len, globalconf.text); + execl(shell, shell, "-c", cmd, NULL); + } + } + + simplewindow_delete(globalconf.sw); + XCloseDisplay(disp); + + return EXIT_SUCCESS; +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/client.c b/client.c index 0ab117aca..298b08208 100644 --- a/client.c +++ b/client.c @@ -32,7 +32,7 @@ #include "ewmh.h" #include "screen.h" #include "widget.h" -#include "xutil.h" +#include "common/xutil.h" #include "layouts/floating.h" extern AwesomeConf globalconf; @@ -57,7 +57,7 @@ client_loadprops(Client * c, int screen) prop = p_new(char, ntags + 2); - if(xgettextprop(c->win, + if(xgettextprop(globalconf.display, c->win, XInternAtom(globalconf.display, "_AWESOME_PROPERTIES", False), prop, ntags + 2)) { @@ -138,8 +138,10 @@ client_get_byname(Client *list, char *name) void client_updatetitle(Client *c) { - if(!xgettextprop(c->win, XInternAtom(globalconf.display, "_NET_WM_NAME", False), c->name, sizeof(c->name))) - xgettextprop(c->win, XInternAtom(globalconf.display, "WM_NAME", False), c->name, sizeof(c->name)); + if(!xgettextprop(globalconf.display, c->win, + XInternAtom(globalconf.display, "_NET_WM_NAME", False), c->name, sizeof(c->name))) + xgettextprop(globalconf.display, c->win, + XInternAtom(globalconf.display, "WM_NAME", False), c->name, sizeof(c->name)); widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS); } diff --git a/common/xutil.c b/common/xutil.c new file mode 100644 index 000000000..b1ee741b5 --- /dev/null +++ b/common/xutil.c @@ -0,0 +1,78 @@ +/* + * common/xutil.c - X-related useful functions + * + * Copyright © 2007-2008 Julien Danjou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include "common/util.h" +#include "common/xutil.h" + +Bool +xgettextprop(Display *disp, Window w, Atom atom, char *text, ssize_t textlen) +{ + char **list = NULL; + int n; + XTextProperty name; + + if(!text || !textlen) + return False; + + text[0] = '\0'; + XGetTextProperty(disp, w, &name, atom); + + if(!name.nitems) + return False; + + if(name.encoding == XA_STRING) + a_strncpy(text, textlen, (char *) name.value, textlen - 1); + + else if(XmbTextPropertyToTextList(disp, &name, &list, &n) >= Success && n > 0 && *list) + { + a_strncpy(text, textlen, *list, textlen - 1); + XFreeStringList(list); + } + + text[textlen - 1] = '\0'; + XFree(name.value); + + return True; +} + +unsigned int +get_numlockmask(Display *disp) +{ + XModifierKeymap *modmap; + unsigned int mask = 0; + int i, j; + + modmap = XGetModifierMapping(disp); + for(i = 0; i < 8; i++) + for(j = 0; j < modmap->max_keypermod; j++) + if(modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(disp, XK_Num_Lock)) + mask = (1 << i); + + XFreeModifiermap(modmap); + + return mask; +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/xutil.h b/common/xutil.h similarity index 78% rename from xutil.h rename to common/xutil.h index 5eab7838f..e7d6b85f8 100644 --- a/xutil.h +++ b/common/xutil.h @@ -1,7 +1,7 @@ /* - * xutil.h - X-related useful functions header + * common/xutil.h - X-related useful functions header * - * Copyright © 2007 Julien Danjou + * Copyright © 2007-2008 Julien Danjou * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,16 +19,13 @@ * */ -#ifndef AWESOME_XUTIL_H -#define AWESOME_XUTIL_H +#ifndef AWESOME_COMMON_XUTIL_H +#define AWESOME_COMMON_XUTIL_H #include -#include "uicb.h" -Bool xgettextprop(Window, Atom, char *, ssize_t); +Bool xgettextprop(Display *, Window, Atom, char *, ssize_t); unsigned int get_numlockmask(Display *); -Uicb uicb_spawn; -Uicb uicb_exec; #endif // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/config.c b/config.c index 837f8c8b8..26c4ee2f6 100644 --- a/config.c +++ b/config.c @@ -27,12 +27,11 @@ #include "rules.h" #include "screen.h" #include "widget.h" -#include "xutil.h" #include "ewmh.h" #include "defconfig.h" #include "layouts/tile.h" #include "common/configopts.h" -#include "common/configopts.h" +#include "common/xutil.h" /* Permit to use mouse with many more buttons */ #ifndef Button6 diff --git a/event.c b/event.c index 64a732c35..5cbd26ba9 100644 --- a/event.c +++ b/event.c @@ -35,7 +35,6 @@ #include "ewmh.h" #include "client.h" #include "widget.h" -#include "xutil.h" #include "rules.h" #include "layouts/tile.h" #include "layouts/floating.h" diff --git a/layout.c b/layout.c index c7e20ab02..d4b1491b4 100644 --- a/layout.c +++ b/layout.c @@ -23,7 +23,6 @@ #include #include "tag.h" -#include "xutil.h" #include "focus.h" #include "widget.h" #include "window.h" diff --git a/rules.c b/rules.c index 2085978d2..4ff05ded8 100644 --- a/rules.c +++ b/rules.c @@ -20,7 +20,7 @@ */ #include "rules.h" -#include "xutil.h" +#include "common/xutil.h" extern AwesomeConf globalconf; @@ -77,7 +77,7 @@ client_match_rule(Client *c, Rule *r) if(r->xprop && r->xpropval_r - && xgettextprop(c->win, + && xgettextprop(globalconf.display, c->win, XInternAtom(globalconf.display, r->xprop, False), buf, ssizeof(buf))) ret = !regexec(r->xpropval_r, buf, 1, &tmp, 0); diff --git a/uicb.c b/uicb.c index c8faf0677..33e29f68b 100644 --- a/uicb.c +++ b/uicb.c @@ -23,8 +23,10 @@ * @defgroup ui_callback User Interface Callbacks */ +#include +#include + #include "awesome.h" -#include "xutil.h" #include "tag.h" #include "mouse.h" #include "statusbar.h" @@ -38,6 +40,76 @@ extern AwesomeConf globalconf; #include "uicbgen.h" +/** Execute another process, replacing the current instance of Awesome + * \param screen Screen ID + * \param arg Command + * \ingroup ui_callback + */ +void +uicb_exec(int screen __attribute__ ((unused)), char *arg) +{ + Client *c; + char path[PATH_MAX]; + + /* remap all clients since some WM won't handle them otherwise */ + for(c = globalconf.clients; c; c = c->next) + client_unban(c); + + XSync(globalconf.display, False); + + if(globalconf.display) + close(ConnectionNumber(globalconf.display)); + + sscanf(arg, "%s", path); + execlp(path, arg, NULL); +} + +/** Spawn another process + * \param screen Screen ID + * \param arg Command + * \ingroup ui_callback + */ +void +uicb_spawn(int screen, char *arg) +{ + static char *shell = NULL; + char *display = NULL; + char *tmp, newdisplay[128]; + + if(!arg) + return; + + if(!shell && !(shell = getenv("SHELL"))) + shell = a_strdup("/bin/sh"); + + if(!XineramaIsActive(globalconf.display) && (tmp = getenv("DISPLAY"))) + { + display = a_strdup(tmp); + if((tmp = strrchr(display, '.'))) + *tmp = '\0'; + snprintf(newdisplay, sizeof(newdisplay), "%s.%d", display, screen); + setenv("DISPLAY", newdisplay, 1); + } + + + /* The double-fork construct avoids zombie processes and keeps the code + * clean from stupid signal handlers. */ + if(fork() == 0) + { + if(fork() == 0) + { + if(globalconf.display) + close(ConnectionNumber(globalconf.display)); + setsid(); + execl(shell, shell, "-c", arg, NULL); + warn("execl '%s -c %s'", shell, arg); + perror(" failed"); + } + exit(EXIT_SUCCESS); + } + wait(0); +} + static int run_uicb(char *cmd) { diff --git a/uicb.h b/uicb.h index e61afc1a5..dee36d5ad 100644 --- a/uicb.h +++ b/uicb.h @@ -26,5 +26,8 @@ typedef void (Uicb)(int, char *); int parse_control(char *); +Uicb uicb_exec; +Uicb uicb_spawn; + #endif // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/xutil.c b/xutil.c deleted file mode 100644 index f04f220e3..000000000 --- a/xutil.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * xutil.c - X-related useful functions - * - * Copyright © 2007 Julien Danjou - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include -#include - -#include "xutil.h" -#include "client.h" - -extern AwesomeConf globalconf; - -/** Execute another process, replacing the current instance of Awesome - * \param screen Screen ID - * \param arg Command - * \ingroup ui_callback - */ -void -uicb_exec(int screen __attribute__ ((unused)), char *arg) -{ - Client *c; - char path[PATH_MAX]; - - /* remap all clients since some WM won't handle them otherwise */ - for(c = globalconf.clients; c; c = c->next) - client_unban(c); - - XSync(globalconf.display, False); - - if(globalconf.display) - close(ConnectionNumber(globalconf.display)); - - sscanf(arg, "%s", path); - execlp(path, arg, NULL); -} - -/** Spawn another process - * \param screen Screen ID - * \param arg Command - * \ingroup ui_callback - */ -void -uicb_spawn(int screen, char *arg) -{ - static char *shell = NULL; - char *display = NULL; - char *tmp, newdisplay[128]; - - if(!shell && !(shell = getenv("SHELL"))) - shell = a_strdup("/bin/sh"); - if(!arg) - return; - - if(!XineramaIsActive(globalconf.display) && (tmp = getenv("DISPLAY"))) - { - display = a_strdup(tmp); - tmp = strchr(display, ':'); - if(tmp && (tmp = strrchr(tmp, '.'))) - *tmp = '\0'; - snprintf(newdisplay, sizeof(newdisplay), "%s.%d", display, screen); - setenv("DISPLAY", newdisplay, 1); - } - - - /* The double-fork construct avoids zombie processes and keeps the code - * clean from stupid signal handlers. */ - if(fork() == 0) - { - if(fork() == 0) - { - if(globalconf.display) - close(ConnectionNumber(globalconf.display)); - setsid(); - execl(shell, shell, "-c", arg, (char *) NULL); - warn("execl '%s -c %s'", shell, arg); - perror(" failed"); - } - exit(EXIT_SUCCESS); - } - wait(0); -} - -Bool -xgettextprop(Window w, Atom atom, char *text, ssize_t textlen) -{ - char **list = NULL; - int n; - XTextProperty name; - - if(!text || !textlen) - return False; - - text[0] = '\0'; - XGetTextProperty(globalconf.display, w, &name, atom); - - if(!name.nitems) - return False; - - if(name.encoding == XA_STRING) - a_strncpy(text, textlen, (char *) name.value, textlen - 1); - - else if(XmbTextPropertyToTextList(globalconf.display, &name, &list, &n) >= Success && n > 0 && *list) - { - a_strncpy(text, textlen, *list, textlen - 1); - XFreeStringList(list); - } - - text[textlen - 1] = '\0'; - XFree(name.value); - - return True; -} - -unsigned int -get_numlockmask(Display *disp) -{ - XModifierKeymap *modmap; - unsigned int mask = 0; - int i, j; - - modmap = XGetModifierMapping(disp); - for(i = 0; i < 8; i++) - for(j = 0; j < modmap->max_keypermod; j++) - if(modmap->modifiermap[i * modmap->max_keypermod + j] - == XKeysymToKeycode(disp, XK_Num_Lock)) - mask = (1 << i); - - XFreeModifiermap(modmap); - - return mask; -} - -// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80