progman.exe^H^H^H^H
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}