/* * Xcorner: * (c) 2001-2004, Luciano Rocha, * Released under the GNU GPL, v2. See http://www.gnu.org/licenses/gpl.html * compile: cc -O2 -o xcorner xcorner.c -L/usr/X11R6/lib -lX11 * * A little program to run commands when your mouse pointer reaches or * stays in a corner. You can specify a command for each corner and special * one ("exit" or by using the -e option). * * The program accepts the following options: * -[g|k|x][w] * -e * * -g informs that the program should be run inside gnome-terminal, while * -k is for konsole and -x for xterm. * -w says that only one instance of the program should be created * (ie, wait for the program to finish before running it again). * -e is a shortcut for - exit, that defines a corner to exit this * cute little program. * * You can specify corners in two ways: * * Upper Left Corner: :Upper Right Corner * +-----------------------+ * |ul/1 2/ur| * | | * | | * | | * | | * | | * |ll/3 4/lr| * +-----------------------+ * Lower Left Corner: :Lower Right Corner * * As an example, all of the following are equivalent: * xcorner -1 gnome-terminal -2 exit -w4 mozilla * xcorner -gul -e2 -w4mozilla * xcorner -wlrmozilla -2exit -1gnome-terminal * * Other useful example: * xcorner -ur gnome-terminal -ll exit -lr 'xscreensaver-command -lock ; * xset dpms force standby' * * You may specify arguments to the commands as long as they don't look * like one for xcorner. If they do, you must use enclose the command and * its arguments with quotes or double quotes, so * xcorner -1 xcreensaver-command -lock * is OK, but * xcorner -1 ls -1 * isn't. You could change it to * xcorner -1 'ls -1' * You may also use full shell commands, like: * xcorner -1 'ps auxw ; read' * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #define GTERM "gnome-terminal" #define KTERM "konsole" #define XTERM "xterm" #include #include #include #include #include #include #include #include #include #include #include #include static struct command { char *command; int wait; pid_t pid; void (*shell)(const char *); } c[4]; static void exec_s(const char *p); static void exec_g(const char *p); static void exec_k(const char *p); static void exec_x(const char *p); static int parsecorner(char **); static int parse(int, char *[]); static int getcorner(int x, int y); static void myexec(int); static char *addstr(char *, char *); static char *myshell; static char *myhome; static int swidth, sheight; int main(int ac, char *av[]) { Display* d; Window rt_win, r_win, rr_win; char *display; int exit_corner, screen, prev, cur, x, y, rx, ry, m; pid_t pid; struct passwd *pw = 0; memset(c, '\0', sizeof c); if (!(myshell = getenv("SHELL"))) { if ((pw = getpwuid(getuid()))) { myshell = pw->pw_shell; } else { myshell = "/bin/sh"; } } if (!(myhome = getenv("HOME"))) { if (pw || (pw = getpwuid(getuid()))) { myhome = pw->pw_dir; } else { myhome = "/"; } } exit_corner = parse(ac, av); for (x = 0; x < 4; ++x) { if (c[x].command && !strcmp(c[x].command, "exit")) { if (exit_corner >= 0) { fprintf(stderr, "Redefinition of exit " "corner\n"); return 1; } c[x].shell = NULL; c[x].command = NULL; exit_corner = x; } } if (!(d = XOpenDisplay(display = getenv("DISPLAY")))) { fprintf(stderr, "Cannot connect to X server %s\n", display); return 1; } screen = DefaultScreen(d); swidth = DisplayWidth(d, screen) - 10; sheight = DisplayHeight(d, screen) - 10; rt_win = RootWindow(d, screen); daemon(0, 0); prev = cur = -1; for (;;) { usleep(150000); if ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { if (c[0].pid == pid) c[0].pid = 0; else if (c[1].pid == pid) c[1].pid = 0; else if (c[2].pid == pid) c[2].pid = 0; else if (c[3].pid == pid) c[3].pid = 0; } if (XQueryPointer(d, rt_win, &rr_win, &r_win, &rx, &ry, &x, &y, &m) == False) continue; if ((cur = getcorner(x, y)) < 0) prev = cur; else if (cur == exit_corner) break; else if (cur != prev) myexec(prev = cur); } XCloseDisplay(d); return 0; } static char *addstr(char *a, char *b) { int alen, blen; char *n; if (!b) n = a; else if (!a) { if (!(n = (char *) malloc(blen = (strlen(b) + 1)))) { fprintf(stderr, "Not enough memory\n"); exit(1); } memcpy(n, b, blen); } else { if (!(n = (char *) malloc((alen = strlen(a)) + (blen = strlen(b)) + 2))) { fprintf(stderr, "Not enough memory\n"); exit(1); } memcpy(n, a, alen); n[alen] = ' '; memcpy(n + alen + 1, b, blen); n[alen + blen + 1] = '\0'; free(a); } return n; } static void myexec(int p) { if (p < 0 || p > 3 || !c[p].shell || (c[p].wait && c[p].pid > 0)) return; if ((c[p].pid = fork()) == 0) { c[p].shell(c[p].command); exit(1); } } static int getcorner(int x, int y) { int r; if (x <= 10 && y <= 10) r = 0; else if (y <= 10 && x >= swidth) r = 1; else if (x <= 10 && y >= sheight) r = 2; else if (x >= swidth && y >= sheight) r = 3; else r = -1; return r; } static int parse(int ac, char *av[]) { char *a, cs, cw; int i, exit_corner, oldc, cn; exit_corner = -1; if (ac < 2) { fprintf(stderr, "Define commands to execute when mouse" "pointer goes to or stays in a corner.\n" "If a command equals \"exit\", the program" "exits when mouse reaches that corner.\n" "Usage: xcorner -[g|k|x][w] " "...\n"); exit(1); } oldc = -1; for (i = 1; i < ac; ++i) { a = av[i]; if (*a != '-') { if (oldc < 0) { fprintf(stderr, "Invalid usage\n"); exit(1); } c[oldc].command = addstr(c[oldc].command, a); continue; } if ((cs = *++a) == 'e') { if (*++a) { if ((cn = parsecorner(&a)) < 0 || *a) { c[oldc].command = addstr(c[oldc].command, av[i]); continue; } } else { if ((cn = parsecorner(av + i + 1)) < 0 || av[i+1][0]) { fprintf(stderr, "Invalid arguments\n"); exit(1); } ++i; } if (exit_corner >= 0) { fprintf(stderr, "Redefinition of exit " "corner\n"); exit(1); } exit_corner = cn; continue; } if (cs == 'g' || cs == 'k' || cs == 'x') ++a; else cs = 's'; if ((cw = *a) == 'w') ++a; if ((cn = parsecorner(&a)) < 0) { if (oldc < 0) { fprintf(stderr, "Invalid usage\n"); exit(1); } c[oldc].command = addstr(c[oldc].command, av[i]); continue; } if (c[cn].command) { fprintf(stderr, "Redefinition of corner %d\n", cn + 1); exit(1); } oldc = cn; switch (cs) { case 'g': c[cn].shell = exec_g; break; case 'k': c[cn].shell = exec_k; break; case 'x': c[cn].shell = exec_x; break; default: c[cn].shell = exec_s; } if (cw == 'w') c[cn].wait = 1; if (*a) c[oldc].command = addstr(c[oldc].command, a); } return exit_corner; } static int parsecorner(char **p) { register char *s, a, b; if (!p || !(s = *p)) return -1; if ((a = *s) >= '1' && a <= '4') { (*p)++; return a - '1'; } if ((a == 'u' || a == 'l') && ((b = s[1]) == 'l' || b == 'r')) { *p += 2; return (a == 'u' ? 0 : 2) + (b == 'l' ? 0 : 1); } return -1; } static void exec_s(const char *p) { chdir(myhome); execlp(myshell, myshell, "-c", p, NULL); } static void exec_g(const char *p) { chdir(myhome); execlp(GTERM, GTERM, p?"-x":0, myshell, "-c", p, NULL); } static void exec_k(const char *p) { chdir(myhome); execlp(KTERM, KTERM, p?"-e":0, myshell, "-c", p, NULL); } static void exec_x(const char *p) { chdir(myhome); execlp(XTERM, XTERM, p?"-e":0, myshell, "-c", p, NULL); }