commit 913c08b9aca6680ed8caffec4de098c6c007892e Author: Adrien Schildknecht Date: Wed Apr 24 21:05:53 2013 +0200 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..19cc560 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright © 2013, Adrien Schildknecht +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY Adrien Schildknecht ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL Adrien Schildknecht BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca892a8 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +CFILES=xcwd.c +CC=gcc +CFLAGS=-Wall -Werror -std=gnu89 #-DDEBUG -g +LDFLAGS=-lX11 +EXE="xcwd" +O=${CFILES:.c=.o} +prefix=/usr/ + +${EXE}: clean ${O} + ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${O} + +.SUFFIXES: .c .o +.c.o: + ${CC} ${CFLAGS} -c $< + +clean: + rm -vf *.o + +distclean: clean + rm -vf ${EXE} + +install: ${EXE} + install -m 0755 ${EXE} $(prefix)/bin + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f4dc509 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +xcwd - X current working directory +================================== +xcwd is a simple tool which print the current working directory of the +currently focused window. +The main goal is to launch applications directly into the same directory +as the focused applications. This is especially useful if you want to open +a new terminal for debugging or compiling purpose. + +Requirements +------------ +- libX11-dev + +Running xwcd +------------ +Simply invoke the 'xcwd' command. +You probably want to use it this way: + ``urxvt -cd `xcwd` `` diff --git a/TODO b/TODO new file mode 100644 index 0000000..3097fad --- /dev/null +++ b/TODO @@ -0,0 +1,12 @@ +TODO +==== + +Features +-------- +check the WM_CLIENT_MACHINE property +multi-monitor ? + +Bugs +---- +xcwd is unable to get the properties of a gtk window. + diff --git a/xcwd.c b/xcwd.c new file mode 100644 index 0000000..e5cb4fb --- /dev/null +++ b/xcwd.c @@ -0,0 +1,238 @@ +/* This is fcwd written by Adrien Schildknecht (c) 2013 + * Email: adrien+dev@schischi.me + * Feel free to copy and redistribute in terms of the + * BSD license + */ + +#include +#include +#include +#include +#include +#include +#include + +#define XA_STRING (XInternAtom(dpy, "STRING", 0)) + +Display *dpy; + +typedef struct processes_s *processes_t; +struct processes_s { + struct proc_s { + long pid; + long ppid; + char name[32]; + } *ps; + size_t n; +}; + +int nameCmp(const void *p1, const void *p2) +{ + return strcasecmp(((struct proc_s *)p1)->name, + ((struct proc_s *)p2)->name); +} + +int ppidCmp(const void *p1, const void *p2) +{ + return ((struct proc_s *)p1)->ppid - ((struct proc_s *)p2)->ppid; +} + +static Window focusedWindow() +{ + Window focuswin; + int focusrevert; + + dpy = XOpenDisplay (NULL); + if (!dpy) + exit (1); + XGetInputFocus (dpy, &focuswin, &focusrevert); +#ifdef DEBUG + fprintf(stderr, "Window ID = %lu\n", focuswin); +#endif + return focuswin; +} + +static long windowPid(Window focuswin) +{ + Atom nameAtom = XInternAtom(dpy, "_NET_WM_PID", 1); + Atom cardinalAtom = XInternAtom(dpy, "CARDINAL", 0); + Atom type; + int format; + long pid = -1; + unsigned long nitems, after; + unsigned char *data = 0; + + if (XGetWindowProperty(dpy, focuswin, nameAtom, 0, 1024, 0, cardinalAtom, + &type, &format, &nitems, &after, &data) == Success) { + if (data) { + pid = *((long*)data); + XFree(data); + } + } +#ifdef DEBUG + if(pid == -1) + fprintf(stderr, "_NET_WM_PID not found\n"); + else + fprintf(stderr, "_NET_WM_PID = %lu\n", pid); +#endif + return pid; +} + +static char* windowStrings(Window focuswin, size_t *size, char* hint) +{ + Atom nameAtom = XInternAtom(dpy, hint, 1); + Atom type; + int format; + int i; + unsigned long after; + unsigned char *data = 0; + char *ret = NULL; + + if (XGetWindowProperty(dpy, focuswin, nameAtom, 0, 1024, 0, AnyPropertyType, + &type, &format, size, &after, &data) == Success) { + if (data) { + if(type == XA_STRING) { + ret = malloc(sizeof(char) * *size); +#ifdef DEBUG + fprintf(stderr, "%s = ", hint); +#endif + for(i = 0; i < *size; ++i) { +#ifdef DEBUG + fprintf(stderr, "%c", data[i] == 0 ? ' ' : data[i]); +#endif + ret[i] = data[i]; + } +#ifdef DEBUG + fprintf(stderr, "\n"); +#endif + } + XFree(data); + } +#ifdef DEBUG + else + fprintf(stderr, "%s not found\n", hint); +#endif + } + return ret; +} + +static void freeProcesses(processes_t p) +{ + free(p->ps); + free(p); +} + +static processes_t getProcesses(void) +{ + glob_t globbuf; + unsigned int i, j; + processes_t p; + + glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &globbuf); + p = malloc(sizeof(struct processes_s)); + p->ps = malloc(globbuf.gl_pathc * sizeof(struct proc_s)); + +#ifdef DEBUG + fprintf(stderr, "Found %ld processes\n", globbuf.gl_pathc); +#endif + for (i = j = 0; i < globbuf.gl_pathc; i++) { + char name[32]; + FILE *tn; + + (void)globbuf.gl_pathv[globbuf.gl_pathc - i - 1]; + snprintf(name, sizeof(name), "%s%s", + globbuf.gl_pathv[globbuf.gl_pathc - i - 1], "/stat"); + tn = fopen(name, "r"); + if (tn == NULL) + continue; + fscanf(tn, "%ld (%32[^)] %*3c %ld", &p->ps[j].pid, p->ps[j].name, + &p->ps[j].ppid); +#ifdef DEBUG + fprintf(stderr, "\t%-20s\tpid=%6ld\tppid=%6ld\n", p->ps[j].name, + p->ps[j].pid, p->ps[j].ppid); +#endif + fclose(tn); + j++; + } + p->n = j; + globfree(&globbuf); + return p; +} + +static long lastChild(processes_t p, long pid) +{ + struct proc_s key, *res; + + do { + key.ppid = pid; + res = (struct proc_s *)bsearch(&key, p->ps, p->n, + sizeof(struct proc_s), ppidCmp); + pid = res ? res->pid : -1; + }while(pid != -1); + return key.ppid; +} + +static void readPath(long pid) +{ + char buf[255]; + char path[64]; + snprintf(path, sizeof(path), "/proc/%ld/cwd", pid); +#ifdef DEBUG + fprintf(stderr, "Read %s\n", path); +#endif + readlink(path, buf, 255); + fprintf(stdout, "%s\n", buf); +} + +int main(int argc, const char *argv[]) +{ + processes_t p; + Window w = focusedWindow(); + long pid; + + pid = windowPid(w); + p = getProcesses(); + if(pid != -1) { // WM_NET_PID + qsort(p->ps, p->n, sizeof(struct proc_s), ppidCmp); + } + else { + size_t size; + char* strings; + int i; + struct proc_s *res = NULL, key; + qsort(p->ps, p->n, sizeof(struct proc_s), nameCmp); + strings = windowStrings(w, &size, "WM_CLASS"); + for(i = 0; i < size; i += strlen(strings + i) + 1) { +#ifdef DEBUG + fprintf(stderr, "pidof %s\n", strings + i); +#endif + strcpy(key.name, strings + i); + res = (struct proc_s *)bsearch(&key, p->ps, p->n, + sizeof(struct proc_s), nameCmp); + if(res) + break; + } + if(res) { + pid = res->pid; +#ifdef DEBUG + fprintf(stderr, "Found %s (%ld)\n", res->name, res->pid); +#endif + } + if(size != 0) + free(strings); + } + if(pid != -1) { + pid = lastChild(p, pid); + readPath(pid); + } + else { +#ifdef DEBUG + fprintf(stderr, "getenv $HOME...\n"); +#endif + fprintf(stdout, "%s\n", getenv("HOME")); + } + freeProcesses(p); + + return EXIT_SUCCESS; +} +