progman.exe^H^H^H^H
at master 231 lines 5.4 kB view raw
1/* 2 * Copyright 2020 joshua stein <jcs@jcs.org> 3 * Copyright 1998-2007 Decklin Foster <decklin@red-bean.com>. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to 7 * deal in the Software without restriction, including without limitation the 8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 * sell copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 19 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 */ 22 23#include <stdlib.h> 24#include <stdio.h> 25#include <string.h> 26#include <unistd.h> 27#include <err.h> 28#include <errno.h> 29#include <sys/types.h> 30#include <X11/Xlib.h> 31#include <X11/Xutil.h> 32#include "progman.h" 33 34void 35fork_exec(char *cmd) 36{ 37 pid_t pid = fork(); 38 39 switch (pid) { 40 case 0: 41 setsid(); 42 execl("/bin/sh", "sh", "-c", cmd, NULL); 43 fprintf(stderr, "exec failed, cleaning up child\n"); 44 exit(1); 45 case -1: 46 fprintf(stderr, "can't fork\n"); 47 } 48} 49 50int 51get_pointer(int *x, int *y) 52{ 53 Window real_root, real_win; 54 int wx, wy; 55 unsigned int mask; 56 57 XQueryPointer(dpy, root, &real_root, &real_win, x, y, &wx, &wy, &mask); 58 return mask; 59} 60 61int 62send_xmessage(Window t, Window w, Atom a, unsigned long x, unsigned long mask) 63{ 64 XClientMessageEvent e; 65 66 e.type = ClientMessage; 67 e.window = w; 68 e.message_type = a; 69 e.format = 32; 70 e.data.l[0] = x; 71 e.data.l[1] = CurrentTime; 72 73 return XSendEvent(dpy, t, False, mask, (XEvent *)&e); 74} 75 76action_t * 77parse_action(char *prefix, char *action) 78{ 79 char *taction = NULL, *targ = NULL; 80 char *sep; 81 char *sarg = NULL; 82 action_t *out = NULL; 83 int iaction = ACTION_NONE; 84 int iarg = 0; 85 86 taction = strdup(action); 87 if ((sep = strchr(taction, ' '))) { 88 *sep = '\0'; 89 targ = sep + 1; 90 } else 91 targ = NULL; 92 93 if (strcmp(taction, "cycle") == 0) 94 iaction = ACTION_CYCLE; 95 else if (strcmp(taction, "reverse_cycle") == 0) 96 iaction = ACTION_REVERSE_CYCLE; 97 else if (strcmp(taction, "desk") == 0) 98 iaction = ACTION_DESK; 99 else if (strcmp(taction, "close") == 0) 100 iaction = ACTION_CLOSE; 101 else if (strcmp(taction, "exec") == 0) 102 iaction = ACTION_EXEC; 103 else if (strcmp(taction, "launcher") == 0) 104 iaction = ACTION_LAUNCHER; 105 else if (strcmp(taction, "restart") == 0) 106 iaction = ACTION_RESTART; 107 else if (strcmp(taction, "quit") == 0) 108 iaction = ACTION_QUIT; 109 else if (strcmp(taction, "drag") == 0) 110 iaction = ACTION_DRAG; 111 else if (taction[0] == '\n' || taction[0] == '\0') 112 iaction = ACTION_NONE; 113 else 114 iaction = ACTION_INVALID; 115 116 /* parse numeric or string args */ 117 switch (iaction) { 118 case ACTION_DESK: 119 if (targ == NULL) { 120 warnx("%s: missing argument for \"%s\"", 121 prefix, taction); 122 goto done; 123 } 124 125 if (strcmp(targ, "next") == 0) 126 iaction = ACTION_DESK_NEXT; 127 else if (strcmp(targ, "previous") == 0) 128 iaction = ACTION_DESK_PREVIOUS; 129 else { 130 errno = 0; 131 iarg = strtol(targ, NULL, 10); 132 if (errno != 0) { 133 warnx("%s: failed parsing numeric argument " 134 "\"%s\" for \"%s\"", prefix, targ, taction); 135 goto done; 136 } 137 } 138 break; 139 case ACTION_EXEC: 140 if (targ == NULL) { 141 warnx("%s: missing string argument for \"%s\"", prefix, 142 taction); 143 goto done; 144 } 145 sarg = strdup(targ); 146 break; 147 case ACTION_INVALID: 148 warnx("%s: invalid action \"%s\"", prefix, taction); 149 goto done; 150 default: 151 /* no args expected of other commands */ 152 if (targ != NULL) { 153 warnx("%s: unexpected argument \"%s\" for \"%s\"", 154 prefix, taction, targ); 155 goto done; 156 } 157 } 158 159 out = malloc(sizeof(action_t)); 160 out->action = iaction; 161 out->iarg = iarg; 162 out->sarg = sarg; 163 164done: 165 if (taction) 166 free(taction); 167 168 return out; 169} 170 171void 172take_action(action_t *action) 173{ 174 client_t *p, *next; 175 176 switch (action->action) { 177 case ACTION_CYCLE: 178 case ACTION_REVERSE_CYCLE: 179 if (!cycle_head) { 180 if (!focused) 181 return; 182 183 cycle_head = focused; 184 } 185 186 if ((next = next_client_for_focus(cycle_head))) 187 focus_client(next, FOCUS_FORCE); 188 else { 189 /* probably at the end of the list, invert it */ 190 p = focused; 191 adjust_client_order(NULL, ORDER_INVERT); 192 193 if (p) 194 /* p should now not be focused */ 195 redraw_frame(p, None); 196 197 focus_client(cycle_head, FOCUS_FORCE); 198 } 199 break; 200 case ACTION_DESK: 201 goto_desk(action->iarg); 202 break; 203 case ACTION_DESK_NEXT: 204 if (cur_desk < ndesks - 1) 205 goto_desk(cur_desk + 1); 206 break; 207 case ACTION_DESK_PREVIOUS: 208 if (cur_desk > 0) 209 goto_desk(cur_desk - 1); 210 break; 211 case ACTION_CLOSE: 212 if (focused) 213 send_wm_delete(focused); 214 break; 215 case ACTION_EXEC: 216 fork_exec(action->sarg); 217 break; 218 case ACTION_LAUNCHER: 219 launcher_show(NULL); 220 break; 221 case ACTION_RESTART: 222 cleanup(); 223 execlp(orig_argv0, orig_argv0, NULL); 224 break; 225 case ACTION_QUIT: 226 quit(); 227 break; 228 default: 229 warnx("unhandled action %d\n", action->action); 230 } 231}