A tiling window manager
1/*
2 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place, Suite 330, Boston, MA 02111-1307 USA.
17 */
18
19#include <unistd.h>
20#include <ctype.h>
21#include <sys/wait.h>
22#include <X11/keysym.h>
23#include <string.h>
24#include <strings.h>
25#include <err.h>
26#include <errno.h>
27#include <signal.h>
28#include <limits.h>
29#include <X11/Xproto.h>
30#include <X11/extensions/XTest.h>
31#include <sys/ioctl.h>
32
33#include "sdorfehs.h"
34
35/* arg_REST and arg_SHELLCMD eat the rest of the input. */
36enum argtype {
37 arg_REST,
38 arg_NUMBER,
39 arg_STRING,
40 arg_FRAME,
41 arg_WINDOW,
42 arg_COMMAND,
43 arg_SHELLCMD,
44 arg_KEYMAP,
45 arg_KEY,
46 arg_GRAVITY,
47 arg_VSCREEN,
48 arg_HOOK,
49 arg_VARIABLE,
50 arg_RAW,
51};
52
53union arg_union {
54 rp_frame *frame;
55 int number;
56 float fnumber;
57 rp_window *win;
58 rp_keymap *keymap;
59 rp_vscreen *vscreen;
60 struct list_head *hook;
61 struct set_var *variable;
62 struct rp_key *key;
63 int gravity;
64};
65
66struct cmdarg {
67 int type;
68 char *string;
69 union arg_union arg;
70 struct list_head node;
71};
72#define ARG_STRING(elt) args[elt]->string
73#define ARG(elt, type) args[elt]->arg.type
74
75struct argspec {
76 int type;
77 char *prompt;
78};
79
80struct set_var {
81 char *var;
82 cmdret *(*set_fn)(struct cmdarg **);
83 int nargs;
84 struct argspec *args;
85 struct list_head node;
86};
87
88struct user_command {
89 char *name;
90 cmdret *(*func)(int, struct cmdarg **);
91 struct argspec *args;
92 int num_args;
93 /*
94 * The number of required arguments. Any arguments after that are
95 * optional and won't be filled in when called interactively.
96 * ni_required_args is used when called non-interactively,
97 * i_required_args when called interactively.
98 */
99 int ni_required_args, i_required_args;
100
101 struct list_head node;
102};
103
104typedef struct {
105 char *name;
106 char *alias;
107} alias_t;
108
109typedef struct rp_frame_undo {
110 char *frames;
111 rp_vscreen *vscreen;
112 struct list_head node;
113} rp_frame_undo;
114
115static LIST_HEAD(user_commands);
116static LIST_HEAD(rp_keymaps);
117static LIST_HEAD(set_vars);
118static LIST_HEAD(rp_frame_undos);
119static LIST_HEAD(rp_frame_redos);
120
121static alias_t *alias_list;
122static int alias_list_size;
123static int alias_list_last;
124
125static const char invalid_negative_arg[] = "invalid negative argument";
126
127/* setter function prototypes */
128static cmdret *set_barborder(struct cmdarg **args);
129static cmdret *set_barbordercolor(struct cmdarg **args);
130static cmdret *set_bargravity(struct cmdarg **args);
131static cmdret *set_barinpadding(struct cmdarg **args);
132static cmdret *set_barpadding(struct cmdarg **args);
133static cmdret *set_barsticky(struct cmdarg **args);
134static cmdret *set_bgcolor(struct cmdarg **args);
135static cmdret *set_border(struct cmdarg **args);
136static cmdret *set_bwcolor(struct cmdarg **args);
137static cmdret *set_fgcolor(struct cmdarg **args);
138static cmdret *set_font(struct cmdarg **args);
139static cmdret *set_framefmt(struct cmdarg **args);
140static cmdret *set_framemsgwait(struct cmdarg **args);
141static cmdret *set_framesels(struct cmdarg **args);
142static cmdret *set_fwcolor(struct cmdarg **args);
143static cmdret *set_gap(struct cmdarg **args);
144static cmdret *set_historysize(struct cmdarg **args);
145static cmdret *set_ignoreresizehints(struct cmdarg **args);
146static cmdret *set_infofmt(struct cmdarg **args);
147static cmdret *set_inputwidth(struct cmdarg **args);
148static cmdret *set_maxsizegravity(struct cmdarg **args);
149static cmdret *set_maxundos(struct cmdarg **args);
150static cmdret *set_msgwait(struct cmdarg **args);
151static cmdret *set_onlyborder(struct cmdarg **args);
152static cmdret *set_padding(struct cmdarg **args);
153static cmdret *set_resizefmt(struct cmdarg **args);
154static cmdret *set_resizeunit(struct cmdarg **args);
155static cmdret *set_rudeness(struct cmdarg **args);
156static cmdret *set_startupmessage(struct cmdarg **args);
157static cmdret *set_stickyfmt(struct cmdarg **args);
158static cmdret *set_topkmap(struct cmdarg **args);
159static cmdret *set_transgravity(struct cmdarg **args);
160static cmdret *set_vscreens(struct cmdarg **args);
161static cmdret *set_waitcursor(struct cmdarg **args);
162static cmdret *set_warp(struct cmdarg **args);
163static cmdret *set_winfmt(struct cmdarg **args);
164static cmdret *set_wingravity(struct cmdarg **args);
165static cmdret *set_winliststyle(struct cmdarg **args);
166static cmdret *set_winname(struct cmdarg **args);
167static cmdret *set_winaddcurvscreen(struct cmdarg **args);
168
169/* command function prototypes. */
170static cmdret *cmd_abort(int interactive, struct cmdarg **args);
171static cmdret *cmd_addhook(int interactive, struct cmdarg **args);
172static cmdret *cmd_alias(int interactive, struct cmdarg **args);
173static cmdret *cmd_banish(int interactive, struct cmdarg **args);
174static cmdret *cmd_banishrel(int interactive, struct cmdarg **args);
175static cmdret *cmd_chdir(int interactive, struct cmdarg **args);
176static cmdret *cmd_clrunmanaged(int interactive, struct cmdarg **args);
177static cmdret *cmd_cnext(int interactive, struct cmdarg **args);
178static cmdret *cmd_colon(int interactive, struct cmdarg **args);
179static cmdret *cmd_commands(int interactive, struct cmdarg **args);
180static cmdret *cmd_cother(int interactive, struct cmdarg **args);
181static cmdret *cmd_cprev(int interactive, struct cmdarg **args);
182static cmdret *cmd_curframe(int interactive, struct cmdarg **args);
183static cmdret *cmd_dedicate(int interactive, struct cmdarg **args);
184static cmdret *cmd_definekey(int interactive, struct cmdarg **args);
185static cmdret *cmd_delete(int interactive, struct cmdarg **args);
186static cmdret *cmd_delkmap(int interactive, struct cmdarg **args);
187static cmdret *cmd_describekey(int interactive, struct cmdarg **args);
188static cmdret *cmd_echo(int interactive, struct cmdarg **args);
189static cmdret *cmd_escape(int interactive, struct cmdarg **args);
190static cmdret *cmd_exchangedown(int interactive, struct cmdarg **args);
191static cmdret *cmd_exchangeleft(int interactive, struct cmdarg **args);
192static cmdret *cmd_exchangeright(int interactive, struct cmdarg **args);
193static cmdret *cmd_exchangeup(int interactive, struct cmdarg **args);
194static cmdret *cmd_exec(int interactive, struct cmdarg **args);
195static cmdret *cmd_execa(int interactive, struct cmdarg **args);
196static cmdret *cmd_execf(int interactive, struct cmdarg **args);
197static cmdret *cmd_execv(int interactive, struct cmdarg **args);
198static cmdret *cmd_execw(int interactive, struct cmdarg **args);
199static cmdret *cmd_fdump(int interactive, struct cmdarg **args);
200static cmdret *cmd_focusdown(int interactive, struct cmdarg **args);
201static cmdret *cmd_focuslast(int interactive, struct cmdarg **args);
202static cmdret *cmd_focusleft(int interactive, struct cmdarg **args);
203static cmdret *cmd_focusright(int interactive, struct cmdarg **args);
204static cmdret *cmd_focusup(int interactive, struct cmdarg **args);
205static cmdret *cmd_frestore(int interactive, struct cmdarg **args);
206static cmdret *cmd_fselect(int interactive, struct cmdarg **args);
207static cmdret *cmd_getenv(int interactive, struct cmdarg **args);
208static cmdret *cmd_getsel(int interactive, struct cmdarg **args);
209static cmdret *cmd_gravity(int interactive, struct cmdarg **args);
210static cmdret *cmd_hsplit(int interactive, struct cmdarg **args);
211static cmdret *cmd_help(int interactive, struct cmdarg **args);
212static cmdret *cmd_inext(int interactive, struct cmdarg **args);
213static cmdret *cmd_info(int interactive, struct cmdarg **args);
214static cmdret *cmd_iother(int interactive, struct cmdarg **args);
215static cmdret *cmd_iprev(int interactive, struct cmdarg **args);
216static cmdret *cmd_kill(int interactive, struct cmdarg **args);
217static cmdret *cmd_lastmsg(int interactive, struct cmdarg **args);
218static cmdret *cmd_link(int interactive, struct cmdarg **args);
219static cmdret *cmd_listhook(int interactive, struct cmdarg **args);
220static cmdret *cmd_meta(int interactive, struct cmdarg **args);
221static cmdret *cmd_newkmap(int interactive, struct cmdarg **args);
222static cmdret *cmd_next(int interactive, struct cmdarg **args);
223static cmdret *cmd_nextframe(int interactive, struct cmdarg **args);
224static cmdret *cmd_nextscreen(int interactive, struct cmdarg **args);
225static cmdret *cmd_number(int interactive, struct cmdarg **args);
226static cmdret *cmd_only(int interactive, struct cmdarg **args);
227static cmdret *cmd_other(int interactive, struct cmdarg **args);
228static cmdret *cmd_prev(int interactive, struct cmdarg **args);
229static cmdret *cmd_prevframe(int interactive, struct cmdarg **args);
230static cmdret *cmd_prevscreen(int interactive, struct cmdarg **args);
231static cmdret *cmd_prompt(int interactive, struct cmdarg **args);
232static cmdret *cmd_putsel(int interactive, struct cmdarg **args);
233static cmdret *cmd_quit(int interactive, struct cmdarg **args);
234static cmdret *cmd_ratclick(int interactive, struct cmdarg **args);
235static cmdret *cmd_rathold(int interactive, struct cmdarg **args);
236static cmdret *cmd_ratinfo(int interactive, struct cmdarg **args);
237static cmdret *cmd_ratrelinfo(int interactive, struct cmdarg **args);
238static cmdret *cmd_ratrelwarp(int interactive, struct cmdarg **args);
239static cmdret *cmd_ratwarp(int interactive, struct cmdarg **args);
240static cmdret *cmd_readkey(int interactive, struct cmdarg **args);
241static cmdret *cmd_redisplay(int interactive, struct cmdarg **args);
242static cmdret *cmd_redo(int interactive, struct cmdarg **args);
243static cmdret *cmd_remhook(int interactive, struct cmdarg **args);
244static cmdret *cmd_remove(int interactive, struct cmdarg **args);
245static cmdret *cmd_rename(int interactive, struct cmdarg **args);
246static cmdret *cmd_resize(int interactive, struct cmdarg **args);
247static cmdret *cmd_restart(int interactive, struct cmdarg **args);
248static cmdret *cmd_sdump(int interactive, struct cmdarg **args);
249static cmdret *cmd_select(int interactive, struct cmdarg **args);
250static cmdret *cmd_set(int interactive, struct cmdarg **args);
251static cmdret *cmd_setenv(int interactive, struct cmdarg **args);
252static cmdret *cmd_sfdump(int interactive, struct cmdarg **args);
253static cmdret *cmd_sfrestore(int interactive, struct cmdarg **args);
254static cmdret *cmd_shrink(int interactive, struct cmdarg **args);
255static cmdret *cmd_smove(int interactive, struct cmdarg **args);
256static cmdret *cmd_source(int interactive, struct cmdarg **args);
257static cmdret *cmd_sselect(int interactive, struct cmdarg **args);
258static cmdret *cmd_stick(int interactive, struct cmdarg **args);
259static cmdret *cmd_swap(int interactive, struct cmdarg **args);
260static cmdret *cmd_unalias(int interactive, struct cmdarg **args);
261static cmdret *cmd_undefinekey(int interactive, struct cmdarg **args);
262static cmdret *cmd_undo(int interactive, struct cmdarg **args);
263static cmdret *cmd_unmanage(int interactive, struct cmdarg **args);
264static cmdret *cmd_unsetenv(int interactive, struct cmdarg **args);
265static cmdret *cmd_unstick(int interactive, struct cmdarg **args);
266static cmdret *cmd_vsplit(int interactive, struct cmdarg **args);
267static cmdret *cmd_verbexec(int interactive, struct cmdarg **args);
268static cmdret *cmd_version(int interactive, struct cmdarg **args);
269static cmdret *cmd_vmove(int interactive, struct cmdarg **args);
270static cmdret *cmd_vnext(int interactive, struct cmdarg **args);
271static cmdret *cmd_vother(int interactive, struct cmdarg **args);
272static cmdret *cmd_vprev(int interactive, struct cmdarg **args);
273static cmdret *cmd_vrename(int interactive, struct cmdarg **args);
274static cmdret *cmd_vscreens(int interactive, struct cmdarg **args);
275static cmdret *cmd_vselect(int interactive, struct cmdarg **args);
276static cmdret *cmd_windows(int interactive, struct cmdarg **args);
277
278static void
279add_set_var(char *name, cmdret *(*fn)(struct cmdarg **), int nargs, ...)
280{
281 int i = 0;
282 struct set_var *var;
283 va_list va;
284
285 var = xmalloc(sizeof(struct set_var));
286 var->var = name;
287 var->set_fn = fn;
288 var->nargs = nargs;
289 var->args = xmalloc(sizeof(struct argspec) * nargs);
290
291 /* Fill var->args */
292 va_start(va, nargs);
293 for (i = 0; i < nargs; i++) {
294 var->args[i].prompt = va_arg(va, char *);
295 var->args[i].type = va_arg(va, int);
296 }
297 va_end(va);
298
299 list_add_tail(&var->node, &set_vars);
300}
301
302static void
303set_var_free(struct set_var *var)
304{
305 if (var == NULL)
306 return;
307
308 free(var->args);
309 free(var);
310}
311
312static void
313init_set_vars(void)
314{
315 /* Keep this sorted alphabetically. */
316 add_set_var("barborder", set_barborder, 1, "", arg_NUMBER);
317 add_set_var("barbordercolor", set_barbordercolor, 1, "", arg_STRING);
318 add_set_var("bargravity", set_bargravity, 1, "", arg_GRAVITY);
319 add_set_var("barinpadding", set_barinpadding, 1, "", arg_NUMBER);
320 add_set_var("barpadding", set_barpadding, 2, "", arg_NUMBER, "",
321 arg_NUMBER);
322 add_set_var("barsticky", set_barsticky, 1, "", arg_NUMBER);
323 add_set_var("bgcolor", set_bgcolor, 1, "", arg_STRING);
324 add_set_var("border", set_border, 1, "", arg_NUMBER);
325 add_set_var("bwcolor", set_bwcolor, 1, "", arg_STRING);
326 add_set_var("fgcolor", set_fgcolor, 1, "", arg_STRING);
327 add_set_var("font", set_font, 1, "", arg_STRING);
328 add_set_var("framefmt", set_framefmt, 1, "", arg_REST);
329 add_set_var("framemsgwait", set_framemsgwait, 1, "", arg_NUMBER);
330 add_set_var("framesels", set_framesels, 1, "", arg_STRING);
331 add_set_var("fwcolor", set_fwcolor, 1, "", arg_STRING);
332 add_set_var("gap", set_gap, 1, "", arg_NUMBER);
333 add_set_var("historysize", set_historysize, 1, "", arg_NUMBER);
334 add_set_var("ignoreresizehints", set_ignoreresizehints, 1, "",
335 arg_NUMBER);
336 add_set_var("infofmt", set_infofmt, 1, "", arg_REST);
337 add_set_var("inputwidth", set_inputwidth, 1, "", arg_NUMBER);
338 add_set_var("maxsizegravity", set_maxsizegravity, 1, "", arg_GRAVITY);
339 add_set_var("maxundos", set_maxundos, 1, "", arg_NUMBER);
340 add_set_var("msgwait", set_msgwait, 1, "", arg_NUMBER);
341 add_set_var("onlyborder", set_onlyborder, 1, "", arg_NUMBER);
342 add_set_var("padding", set_padding, 4, "", arg_NUMBER, "", arg_NUMBER,
343 "", arg_NUMBER, "", arg_NUMBER);
344 add_set_var("resizefmt", set_resizefmt, 1, "", arg_REST);
345 add_set_var("resizeunit", set_resizeunit, 1, "", arg_NUMBER);
346 add_set_var("rudeness", set_rudeness, 1, "", arg_NUMBER);
347 add_set_var("startupmessage", set_startupmessage, 1, "", arg_NUMBER);
348 add_set_var("stickyfmt", set_stickyfmt, 1, "", arg_REST);
349 add_set_var("topkmap", set_topkmap, 1, "", arg_STRING);
350 add_set_var("transgravity", set_transgravity, 1, "", arg_GRAVITY);
351 add_set_var("vscreens", set_vscreens, 1, "", arg_NUMBER);
352 add_set_var("waitcursor", set_waitcursor, 1, "", arg_NUMBER);
353 add_set_var("warp", set_warp, 1, "", arg_NUMBER);
354 add_set_var("winfmt", set_winfmt, 1, "", arg_REST);
355 add_set_var("wingravity", set_wingravity, 1, "", arg_GRAVITY);
356 add_set_var("winliststyle", set_winliststyle, 1, "", arg_STRING);
357 add_set_var("winname", set_winname, 1, "", arg_STRING);
358 add_set_var("winaddcurvscreen", set_winaddcurvscreen, 1, "", arg_NUMBER);
359}
360
361/*
362 * i_nrequired is the number required when called interactively. ni_nrequired
363 * is when called non-interactively.
364 */
365static void
366add_command(char *name, cmdret *(*fn)(int, struct cmdarg **), int nargs,
367 int i_nrequired, int ni_nrequired, ...)
368{
369 int i = 0;
370 struct user_command *cmd;
371 va_list va;
372
373 cmd = xmalloc(sizeof(struct user_command));
374 cmd->name = name;
375 cmd->func = fn;
376 cmd->num_args = nargs;
377 cmd->ni_required_args = ni_nrequired;
378 cmd->i_required_args = i_nrequired;
379 cmd->args = nargs ? xmalloc(nargs * sizeof(struct argspec)) : NULL;
380
381 /* Fill cmd->args */
382 va_start(va, ni_nrequired);
383 for (i = 0; i < nargs; i++) {
384 cmd->args[i].prompt = va_arg(va, char *);
385 cmd->args[i].type = va_arg(va, int);
386 }
387 va_end(va);
388
389 list_add(&cmd->node, &user_commands);
390}
391
392static void
393user_command_free(struct user_command *cmd)
394{
395 if (cmd == NULL)
396 return;
397
398 free(cmd->args);
399 free(cmd);
400}
401
402void
403init_user_commands(void)
404{
405 /* @begin (tag required for genrpbindings) */
406 add_command("abort", cmd_abort, 0, 0, 0);
407 add_command("addhook", cmd_addhook, 2, 2, 2,
408 "Hook: ", arg_HOOK,
409 "Command: ", arg_COMMAND);
410 add_command("alias", cmd_alias, 2, 2, 2,
411 "Alias: ", arg_STRING,
412 "Command: ", arg_COMMAND);
413 add_command("banish", cmd_banish, 0, 0, 0);
414 add_command("chdir", cmd_chdir, 1, 0, 0,
415 "Dir: ", arg_REST);
416 add_command("clrunmanaged", cmd_clrunmanaged, 0, 0, 0);
417 add_command("cnext", cmd_cnext, 0, 0, 0);
418 add_command("colon", cmd_colon, 1, 0, 0,
419 "", arg_REST);
420 add_command("commands", cmd_commands, 0, 0, 0);
421 add_command("cother", cmd_cother, 0, 0, 0);
422 add_command("cprev", cmd_cprev, 0, 0, 0);
423 add_command("curframe", cmd_curframe, 0, 0, 0);
424 add_command("dedicate", cmd_dedicate, 1, 0, 0,
425 "", arg_NUMBER);
426 add_command("definekey", cmd_definekey, 3, 3, 3,
427 "Keymap: ", arg_KEYMAP,
428 "Key: ", arg_KEY,
429 "Command: ", arg_COMMAND);
430 add_command("delete", cmd_delete, 0, 0, 0);
431 add_command("delkmap", cmd_delkmap, 1, 1, 1,
432 "Keymap: ", arg_KEYMAP);
433 add_command("describekey", cmd_describekey, 1, 1, 1,
434 "Keymap: ", arg_KEYMAP);
435 add_command("echo", cmd_echo, 1, 1, 1,
436 "Echo: ", arg_RAW);
437 add_command("escape", cmd_escape, 1, 1, 1,
438 "Key: ", arg_KEY);
439 add_command("exchangedown", cmd_exchangedown, 0, 0, 0);
440 add_command("exchangeleft", cmd_exchangeleft, 0, 0, 0);
441 add_command("exchangeright", cmd_exchangeright, 0, 0, 0);
442 add_command("exchangeup", cmd_exchangeup, 0, 0, 0);
443 add_command("exec", cmd_exec, 1, 1, 1,
444 "/bin/sh -c ", arg_SHELLCMD);
445 add_command("execa", cmd_execa, 1, 1, 1,
446 "/bin/sh -c ", arg_SHELLCMD);
447 add_command("execf", cmd_execf, 2, 2, 2,
448 "frame to execute in:", arg_FRAME,
449 "/bin/sh -c ", arg_SHELLCMD);
450 add_command("execv", cmd_execv, 2, 2, 2,
451 "vscreen to execute in:", arg_VSCREEN,
452 "/bin/sh -c ", arg_SHELLCMD);
453 add_command("execw", cmd_execw, 1, 1, 1,
454 "/bin/sh -c ", arg_SHELLCMD);
455 add_command("fdump", cmd_fdump, 1, 0, 0,
456 "", arg_NUMBER);
457 add_command("focus", cmd_nextframe, 0, 0, 0);
458 add_command("focusdown", cmd_focusdown, 0, 0, 0);
459 add_command("focusprev", cmd_prevframe, 0, 0, 0);
460 add_command("focuslast", cmd_focuslast, 0, 0, 0);
461 add_command("focusleft", cmd_focusleft, 0, 0, 0);
462 add_command("focusright", cmd_focusright, 0, 0, 0);
463 add_command("focusup", cmd_focusup, 0, 0, 0);
464 add_command("frestore", cmd_frestore, 1, 1, 1,
465 "Frames: ", arg_REST);
466 add_command("fselect", cmd_fselect, 1, 1, 1,
467 "", arg_FRAME);
468 add_command("getenv", cmd_getenv, 1, 1, 1,
469 "Variable: ", arg_STRING);
470 add_command("getsel", cmd_getsel, 0, 0, 0);
471 add_command("gravity", cmd_gravity, 1, 0, 0,
472 "Gravity: ", arg_GRAVITY);
473 add_command("help", cmd_help, 1, 0, 0,
474 "Keymap: ", arg_KEYMAP);
475 add_command("hsplit", cmd_hsplit, 1, 0, 0,
476 "Split: ", arg_STRING);
477 add_command("inext", cmd_inext, 0, 0, 0);
478 add_command("info", cmd_info, 1, 0, 0,
479 "Format: ", arg_REST);
480 add_command("iother", cmd_iother, 0, 0, 0);
481 add_command("iprev", cmd_iprev, 0, 0, 0);
482 add_command("kill", cmd_kill, 0, 0, 0);
483 add_command("lastmsg", cmd_lastmsg, 0, 0, 0);
484 add_command("link", cmd_link, 2, 1, 1,
485 "Key: ", arg_STRING,
486 "Keymap: ", arg_KEYMAP);
487 add_command("listhook", cmd_listhook, 1, 1, 1,
488 "Hook: ", arg_HOOK);
489 add_command("meta", cmd_meta, 1, 0, 0,
490 "key: ", arg_KEY);
491 add_command("newkmap", cmd_newkmap, 1, 1, 1,
492 "Keymap: ", arg_STRING);
493 add_command("next", cmd_next, 0, 0, 0);
494 add_command("nextscreen", cmd_nextscreen, 0, 0, 0);
495 add_command("number", cmd_number, 2, 1, 1,
496 "Number: ", arg_NUMBER,
497 "Number: ", arg_NUMBER);
498 add_command("only", cmd_only, 0, 0, 0);
499 add_command("other", cmd_other, 0, 0, 0);
500 add_command("prev", cmd_prev, 0, 0, 0);
501 add_command("prevscreen", cmd_prevscreen, 0, 0, 0);
502 add_command("prompt", cmd_prompt, 1, 0, 0,
503 "", arg_REST);
504 add_command("putsel", cmd_putsel, 1, 1, 1,
505 "Text: ", arg_RAW);
506 add_command("quit", cmd_quit, 0, 0, 0);
507 add_command("ratinfo", cmd_ratinfo, 0, 0, 0);
508 add_command("ratrelinfo", cmd_ratrelinfo, 0, 0, 0);
509 add_command("banishrel", cmd_banishrel, 0, 0, 0);
510 add_command("ratwarp", cmd_ratwarp, 2, 2, 2,
511 "X: ", arg_NUMBER,
512 "Y: ", arg_NUMBER);
513 add_command("ratrelwarp", cmd_ratrelwarp, 2, 2, 2,
514 "X: ", arg_NUMBER,
515 "Y: ", arg_NUMBER);
516 add_command("ratclick", cmd_ratclick, 1, 0, 0,
517 "Button: ", arg_NUMBER);
518 add_command("rathold", cmd_rathold, 2, 1, 1,
519 "State: ", arg_STRING,
520 "Button: ", arg_NUMBER);
521 add_command("readkey", cmd_readkey, 1, 1, 1,
522 "Keymap: ", arg_KEYMAP);
523 add_command("redisplay", cmd_redisplay, 0, 0, 0);
524 add_command("redo", cmd_redo, 0, 0, 0);
525 add_command("remhook", cmd_remhook, 2, 2, 2,
526 "Hook: ", arg_HOOK,
527 "Command: ", arg_COMMAND);
528 add_command("remove", cmd_remove, 0, 0, 0);
529 add_command("resize", cmd_resize, 2, 0, 2,
530 "", arg_NUMBER,
531 "", arg_NUMBER);
532 add_command("restart", cmd_restart, 0, 0, 0);
533 add_command("sdump", cmd_sdump, 0, 0, 0);
534 add_command("select", cmd_select, 1, 0, 1,
535 "Select window: ", arg_REST);
536 add_command("set", cmd_set, 2, 0, 0,
537 "", arg_VARIABLE,
538 "", arg_REST);
539 add_command("setenv", cmd_setenv, 2, 2, 2,
540 "Variable: ", arg_STRING,
541 "Value: ", arg_REST);
542 add_command("sfdump", cmd_sfdump, 0, 0, 0);
543 add_command("sfrestore", cmd_sfrestore, 1, 1, 1,
544 "Frames: ", arg_REST);
545 add_command("shrink", cmd_shrink, 0, 0, 0);
546 add_command("source", cmd_source, 1, 1, 1,
547 "File: ", arg_REST);
548 add_command("smove", cmd_smove, 1, 1, 1,
549 "Screen: ", arg_NUMBER);
550 add_command("sselect", cmd_sselect, 1, 1, 1,
551 "Screen: ", arg_NUMBER);
552 add_command("stick", cmd_stick, 0, 0, 0);
553 add_command("swap", cmd_swap, 2, 1, 1,
554 "destination frame: ", arg_FRAME,
555 "source frame: ", arg_FRAME);
556 add_command("title", cmd_rename, 1, 1, 1,
557 "Set window's title to: ", arg_REST);
558 add_command("unalias", cmd_unalias, 1, 1, 1,
559 "Alias: ", arg_STRING);
560 add_command("undefinekey", cmd_undefinekey, 2, 2, 2,
561 "Keymap: ", arg_KEYMAP,
562 "Key: ", arg_KEY);
563 add_command("undo", cmd_undo, 0, 0, 0);
564 add_command("unmanage", cmd_unmanage, 1, 1, 0,
565 "Unmanage: ", arg_REST);
566 add_command("unsetenv", cmd_unsetenv, 1, 1, 1,
567 "Variable: ", arg_STRING);
568 add_command("unstick", cmd_unstick, 0, 0, 0);
569 add_command("verbexec", cmd_verbexec, 1, 1, 1,
570 "/bin/sh -c ", arg_SHELLCMD);
571 add_command("version", cmd_version, 0, 0, 0);
572 add_command("vmove", cmd_vmove, 1, 1, 1,
573 "Virtual Screen: ", arg_VSCREEN);
574 add_command("vnext", cmd_vnext, 0, 0, 0);
575 add_command("vother", cmd_vother, 0, 0, 0);
576 add_command("vprev", cmd_vprev, 0, 0, 0);
577 add_command("vrename", cmd_vrename, 1, 1, 1,
578 "Change virtual screen name to: ", arg_REST);
579 add_command("vscreens", cmd_vscreens, 0, 0, 0);
580 add_command("vselect", cmd_vselect, 1, 1, 1,
581 "Virtual Screen: ", arg_VSCREEN);
582 add_command("vsplit", cmd_vsplit, 1, 0, 0,
583 "Split: ", arg_STRING);
584 add_command("windows", cmd_windows, 1, 0, 0,
585 "", arg_REST);
586 /* @end (tag required for genrpbindings) */
587
588 init_set_vars();
589}
590
591/* Delete all entries in the redo list. */
592static void
593clear_frame_redos(void)
594{
595 rp_frame_undo *cur;
596 struct list_head *tmp, *iter;
597
598 list_for_each_safe_entry(cur, iter, tmp, &rp_frame_redos, node) {
599 free(cur->frames);
600 list_del(&(cur->node));
601 }
602}
603
604static void
605del_frame_undo(rp_frame_undo *u)
606{
607 if (!u)
608 return;
609
610 free(u->frames);
611 list_del(&(u->node));
612 free(u);
613}
614
615void
616clear_frame_undos(void)
617{
618 while (list_size(&rp_frame_undos) > 0) {
619 /* Delete the oldest node */
620 rp_frame_undo *cur;
621 list_last(cur, &rp_frame_undos, node);
622 del_frame_undo(cur);
623 }
624}
625
626static void
627push_frame_undo(rp_vscreen *vscreen)
628{
629 rp_frame_undo *cur;
630
631 if (list_size(&rp_frame_undos) > defaults.maxundos) {
632 /* Delete the oldest node */
633 list_last(cur, &rp_frame_undos, node);
634 del_frame_undo(cur);
635 }
636 cur = xmalloc(sizeof(rp_frame_undo));
637 cur->frames = fdump(vscreen);
638 cur->vscreen = vscreen;
639 list_add(&cur->node, &rp_frame_undos);
640 /*
641 * Since we're creating new frames the redo list is now invalid, so
642 * clear it.
643 */
644 clear_frame_redos();
645}
646
647static rp_frame_undo *
648pop_frame_list(struct list_head *undo_list, struct list_head *redo_list)
649{
650 rp_vscreen *vscreen = rp_current_vscreen;
651 rp_frame_undo *first, *new;
652
653 /* Is there something to restore? */
654 list_first(first, undo_list, node);
655 if (!first)
656 return NULL;
657
658 /* First save the current layout into undo */
659 new = xmalloc(sizeof(rp_frame_undo));
660 new->frames = fdump(vscreen);
661 new->vscreen = vscreen;
662 list_add(&new->node, redo_list);
663
664 list_del(&(first->node));
665 return first;
666}
667
668/* Pop the head of the frame undo list off and put it in the redo list. */
669static rp_frame_undo *
670pop_frame_undo(void)
671{
672 return pop_frame_list(&rp_frame_undos, &rp_frame_redos);
673}
674
675/* Pop the head of the frame redo list off and put it in the undo list. */
676static rp_frame_undo *
677pop_frame_redo(void)
678{
679 return pop_frame_list(&rp_frame_redos, &rp_frame_undos);
680}
681
682rp_action *
683find_keybinding_by_action(char *action, rp_keymap *map)
684{
685 int i;
686
687 for (i = 0; i < map->actions_last; i++) {
688 if (!strcmp(map->actions[i].data, action)) {
689 return &map->actions[i];
690 }
691 }
692
693 return NULL;
694}
695
696rp_action *
697find_keybinding(KeySym keysym, unsigned int state, rp_keymap *map)
698{
699 int i;
700 for (i = 0; i < map->actions_last; i++) {
701 if (map->actions[i].key == keysym &&
702 map->actions[i].state == state)
703 return &map->actions[i];
704 }
705 return NULL;
706}
707
708static char *
709find_command_by_keydesc(char *desc, rp_keymap *map)
710{
711 int i = 0;
712 char *keysym_name;
713
714 while (i < map->actions_last) {
715 keysym_name = keysym_to_string(map->actions[i].key,
716 map->actions[i].state);
717 if (!strcmp(keysym_name, desc)) {
718 free(keysym_name);
719 return map->actions[i].data;
720 }
721 free(keysym_name);
722 i++;
723 }
724
725 return NULL;
726}
727
728static char *
729resolve_command_from_keydesc(char *desc, int depth, rp_keymap *map)
730{
731 char *cmd, *c;
732
733 c = find_command_by_keydesc(desc, map);
734 if (!c)
735 return NULL;
736
737 /* is it a link? */
738 if (strncmp(c, "link", 4) || depth > MAX_LINK_DEPTH)
739 /* it is not */
740 return c;
741
742 cmd = resolve_command_from_keydesc(&c[5], depth + 1, map);
743 return (cmd != NULL) ? cmd : c;
744}
745
746static void
747add_keybinding(KeySym keysym, int state, char *cmd, rp_keymap *map)
748{
749 if (map->actions_last >= map->actions_size) {
750 /* double the key table size */
751 map->actions_size *= 2;
752 map->actions = xrealloc(map->actions,
753 sizeof(rp_action) * map->actions_size);
754 PRINT_DEBUG(("realloc()ed key_table %d\n", map->actions_size));
755 }
756 map->actions[map->actions_last].key = keysym;
757 map->actions[map->actions_last].state = state;
758 /* free this on shutdown, or re/unbinding */
759 map->actions[map->actions_last].data = xstrdup(cmd);
760
761 map->actions_last++;
762}
763
764static void
765replace_keybinding(rp_action * key_action, char *newcmd)
766{
767 free(key_action->data);
768 key_action->data = xstrdup(newcmd);
769}
770
771static int
772remove_keybinding(KeySym keysym, unsigned int state, rp_keymap *map)
773{
774 int i;
775 int found = -1;
776
777 for (i = 0; i < map->actions_last; i++) {
778 if (map->actions[i].key == keysym &&
779 map->actions[i].state == state) {
780 found = i;
781 break;
782 }
783 }
784
785 if (found >= 0) {
786 free(map->actions[found].data);
787
788 memmove(&map->actions[found], &map->actions[found + 1],
789 sizeof(rp_action) * (map->actions_last - found - 1));
790 map->actions_last--;
791
792 return 1;
793 }
794
795 return 0;
796}
797
798static rp_keymap *
799keymap_new(char *name)
800{
801 rp_keymap *map;
802
803 /* All keymaps must have a name. */
804 if (name == NULL)
805 return NULL;
806
807 map = xmalloc(sizeof(rp_keymap));
808 map->name = xstrdup(name);
809 map->actions_size = 1;
810 map->actions = xmalloc(sizeof(rp_action) * map->actions_size);
811 map->actions_last = 0;
812
813 return map;
814}
815
816rp_keymap *
817find_keymap(char *name)
818{
819 rp_keymap *cur;
820
821 list_for_each_entry(cur, &rp_keymaps, node) {
822 if (!strcmp(name, cur->name)) {
823 return cur;
824 }
825 }
826
827 return NULL;
828}
829
830/*
831 * Search the alias table for a match. If a match is found, return its index
832 * into the table. Otherwise return -1.
833 */
834static int
835find_alias_index(char *name)
836{
837 int i;
838
839 for (i = 0; i < alias_list_last; i++)
840 if (!strcmp(name, alias_list[i].name))
841 return i;
842
843 return -1;
844}
845
846static void
847add_alias(char *name, char *alias)
848{
849 int i;
850
851 /* Are we updating an existing alias, or creating a new one? */
852 i = find_alias_index(name);
853 if (i >= 0) {
854 free(alias_list[i].alias);
855 alias_list[i].alias = xstrdup(alias);
856 } else {
857 if (alias_list_last >= alias_list_size) {
858 alias_list_size *= 2;
859 alias_list = xrealloc(alias_list,
860 sizeof(alias_t) * alias_list_size);
861 }
862 alias_list[alias_list_last].name = xstrdup(name);
863 alias_list[alias_list_last].alias = xstrdup(alias);
864 alias_list_last++;
865 }
866}
867
868void
869initialize_default_keybindings(void)
870{
871 rp_keymap *map, *top;
872
873 map = keymap_new(ROOT_KEYMAP);
874 list_add(&map->node, &rp_keymaps);
875
876 top = keymap_new(defaults.top_kmap);
877 list_add(&top->node, &rp_keymaps);
878
879 /* Initialize the alias list. */
880 alias_list_size = 5;
881 alias_list_last = 0;
882 alias_list = xmalloc(sizeof(alias_t) * alias_list_size);
883
884 prefix_key.sym = KEY_PREFIX;
885 prefix_key.state = MODIFIER_PREFIX;
886
887 /* Add the prefix key to the top-level map. */
888 add_keybinding(prefix_key.sym, prefix_key.state, "readkey " ROOT_KEYMAP,
889 top);
890
891 add_keybinding(prefix_key.sym, prefix_key.state, "other", map);
892 add_keybinding(prefix_key.sym, 0, "meta", map);
893 add_keybinding(XK_g, RP_CONTROL_MASK, "abort", map);
894 add_keybinding(XK_0, 0, "select 0", map);
895 add_keybinding(XK_1, 0, "select 1", map);
896 add_keybinding(XK_2, 0, "select 2", map);
897 add_keybinding(XK_3, 0, "select 3", map);
898 add_keybinding(XK_4, 0, "select 4", map);
899 add_keybinding(XK_5, 0, "select 5", map);
900 add_keybinding(XK_6, 0, "select 6", map);
901 add_keybinding(XK_7, 0, "select 7", map);
902 add_keybinding(XK_8, 0, "select 8", map);
903 add_keybinding(XK_9, 0, "select 9", map);
904 add_keybinding(XK_minus, 0, "select -", map);
905 add_keybinding(XK_A, 0, "title", map);
906 add_keybinding(XK_A, RP_CONTROL_MASK, "title", map);
907 add_keybinding(XK_K, 0, "kill", map);
908 add_keybinding(XK_K, RP_CONTROL_MASK, "kill", map);
909 add_keybinding(XK_Return, 0, "next", map);
910 add_keybinding(XK_Return, RP_CONTROL_MASK, "next", map);
911 add_keybinding(XK_b, 0, "banish", map);
912 add_keybinding(XK_b, RP_CONTROL_MASK, "banish", map);
913 add_keybinding(XK_c, 0, "exec " TERM_PROG, map);
914 add_keybinding(XK_c, RP_CONTROL_MASK, "exec " TERM_PROG, map);
915 add_keybinding(XK_colon, 0, "colon", map);
916 add_keybinding(XK_exclam, 0, "exec", map);
917 add_keybinding(XK_exclam, RP_CONTROL_MASK,
918 "colon exec " TERM_PROG " -e ", map);
919 add_keybinding(XK_i, 0, "info", map);
920 add_keybinding(XK_i, RP_CONTROL_MASK, "info", map);
921 add_keybinding(XK_k, 0, "delete", map);
922 add_keybinding(XK_k, RP_CONTROL_MASK, "delete", map);
923 add_keybinding(XK_l, 0, "redisplay", map);
924 add_keybinding(XK_l, RP_CONTROL_MASK, "redisplay", map);
925 add_keybinding(XK_m, 0, "lastmsg", map);
926 add_keybinding(XK_m, RP_CONTROL_MASK, "lastmsg", map);
927 add_keybinding(XK_n, 0, "next", map);
928 add_keybinding(XK_n, RP_CONTROL_MASK, "next", map);
929 add_keybinding(XK_p, 0, "prev", map);
930 add_keybinding(XK_p, RP_CONTROL_MASK, "prev", map);
931 add_keybinding(XK_quoteright, 0, "select", map);
932 add_keybinding(XK_quoteright, RP_CONTROL_MASK, "select", map);
933 add_keybinding(XK_space, 0, "next", map);
934 add_keybinding(XK_space, RP_CONTROL_MASK, "next", map);
935 add_keybinding(XK_v, 0, "version", map);
936 add_keybinding(XK_v, RP_CONTROL_MASK, "version", map);
937 add_keybinding(XK_w, 0, "windows", map);
938 add_keybinding(XK_w, RP_CONTROL_MASK, "windows", map);
939 add_keybinding(XK_s, 0, "split", map);
940 add_keybinding(XK_s, RP_CONTROL_MASK, "split", map);
941 add_keybinding(XK_S, 0, "hsplit", map);
942 add_keybinding(XK_S, RP_CONTROL_MASK, "hsplit", map);
943 add_keybinding(XK_Tab, 0, "focus", map);
944 add_keybinding(XK_Tab, RP_META_MASK, "focuslast", map);
945 add_keybinding(XK_Left, RP_CONTROL_MASK, "exchangeleft", map);
946 add_keybinding(XK_Right, RP_CONTROL_MASK, "exchangeright", map);
947 add_keybinding(XK_Up, RP_CONTROL_MASK, "exchangeup", map);
948 add_keybinding(XK_Down, RP_CONTROL_MASK, "exchangedown", map);
949 add_keybinding(XK_Left, 0, "focusleft", map);
950 add_keybinding(XK_Right, 0, "focusright", map);
951 add_keybinding(XK_Up, 0, "focusup", map);
952 add_keybinding(XK_Down, 0, "focusdown", map);
953 add_keybinding(XK_Q, 0, "only", map);
954 add_keybinding(XK_R, 0, "remove", map);
955 add_keybinding(XK_f, 0, "fselect", map);
956 add_keybinding(XK_f, RP_CONTROL_MASK, "fselect", map);
957 add_keybinding(XK_F, 0, "curframe", map);
958 add_keybinding(XK_r, 0, "resize", map);
959 add_keybinding(XK_r, RP_CONTROL_MASK, "resize", map);
960 add_keybinding(XK_question, 0, "help " ROOT_KEYMAP, map);
961 add_keybinding(XK_underscore, RP_CONTROL_MASK, "undo", map);
962 add_keybinding(XK_u, 0, "undo", map);
963 add_keybinding(XK_u, RP_CONTROL_MASK, "undo", map);
964 add_keybinding(XK_U, 0, "redo", map);
965 add_keybinding(XK_x, 0, "swap", map);
966 add_keybinding(XK_x, RP_CONTROL_MASK, "swap", map);
967 add_keybinding(XK_N, 0, "nextscreen", map);
968 add_keybinding(XK_P, 0, "prevscreen", map);
969 add_keybinding(XK_F1, 0, "vselect 0", map);
970 add_keybinding(XK_F2, 0, "vselect 1", map);
971 add_keybinding(XK_F3, 0, "vselect 2", map);
972 add_keybinding(XK_F4, 0, "vselect 3", map);
973 add_keybinding(XK_F5, 0, "vselect 4", map);
974 add_keybinding(XK_F6, 0, "vselect 5", map);
975 add_keybinding(XK_F7, 0, "vselect 6", map);
976 add_keybinding(XK_F8, 0, "vselect 7", map);
977 add_keybinding(XK_F9, 0, "vselect 8", map);
978 add_keybinding(XK_F10, 0, "vselect 9", map);
979 add_keybinding(XK_F11, 0, "vselect 10", map);
980 add_keybinding(XK_F12, 0, "vselect 11", map);
981
982 add_alias("unbind", "undefinekey " ROOT_KEYMAP);
983 add_alias("bind", "definekey " ROOT_KEYMAP);
984 add_alias("split", "vsplit");
985}
986
987cmdret *
988cmdret_new(int success, char *fmt,...)
989{
990 cmdret *ret;
991 va_list ap;
992
993 ret = xmalloc(sizeof(cmdret));
994 ret->success = success;
995
996 if (fmt) {
997 va_start(ap, fmt);
998 ret->output = xvsprintf(fmt, ap);
999 va_end(ap);
1000 } else
1001 ret->output = NULL;
1002
1003 return ret;
1004}
1005
1006void
1007cmdret_free(cmdret *ret)
1008{
1009 free(ret->output);
1010 free(ret);
1011}
1012
1013static void
1014keymap_free(rp_keymap *map)
1015{
1016 int i;
1017
1018 /* Free the data in the actions. */
1019 for (i = 0; i < map->actions_last; i++) {
1020 free(map->actions[i].data);
1021 }
1022
1023 /* Free the map data. */
1024 free(map->actions);
1025 free(map->name);
1026
1027 /* ...and the map itself. */
1028 free(map);
1029}
1030
1031void
1032free_keymaps(void)
1033{
1034 rp_keymap *cur;
1035 struct list_head *tmp, *iter;
1036
1037 list_for_each_safe_entry(cur, iter, tmp, &rp_keymaps, node) {
1038 list_del(&cur->node);
1039 keymap_free(cur);
1040 }
1041}
1042
1043void
1044free_aliases(void)
1045{
1046 int i;
1047
1048 /* Free the alias data. */
1049 for (i = 0; i < alias_list_last; i++) {
1050 free(alias_list[i].name);
1051 free(alias_list[i].alias);
1052 }
1053
1054 /* Free the alias list. */
1055 free(alias_list);
1056}
1057
1058void
1059free_user_commands(void)
1060{
1061 struct user_command *cur;
1062 struct set_var *var;
1063 struct list_head *tmp, *iter;
1064
1065 list_for_each_safe_entry(cur, iter, tmp, &user_commands, node) {
1066 list_del(&cur->node);
1067 user_command_free(cur);
1068 }
1069 list_for_each_safe_entry(var, iter, tmp, &set_vars, node) {
1070 list_del(&var->node);
1071 set_var_free(var);
1072 }
1073}
1074
1075/*
1076 * return a KeySym from a string that contains either a hex value or an X
1077 * keysym description
1078 */
1079static int
1080string_to_keysym(char *str)
1081{
1082 int retval;
1083 int keysym;
1084
1085 retval = sscanf(str, "0x%x", &keysym);
1086
1087 if (!retval || retval == EOF)
1088 keysym = XStringToKeysym(str);
1089
1090 return keysym;
1091}
1092
1093static cmdret *
1094parse_keydesc(char *keydesc, struct rp_key *key)
1095{
1096 char *token, *next_token;
1097
1098 if (keydesc == NULL)
1099 return NULL;
1100
1101 key->state = 0;
1102 key->sym = 0;
1103
1104 if (!strchr(keydesc, '-')) {
1105 /* Its got no hyphens in it, so just grab the keysym */
1106 key->sym = string_to_keysym(keydesc);
1107
1108 if (key->sym == NoSymbol)
1109 return cmdret_new(RET_FAILURE,
1110 "parse_keydesc: Unknown key '%s'", keydesc);
1111 } else if (keydesc[strlen(keydesc) - 1] == '-') {
1112 /* A key description can't end in a -. */
1113 return cmdret_new(RET_FAILURE,
1114 "parse_keydesc: Can't parse key '%s'", keydesc);
1115 } else {
1116 /* Its got hyphens, so parse out the modifiers and keysym */
1117 char *copy;
1118
1119 copy = xstrdup(keydesc);
1120
1121 token = strtok(copy, "-");
1122 if (token == NULL) {
1123 /* It was nothing but hyphens */
1124 free(copy);
1125 return cmdret_new(RET_FAILURE,
1126 "parse_keydesc: Can't parse key '%s'", keydesc);
1127 }
1128 do {
1129 next_token = strtok(NULL, "-");
1130
1131 if (next_token == NULL) {
1132 /*
1133 * There is nothing more to parse and token
1134 * contains the keysym name.
1135 */
1136 key->sym = string_to_keysym(token);
1137
1138 if (key->sym == NoSymbol) {
1139 cmdret *ret = cmdret_new(RET_FAILURE,
1140 "parse_keydesc: Unknown key '%s'",
1141 token);
1142 free(copy);
1143 return ret;
1144 }
1145 } else {
1146 /*
1147 * Which modifier is it? Only accept modifiers
1148 * that are present. ie don't accept a hyper
1149 * modifier if the keymap has no hyper key.
1150 */
1151 if (!strcmp(token, "C")) {
1152 key->state |= RP_CONTROL_MASK;
1153 } else if (!strcmp(token, "M")) {
1154 key->state |= RP_META_MASK;
1155 } else if (!strcmp(token, "A")) {
1156 key->state |= RP_ALT_MASK;
1157 } else if (!strcmp(token, "S")) {
1158 key->state |= RP_SHIFT_MASK;
1159 } else if (!strcmp(token, "s")) {
1160 key->state |= RP_SUPER_MASK;
1161 } else if (!strcmp(token, "H")) {
1162 key->state |= RP_HYPER_MASK;
1163 } else {
1164 free(copy);
1165 return cmdret_new(RET_FAILURE,
1166 "parse_keydesc: Unknown modifier '%s'",
1167 token);
1168 }
1169 }
1170
1171 token = next_token;
1172 } while (next_token != NULL);
1173
1174 free(copy);
1175 }
1176
1177 /* Successfully parsed the key. */
1178 return NULL;
1179}
1180
1181static void
1182grab_rat(void)
1183{
1184 XGrabPointer(dpy, rp_current_screen->root, True, 0, GrabModeAsync,
1185 GrabModeAsync, None, rp_current_screen->rat, CurrentTime);
1186}
1187
1188static void
1189ungrab_rat(void)
1190{
1191 XUngrabPointer(dpy, CurrentTime);
1192}
1193
1194/* Unmanage window */
1195cmdret *
1196cmd_unmanage(int interactive, struct cmdarg **args)
1197{
1198 if (args[0] == NULL && !interactive) {
1199 cmdret *ret;
1200 char *s = list_unmanaged_windows();
1201
1202 if (s)
1203 ret = cmdret_new(RET_SUCCESS, "%s", s);
1204 else
1205 ret = cmdret_new(RET_SUCCESS, NULL);
1206
1207 free(s);
1208 return ret;
1209 }
1210
1211 if (!args[0])
1212 return cmdret_new(RET_FAILURE,
1213 "unmanage: at least one argument required");
1214
1215 add_unmanaged_window(ARG_STRING(0));
1216
1217 return cmdret_new(RET_SUCCESS, NULL);
1218}
1219
1220/* Clear the unmanaged window list */
1221cmdret *
1222cmd_clrunmanaged(int interactive, struct cmdarg **args)
1223{
1224 clear_unmanaged_list();
1225 return cmdret_new(RET_SUCCESS, NULL);
1226}
1227
1228cmdret *
1229cmd_undefinekey(int interactive, struct cmdarg **args)
1230{
1231 cmdret *ret = NULL;
1232 rp_keymap *map;
1233 struct rp_key *key;
1234
1235 map = ARG(0, keymap);
1236 key = ARG(1, key);
1237
1238 /*
1239 * If we're updating the top level map, we'll need to update the keys
1240 * grabbed.
1241 */
1242 if (map == find_keymap(defaults.top_kmap))
1243 ungrab_keys_all_wins();
1244
1245 /* If no comand is specified, then unbind the key. */
1246 if (!remove_keybinding(key->sym, key->state, map))
1247 ret = cmdret_new(RET_FAILURE,
1248 "undefinekey: key '%s' is not bound", ARG_STRING(1));
1249
1250 /* Update the grabbed keys. */
1251 if (map == find_keymap(defaults.top_kmap))
1252 grab_keys_all_wins();
1253 XSync(dpy, False);
1254
1255 if (ret)
1256 return ret;
1257
1258 return cmdret_new(RET_SUCCESS, NULL);
1259}
1260
1261cmdret *
1262cmd_definekey(int interactive, struct cmdarg **args)
1263{
1264 cmdret *ret = NULL;
1265 rp_keymap *map;
1266 struct rp_key *key;
1267 char *cmd;
1268 rp_action *key_action;
1269
1270 map = ARG(0, keymap);
1271 key = ARG(1, key);
1272 cmd = ARG_STRING(2);
1273
1274 /*
1275 * If we're updating the top level map, we'll need to update the keys
1276 * grabbed.
1277 */
1278 if (map == find_keymap(defaults.top_kmap))
1279 ungrab_keys_all_wins();
1280
1281 if ((key_action = find_keybinding(key->sym, key->state, map)))
1282 replace_keybinding(key_action, cmd);
1283 else
1284 add_keybinding(key->sym, key->state, cmd, map);
1285
1286 /* Update the grabbed keys. */
1287 if (map == find_keymap(defaults.top_kmap))
1288 grab_keys_all_wins();
1289 XSync(dpy, False);
1290
1291 if (ret)
1292 return ret;
1293
1294 return cmdret_new(RET_SUCCESS, NULL);
1295}
1296
1297cmdret *
1298cmd_source(int interactive, struct cmdarg **args)
1299{
1300 FILE *fileptr;
1301
1302 if ((fileptr = fopen(ARG_STRING(0), "r")) == NULL)
1303 return cmdret_new(RET_FAILURE, "source: %s: %s", ARG_STRING(0),
1304 strerror(errno));
1305
1306 set_close_on_exec(fileno(fileptr));
1307 read_rc_file(fileptr);
1308 fclose(fileptr);
1309
1310 return cmdret_new(RET_SUCCESS, NULL);
1311}
1312
1313cmdret *
1314cmd_meta(int interactive, struct cmdarg **args)
1315{
1316 XEvent ev;
1317
1318 memset(&ev, 0, sizeof(ev));
1319 /*
1320 * Redundant with the line above, but points out that passing some
1321 * garbage time value trips up some clients
1322 */
1323 ev.xkey.time = CurrentTime;
1324
1325 if (current_window() == NULL)
1326 return cmdret_new(RET_FAILURE, NULL);
1327
1328 ev.xkey.type = KeyPress;
1329 ev.xkey.display = dpy;
1330 ev.xkey.window = current_window()->w;
1331
1332 if (args[0]) {
1333 struct rp_key key;
1334 cmdret *ret;
1335
1336 memset(&key, 0, sizeof(key));
1337 ret = parse_keydesc(ARG_STRING(0), &key);
1338 if (ret != NULL)
1339 return ret;
1340
1341 ev.xkey.state = rp_mask_to_x11_mask(key.state);
1342 ev.xkey.keycode = XKeysymToKeycode(dpy, key.sym);
1343 if (ev.xkey.keycode == NoSymbol)
1344 return cmdret_new(RET_FAILURE,
1345 "meta: Couldn't convert keysym to keycode");
1346 } else {
1347 ev.xkey.state = rp_mask_to_x11_mask(prefix_key.state);
1348 ev.xkey.keycode = XKeysymToKeycode(dpy, prefix_key.sym);
1349 }
1350
1351 XSendEvent(dpy, current_window()->w, False, KeyPressMask, &ev);
1352 XSync(dpy, False);
1353
1354 return cmdret_new(RET_SUCCESS, NULL);
1355}
1356
1357cmdret *
1358cmd_prev(int interactive, struct cmdarg **args)
1359{
1360 rp_window *cur, *win;
1361 cur = current_window();
1362 win = vscreen_prev_window(rp_current_vscreen, cur);
1363
1364 if (win)
1365 set_active_window(win);
1366 else if (cur)
1367 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
1368 else
1369 return cmdret_new(RET_FAILURE, "%s",
1370 MESSAGE_NO_MANAGED_WINDOWS);
1371
1372 return cmdret_new(RET_SUCCESS, NULL);
1373}
1374
1375cmdret *
1376cmd_prevframe(int interactive, struct cmdarg **args)
1377{
1378 rp_frame *frame;
1379
1380 frame = find_frame_prev(current_frame(rp_current_vscreen));
1381 if (!frame)
1382 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_FRAME);
1383
1384 set_active_frame(frame, 0);
1385
1386 return cmdret_new(RET_SUCCESS, NULL);
1387}
1388
1389cmdret *
1390cmd_next(int interactive, struct cmdarg **args)
1391{
1392 rp_window *cur, *win;
1393 cur = current_window();
1394 win = vscreen_next_window(rp_current_vscreen, cur);
1395
1396 if (win)
1397 set_active_window(win);
1398 else if (cur)
1399 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
1400 else
1401 return cmdret_new(RET_FAILURE, "%s",
1402 MESSAGE_NO_MANAGED_WINDOWS);
1403
1404 return cmdret_new(RET_SUCCESS, NULL);
1405}
1406
1407cmdret *
1408cmd_nextframe(int interactive, struct cmdarg **args)
1409{
1410 rp_frame *frame;
1411
1412 frame = find_frame_next(current_frame(rp_current_vscreen));
1413 if (!frame)
1414 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_FRAME);
1415
1416 set_active_frame(frame, 0);
1417
1418 return cmdret_new(RET_SUCCESS, NULL);
1419}
1420
1421cmdret *
1422cmd_other(int interactive, struct cmdarg **args)
1423{
1424 rp_window *w;
1425
1426 /* w = find_window_other (); */
1427 w = vscreen_last_window(rp_current_vscreen);
1428 if (!w)
1429 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
1430
1431 set_active_window_force(w);
1432
1433 return cmdret_new(RET_SUCCESS, NULL);
1434}
1435
1436/* Parse a positive or null number, returns -1 on failure. */
1437static int
1438string_to_positive_int(char *str)
1439{
1440 char *ep;
1441 long lval;
1442
1443 errno = 0;
1444 lval = strtol(str, &ep, 10);
1445 if (str[0] == '\0' || *ep != '\0')
1446 return -1;
1447 if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) ||
1448 (lval > INT_MAX || lval < 0))
1449 return -1;
1450
1451 return (int) lval;
1452}
1453
1454static struct list_head *
1455trivial_completions(char *str)
1456{
1457 struct list_head *list;
1458
1459 /* Initialize our list. */
1460 list = xmalloc(sizeof(struct list_head));
1461 INIT_LIST_HEAD(list);
1462
1463 return list;
1464}
1465
1466static struct list_head *
1467keymap_completions(char *str)
1468{
1469 rp_keymap *cur;
1470 struct list_head *list;
1471
1472 /* Initialize our list. */
1473 list = xmalloc(sizeof(struct list_head));
1474 INIT_LIST_HEAD(list);
1475
1476 list_for_each_entry(cur, &rp_keymaps, node) {
1477 struct sbuf *name;
1478
1479 name = sbuf_new(0);
1480 sbuf_copy(name, cur->name);
1481 list_add_tail(&name->node, list);
1482 }
1483
1484 return list;
1485}
1486
1487static struct list_head *
1488window_completions(char *str)
1489{
1490 rp_window_elem *cur;
1491 struct list_head *list;
1492
1493 /* Initialize our list. */
1494 list = xmalloc(sizeof(struct list_head));
1495 INIT_LIST_HEAD(list);
1496
1497 /* Gather the names of all the windows. */
1498 list_for_each_entry(cur, &rp_current_vscreen->mapped_windows, node) {
1499 struct sbuf *name;
1500
1501 name = sbuf_new(0);
1502 sbuf_copy(name, window_name(cur->win));
1503 list_add_tail(&name->node, list);
1504 }
1505
1506 return list;
1507}
1508
1509/* switch to window number or name */
1510cmdret *
1511cmd_select(int interactive, struct cmdarg **args)
1512{
1513 cmdret *ret = NULL;
1514 char *str;
1515 int n;
1516
1517 /*
1518 * FIXME: This is manually done because of the kinds of things select
1519 * accepts.
1520 */
1521 if (args[0] == NULL)
1522 str = get_more_input(MESSAGE_PROMPT_SWITCH_TO_WINDOW, "",
1523 hist_SELECT, SUBSTRING, window_completions);
1524 else
1525 str = xstrdup(ARG_STRING(0));
1526
1527 /* User aborted. */
1528 if (str == NULL)
1529 return cmdret_new(RET_FAILURE, NULL);
1530
1531 /* Only search if the string contains something to search for. */
1532 if (strlen(str) > 0) {
1533 if (strlen(str) == 1 && str[0] == '-') {
1534 blank_frame(current_frame(rp_current_vscreen));
1535 ret = cmdret_new(RET_SUCCESS, NULL);
1536 } else if ((n = string_to_positive_int(str)) >= 0) {
1537 /* try by number */
1538 rp_window_elem *elem = vscreen_find_window_by_number(
1539 rp_current_vscreen, n);
1540
1541 if (elem) {
1542 goto_window(elem->win);
1543 ret = cmdret_new(RET_SUCCESS, NULL);
1544 } else {
1545 if (interactive) {
1546 /* show the window list as feedback */
1547 show_bar(rp_current_screen,
1548 defaults.window_fmt);
1549 ret = cmdret_new(RET_SUCCESS, NULL);
1550 } else {
1551 ret = cmdret_new(RET_FAILURE,
1552 "select: unknown window number '%d'",
1553 n);
1554 }
1555 }
1556 } else {
1557 /* try by name */
1558 rp_window *win = find_window_name(str, 1);
1559
1560 if (!win)
1561 win = find_window_name(str, 0);
1562
1563 if (win) {
1564 goto_window(win);
1565 ret = cmdret_new(RET_SUCCESS, NULL);
1566 } else
1567 ret = cmdret_new(RET_FAILURE,
1568 "select: unknown window '%s'", str);
1569 }
1570 } else
1571 /* Silently fail, since the user didn't provide a window spec */
1572 ret = cmdret_new(RET_SUCCESS, NULL);
1573
1574 free(str);
1575
1576 return ret;
1577}
1578
1579cmdret *
1580cmd_rename(int interactive, struct cmdarg **args)
1581{
1582 if (current_window() == NULL)
1583 return cmdret_new(RET_FAILURE, NULL);
1584
1585 free(current_window()->user_name);
1586 current_window()->user_name = xstrdup(ARG_STRING(0));
1587 current_window()->named = 1;
1588 hook_run(&rp_title_changed_hook);
1589
1590 /* Update the program bar. */
1591 update_window_names(rp_current_screen, defaults.window_fmt);
1592
1593 return cmdret_new(RET_SUCCESS, NULL);
1594}
1595
1596cmdret *
1597cmd_delete(int interactive, struct cmdarg **args)
1598{
1599 XEvent ev;
1600 int status;
1601
1602 if (current_window() == NULL)
1603 return cmdret_new(RET_FAILURE, NULL);
1604
1605 ev.xclient.type = ClientMessage;
1606 ev.xclient.window = current_window()->w;
1607 ev.xclient.message_type = wm_protocols;
1608 ev.xclient.format = 32;
1609 ev.xclient.data.l[0] = wm_delete;
1610 ev.xclient.data.l[1] = CurrentTime;
1611
1612 status = XSendEvent(dpy, current_window()->w, False, 0, &ev);
1613 if (status == 0)
1614 PRINT_DEBUG(("Delete window failed\n"));
1615
1616 return cmdret_new(RET_SUCCESS, NULL);
1617}
1618
1619cmdret *
1620cmd_kill(int interactive, struct cmdarg **args)
1621{
1622 if (current_window() == NULL)
1623 return cmdret_new(RET_FAILURE, NULL);
1624
1625 if (XKillClient(dpy, current_window()->w) == BadValue)
1626 return cmdret_new(RET_FAILURE,
1627 "kill failed (got BadValue, this may be a bug)");
1628
1629 return cmdret_new(RET_SUCCESS, NULL);
1630}
1631
1632cmdret *
1633cmd_version(int interactive, struct cmdarg **args)
1634{
1635 return cmdret_new(RET_SUCCESS, "%s %s", PROGNAME, VERSION);
1636}
1637
1638static char *
1639frame_selector(unsigned int n)
1640{
1641 if (n < strlen(defaults.frame_selectors)) {
1642 return xsprintf(" %c ", defaults.frame_selectors[n]);
1643 } else {
1644 return xsprintf(" %d ", n);
1645 }
1646}
1647
1648/* Return true if ch is nth frame selector. */
1649static int
1650frame_selector_match(char ch)
1651{
1652 size_t i;
1653
1654 /* Is it in the frame selector string? */
1655 for (i = 0; i < strlen(defaults.frame_selectors); i++) {
1656 if (ch == defaults.frame_selectors[i])
1657 return i;
1658 }
1659
1660 /*
1661 * Maybe it's a number less than 9 and the frame selector doesn't
1662 * define that many selectors.
1663 */
1664 if (ch >= '0' && ch <= '9'
1665 && (size_t) (ch - '0') >= strlen(defaults.frame_selectors)) {
1666 return ch - '0';
1667 }
1668 return -1;
1669}
1670
1671static cmdret *
1672read_string(struct argspec *spec, struct sbuf *s, int history_id,
1673 completion_fn fn, struct cmdarg ** arg)
1674{
1675 char *input;
1676
1677 if (s)
1678 input = xstrdup(sbuf_get(s));
1679 else
1680 input = get_input(spec->prompt, history_id, fn);
1681
1682 if (input) {
1683 *arg = xmalloc(sizeof(struct cmdarg));
1684 (*arg)->type = spec->type;
1685 (*arg)->string = input;
1686 return NULL;
1687 }
1688
1689 *arg = NULL;
1690 return cmdret_new(RET_SUCCESS, NULL);
1691}
1692
1693static cmdret *
1694read_keymap(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
1695{
1696 char *input;
1697
1698 if (s)
1699 input = xstrdup(sbuf_get(s));
1700 else
1701 input = get_input(spec->prompt, hist_KEYMAP, keymap_completions);
1702
1703 if (input) {
1704 rp_keymap *map;
1705
1706 map = find_keymap(input);
1707 if (map == NULL) {
1708 cmdret *ret = cmdret_new(RET_FAILURE,
1709 "unknown keymap '%s'", input);
1710 free(input);
1711 return ret;
1712 }
1713
1714 *arg = xmalloc(sizeof(struct cmdarg));
1715 (*arg)->type = spec->type;
1716 (*arg)->arg.keymap = map;
1717 (*arg)->string = input;
1718
1719 return NULL;
1720 }
1721
1722 *arg = NULL;
1723 return cmdret_new(RET_SUCCESS, NULL);
1724}
1725
1726static cmdret *
1727read_keydesc(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
1728{
1729 char *input;
1730
1731 if (s)
1732 input = xstrdup(sbuf_get(s));
1733 else
1734 input = get_input(spec->prompt, hist_KEY, trivial_completions);
1735
1736 if (input) {
1737 cmdret *ret;
1738 struct rp_key *key = xmalloc(sizeof(struct rp_key));
1739
1740 ret = parse_keydesc(input, key);
1741 if (ret) {
1742 free(key);
1743 return ret;
1744 }
1745
1746 *arg = xmalloc(sizeof(struct cmdarg));
1747 (*arg)->type = spec->type;
1748 (*arg)->arg.key = key;
1749 (*arg)->string = input;
1750
1751 return NULL;
1752 }
1753
1754 *arg = NULL;
1755 return cmdret_new(RET_SUCCESS, NULL);
1756}
1757
1758static struct list_head *
1759vscreen_completions(char *str)
1760{
1761 struct list_head *list;
1762 rp_vscreen *cur;
1763
1764 /* Initialize our list. */
1765 list = xmalloc(sizeof(struct list_head));
1766 INIT_LIST_HEAD(list);
1767
1768 /* Grab all the vscreen names. */
1769 list_for_each_entry(cur, &(rp_current_screen)->vscreens, node) {
1770 struct sbuf *s;
1771
1772 s = sbuf_new(0);
1773 if (cur->name) {
1774 sbuf_copy(s, cur->name);
1775 } else {
1776 sbuf_printf(s, "%d", cur->number);
1777 }
1778
1779 list_add_tail(&s->node, list);
1780 }
1781
1782 return list;
1783}
1784
1785static struct list_head *
1786colon_completions(char *str)
1787{
1788 int i;
1789 struct user_command *uc;
1790 struct sbuf *s;
1791 struct list_head *list;
1792
1793 /* Initialize our list. */
1794 list = xmalloc(sizeof(struct list_head));
1795 INIT_LIST_HEAD(list);
1796
1797 /* Put all the aliases in our list. */
1798 for (i = 0; i < alias_list_last; ++i) {
1799 s = sbuf_new(0);
1800 sbuf_copy(s, alias_list[i].name);
1801 /*
1802 * The space is so when the user completes a space is
1803 * conveniently inserted after the command.
1804 */
1805 sbuf_concat(s, " ");
1806 list_add_tail(&s->node, list);
1807 }
1808
1809 /* Put all the commands in our list. */
1810 list_for_each_entry(uc, &user_commands, node) {
1811 s = sbuf_new(0);
1812 sbuf_copy(s, uc->name);
1813 /*
1814 * The space is so when the user completes a space is
1815 * conveniently inserted after the command.
1816 */
1817 sbuf_concat(s, " ");
1818 list_add_tail(&s->node, list);
1819 }
1820
1821 return list;
1822}
1823
1824static cmdret *
1825read_command(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
1826{
1827 return read_string(spec, s, hist_COMMAND, colon_completions, arg);
1828}
1829
1830static struct list_head *
1831exec_completions(char *str)
1832{
1833 size_t n = 256;
1834 char *partial;
1835 struct sbuf *line;
1836 FILE *file;
1837 struct list_head *head;
1838 char *completion_string;
1839
1840 /* Initialize our list. */
1841 head = xmalloc(sizeof(struct list_head));
1842 INIT_LIST_HEAD(head);
1843
1844 /* FIXME: A Bash dependancy?? */
1845 completion_string = xsprintf("bash -c \"compgen -ac %s|sort\"", str);
1846 file = popen(completion_string, "r");
1847 free(completion_string);
1848 if (!file) {
1849 warn("popen failed");
1850 return head;
1851 }
1852 partial = xmalloc(n);
1853
1854 /*
1855 * Read data from the file, split it into lines and store it in a list.
1856 */
1857 line = sbuf_new(0);
1858 while (fgets(partial, n, file) != NULL) {
1859 /* Read a chunk from the file into our line accumulator. */
1860 sbuf_concat(line, partial);
1861
1862 if (feof(file) ||
1863 (*(sbuf_get(line) + strlen(sbuf_get(line)) - 1) == '\n')) {
1864 char *s;
1865 struct sbuf *elem;
1866
1867 s = sbuf_get(line);
1868
1869 /* Frob the newline into */
1870 if (*(s + strlen(s) - 1) == '\n')
1871 *(s + strlen(s) - 1) = '\0';
1872
1873 /* Add our line to the list. */
1874 elem = sbuf_new(0);
1875 sbuf_copy(elem, s);
1876 list_add_tail(&elem->node, head);
1877
1878 sbuf_clear(line);
1879 }
1880 }
1881
1882 sbuf_free(line);
1883
1884 free(partial);
1885 pclose(file);
1886
1887 return head;
1888}
1889
1890static cmdret *
1891read_shellcmd(struct argspec *spec, struct sbuf *s, struct cmdarg **arg,
1892 const char *command_name)
1893{
1894 cmdret *ret;
1895
1896 ret = read_string(spec, s, hist_SHELLCMD, exec_completions, arg);
1897 if (command_name && !s && !ret && (*arg)->string) {
1898 /* store for command history */
1899 struct sbuf *buf;
1900
1901 buf = sbuf_new(0);
1902 sbuf_printf(buf, "%s %s", command_name, (*arg)->string);
1903 history_add(hist_COMMAND, sbuf_get(buf));
1904 sbuf_free(buf);
1905 }
1906
1907 return ret;
1908}
1909
1910static cmdret *
1911read_frame(struct sbuf *s, struct cmdarg **arg)
1912{
1913 rp_frame *frame;
1914 XSetWindowAttributes attr;
1915 int fnum = -1;
1916 KeySym c;
1917 char keysym_buf[513];
1918 int keysym_bufsize = sizeof(keysym_buf);
1919 unsigned int mod;
1920 Window *wins;
1921 int i = 0;
1922 rp_frame *cur_frame;
1923 rp_screen *cur_screen = rp_current_screen;
1924 int frames;
1925
1926 if (s == NULL) {
1927 frames = num_frames(rp_current_vscreen);
1928 wins = xmalloc(sizeof(Window) * frames);
1929
1930 /*
1931 * Loop through each frame and display its number in its top
1932 * left corner.
1933 */
1934 attr.border_pixel = rp_glob_screen.fgcolor;
1935 attr.background_pixel = rp_glob_screen.bgcolor;
1936 attr.override_redirect = True;
1937
1938 list_for_each_entry(cur_frame, &rp_current_vscreen->frames,
1939 node) {
1940 int width, height;
1941 char *num;
1942
1943 /*
1944 * Create the string to be displayed in the
1945 * window and determine the height and width of
1946 * the window.
1947 */
1948 num = frame_selector(cur_frame->number);
1949 width = defaults.bar_x_padding * 2 +
1950 rp_text_width(cur_screen, num, -1, NULL);
1951 height = (FONT_HEIGHT(cur_screen) +
1952 defaults.bar_y_padding * 2);
1953
1954 /* Create and map the window. */
1955 wins[i] = XCreateWindow(dpy, cur_screen->root,
1956 cur_frame->x,
1957 cur_frame->y, width,
1958 height, defaults.bar_border_width,
1959 CopyFromParent, CopyFromParent,
1960 CopyFromParent,
1961 CWOverrideRedirect|CWBorderPixel|CWBackPixel,
1962 &attr);
1963 XMapWindow(dpy, wins[i]);
1964 XClearWindow(dpy, wins[i]);
1965
1966 /* Display the frame's number inside the window. */
1967 rp_draw_string(cur_screen, wins[i], STYLE_NORMAL,
1968 defaults.bar_x_padding,
1969 defaults.bar_y_padding +
1970 FONT_ASCENT(cur_screen), num, -1,
1971 NULL, NULL);
1972
1973 free(num);
1974 i++;
1975 }
1976 XSync(dpy, False);
1977
1978 /* Read a key. */
1979 read_single_key(&c, &mod, keysym_buf, keysym_bufsize);
1980
1981 /* Destroy our number windows and free the array. */
1982 for (i = 0; i < frames; i++)
1983 XDestroyWindow(dpy, wins[i]);
1984
1985 free(wins);
1986
1987 /* FIXME: We only handle one character long keysym names. */
1988 if (strlen(keysym_buf) == 1) {
1989 fnum = frame_selector_match(keysym_buf[0]);
1990 if (fnum == -1)
1991 return cmdret_new(RET_FAILURE,
1992 "unknown frame selector `%s'", keysym_buf);
1993 } else {
1994 return cmdret_new(RET_FAILURE,
1995 "frame selector too long `%s'", keysym_buf);
1996 }
1997 } else {
1998 fnum = string_to_positive_int(sbuf_get(s));
1999 if (fnum == -1)
2000 return cmdret_new(RET_FAILURE,
2001 "invalid frame selector `%s', negative or too big",
2002 sbuf_get(s));
2003 }
2004
2005 /*
2006 * Now that we have a frame number to go to, let's try to jump to it.
2007 */
2008 frame = find_frame_number(rp_current_vscreen, fnum);
2009 if (frame) {
2010 /*
2011 * We have to return a string, because commands get lists of
2012 * strings. Sucky, yes. The command is simply going to parse
2013 * it back into an rp_frame.
2014 */
2015 *arg = xmalloc(sizeof(struct cmdarg));
2016 (*arg)->type = arg_FRAME;
2017 (*arg)->string = NULL;
2018 (*arg)->arg.frame = frame;
2019 return NULL;
2020 }
2021
2022 return cmdret_new(RET_FAILURE, "frame not found");
2023}
2024
2025static cmdret *
2026read_window(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
2027{
2028 rp_window *win = NULL;
2029 char *name;
2030 int n;
2031
2032 if (s)
2033 name = xstrdup(sbuf_get(s));
2034 else
2035 name = get_input(spec->prompt, hist_WINDOW, window_completions);
2036
2037 if (name) {
2038 /* try by number */
2039 if ((n = string_to_positive_int(name)) >= 0) {
2040 rp_window_elem *elem = vscreen_find_window_by_number(
2041 rp_current_vscreen, n);
2042 if (elem)
2043 win = elem->win;
2044 } else {
2045 /* try by name */
2046 win = find_window_name(name, 1);
2047 if (win == NULL)
2048 win = find_window_name(name, 0);
2049 }
2050
2051 if (win) {
2052 *arg = xmalloc(sizeof(struct cmdarg));
2053 (*arg)->type = arg_WINDOW;
2054 (*arg)->arg.win = win;
2055 (*arg)->string = name;
2056 return NULL;
2057 }
2058
2059 free(name);
2060 *arg = NULL;
2061 return cmdret_new(RET_SUCCESS, NULL);
2062 }
2063
2064 /* user abort. */
2065 *arg = NULL;
2066 return cmdret_new(RET_SUCCESS, NULL);
2067}
2068
2069static int
2070parse_wingravity(char *data)
2071{
2072 int ret = -1;
2073
2074 if (!strcasecmp(data, "northwest") || !strcasecmp(data, "nw") || !strcmp(data, "7"))
2075 ret = NorthWestGravity;
2076 if (!strcasecmp(data, "north") || !strcasecmp(data, "n") || !strcmp(data, "8"))
2077 ret = NorthGravity;
2078 if (!strcasecmp(data, "northeast") || !strcasecmp(data, "ne") || !strcmp(data, "9"))
2079 ret = NorthEastGravity;
2080 if (!strcasecmp(data, "west") || !strcasecmp(data, "w") || !strcmp(data, "4"))
2081 ret = WestGravity;
2082 if (!strcasecmp(data, "center") || !strcasecmp(data, "c") || !strcmp(data, "5"))
2083 ret = CenterGravity;
2084 if (!strcasecmp(data, "east") || !strcasecmp(data, "e") || !strcmp(data, "6"))
2085 ret = EastGravity;
2086 if (!strcasecmp(data, "southwest") || !strcasecmp(data, "sw") || !strcmp(data, "1"))
2087 ret = SouthWestGravity;
2088 if (!strcasecmp(data, "south") || !strcasecmp(data, "s") || !strcmp(data, "2"))
2089 ret = SouthGravity;
2090 if (!strcasecmp(data, "southeast") || !strcasecmp(data, "se") || !strcmp(data, "3"))
2091 ret = SouthEastGravity;
2092
2093 return ret;
2094}
2095
2096static cmdret *
2097read_gravity(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
2098{
2099 char *input;
2100
2101 if (s)
2102 input = xstrdup(sbuf_get(s));
2103 else
2104 input = get_input(spec->prompt, hist_GRAVITY,
2105 trivial_completions);
2106
2107 if (input) {
2108 int g = parse_wingravity(input);
2109
2110 if (g == -1) {
2111 cmdret *ret = cmdret_new(RET_FAILURE,
2112 "bad gravity '%s'", input);
2113 free(input);
2114 return ret;
2115 }
2116
2117 *arg = xmalloc(sizeof(struct cmdarg));
2118 (*arg)->type = arg_GRAVITY;
2119 (*arg)->arg.gravity = g;
2120 (*arg)->string = input;
2121
2122 return NULL;
2123 }
2124
2125 *arg = NULL;
2126 return cmdret_new(RET_SUCCESS, NULL);
2127}
2128
2129/*
2130 * Given a string, find a matching vscreen. First check if the string exactly
2131 * matches a vscreen name, then check if it is a number & lastly check if it
2132 * partially matches the name of a vscreen.
2133 */
2134static rp_vscreen *
2135find_vscreen(char *str)
2136{
2137 rp_vscreen *vscreen;
2138 int n;
2139
2140 /* Exact matches are special cases. */
2141 if ((vscreen = screen_find_vscreen_by_name(rp_current_screen, str, 1)))
2142 return vscreen;
2143
2144 /* Check if the user typed a vsceen number. */
2145 n = string_to_positive_int(str);
2146 if (n >= 0) {
2147 vscreen = screen_find_vscreen_by_number(rp_current_screen, n);
2148 if (vscreen)
2149 return vscreen;
2150 }
2151
2152 vscreen = screen_find_vscreen_by_name(rp_current_screen, str, 0);
2153 return vscreen;
2154}
2155
2156static cmdret *
2157read_vscreen(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
2158{
2159 char *input;
2160
2161 if (s)
2162 input = xstrdup(sbuf_get(s));
2163 else
2164 input = get_input(spec->prompt, hist_VSCREEN,
2165 vscreen_completions);
2166
2167 if (input) {
2168 rp_vscreen *v = find_vscreen(input);
2169 if (v) {
2170 *arg = xmalloc(sizeof(struct cmdarg));
2171 (*arg)->type = arg_VSCREEN;
2172 (*arg)->arg.vscreen = v;
2173 (*arg)->string = input;
2174 return NULL;
2175 }
2176
2177 cmdret *ret = cmdret_new(RET_FAILURE, "unknown vscreen '%s'",
2178 input);
2179 free(input);
2180 return ret;
2181 }
2182
2183 *arg = NULL;
2184 return cmdret_new(RET_SUCCESS, NULL);
2185}
2186
2187static struct list_head *
2188hook_completions(char *str)
2189{
2190 struct list_head *list;
2191 struct rp_hook_db_entry *entry;
2192
2193 /* Initialize our list. */
2194 list = xmalloc(sizeof(struct list_head));
2195 INIT_LIST_HEAD(list);
2196
2197 for (entry = rp_hook_db; entry->name; entry++) {
2198 struct sbuf *hookname;
2199
2200 hookname = sbuf_new(0);
2201 sbuf_copy(hookname, entry->name);
2202 list_add_tail(&hookname->node, list);
2203 }
2204
2205 return list;
2206}
2207
2208static cmdret *
2209read_hook(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
2210{
2211 char *input;
2212
2213 if (s)
2214 input = xstrdup(sbuf_get(s));
2215 else
2216 input = get_input(spec->prompt, hist_HOOK, hook_completions);
2217
2218 if (input) {
2219 struct list_head *hook = hook_lookup(input);
2220
2221 if (hook) {
2222 *arg = xmalloc(sizeof(struct cmdarg));
2223 (*arg)->type = arg_HOOK;
2224 (*arg)->arg.hook = hook;
2225 (*arg)->string = input;
2226 return NULL;
2227 }
2228
2229 cmdret *ret = cmdret_new(RET_FAILURE, "unknown hook '%s'",
2230 input);
2231 free(input);
2232 return ret;
2233 }
2234
2235 *arg = NULL;
2236 return cmdret_new(RET_SUCCESS, NULL);
2237}
2238
2239static struct set_var *
2240find_variable(char *str)
2241{
2242 struct set_var *cur;
2243
2244 list_for_each_entry(cur, &set_vars, node) {
2245 if (!strcmp(str, cur->var))
2246 return cur;
2247 }
2248
2249 return NULL;
2250}
2251
2252static struct list_head *
2253var_completions(char *str)
2254{
2255 struct list_head *list;
2256 struct set_var *cur;
2257
2258 /* Initialize our list. */
2259 list = xmalloc(sizeof(struct list_head));
2260 INIT_LIST_HEAD(list);
2261
2262 /* Grab all the vscreen names. */
2263 list_for_each_entry(cur, &set_vars, node) {
2264 struct sbuf *s;
2265
2266 s = sbuf_new(0);
2267 sbuf_copy(s, cur->var);
2268 list_add_tail(&s->node, list);
2269 }
2270
2271 return list;
2272}
2273
2274static cmdret *
2275read_variable(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
2276{
2277 char *input;
2278
2279 if (s)
2280 input = xstrdup(sbuf_get(s));
2281 else
2282 input = get_input(spec->prompt, hist_VARIABLE, var_completions);
2283
2284 if (input) {
2285 struct set_var *var = find_variable(input);
2286
2287 if (var == NULL) {
2288 cmdret *ret = cmdret_new(RET_FAILURE,
2289 "unknown variable '%s'", input);
2290 free(input);
2291 return ret;
2292 }
2293
2294 *arg = xmalloc(sizeof(struct cmdarg));
2295 (*arg)->type = arg_VARIABLE;
2296 (*arg)->arg.variable = var;
2297 (*arg)->string = input;
2298
2299 return NULL;
2300 }
2301
2302 *arg = NULL;
2303 return cmdret_new(RET_SUCCESS, NULL);
2304}
2305
2306static cmdret *
2307read_number(struct argspec *spec, struct sbuf *s, struct cmdarg **arg)
2308{
2309 char *input;
2310
2311 if (s)
2312 input = xstrdup(sbuf_get(s));
2313 else
2314 /* numbers should perhaps be more fine grained, or hist_NONE */
2315 input = get_input(spec->prompt, hist_OTHER,
2316 trivial_completions);
2317
2318 if (input) {
2319 char *ep;
2320 long lval;
2321
2322 errno = 0;
2323 lval = strtol(input, &ep, 10);
2324 if (input[0] == '\0' || *ep != '\0')
2325 return cmdret_new(RET_FAILURE, "malformed number `%s'",
2326 input);
2327 if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) ||
2328 (lval > INT_MAX || lval < INT_MIN))
2329 return cmdret_new(RET_FAILURE, "out of range number `%s'",
2330 input);
2331
2332 *arg = xmalloc(sizeof(struct cmdarg));
2333 (*arg)->type = arg_NUMBER;
2334 (*arg)->arg.number = lval;
2335 (*arg)->string = input;
2336
2337 return NULL;
2338 }
2339
2340 *arg = NULL;
2341 return cmdret_new(RET_SUCCESS, NULL);
2342}
2343
2344static cmdret *
2345read_arg(struct argspec *spec, struct sbuf *s, struct cmdarg **arg,
2346 const char *command_name)
2347{
2348 cmdret *ret = NULL;
2349
2350 switch (spec->type) {
2351 case arg_STRING:
2352 case arg_REST:
2353 case arg_RAW:
2354 ret = read_string(spec, s, hist_OTHER, trivial_completions, arg);
2355 break;
2356 case arg_KEYMAP:
2357 ret = read_keymap(spec, s, arg);
2358 break;
2359 case arg_KEY:
2360 ret = read_keydesc(spec, s, arg);
2361 break;
2362 case arg_NUMBER:
2363 ret = read_number(spec, s, arg);
2364 break;
2365 case arg_GRAVITY:
2366 ret = read_gravity(spec, s, arg);
2367 break;
2368 case arg_COMMAND:
2369 ret = read_command(spec, s, arg);
2370 break;
2371 case arg_SHELLCMD:
2372 ret = read_shellcmd(spec, s, arg, command_name);
2373 break;
2374 case arg_WINDOW:
2375 ret = read_window(spec, s, arg);
2376 break;
2377 case arg_FRAME:
2378 ret = read_frame(s, arg);
2379 break;
2380 case arg_VSCREEN:
2381 ret = read_vscreen(spec, s, arg);
2382 break;
2383 case arg_HOOK:
2384 ret = read_hook(spec, s, arg);
2385 break;
2386 case arg_VARIABLE:
2387 ret = read_variable(spec, s, arg);
2388 break;
2389 }
2390
2391 return ret;
2392}
2393
2394/* Return -1 on failure. Return the number of args on success. */
2395static cmdret *
2396parsed_input_to_args(int num_args, struct argspec *argspec,
2397 struct list_head *list, struct list_head *args, int *parsed_args,
2398 const char *command_name)
2399{
2400 struct sbuf *s;
2401 struct cmdarg *arg;
2402 cmdret *ret;
2403
2404 PRINT_DEBUG(("list len: %d\n", list_size(list)));
2405
2406 *parsed_args = 0;
2407
2408 /* Convert the existing entries to cmdarg's. */
2409 list_for_each_entry(s, list, node) {
2410 if (*parsed_args >= num_args)
2411 break;
2412
2413 ret = read_arg(&argspec[*parsed_args], s, &arg, command_name);
2414 /* If there was an error, then abort. */
2415 if (ret)
2416 return ret;
2417
2418 list_add_tail(&arg->node, args);
2419 (*parsed_args)++;
2420 }
2421
2422 return NULL;
2423}
2424
2425/*
2426 * Prompt the user for missing arguments. Returns non-zero on failure. 0 on
2427 * success.
2428 */
2429static cmdret *
2430fill_in_missing_args(struct user_command *cmd, struct list_head *list,
2431 struct list_head *args, const char *command_name)
2432{
2433 cmdret *ret;
2434 struct cmdarg *arg;
2435 int i = 0;
2436
2437 ret = parsed_input_to_args(cmd->num_args, cmd->args, list, args, &i,
2438 command_name);
2439 if (ret)
2440 return ret;
2441
2442 /* Fill in the rest of the required arguments. */
2443 for (; i < cmd->i_required_args; i++) {
2444 ret = read_arg(&cmd->args[i], NULL, &arg, command_name);
2445 if (ret)
2446 return ret;
2447 list_add_tail(&arg->node, args);
2448 }
2449
2450 return NULL;
2451}
2452
2453/*
2454 * Stick a list of sbuf's in list. if nargs >= 0 then only parse nargs
2455 * arguments and and the rest of the string to the list. Return 0 on success.
2456 * non-zero on failure. When raw is true, then when we hit nargs, we should
2457 * keep any whitespace at the beginning. When false, gobble the whitespace.
2458 */
2459static cmdret *
2460parse_args(char *str, struct list_head * list, int nargs, int raw)
2461{
2462 cmdret *ret = NULL;
2463 char *i;
2464 char *tmp;
2465 int len = 0;
2466 int str_escape = 0;
2467 int in_str = 0;
2468 int gobble = 1;
2469 int parsed_args = 0;
2470
2471 if (str == NULL)
2472 return NULL;
2473
2474 tmp = xmalloc(strlen(str) + 1);
2475
2476 for (i = str; *i; i++) {
2477 /* Have we hit the arg limit? */
2478 if (raw && parsed_args >= nargs) {
2479 struct sbuf *s = sbuf_new(0);
2480 if (!raw) {
2481 while (*i && isspace((unsigned char) *i))
2482 i++;
2483 }
2484 if (*i) {
2485 sbuf_concat(s, i);
2486 list_add_tail(&s->node, list);
2487 }
2488 len = 0;
2489 break;
2490 }
2491
2492 /* Should we eat the whitespace? */
2493 if (gobble) {
2494 while (*i && isspace((unsigned char) *i))
2495 i++;
2496 gobble = 0;
2497 }
2498
2499 /* Escaped characters always get added. */
2500 if (str_escape) {
2501 tmp[len] = *i;
2502 len++;
2503 str_escape = 0;
2504 } else if (*i == '\\') {
2505 str_escape = 1;
2506 } else if (*i == '"') {
2507 if (in_str) {
2508 /* End the arg. */
2509 struct sbuf *s = sbuf_new(0);
2510
2511 sbuf_nconcat(s, tmp, len);
2512 list_add_tail(&s->node, list);
2513 len = 0;
2514 gobble = 1;
2515 in_str = 0;
2516 parsed_args++;
2517 } else if (len == 0) {
2518 /*
2519 * A string open can only start at the
2520 * beginning of an argument.
2521 */
2522 in_str = 1;
2523 } else {
2524 ret = cmdret_new(RET_FAILURE,
2525 "parse error in '%s'", str);
2526 break;
2527 }
2528 } else if (isspace((unsigned char) *i) && !in_str) {
2529 /* End the current arg, and start a new one. */
2530 struct sbuf *s = sbuf_new(0);
2531 sbuf_nconcat(s, tmp, len);
2532 list_add_tail(&s->node, list);
2533 len = 0;
2534 gobble = 1;
2535 parsed_args++;
2536 } else {
2537 /* Add the character to the argument. */
2538 tmp[len] = *i;
2539 len++;
2540 }
2541 }
2542
2543 /* Add the remaining text in tmp. */
2544 if (ret == NULL && len) {
2545 struct sbuf *s = sbuf_new(0);
2546 sbuf_nconcat(s, tmp, len);
2547 list_add_tail(&s->node, list);
2548 }
2549
2550 free(tmp);
2551 return ret;
2552}
2553
2554/* Convert the list to an array, for easier access in commands. */
2555static struct cmdarg **
2556arg_array(struct list_head *head)
2557{
2558 int i = 0;
2559 struct cmdarg **args, *cur;
2560
2561 args = xmalloc(sizeof(struct cmdarg *) * (list_size(head) + 1));
2562 list_for_each_entry(cur, head, node) {
2563 args[i] = cur;
2564 i++;
2565 }
2566
2567 /* NULL terminate the array. */
2568 args[list_size(head)] = NULL;
2569 return args;
2570}
2571
2572static void
2573arg_free(struct cmdarg *arg)
2574{
2575 if (!arg)
2576 return;
2577
2578 /* read_frame doesn't fill in string. */
2579 free(arg->string);
2580
2581 switch (arg->type) {
2582 case arg_KEY:
2583 free(arg->arg.key);
2584 break;
2585 case arg_REST:
2586 case arg_STRING:
2587 case arg_NUMBER:
2588 case arg_WINDOW:
2589 case arg_FRAME:
2590 case arg_COMMAND:
2591 case arg_SHELLCMD:
2592 case arg_KEYMAP:
2593 case arg_GRAVITY:
2594 case arg_VSCREEN:
2595 case arg_HOOK:
2596 case arg_VARIABLE:
2597 case arg_RAW:
2598 /* Do nothing */
2599 break;
2600 default:
2601 warnx("unknown arg type %d\n", arg->type);
2602 break;
2603 }
2604
2605 free(arg);
2606}
2607
2608cmdret *
2609command(int interactive, char *data)
2610{
2611 /* This static counter is used to exit from recursive alias calls. */
2612 static int alias_recursive_depth = 0;
2613 cmdret *result = NULL;
2614 char *cmd, *rest;
2615 char *input;
2616 struct user_command *uc;
2617 int i;
2618
2619 if (data == NULL)
2620 return cmdret_new(RET_FAILURE, NULL);
2621
2622 /* get a writable copy for strtok() */
2623 input = xstrdup(data);
2624
2625 cmd = input;
2626 /* skip beginning whitespace. */
2627 while (*cmd && isspace((unsigned char) *cmd))
2628 cmd++;
2629 rest = cmd;
2630 /* skip til we get to whitespace */
2631 while (*rest && !isspace((unsigned char) *rest))
2632 rest++;
2633 /*
2634 * mark that spot as the end of the command and make rest point to the
2635 * rest of the string.
2636 */
2637 if (*rest) {
2638 *rest = 0;
2639 rest++;
2640 }
2641 PRINT_DEBUG(("cmd==%s rest==%s\n", cmd, rest ? rest : "NULL"));
2642
2643 /* Look for it in the aliases, first. */
2644 for (i = 0; i < alias_list_last; i++) {
2645 if (strcmp(cmd, alias_list[i].name) != 0)
2646 continue;
2647
2648 struct sbuf *s;
2649
2650 /*
2651 * Append any arguments onto the end of the alias'
2652 * command.
2653 */
2654 s = sbuf_new(0);
2655 sbuf_concat(s, alias_list[i].alias);
2656 if (rest != NULL && *rest)
2657 sbuf_printf_concat(s, " %s", rest);
2658
2659 alias_recursive_depth++;
2660 if (alias_recursive_depth >= MAX_ALIAS_RECURSIVE_DEPTH)
2661 result = cmdret_new(RET_FAILURE,
2662 "command: alias recursion has exceeded "
2663 "maximum depth");
2664 else
2665 result = command(interactive, sbuf_get(s));
2666 alias_recursive_depth--;
2667
2668 sbuf_free(s);
2669 goto done;
2670 }
2671
2672 /* If it wasn't an alias, maybe its a command. */
2673 list_for_each_entry(uc, &user_commands, node) {
2674 if (strcmp(cmd, uc->name) != 0)
2675 continue;
2676
2677 struct sbuf *scur;
2678 struct cmdarg *acur;
2679 struct list_head *iter, *tmp;
2680 struct list_head head, args;
2681 int nargs = 0, raw = 0;
2682
2683 INIT_LIST_HEAD(&args);
2684 INIT_LIST_HEAD(&head);
2685
2686 /*
2687 * We need to tell parse_args about arg_REST and
2688 * arg_SHELLCMD.
2689 */
2690 for (i = 0; i < uc->num_args; i++)
2691 if (uc->args[i].type == arg_REST ||
2692 uc->args[i].type == arg_COMMAND ||
2693 uc->args[i].type == arg_SHELLCMD ||
2694 uc->args[i].type == arg_RAW) {
2695 raw = 1;
2696 nargs = i;
2697 break;
2698 }
2699
2700 /* Parse the arguments and call the function. */
2701 result = parse_args(rest, &head, nargs, raw);
2702 if (result)
2703 goto free_lists;
2704
2705 /* Interactive commands prompt the user for missing args. */
2706 if (interactive)
2707 result = fill_in_missing_args(uc, &head, &args,
2708 uc->name);
2709 else {
2710 int parsed_args;
2711 result = parsed_input_to_args(uc->num_args,
2712 uc->args, &head, &args, &parsed_args,
2713 uc->name);
2714 }
2715
2716 if (result == NULL) {
2717 if ((interactive && list_size(&args) < uc->i_required_args) ||
2718 (!interactive && list_size(&args) < uc->ni_required_args)) {
2719 result = cmdret_new(RET_FAILURE,
2720 "not enough arguments.");
2721 goto free_lists;
2722 } else if (list_size(&head) > uc->num_args) {
2723 result = cmdret_new(RET_FAILURE,
2724 "command: too many arguments.");
2725 goto free_lists;
2726 } else {
2727 struct cmdarg **cmdargs = arg_array(&args);
2728 result = uc->func(interactive, cmdargs);
2729 free(cmdargs);
2730 }
2731 }
2732free_lists:
2733 /* Free the parsed strings */
2734 list_for_each_safe_entry(scur, iter, tmp, &head, node)
2735 sbuf_free(scur);
2736
2737 /* Free the args */
2738 list_for_each_safe_entry(acur, iter, tmp, &args, node)
2739 arg_free(acur);
2740
2741 goto done;
2742 }
2743
2744 result = cmdret_new(RET_FAILURE, MESSAGE_UNKNOWN_COMMAND, cmd);
2745
2746done:
2747 free(input);
2748 return result;
2749}
2750
2751cmdret *
2752cmd_colon(int interactive, struct cmdarg **args)
2753{
2754 cmdret *result;
2755 char *input;
2756
2757 if (args[0] == NULL)
2758 input = get_input(MESSAGE_PROMPT_COMMAND, hist_COMMAND,
2759 colon_completions);
2760 else
2761 input = get_more_input(MESSAGE_PROMPT_COMMAND, ARG_STRING(0),
2762 hist_COMMAND, BASIC, colon_completions);
2763
2764 /* User aborted. */
2765 if (input == NULL)
2766 return cmdret_new(RET_FAILURE, NULL);
2767
2768 result = command(1, input);
2769 free(input);
2770 return result;
2771}
2772
2773cmdret *
2774cmd_exec(int interactive, struct cmdarg **args)
2775{
2776 spawn(ARG_STRING(0), current_frame(rp_current_vscreen));
2777 return cmdret_new(RET_SUCCESS, NULL);
2778}
2779
2780cmdret *
2781cmd_execw(int interactive, struct cmdarg **args)
2782{
2783 int status = -1;
2784 pid_t pid = spawn(ARG_STRING(0), current_frame(rp_current_vscreen));
2785 if (waitpid(pid, &status, 0) == -1)
2786 perror("cmd_execw");
2787 else
2788 status = WEXITSTATUS(status);
2789 return cmdret_new(status == 0? RET_SUCCESS: RET_FAILURE, NULL);
2790}
2791
2792cmdret *
2793cmd_execa(int interactive, struct cmdarg **args)
2794{
2795 spawn(ARG_STRING(0), NULL);
2796 return cmdret_new(RET_SUCCESS, NULL);
2797}
2798
2799cmdret *
2800cmd_execf(int interactive, struct cmdarg **args)
2801{
2802 /*
2803 * Tell spawn's shell to exec the command it finds, reusing the pid
2804 * that fork() returned so matching to its NET_WM_PID later works
2805 * properly.
2806 */
2807 char *cmd = xsprintf("exec %s", ARG_STRING(1));
2808 spawn(cmd, ARG(0, frame));
2809 return cmdret_new(RET_SUCCESS, NULL);
2810}
2811
2812cmdret *
2813cmd_execv(int interactive, struct cmdarg **args)
2814{
2815 rp_vscreen *vscreen;
2816 if (!(vscreen = find_vscreen(ARG_STRING(0))))
2817 return cmdret_new(RET_FAILURE, NULL);
2818 vspawn(ARG_STRING(1), current_frame(vscreen), vscreen);
2819 return cmdret_new(RET_SUCCESS, NULL);
2820}
2821
2822int
2823spawn(char *cmd, rp_frame *frame) { return vspawn(cmd, frame, NULL); }
2824int
2825vspawn(char *cmd, rp_frame *frame, rp_vscreen *vscreen)
2826{
2827 rp_child_info *child;
2828 int pid;
2829
2830 pid = fork();
2831 if (pid == 0) {
2832 /*
2833 * Some process setup to make sure the spawned process runs in
2834 * its own session.
2835 */
2836 putenv(rp_current_screen->display_string);
2837 if (setsid() == -1)
2838 {
2839 int ctty;
2840
2841 ctty = open("/dev/tty", O_RDONLY);
2842 if (ctty != -1) {
2843 ioctl(ctty, TIOCNOTTY);
2844 close(ctty);
2845 }
2846
2847 setpgid(0, 0);
2848 }
2849
2850 execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
2851 _exit(1);
2852 }
2853
2854 /* wait((int *) 0); */
2855 PRINT_DEBUG(("spawned %s, pid %d, frame %d, vscreen %d, screen %d\n",
2856 cmd, pid, frame ? frame->number : -1,
2857 (vscreen? vscreen: rp_current_vscreen)->number,
2858 rp_current_screen->number));
2859
2860 /* Add this child process to our list. */
2861 child = xmalloc(sizeof(rp_child_info));
2862 child->cmd = xstrdup(cmd);
2863 child->pid = pid;
2864 child->terminated = 0;
2865 child->frame = frame;
2866 child->vscreen = vscreen? vscreen: rp_current_vscreen;
2867 child->screen = rp_current_screen;
2868 child->window_mapped = 0;
2869
2870 list_add(&child->node, &rp_children);
2871
2872 return pid;
2873}
2874
2875cmdret *
2876cmd_quit(int interactive, struct cmdarg **args)
2877{
2878 kill_signalled = 1;
2879 return cmdret_new(RET_SUCCESS, NULL);
2880}
2881
2882/* Assign a new number to a window ala screen's number command. */
2883cmdret *
2884cmd_number(int interactive, struct cmdarg **args)
2885{
2886 int old_number, new_number;
2887 rp_window_elem *other_win, *win;
2888
2889 /* Gather the args. */
2890 new_number = ARG(0, number);
2891 if (args[1])
2892 win = vscreen_find_window_by_number(rp_current_vscreen,
2893 ARG(1, number));
2894 else
2895 win = vscreen_find_window(&rp_current_vscreen->mapped_windows,
2896 current_window());
2897
2898 /* Make the switch. */
2899 if (new_number >= 0 && win) {
2900 /* Find other window with same number and give it old number. */
2901 other_win = vscreen_find_window_by_number(rp_current_vscreen,
2902 new_number);
2903 if (other_win != NULL) {
2904 old_number = win->number;
2905 other_win->number = old_number;
2906
2907 /* Resort the window in the list */
2908 vscreen_resort_window(rp_current_vscreen, other_win);
2909 } else {
2910 numset_release(rp_current_vscreen->numset, win->number);
2911 }
2912
2913 win->number = new_number;
2914 numset_add_num(rp_current_vscreen->numset, new_number);
2915
2916 /* resort the the window in the list */
2917 vscreen_resort_window(rp_current_vscreen, win);
2918
2919 /* Update the window list. */
2920 update_window_names(win->win->vscreen->screen,
2921 defaults.window_fmt);
2922 }
2923
2924 return cmdret_new(RET_SUCCESS, NULL);
2925}
2926
2927/* Toggle the display of the program bar */
2928cmdret *
2929cmd_windows(int interactive, struct cmdarg **args)
2930{
2931 struct sbuf *window_list = NULL;
2932 int dummy;
2933 char *fmt;
2934 cmdret *ret;
2935
2936 if (args[0] == NULL)
2937 fmt = defaults.window_fmt;
2938 else
2939 fmt = ARG_STRING(0);
2940
2941 if (interactive) {
2942 rp_screen *s;
2943 s = rp_current_screen;
2944 ret = cmdret_new(RET_SUCCESS, NULL);
2945 if (defaults.bar_timeout == 0 && s->bar_is_raised) {
2946 hide_bar(s, 0);
2947 return ret;
2948 }
2949 show_bar(s, fmt);
2950 } else {
2951 window_list = sbuf_new(0);
2952 get_window_list(fmt, "\n", window_list, &dummy, &dummy);
2953 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(window_list));
2954 sbuf_free(window_list);
2955 }
2956 return ret;
2957}
2958
2959cmdret *
2960cmd_abort(int interactive, struct cmdarg **args)
2961{
2962 return cmdret_new(RET_SUCCESS, NULL);
2963}
2964
2965/* Redisplay the current window by sending 2 resize events. */
2966cmdret *
2967cmd_redisplay(int interactive, struct cmdarg **args)
2968{
2969 force_maximize(current_window());
2970 return cmdret_new(RET_SUCCESS, NULL);
2971}
2972
2973/* Reassign the prefix key. */
2974cmdret *
2975cmd_escape(int interactive, struct cmdarg **args)
2976{
2977 struct rp_key *key;
2978 rp_action *action;
2979 rp_keymap *map, *top;
2980
2981 top = find_keymap(defaults.top_kmap);
2982 map = find_keymap(ROOT_KEYMAP);
2983 key = ARG(0, key);
2984
2985 /* Update the "other" keybinding */
2986 action = find_keybinding(prefix_key.sym, prefix_key.state, map);
2987 if (action != NULL && !strcmp(action->data, "other")) {
2988 action->key = key->sym;
2989 action->state = key->state;
2990 }
2991
2992 /* Update the "meta" keybinding */
2993 action = find_keybinding(prefix_key.sym, 0, map);
2994 if (action != NULL && !strcmp(action->data, "meta")) {
2995 action->key = key->sym;
2996 if (key->state != 0)
2997 action->state = 0;
2998 else
2999 action->state = RP_CONTROL_MASK;
3000 }
3001
3002 /* Remove the grab on the current prefix key */
3003 ungrab_keys_all_wins();
3004
3005 action = find_keybinding(prefix_key.sym, prefix_key.state, top);
3006 if (action != NULL && !strcmp(action->data, "readkey " ROOT_KEYMAP)) {
3007 action->key = key->sym;
3008 action->state = key->state;
3009 }
3010
3011 /* Add the grab for the new prefix key */
3012 grab_keys_all_wins();
3013
3014 /* Finally, keep track of the current prefix. */
3015 prefix_key.sym = key->sym;
3016 prefix_key.state = key->state;
3017
3018 return cmdret_new(RET_SUCCESS, NULL);
3019}
3020
3021/* User accessible call to display the passed in string. */
3022cmdret *
3023cmd_echo(int interactive, struct cmdarg **args)
3024{
3025 marked_message_printf(0, 0, "%s", ARG_STRING(0));
3026
3027 return cmdret_new(RET_SUCCESS, NULL);
3028}
3029
3030
3031static cmdret *
3032read_split(char *str, int max, int *p)
3033{
3034 int a, b;
3035
3036 if (sscanf(str, "%d/%d", &a, &b) == 2) {
3037 *p = (int) (max * (float) (a) / (float) (b));
3038 } else if (sscanf(str, "%d", p) == 1) {
3039 if (*p < 0)
3040 *p = max + *p;
3041 } else {
3042 /* Failed to read input. */
3043 return cmdret_new(RET_FAILURE, "bad split '%s'", str);
3044 }
3045
3046 return NULL;
3047}
3048
3049cmdret *
3050cmd_vsplit(int interactive, struct cmdarg **args)
3051{
3052 cmdret *ret;
3053 rp_frame *frame;
3054 int pixels;
3055
3056 push_frame_undo(rp_current_vscreen); /* fdump to stack */
3057 frame = current_frame(rp_current_vscreen);
3058
3059 /* Default to dividing the frame in half. */
3060 if (args[0] == NULL)
3061 pixels = frame->height / 2;
3062 else {
3063 ret = read_split(ARG_STRING(0), frame->height, &pixels);
3064 if (ret)
3065 return ret;
3066 }
3067
3068 if (pixels > 0)
3069 h_split_frame(frame, pixels);
3070 else
3071 return cmdret_new(RET_FAILURE, "vsplit: %s",
3072 invalid_negative_arg);
3073
3074 return cmdret_new(RET_SUCCESS, NULL);
3075}
3076
3077cmdret *
3078cmd_hsplit(int interactive, struct cmdarg **args)
3079{
3080 cmdret *ret;
3081 rp_frame *frame;
3082 int pixels;
3083
3084 push_frame_undo(rp_current_vscreen); /* fdump to stack */
3085 frame = current_frame(rp_current_vscreen);
3086
3087 /* Default to dividing the frame in half. */
3088 if (args[0] == NULL)
3089 pixels = frame->width / 2;
3090 else {
3091 ret = read_split(ARG_STRING(0), frame->width, &pixels);
3092 if (ret)
3093 return ret;
3094 }
3095
3096 if (pixels > 0)
3097 v_split_frame(frame, pixels);
3098 else
3099 return cmdret_new(RET_FAILURE, "hsplit: %s",
3100 invalid_negative_arg);
3101
3102 return cmdret_new(RET_SUCCESS, NULL);
3103}
3104
3105cmdret *
3106cmd_only(int interactive, struct cmdarg **args)
3107{
3108 push_frame_undo(rp_current_vscreen); /* fdump to stack */
3109 remove_all_splits();
3110 maximize(current_window());
3111
3112 return cmdret_new(RET_SUCCESS, NULL);
3113}
3114
3115cmdret *
3116cmd_remove(int interactive, struct cmdarg **args)
3117{
3118 rp_frame *frame;
3119
3120 push_frame_undo(rp_current_vscreen); /* fdump to stack */
3121
3122 if (num_frames(rp_current_vscreen) <= 1)
3123 return cmdret_new(RET_FAILURE,
3124 "remove: cannot remove only frame");
3125
3126 frame = find_frame_next(current_frame(rp_current_vscreen));
3127 if (frame) {
3128 remove_frame(current_frame(rp_current_vscreen));
3129 set_active_frame(frame, 0);
3130 show_frame_indicator(0);
3131 }
3132
3133 return cmdret_new(RET_SUCCESS, NULL);
3134}
3135
3136cmdret *
3137cmd_shrink(int interactive, struct cmdarg **args)
3138{
3139 push_frame_undo(rp_current_vscreen); /* fdump to stack */
3140 resize_shrink_to_window(current_frame(rp_current_vscreen));
3141 return cmdret_new(RET_SUCCESS, NULL);
3142}
3143
3144typedef struct resize_binding resize_binding;
3145
3146struct resize_binding {
3147 struct rp_key key;
3148 enum resize_action {
3149 RESIZE_UNKNOWN = 0,
3150 RESIZE_VGROW,
3151 RESIZE_VSHRINK,
3152 RESIZE_HGROW,
3153 RESIZE_HSHRINK,
3154 RESIZE_TO_WINDOW,
3155 RESIZE_ABORT,
3156 RESIZE_END
3157 } action;
3158};
3159
3160static resize_binding resize_bindings[] = {
3161 { { INPUT_ABORT_KEY, INPUT_ABORT_MODIFIER }, RESIZE_ABORT},
3162 { { RESIZE_VGROW_KEY, RESIZE_VGROW_MODIFIER }, RESIZE_VGROW},
3163 { { RESIZE_VSHRINK_KEY, RESIZE_VSHRINK_MODIFIER }, RESIZE_VSHRINK},
3164 { { RESIZE_HGROW_KEY, RESIZE_HGROW_MODIFIER }, RESIZE_HGROW},
3165 { { RESIZE_HSHRINK_KEY, RESIZE_HSHRINK_MODIFIER }, RESIZE_HSHRINK},
3166 { { RESIZE_SHRINK_TO_WINDOW_KEY, RESIZE_SHRINK_TO_WINDOW_MODIFIER }, RESIZE_TO_WINDOW},
3167 { { RESIZE_END_KEY, RESIZE_END_MODIFIER }, RESIZE_END},
3168
3169 /*
3170 * Some more default keys (after the values from conf.h, so that they have lower
3171 * priority): first the arrow keys:
3172 */
3173 { {XK_Escape, 0 }, RESIZE_ABORT},
3174 { {XK_Down, 0 }, RESIZE_VGROW},
3175 { {XK_Up, 0 }, RESIZE_VSHRINK},
3176 { {XK_Right, 0 }, RESIZE_HGROW},
3177 { {XK_Left, 0 }, RESIZE_HSHRINK},
3178
3179 /* some vi-like bindings: */
3180 { {XK_j, 0 }, RESIZE_VGROW},
3181 { {XK_k, 0 }, RESIZE_VSHRINK},
3182 { {XK_l, 0 }, RESIZE_HGROW},
3183 { {XK_h, 0 }, RESIZE_HSHRINK},
3184
3185 { {0, 0 }, RESIZE_UNKNOWN},
3186};
3187
3188cmdret *
3189cmd_resize(int interactive, struct cmdarg **args)
3190{
3191 rp_screen *s = rp_current_screen;
3192
3193 /*
3194 * If the user calls resize with arguments, treat it like the
3195 * non-interactive version.
3196 */
3197 if (interactive && args[0] == NULL) {
3198 char buffer[513];
3199 unsigned int mod;
3200 KeySym c;
3201 struct list_head *bk;
3202
3203 /*
3204 * If we haven't got at least 2 frames, there isn't anything to
3205 * scale.
3206 */
3207 if (num_frames(rp_current_vscreen) < 2)
3208 return cmdret_new(RET_FAILURE, NULL);
3209
3210 /* Save the frameset in case the user aborts. */
3211 bk = vscreen_copy_frameset(rp_current_vscreen);
3212
3213 /* Get ready to read keys. */
3214 grab_rat();
3215 XGrabKeyboard(dpy, s->key_window, False, GrabModeAsync,
3216 GrabModeAsync, CurrentTime);
3217
3218 while (1) {
3219 struct resize_binding *binding;
3220
3221 show_frame_message(defaults.resize_fmt);
3222 read_key(&c, &mod, buffer, sizeof(buffer));
3223
3224 /* Convert the mask to be compatible with us. */
3225 mod = x11_mask_to_rp_mask(mod);
3226
3227 for (binding = resize_bindings; binding->action; binding++) {
3228 if (c == binding->key.sym &&
3229 mod == binding->key.state)
3230 break;
3231 }
3232
3233 if (binding->action == RESIZE_VGROW)
3234 resize_frame_vertically(
3235 current_frame(rp_current_vscreen),
3236 defaults.frame_resize_unit);
3237 else if (binding->action == RESIZE_VSHRINK)
3238 resize_frame_vertically(
3239 current_frame(rp_current_vscreen),
3240 -defaults.frame_resize_unit);
3241 else if (binding->action == RESIZE_HGROW)
3242 resize_frame_horizontally(
3243 current_frame(rp_current_vscreen),
3244 defaults.frame_resize_unit);
3245 else if (binding->action == RESIZE_HSHRINK)
3246 resize_frame_horizontally(
3247 current_frame(rp_current_vscreen),
3248 -defaults.frame_resize_unit);
3249 else if (binding->action == RESIZE_TO_WINDOW)
3250 resize_shrink_to_window(
3251 current_frame(rp_current_vscreen));
3252 else if (binding->action == RESIZE_ABORT) {
3253 rp_frame *cur;
3254
3255 vscreen_restore_frameset(rp_current_vscreen, bk);
3256 list_for_each_entry(cur,
3257 &(rp_current_vscreen->frames), node) {
3258 maximize_all_windows_in_frame(cur);
3259 }
3260 break;
3261 } else if (binding->action == RESIZE_END) {
3262 frameset_free(bk);
3263 break;
3264 }
3265 }
3266
3267 /* It is our responsibility to free this. */
3268 free(bk);
3269
3270 hide_frame_indicator();
3271 ungrab_rat();
3272 XUngrabKeyboard(dpy, CurrentTime);
3273 } else {
3274 if (args[0] && args[1]) {
3275 resize_frame_horizontally(
3276 current_frame(rp_current_vscreen),
3277 ARG(0, number));
3278 resize_frame_vertically(
3279 current_frame(rp_current_vscreen),
3280 ARG(1, number));
3281 } else
3282 return cmdret_new(RET_FAILURE,
3283 "resize: two numeric arguments required");
3284 }
3285
3286 return cmdret_new(RET_SUCCESS, NULL);
3287}
3288
3289static cmdret *
3290set_resizeunit(struct cmdarg **args)
3291{
3292 if (args[0] == NULL)
3293 return cmdret_new(RET_SUCCESS, "%d",
3294 defaults.frame_resize_unit);
3295
3296 if (ARG(0, number) >= 0)
3297 defaults.frame_resize_unit = ARG(0, number);
3298 else
3299 return cmdret_new(RET_FAILURE, "set resizeunit: %s",
3300 invalid_negative_arg);
3301
3302 return cmdret_new(RET_SUCCESS, NULL);
3303}
3304
3305/* banish the rat pointer */
3306cmdret *
3307cmd_banish(int interactive, struct cmdarg **args)
3308{
3309 rp_screen *s;
3310
3311 s = rp_current_screen;
3312
3313 XWarpPointer(dpy, None, s->root, 0, 0, 0, 0, s->left + s->width - 2,
3314 s->top + s->height - 2);
3315
3316 return cmdret_new(RET_SUCCESS, NULL);
3317}
3318
3319cmdret *
3320cmd_banishrel(int interactive, struct cmdarg **args)
3321{
3322 rp_screen *s = rp_current_screen;
3323 rp_window *w = current_window();
3324 rp_frame *f = current_frame(rp_current_vscreen);
3325
3326 if (w)
3327 XWarpPointer(dpy, None, w->w, 0, 0, 0, 0, w->x + w->width - 2,
3328 w->y + w->height - 2);
3329 else
3330 XWarpPointer(dpy, None, s->root, 0, 0, 0, 0, f->x + f->width,
3331 f->y + f->height);
3332
3333 return cmdret_new(RET_SUCCESS, NULL);
3334}
3335
3336cmdret *
3337cmd_ratinfo(int interactive, struct cmdarg **args)
3338{
3339 rp_screen *s;
3340 Window root_win, child_win;
3341 int mouse_x, mouse_y, root_x, root_y;
3342 unsigned int mask;
3343
3344 s = rp_current_screen;
3345 XQueryPointer(dpy, s->root, &root_win, &child_win, &mouse_x, &mouse_y,
3346 &root_x, &root_y, &mask);
3347
3348 return cmdret_new(RET_SUCCESS, "%d %d", mouse_x, mouse_y);
3349}
3350
3351cmdret *
3352cmd_ratrelinfo(int interactive, struct cmdarg **args)
3353{
3354 rp_screen *s;
3355 rp_window *rpw;
3356 rp_frame *f;
3357 Window root_win, child_win;
3358 int mouse_x, mouse_y, root_x, root_y;
3359 unsigned int mask;
3360
3361 s = rp_current_screen;
3362 rpw = current_window();
3363 f = current_frame(rp_current_vscreen);
3364
3365 if (rpw)
3366 XQueryPointer(dpy, rpw->w, &root_win, &child_win, &mouse_x,
3367 &mouse_y, &root_x, &root_y, &mask);
3368 else {
3369 XQueryPointer(dpy, s->root, &root_win, &child_win, &mouse_x,
3370 &mouse_y, &root_x, &root_y, &mask);
3371 root_x -= f->x;
3372 root_y -= f->y;
3373 }
3374
3375 return cmdret_new(RET_SUCCESS, "%d %d", root_x, root_y);
3376}
3377
3378cmdret *
3379cmd_ratwarp(int interactive, struct cmdarg **args)
3380{
3381 rp_screen *s;
3382
3383 s = rp_current_screen;
3384 XWarpPointer(dpy, None, s->root, 0, 0, 0, 0, ARG(0, number),
3385 ARG(1, number));
3386 return cmdret_new(RET_SUCCESS, NULL);
3387}
3388
3389cmdret *
3390cmd_ratrelwarp(int interactive, struct cmdarg **args)
3391{
3392 XWarpPointer(dpy, None, None, 0, 0, 0, 0, ARG(0, number),
3393 ARG(1, number));
3394 return cmdret_new(RET_SUCCESS, NULL);
3395}
3396
3397cmdret *
3398cmd_ratclick(int interactive, struct cmdarg **args)
3399{
3400 int button = 1;
3401
3402 if (args[0]) {
3403 button = ARG(0, number);
3404 if (button < 1 || button > 3)
3405 return cmdret_new(RET_SUCCESS,
3406 "ratclick: invalid argument");
3407 }
3408
3409 XTestFakeButtonEvent(dpy, button, True, CurrentTime);
3410 XTestFakeButtonEvent(dpy, button, False, CurrentTime);
3411 return cmdret_new(RET_SUCCESS, NULL);
3412}
3413
3414cmdret *
3415cmd_rathold(int interactive, struct cmdarg **args)
3416{
3417 int button = 1;
3418
3419 if (args[1]) {
3420 button = ARG(1, number);
3421 if (button < 1 || button > 3)
3422 return cmdret_new(RET_SUCCESS,
3423 "ratclick: invalid argument");
3424 }
3425
3426 if (!strcmp(ARG_STRING(0), "down"))
3427 XTestFakeButtonEvent(dpy, button, True, CurrentTime);
3428 else if (!strcmp(ARG_STRING(0), "up"))
3429 XTestFakeButtonEvent(dpy, button, False, CurrentTime);
3430 else
3431 return cmdret_new(RET_FAILURE,
3432 "rathold: '%s' invalid argument", ARG_STRING(0));
3433
3434 return cmdret_new(RET_SUCCESS, NULL);
3435}
3436
3437cmdret *
3438cmd_curframe(int interactive, struct cmdarg **args)
3439{
3440 if (interactive) {
3441 show_frame_indicator(1);
3442 return cmdret_new(RET_SUCCESS, NULL);
3443 }
3444
3445 return cmdret_new(RET_SUCCESS, "%d",
3446 current_frame(rp_current_vscreen)->number);
3447}
3448
3449cmdret *
3450cmd_help(int interactive, struct cmdarg **args)
3451{
3452 rp_keymap *map;
3453
3454 if (args[0])
3455 map = ARG(0, keymap);
3456 else
3457 map = find_keymap(ROOT_KEYMAP);
3458
3459 if (interactive) {
3460 rp_screen *s = rp_current_screen;
3461 int i, old_i;
3462 int x = 20;
3463 int y = 20;
3464 int header_offset;
3465 int width, max_width = 0;
3466 /* 1 if we are drawing keys, 0 if we are drawing commands */
3467 int drawing_keys = 1;
3468 char *keysym_name;
3469
3470 /* Switch to the default colormap. */
3471 if (current_window())
3472 XUninstallColormap(dpy, current_window()->colormap);
3473 XInstallColormap(dpy, s->def_cmap);
3474
3475 XMapRaised(dpy, s->help_window);
3476
3477 rp_draw_string(s, s->help_window, STYLE_NORMAL,
3478 x, y + FONT_ASCENT(s), PROGNAME " key bindings", -1, NULL,
3479 NULL);
3480
3481 y += FONT_HEIGHT(s) * 2;
3482
3483 /* Only print the "Command key" for the root keymap */
3484 if (map == find_keymap(ROOT_KEYMAP)) {
3485 rp_draw_string(s, s->help_window, STYLE_NORMAL,
3486 x, y + FONT_ASCENT(s),
3487 "Command key: ", -1, NULL, NULL);
3488
3489 keysym_name = keysym_to_string(prefix_key.sym,
3490 prefix_key.state);
3491 rp_draw_string(s, s->help_window, STYLE_NORMAL,
3492 x + rp_text_width(s, "Command key: ", -1, NULL),
3493 y + FONT_ASCENT(s),
3494 keysym_name, -1, NULL, NULL);
3495 free(keysym_name);
3496
3497 y += FONT_HEIGHT(s) * 2;
3498 }
3499 header_offset = y;
3500
3501 i = old_i = 0;
3502 while (i < map->actions_last && old_i < map->actions_last) {
3503 if (drawing_keys) {
3504 keysym_name = keysym_to_string(map->actions[i].key,
3505 map->actions[i].state);
3506
3507 rp_draw_string(s, s->help_window, STYLE_NORMAL,
3508 x, y + FONT_ASCENT(s),
3509 keysym_name, -1, NULL, NULL);
3510
3511 width = rp_text_width(s, keysym_name, -1, NULL);
3512 if (width > max_width)
3513 max_width = width;
3514
3515 free(keysym_name);
3516 } else {
3517 rp_draw_string(s, s->help_window, STYLE_NORMAL,
3518 x, y + FONT_ASCENT(s),
3519 map->actions[i].data, -1, NULL, NULL);
3520
3521 width = rp_text_width(s, map->actions[i].data,
3522 -1, NULL);
3523 if (width > max_width)
3524 max_width = width;
3525 }
3526
3527 y += FONT_HEIGHT(s);
3528 /* Make sure the next line fits entirely within the window. */
3529 if (y + FONT_HEIGHT(s) >= (s->top + s->height)) {
3530 if (drawing_keys) {
3531 x += max_width + 10;
3532 drawing_keys = 0;
3533 i = old_i;
3534 } else {
3535 x += max_width + 20;
3536 drawing_keys = 1;
3537 i++;
3538 old_i = i;
3539 }
3540
3541 max_width = 0;
3542 y = header_offset;
3543 } else {
3544 i++;
3545 if (i == map->actions_last && drawing_keys) {
3546 x += max_width + 10;
3547 drawing_keys = 0;
3548 y = header_offset;
3549 i = old_i;
3550 max_width = 0;
3551 }
3552 }
3553 }
3554
3555 read_any_key();
3556 XUnmapWindow(dpy, s->help_window);
3557
3558 /* Possibly restore colormap. */
3559 if (current_window()) {
3560 XUninstallColormap(dpy, s->def_cmap);
3561 XInstallColormap(dpy, current_window()->colormap);
3562 }
3563 /* The help window overlaps the bar, so redraw it. */
3564 if (rp_current_screen->bar_is_raised)
3565 redraw_last_message();
3566
3567 return cmdret_new(RET_SUCCESS, NULL);
3568 } else {
3569 struct sbuf *help_list;
3570 char *keysym_name;
3571 int i;
3572 cmdret *ret;
3573
3574 help_list = sbuf_new(0);
3575
3576 for (i = 0; i < map->actions_last; i++) {
3577 keysym_name = keysym_to_string(map->actions[i].key,
3578 map->actions[i].state);
3579 sbuf_concat(help_list, keysym_name);
3580 free(keysym_name);
3581 sbuf_concat(help_list, " ");
3582 sbuf_concat(help_list, map->actions[i].data);
3583 if (i < map->actions_last - 1)
3584 sbuf_concat(help_list, "\n");
3585 }
3586
3587 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(help_list));
3588 sbuf_free(help_list);
3589 return ret;
3590 }
3591
3592 return cmdret_new(RET_SUCCESS, NULL);
3593}
3594
3595cmdret *
3596set_rudeness(struct cmdarg **args)
3597{
3598 int num;
3599
3600 if (args[0] == NULL)
3601 return cmdret_new(RET_SUCCESS, "%d",
3602 rp_honour_transient_raise |
3603 (rp_honour_normal_raise << 1) |
3604 (rp_honour_transient_map << 2) |
3605 (rp_honour_normal_map << 3) |
3606 (rp_honour_vscreen_switch << 4));
3607
3608 num = ARG(0, number);
3609 if (num < 0 || num > 31)
3610 return cmdret_new(RET_FAILURE, "rudeness: invalid level '%s'",
3611 ARG_STRING(0));
3612
3613 rp_honour_transient_raise = num & 1 ? 1 : 0;
3614 rp_honour_normal_raise = num & 2 ? 1 : 0;
3615 rp_honour_transient_map = num & 4 ? 1 : 0;
3616 rp_honour_normal_map = num & 8 ? 1 : 0;
3617 rp_honour_vscreen_switch = num & 16 ? 1 : 0;
3618
3619 return cmdret_new(RET_SUCCESS, NULL);
3620}
3621
3622char *
3623wingravity_to_string(int g)
3624{
3625 switch (g) {
3626 case NorthWestGravity:
3627 return "nw";
3628 case WestGravity:
3629 return "w";
3630 case SouthWestGravity:
3631 return "sw";
3632 case NorthGravity:
3633 return "n";
3634 case CenterGravity:
3635 return "c";
3636 case SouthGravity:
3637 return "s";
3638 case NorthEastGravity:
3639 return "ne";
3640 case EastGravity:
3641 return "e";
3642 case SouthEastGravity:
3643 return "se";
3644 }
3645
3646 PRINT_DEBUG(("Unknown gravity!\n"));
3647 return "Unknown";
3648}
3649
3650cmdret *
3651cmd_gravity(int interactive, struct cmdarg **args)
3652{
3653 int gravity;
3654 rp_window *win;
3655
3656 win = current_window();
3657 if (!win)
3658 return cmdret_new(RET_FAILURE, NULL);
3659
3660 if (args[0] == NULL)
3661 return cmdret_new(RET_SUCCESS, "%s",
3662 wingravity_to_string(win->gravity));
3663
3664 if ((gravity = parse_wingravity(ARG_STRING(0))) < 0)
3665 return cmdret_new(RET_FAILURE, "gravity: unknown gravity");
3666 else {
3667 win->gravity = gravity;
3668 maximize(win);
3669 }
3670
3671 return cmdret_new(RET_SUCCESS, NULL);
3672}
3673
3674static cmdret *
3675set_wingravity(struct cmdarg **args)
3676{
3677 if (args[0] == NULL)
3678 return cmdret_new(RET_SUCCESS, "%s",
3679 wingravity_to_string(defaults.win_gravity));
3680
3681 defaults.win_gravity = ARG(0, gravity);
3682
3683 return cmdret_new(RET_SUCCESS, NULL);
3684}
3685
3686static cmdret *
3687set_transgravity(struct cmdarg **args)
3688{
3689 if (args[0] == NULL)
3690 return cmdret_new(RET_SUCCESS, "%s",
3691 wingravity_to_string(defaults.trans_gravity));
3692
3693 defaults.trans_gravity = ARG(0, gravity);
3694
3695 return cmdret_new(RET_SUCCESS, NULL);
3696}
3697
3698static cmdret *
3699set_maxsizegravity(struct cmdarg **args)
3700{
3701 if (args[0] == NULL)
3702 return cmdret_new(RET_SUCCESS, "%s",
3703 wingravity_to_string(defaults.maxsize_gravity));
3704
3705 defaults.maxsize_gravity = ARG(0, gravity);
3706
3707 return cmdret_new(RET_SUCCESS, NULL);
3708}
3709
3710static cmdret *
3711set_msgwait(struct cmdarg **args)
3712{
3713 if (args[0] == NULL)
3714 return cmdret_new(RET_SUCCESS, "%d", defaults.bar_timeout);
3715
3716 if (ARG(0, number) < 0)
3717 return cmdret_new(RET_FAILURE, "msgwait: %s",
3718 invalid_negative_arg);
3719 else
3720 defaults.bar_timeout = ARG(0, number);
3721
3722 return cmdret_new(RET_SUCCESS, NULL);
3723}
3724
3725static cmdret *
3726set_framemsgwait(struct cmdarg **args)
3727{
3728 if (args[0] == NULL)
3729 return cmdret_new(RET_SUCCESS, "%d",
3730 defaults.frame_indicator_timeout);
3731
3732 if (ARG(0, number) < -1)
3733 return cmdret_new(RET_FAILURE, "framemsgwait: %s",
3734 invalid_negative_arg);
3735 else
3736 defaults.frame_indicator_timeout = ARG(0, number);
3737
3738 return cmdret_new(RET_SUCCESS, NULL);
3739}
3740
3741static cmdret *
3742set_bargravity(struct cmdarg **args)
3743{
3744 rp_screen *s;
3745
3746 if (args[0] == NULL)
3747 return cmdret_new(RET_SUCCESS, "%s",
3748 wingravity_to_string(defaults.bar_location));
3749
3750 mark_edge_frames();
3751
3752 defaults.bar_location = ARG(0, gravity);
3753
3754 list_for_each_entry(s, &rp_screens, node) {
3755 screen_update_workarea(s);
3756 screen_update_frames(s);
3757 }
3758
3759 return cmdret_new(RET_SUCCESS, NULL);
3760}
3761
3762static void
3763update_gc(rp_screen * s)
3764{
3765 XGCValues gcv;
3766
3767 gcv.foreground = rp_glob_screen.fgcolor;
3768 gcv.background = rp_glob_screen.bgcolor;
3769 gcv.function = GXcopy;
3770 gcv.line_width = 1;
3771 gcv.subwindow_mode = IncludeInferiors;
3772 XFreeGC(dpy, s->normal_gc);
3773 s->normal_gc = XCreateGC(dpy, s->root,
3774 GCForeground | GCBackground | GCFunction | GCLineWidth |
3775 GCSubwindowMode, &gcv);
3776 gcv.foreground = rp_glob_screen.bgcolor;
3777 gcv.background = rp_glob_screen.fgcolor;
3778 XFreeGC(dpy, s->inverse_gc);
3779 s->inverse_gc = XCreateGC(dpy, s->root,
3780 GCForeground | GCBackground | GCFunction | GCLineWidth |
3781 GCSubwindowMode, &gcv);
3782}
3783
3784static cmdret *
3785set_historysize(struct cmdarg **args)
3786{
3787 if (args[0] == NULL)
3788 return cmdret_new(RET_SUCCESS, "%d", defaults.history_size);
3789
3790 if (ARG(0, number) < 0)
3791 return cmdret_new(RET_FAILURE, "set historysize: %s",
3792 invalid_negative_arg);
3793
3794 defaults.history_size = ARG(0, number);
3795 return cmdret_new(RET_SUCCESS, NULL);
3796}
3797
3798static cmdret *
3799set_font(struct cmdarg **args)
3800{
3801 XftFont *font;
3802 rp_screen *s;
3803
3804 if (args[0] == NULL)
3805 return cmdret_new(RET_SUCCESS, "%s", defaults.font_string);
3806
3807 mark_edge_frames();
3808
3809 list_for_each_entry(s, &rp_screens, node) {
3810 font = XftFontOpenName(dpy, s->screen_num, ARG_STRING(0));
3811 if (font == NULL)
3812 return cmdret_new(RET_FAILURE, "set font: unknown font");
3813
3814 XftFontClose(dpy, s->xft_font);
3815 s->xft_font = font;
3816 }
3817
3818 free(defaults.font_string);
3819
3820 defaults.font_string = xstrdup(ARG_STRING(0));
3821
3822 list_for_each_entry(s, &rp_screens, node) {
3823 screen_update_frames(s);
3824 }
3825
3826 return cmdret_new(RET_SUCCESS, NULL);
3827}
3828
3829static cmdret *
3830set_padding(struct cmdarg **args)
3831{
3832 rp_screen *s;
3833 int l, t, r, b;
3834
3835 if (args[0] == NULL)
3836 return cmdret_new(RET_SUCCESS, "%d %d %d %d",
3837 defaults.padding_left,
3838 defaults.padding_top,
3839 defaults.padding_right,
3840 defaults.padding_bottom);
3841
3842 l = ARG(0, number);
3843 t = ARG(1, number);
3844 r = ARG(2, number);
3845 b = ARG(3, number);
3846
3847 if (l < 0 || t < 0 || r < 0 || b < 0)
3848 return cmdret_new(RET_FAILURE, "set padding: %s",
3849 invalid_negative_arg);
3850
3851 mark_edge_frames();
3852
3853 defaults.padding_left = l;
3854 defaults.padding_right = r;
3855 defaults.padding_top = t;
3856 defaults.padding_bottom = b;
3857
3858 list_for_each_entry(s, &rp_screens, node) {
3859 screen_update_workarea(s);
3860 screen_update_frames(s);
3861 }
3862
3863 return cmdret_new(RET_SUCCESS, NULL);
3864}
3865
3866static cmdret *
3867set_border(struct cmdarg **args)
3868{
3869 rp_screen *s;
3870
3871 if (args[0] == NULL)
3872 return cmdret_new(RET_SUCCESS, "%d",
3873 defaults.window_border_width);
3874
3875 if (ARG(0, number) < 0)
3876 return cmdret_new(RET_FAILURE, "set border: %s",
3877 invalid_negative_arg);
3878
3879 mark_edge_frames();
3880
3881 defaults.window_border_width = ARG(0, number);
3882
3883 list_for_each_entry(s, &rp_screens, node) {
3884 screen_update_frames(s);
3885 }
3886
3887 return cmdret_new(RET_SUCCESS, NULL);
3888}
3889
3890static cmdret *
3891set_onlyborder(struct cmdarg **args)
3892{
3893 rp_screen *s;
3894
3895 if (args[0] == NULL)
3896 return cmdret_new(RET_SUCCESS, "%d", defaults.only_border);
3897
3898 if (ARG(0, number) != 0 && ARG(0, number) != 1)
3899 return cmdret_new(RET_FAILURE, "set onlyborder: invalid argument");
3900
3901 mark_edge_frames();
3902
3903 defaults.only_border = ARG(0, number);
3904
3905 list_for_each_entry(s, &rp_screens, node) {
3906 screen_update_frames(s);
3907 }
3908
3909 return cmdret_new(RET_SUCCESS, NULL);
3910}
3911
3912static cmdret *
3913set_barborder(struct cmdarg **args)
3914{
3915 rp_screen *s;
3916
3917 if (args[0] == NULL)
3918 return cmdret_new(RET_SUCCESS, "%d", defaults.bar_border_width);
3919
3920 if (ARG(0, number) < 0)
3921 return cmdret_new(RET_FAILURE, "set barborder: %s",
3922 invalid_negative_arg);
3923
3924 mark_edge_frames();
3925
3926 defaults.bar_border_width = ARG(0, number);
3927
3928 /* Update the frame and bar windows. */
3929 list_for_each_entry(s, &rp_screens, node) {
3930 XSetWindowBorderWidth(dpy, s->bar_window,
3931 defaults.bar_border_width);
3932 XSetWindowBorderWidth(dpy, s->frame_window,
3933 defaults.bar_border_width);
3934 XSetWindowBorderWidth(dpy, s->input_window,
3935 defaults.bar_border_width);
3936
3937 screen_update_workarea(s);
3938 screen_update_frames(s);
3939 }
3940
3941 return cmdret_new(RET_SUCCESS, NULL);
3942}
3943
3944static cmdret *
3945set_barinpadding(struct cmdarg **args)
3946{
3947 rp_screen *s;
3948 int new_value;
3949
3950 if (args[0] == NULL)
3951 return cmdret_new(RET_SUCCESS, "%d", defaults.bar_in_padding);
3952
3953 new_value = ARG(0, number);
3954 if (new_value != 0 && new_value != 1)
3955 return cmdret_new(RET_FAILURE,
3956 "set barinpadding: invalid argument");
3957
3958 mark_edge_frames();
3959
3960 defaults.bar_in_padding = new_value;
3961
3962 list_for_each_entry(s, &rp_screens, node) {
3963 screen_update_workarea(s);
3964 screen_update_frames(s);
3965 }
3966
3967 return cmdret_new(RET_SUCCESS, NULL);
3968}
3969
3970static cmdret *
3971set_inputwidth(struct cmdarg **args)
3972{
3973 if (args[0] == NULL)
3974 return cmdret_new(RET_SUCCESS, "%d", defaults.input_window_size);
3975
3976 if (ARG(0, number) < 0)
3977 return cmdret_new(RET_FAILURE, "set inputwidth: %s",
3978 invalid_negative_arg);
3979
3980 defaults.input_window_size = ARG(0, number);
3981 return cmdret_new(RET_SUCCESS, NULL);
3982}
3983
3984static cmdret *
3985set_waitcursor(struct cmdarg **args)
3986{
3987 if (args[0] == NULL)
3988 return cmdret_new(RET_SUCCESS, "%d",
3989 defaults.wait_for_key_cursor);
3990
3991 if (ARG(0, number) != 0 && ARG(0, number) != 1)
3992 return cmdret_new(RET_FAILURE,
3993 "set waitcursor: invalid argument");
3994
3995 defaults.wait_for_key_cursor = ARG(0, number);
3996 return cmdret_new(RET_SUCCESS, NULL);
3997}
3998
3999static cmdret *
4000set_infofmt(struct cmdarg **args)
4001{
4002 if (args[0] == NULL)
4003 return cmdret_new(RET_SUCCESS, "%s", defaults.info_fmt);
4004
4005 free(defaults.info_fmt);
4006 defaults.info_fmt = xstrdup(ARG_STRING(0));
4007
4008 return cmdret_new(RET_SUCCESS, NULL);
4009}
4010
4011static cmdret *
4012set_topkmap(struct cmdarg **args)
4013{
4014 if (args[0] == NULL)
4015 return cmdret_new(RET_SUCCESS, "%s", defaults.top_kmap);
4016
4017 if (!find_keymap(ARG_STRING(0)))
4018 return cmdret_new(RET_FAILURE, "Unknown keymap %s",
4019 ARG_STRING(0));
4020
4021 ungrab_keys_all_wins();
4022
4023 free(defaults.top_kmap);
4024 defaults.top_kmap = xstrdup(ARG_STRING(0));
4025
4026 grab_keys_all_wins();
4027 XSync(dpy, False);
4028
4029 return cmdret_new(RET_SUCCESS, NULL);
4030}
4031
4032static cmdret *
4033set_winfmt(struct cmdarg **args)
4034{
4035 if (args[0] == NULL)
4036 return cmdret_new(RET_SUCCESS, "%s", defaults.window_fmt);
4037
4038 free(defaults.window_fmt);
4039 defaults.window_fmt = xstrdup(ARG_STRING(0));
4040
4041 return cmdret_new(RET_SUCCESS, NULL);
4042}
4043
4044static cmdret *
4045set_winname(struct cmdarg **args)
4046{
4047 char *name;
4048
4049 if (args[0] == NULL)
4050 switch (defaults.win_name) {
4051 case WIN_NAME_TITLE:
4052 return cmdret_new(RET_SUCCESS, "title");
4053 case WIN_NAME_RES_NAME:
4054 return cmdret_new(RET_SUCCESS, "name");
4055 case WIN_NAME_RES_CLASS:
4056 return cmdret_new(RET_SUCCESS, "class");
4057 default:
4058 PRINT_DEBUG(("Unknown win_name\n"));
4059 return cmdret_new(RET_FAILURE, "unknown");
4060 }
4061
4062 name = ARG_STRING(0);
4063
4064 if (!strncmp(name, "title", sizeof("title")))
4065 defaults.win_name = WIN_NAME_TITLE;
4066 else if (!strncmp(name, "name", sizeof("name")))
4067 defaults.win_name = WIN_NAME_RES_NAME;
4068 else if (!strncmp(name, "class", sizeof("class")))
4069 defaults.win_name = WIN_NAME_RES_CLASS;
4070 else
4071 return cmdret_new(RET_FAILURE,
4072 "set winname: invalid argument `%s'", name);
4073
4074 return cmdret_new(RET_SUCCESS, NULL);
4075}
4076
4077static cmdret *
4078set_framefmt(struct cmdarg **args)
4079{
4080 if (args[0] == NULL)
4081 return cmdret_new(RET_SUCCESS, "%s", defaults.frame_fmt);
4082
4083 free(defaults.frame_fmt);
4084 defaults.frame_fmt = xstrdup(ARG_STRING(0));
4085
4086 return cmdret_new(RET_SUCCESS, NULL);
4087}
4088
4089static cmdret *
4090set_fgcolor(struct cmdarg **args)
4091{
4092 XColor color, junk;
4093 rp_screen *s;
4094
4095 if (args[0] == NULL)
4096 return cmdret_new(RET_SUCCESS, "%s", defaults.fgcolor_string);
4097
4098 list_for_each_entry(s, &rp_screens, node) {
4099 if (!XAllocNamedColor(dpy, s->def_cmap, ARG_STRING(0), &color,
4100 &junk))
4101 return cmdret_new(RET_FAILURE,
4102 "set fgcolor: unknown color");
4103
4104 rp_glob_screen.fgcolor = color.pixel | (0xff << 24);
4105 update_gc(s);
4106
4107 if (!XftColorAllocName(dpy, DefaultVisual(dpy, s->screen_num),
4108 DefaultColormap(dpy, s->screen_num), ARG_STRING(0),
4109 &s->xft_fgcolor))
4110 return cmdret_new(RET_FAILURE,
4111 "set fgcolor: unknown color");
4112
4113 free(defaults.fgcolor_string);
4114 defaults.fgcolor_string = xstrdup(ARG_STRING(0));
4115 }
4116
4117 redraw_sticky_bar_text(1);
4118
4119 return cmdret_new(RET_SUCCESS, NULL);
4120}
4121
4122static cmdret *
4123set_bgcolor(struct cmdarg **args)
4124{
4125 XColor color, junk;
4126 rp_screen *s;
4127
4128 if (args[0] == NULL)
4129 return cmdret_new(RET_SUCCESS, "%s", defaults.bgcolor_string);
4130
4131 list_for_each_entry(s, &rp_screens, node) {
4132 if (!XAllocNamedColor(dpy, s->def_cmap, ARG_STRING(0), &color,
4133 &junk))
4134 return cmdret_new(RET_FAILURE,
4135 "set bgcolor: unknown color");
4136
4137 color.pixel |= (0xff << 24);
4138 rp_glob_screen.bgcolor = color.pixel;
4139 update_gc(s);
4140 XSetWindowBackground(dpy, s->bar_window, color.pixel);
4141 XSetWindowBackground(dpy, s->input_window, color.pixel);
4142 XSetWindowBackground(dpy, s->frame_window, color.pixel);
4143 XSetWindowBackground(dpy, s->help_window, color.pixel);
4144
4145 if (!XftColorAllocName(dpy, DefaultVisual(dpy, s->screen_num),
4146 DefaultColormap(dpy, s->screen_num), ARG_STRING(0),
4147 &s->xft_bgcolor))
4148 return cmdret_new(RET_FAILURE,
4149 "set fgcolor: unknown color");
4150
4151 free(defaults.bgcolor_string);
4152 defaults.bgcolor_string = xstrdup(ARG_STRING(0));
4153 }
4154
4155 redraw_sticky_bar_text(1);
4156
4157 return cmdret_new(RET_SUCCESS, NULL);
4158}
4159
4160static cmdret *
4161set_fwcolor(struct cmdarg **args)
4162{
4163 XColor color, junk;
4164 rp_window *win = current_window();
4165 rp_screen *s;
4166
4167 if (args[0] == NULL)
4168 return cmdret_new(RET_SUCCESS, "%s", defaults.fwcolor_string);
4169
4170 list_for_each_entry(s, &rp_screens, node) {
4171 if (!XAllocNamedColor(dpy, s->def_cmap, ARG_STRING(0), &color,
4172 &junk))
4173 return cmdret_new(RET_FAILURE,
4174 "set fwcolor: unknown color");
4175
4176 rp_glob_screen.fwcolor = color.pixel | (0xff << 24);
4177 update_gc(s);
4178
4179 free(defaults.fwcolor_string);
4180 defaults.fwcolor_string = xstrdup(ARG_STRING(0));
4181 }
4182
4183 /* Update current window. */
4184 if (win != NULL)
4185 XSetWindowBorder(dpy, win->w, rp_glob_screen.fwcolor);
4186
4187 return cmdret_new(RET_SUCCESS, NULL);
4188}
4189
4190static cmdret *
4191set_bwcolor(struct cmdarg **args)
4192{
4193 XColor color, junk;
4194 rp_window *win, *cur_win = current_window();
4195 rp_screen *s;
4196
4197 if (args[0] == NULL)
4198 return cmdret_new(RET_SUCCESS, "%s", defaults.bwcolor_string);
4199
4200 list_for_each_entry(s, &rp_screens, node) {
4201 if (!XAllocNamedColor(dpy, s->def_cmap, ARG_STRING(0), &color,
4202 &junk))
4203 return cmdret_new(RET_FAILURE,
4204 "set bwcolor: unknown color");
4205
4206 rp_glob_screen.bwcolor = color.pixel | (0xff << 24);
4207 update_gc(s);
4208
4209 free(defaults.bwcolor_string);
4210 defaults.bwcolor_string = xstrdup(ARG_STRING(0));
4211 }
4212
4213 /* Update all the visible windows. */
4214 list_for_each_entry(win, &rp_mapped_window, node) {
4215 if (win != cur_win)
4216 XSetWindowBorder(dpy, win->w, rp_glob_screen.bwcolor);
4217 }
4218
4219 return cmdret_new(RET_SUCCESS, NULL);
4220}
4221
4222static cmdret *
4223set_barbordercolor(struct cmdarg **args)
4224{
4225 XColor color, junk;
4226 rp_screen *s;
4227
4228 if (args[0] == NULL)
4229 return cmdret_new(RET_SUCCESS, "%s",
4230 defaults.barbordercolor_string);
4231
4232 list_for_each_entry(s, &rp_screens, node) {
4233 if (!XAllocNamedColor(dpy, s->def_cmap, ARG_STRING(0), &color,
4234 &junk))
4235 return cmdret_new(RET_FAILURE,
4236 "set barbordercolor: unknown color");
4237
4238 color.pixel |= (0xff << 24);
4239 rp_glob_screen.bar_bordercolor = color.pixel;
4240 update_gc(s);
4241 XSetWindowBorder(dpy, s->bar_window, color.pixel);
4242 XSetWindowBorder(dpy, s->input_window, color.pixel);
4243 XSetWindowBorder(dpy, s->frame_window, color.pixel);
4244 XSetWindowBorder(dpy, s->help_window, color.pixel);
4245
4246 free(defaults.barbordercolor_string);
4247 defaults.barbordercolor_string = xstrdup(ARG_STRING(0));
4248 }
4249
4250 redraw_sticky_bar_text(1);
4251
4252 return cmdret_new(RET_SUCCESS, NULL);
4253}
4254
4255static cmdret *
4256set_vscreens(struct cmdarg **args)
4257{
4258 if (args[0] == NULL)
4259 return cmdret_new(RET_SUCCESS, "%d", defaults.vscreens);
4260
4261 if (ARG(0, number) < 1)
4262 return cmdret_new(RET_FAILURE, "vscreens: invalid argument");
4263
4264 if (vscreens_resize(ARG(0, number)) != 0)
4265 return cmdret_new(RET_FAILURE, "vscreens: failed resizing");
4266
4267 return cmdret_new(RET_SUCCESS, NULL);
4268}
4269
4270static cmdret *
4271set_gap(struct cmdarg **args)
4272{
4273 rp_screen *s;
4274
4275 if (args[0] == NULL)
4276 return cmdret_new(RET_SUCCESS, "%d", defaults.gap);
4277
4278 if (ARG(0, number) < 0)
4279 return cmdret_new(RET_FAILURE, "gap: invalid argument");
4280
4281 mark_edge_frames();
4282
4283 defaults.gap = ARG(0, number);
4284
4285 list_for_each_entry(s, &rp_screens, node) {
4286 screen_update_workarea(s);
4287 screen_update_frames(s);
4288 }
4289
4290 return cmdret_new(RET_SUCCESS, NULL);
4291}
4292
4293static cmdret *
4294set_ignoreresizehints(struct cmdarg **args)
4295{
4296 rp_screen *s;
4297
4298 if (args[0] == NULL)
4299 return cmdret_new(RET_SUCCESS, "%d",
4300 defaults.ignore_resize_hints);
4301
4302 if (ARG(0, number) < 0 || ARG(0, number) > 1)
4303 return cmdret_new(RET_FAILURE,
4304 "ignoreresizehints: invalid argument");
4305
4306 mark_edge_frames();
4307
4308 defaults.ignore_resize_hints = ARG(0, number);
4309
4310 list_for_each_entry(s, &rp_screens, node) {
4311 screen_update_frames(s);
4312 }
4313
4314 return cmdret_new(RET_SUCCESS, NULL);
4315}
4316
4317static cmdret *
4318set_resizefmt(struct cmdarg **args)
4319{
4320 if (args[0] == NULL)
4321 return cmdret_new(RET_SUCCESS, "%s", defaults.resize_fmt);
4322
4323 free(defaults.resize_fmt);
4324 defaults.resize_fmt = xstrdup(ARG_STRING(0));
4325
4326 return cmdret_new(RET_SUCCESS, NULL);
4327}
4328
4329static cmdret *
4330set_winaddcurvscreen(struct cmdarg **args)
4331{
4332 if (args[0] == NULL)
4333 return cmdret_new(RET_SUCCESS, "%d",
4334 defaults.win_add_cur_vscreen);
4335
4336 if (ARG(0, number) < 0 || ARG(0, number) > 1)
4337 return cmdret_new(RET_FAILURE,
4338 "winaddcurvscreen: invalid argument");
4339
4340 defaults.win_add_cur_vscreen = ARG(0, number);
4341
4342 return cmdret_new(RET_SUCCESS, NULL);
4343}
4344cmdret *
4345cmd_setenv(int interactive, struct cmdarg **args)
4346{
4347 const char *var = ARG_STRING(0), *val = ARG_STRING(1);
4348
4349 PRINT_DEBUG(("setenv (\"%s\", \"%s\", 1)\n", var, val));
4350 if (setenv(var, val, 1) == -1)
4351 return cmdret_new(RET_FAILURE, "cmd_setenv failed: %s",
4352 strerror(errno));
4353
4354 return cmdret_new(RET_SUCCESS, NULL);
4355}
4356
4357cmdret *
4358cmd_getenv(int interactive, struct cmdarg **args)
4359{
4360 char *value;
4361
4362 value = getenv(ARG_STRING(0));
4363 if (value)
4364 return cmdret_new(RET_SUCCESS, "%s", value);
4365
4366 return cmdret_new(RET_FAILURE, NULL);
4367}
4368
4369/*
4370 * Thanks to Gergely Nagy <algernon@debian.org> for the original patch.
4371 */
4372cmdret *
4373cmd_chdir(int interactive, struct cmdarg **args)
4374{
4375 const char *dir;
4376
4377 if (args[0] == NULL) {
4378 dir = get_homedir();
4379 if (dir == NULL) {
4380 return cmdret_new(RET_FAILURE,
4381 "chdir: unable to find your HOME directory");
4382 }
4383 } else
4384 dir = ARG_STRING(0);
4385
4386 if (chdir(dir) == -1)
4387 return cmdret_new(RET_FAILURE, "chdir: %s: %s", dir,
4388 strerror(errno));
4389
4390 return cmdret_new(RET_SUCCESS, NULL);
4391}
4392
4393/*
4394 * Thanks to Gergely Nagy <algernon@debian.org> for the original patch.
4395 */
4396cmdret *
4397cmd_unsetenv(int interactive, struct cmdarg **args)
4398{
4399 const char *var = ARG_STRING(0);
4400
4401 /*
4402 * Use unsetenv() where possible since putenv("FOO") is not legit
4403 * everywhere
4404 */
4405 if (unsetenv(var) == -1)
4406 return cmdret_new(RET_FAILURE, "cmd_unsetenv failed: %s",
4407 strerror(errno));
4408
4409 return cmdret_new(RET_SUCCESS, NULL);
4410}
4411
4412/*
4413 * Thanks to Gergely Nagy <algernon@debian.org> for the original patch.
4414 */
4415cmdret *
4416cmd_info(int interactive, struct cmdarg **args)
4417{
4418 struct sbuf *buf;
4419 if (current_window() != NULL) {
4420 rp_window *win = current_window();
4421 rp_window_elem *win_elem;
4422 win_elem = vscreen_find_window(&rp_current_vscreen->mapped_windows,
4423 win);
4424 if (!win_elem)
4425 win_elem = vscreen_find_window(
4426 &rp_current_vscreen->unmapped_windows, win);
4427
4428 if (win_elem) {
4429 char *s;
4430 cmdret *ret;
4431
4432 if (args[0] == NULL)
4433 s = defaults.info_fmt;
4434 else
4435 s = ARG_STRING(0);
4436 buf = sbuf_new(0);
4437 format_string(s, win_elem, buf);
4438 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(buf));
4439 sbuf_free(buf);
4440 return ret;
4441 }
4442 }
4443
4444 return cmdret_new(RET_SUCCESS, "No window.");
4445}
4446
4447/*
4448 * Thanks to Gergely Nagy <algernon@debian.org> for the original patch.
4449 */
4450cmdret *
4451cmd_lastmsg(int interactive, struct cmdarg **args)
4452{
4453 show_last_message();
4454 return cmdret_new(RET_SUCCESS, NULL);
4455}
4456
4457cmdret *
4458cmd_focusup(int interactive, struct cmdarg **args)
4459{
4460 rp_frame *frame;
4461
4462 if ((frame = find_frame_up(current_frame(rp_current_vscreen))))
4463 set_active_frame(frame, 0);
4464 else
4465 show_frame_indicator(0);
4466
4467 return cmdret_new(RET_SUCCESS, NULL);
4468}
4469
4470cmdret *
4471cmd_focusdown(int interactive, struct cmdarg **args)
4472{
4473 rp_frame *frame;
4474
4475 if ((frame = find_frame_down(current_frame(rp_current_vscreen))))
4476 set_active_frame(frame, 0);
4477 else
4478 show_frame_indicator(0);
4479
4480 return cmdret_new(RET_SUCCESS, NULL);
4481}
4482
4483cmdret *
4484cmd_focusleft(int interactive, struct cmdarg **args)
4485{
4486 rp_frame *frame;
4487
4488 if ((frame = find_frame_left(current_frame(rp_current_vscreen))))
4489 set_active_frame(frame, 0);
4490 else
4491 show_frame_indicator(0);
4492
4493 return cmdret_new(RET_SUCCESS, NULL);
4494}
4495
4496cmdret *
4497cmd_focusright(int interactive, struct cmdarg **args)
4498{
4499 rp_frame *frame;
4500
4501 if ((frame = find_frame_right(current_frame(rp_current_vscreen))))
4502 set_active_frame(frame, 0);
4503 else
4504 show_frame_indicator(0);
4505
4506 return cmdret_new(RET_SUCCESS, NULL);
4507}
4508
4509cmdret *
4510cmd_exchangeup(int interactive, struct cmdarg **args)
4511{
4512 rp_frame *frame;
4513
4514 if ((frame = find_frame_up(current_frame(rp_current_vscreen))))
4515 exchange_with_frame(current_frame(rp_current_vscreen), frame);
4516
4517 return cmdret_new(RET_SUCCESS, NULL);
4518}
4519
4520cmdret *
4521cmd_exchangedown(int interactive, struct cmdarg **args)
4522{
4523 rp_frame *frame;
4524
4525 if ((frame = find_frame_down(current_frame(rp_current_vscreen))))
4526 exchange_with_frame(current_frame(rp_current_vscreen), frame);
4527
4528 return cmdret_new(RET_SUCCESS, NULL);
4529}
4530
4531cmdret *
4532cmd_exchangeleft(int interactive, struct cmdarg **args)
4533{
4534 rp_frame *frame;
4535
4536 if ((frame = find_frame_left(current_frame(rp_current_vscreen))))
4537 exchange_with_frame(current_frame(rp_current_vscreen), frame);
4538
4539 return cmdret_new(RET_SUCCESS, NULL);
4540}
4541
4542cmdret *
4543cmd_exchangeright(int interactive, struct cmdarg **args)
4544{
4545 rp_frame *frame;
4546
4547 if ((frame = find_frame_right(current_frame(rp_current_vscreen))))
4548 exchange_with_frame(current_frame(rp_current_vscreen), frame);
4549
4550 return cmdret_new(RET_SUCCESS, NULL);
4551}
4552
4553cmdret *
4554cmd_swap(int interactive, struct cmdarg **args)
4555{
4556 rp_frame *dest_frame;
4557 rp_frame *src_frame;
4558
4559 dest_frame = ARG(0, frame);
4560 src_frame = args[1] ? ARG(1, frame) : current_frame(rp_current_vscreen);
4561
4562 if (!rp_have_xrandr) {
4563 if (vscreen_find_frame_by_frame(src_frame->vscreen,
4564 dest_frame) == NULL)
4565 return cmdret_new(RET_FAILURE,
4566 "swap: frames on different screens");
4567 }
4568 exchange_with_frame(src_frame, dest_frame);
4569
4570 return cmdret_new(RET_SUCCESS, NULL);
4571}
4572
4573cmdret *
4574cmd_restart(int interactive, struct cmdarg **args)
4575{
4576 hup_signalled = 1;
4577 return cmdret_new(RET_SUCCESS, NULL);
4578}
4579
4580static cmdret *
4581set_startupmessage(struct cmdarg **args)
4582{
4583 if (args[0] == NULL)
4584 return cmdret_new(RET_SUCCESS, "%d", defaults.startup_message);
4585
4586 if (ARG(0, number) != 0 && ARG(0, number) != 1)
4587 return cmdret_new(RET_FAILURE,
4588 "set startupmessage: invalid argument");
4589
4590 defaults.startup_message = ARG(0, number);
4591 return cmdret_new(RET_SUCCESS, NULL);
4592}
4593
4594cmdret *
4595cmd_focuslast(int interactive, struct cmdarg **args)
4596{
4597 rp_frame *frame = find_last_frame(rp_current_vscreen);
4598
4599 if (frame)
4600 set_active_frame(frame, 0);
4601 else
4602 return cmdret_new(RET_FAILURE, "focuslast: no other frame");
4603
4604 return cmdret_new(RET_SUCCESS, NULL);
4605}
4606
4607cmdret *
4608cmd_link(int interactive, struct cmdarg **args)
4609{
4610 char *cmd = NULL;
4611 rp_keymap *map;
4612
4613 if (args[1])
4614 map = ARG(1, keymap);
4615 else
4616 map = find_keymap(ROOT_KEYMAP);
4617
4618 cmd = resolve_command_from_keydesc(args[0]->string, 0, map);
4619 if (cmd)
4620 return command(interactive, cmd);
4621
4622 return cmdret_new(RET_SUCCESS, NULL);
4623}
4624
4625/*
4626 * Thanks to Doug Kearns <djkea2@mugc.its.monash.edu.au> for the original patch.
4627 */
4628static cmdret *
4629set_barpadding(struct cmdarg **args)
4630{
4631 rp_screen *s;
4632 int x, y;
4633
4634 if (args[0] == NULL)
4635 return cmdret_new(RET_SUCCESS, "%d %d", defaults.bar_x_padding,
4636 defaults.bar_y_padding);
4637
4638 x = ARG(0, number);
4639 y = ARG(1, number);
4640
4641 if (x < 0 || y < 0)
4642 return cmdret_new(RET_FAILURE, "set barpadding: %s",
4643 invalid_negative_arg);
4644
4645 mark_edge_frames();
4646
4647 defaults.bar_x_padding = x;
4648 defaults.bar_y_padding = y;
4649
4650 list_for_each_entry(s, &rp_screens, node) {
4651 screen_update_workarea(s);
4652 screen_update_frames(s);
4653 }
4654
4655 return cmdret_new(RET_SUCCESS, NULL);
4656}
4657
4658static cmdret *
4659set_barsticky(struct cmdarg **args)
4660{
4661 rp_screen *s;
4662
4663 if (args[0] == NULL)
4664 return cmdret_new(RET_SUCCESS, "%d", defaults.bar_sticky);
4665
4666 if (ARG(0, number) != 0 && ARG(0, number) != 1)
4667 return cmdret_new(RET_FAILURE,
4668 "set barsticky: invalid argument");
4669
4670 mark_edge_frames();
4671
4672 defaults.bar_sticky = ARG(0, number);
4673
4674 list_for_each_entry(s, &rp_screens, node) {
4675 hide_bar(s, 0);
4676 screen_update_workarea(s);
4677 screen_update_frames(s);
4678 }
4679
4680 return cmdret_new(RET_SUCCESS, NULL);
4681}
4682
4683static cmdret *
4684set_stickyfmt(struct cmdarg **args)
4685{
4686 rp_screen *s;
4687
4688 if (args[0] == NULL)
4689 return cmdret_new(RET_SUCCESS, "%s", defaults.sticky_fmt);
4690
4691 free(defaults.sticky_fmt);
4692 defaults.sticky_fmt = xstrdup(ARG_STRING(0));
4693
4694 list_for_each_entry(s, &rp_screens, node) {
4695 hide_bar(s, 0);
4696 }
4697
4698 return cmdret_new(RET_SUCCESS, NULL);
4699}
4700
4701cmdret *
4702cmd_alias(int interactive, struct cmdarg **args)
4703{
4704 /* Add or update the alias. */
4705 add_alias(ARG_STRING(0), ARG_STRING(1));
4706 return cmdret_new(RET_SUCCESS, NULL);
4707}
4708
4709cmdret *
4710cmd_unalias(int interactive, struct cmdarg **args)
4711{
4712 char *tmp;
4713 int i;
4714
4715 /* Are we updating an existing alias, or creating a new one? */
4716 i = find_alias_index(ARG_STRING(0));
4717 if (i < 0)
4718 return cmdret_new(RET_SUCCESS, "unalias: alias not found");
4719
4720 alias_list_last--;
4721
4722 /*
4723 * Free the alias and put the last alias in the the space to
4724 * keep alias_list from becoming sparse. This code must jump
4725 * through some hoops to correctly handle the case when
4726 * alias_list_last == i.
4727 */
4728 tmp = alias_list[i].alias;
4729 alias_list[i].alias = xstrdup(alias_list[alias_list_last].alias);
4730 free(tmp);
4731 free(alias_list[alias_list_last].alias);
4732
4733 /* Do the same for the name element. */
4734 tmp = alias_list[i].name;
4735 alias_list[i].name = xstrdup(alias_list[alias_list_last].name);
4736 free(tmp);
4737 free(alias_list[alias_list_last].name);
4738
4739 return cmdret_new(RET_SUCCESS, NULL);
4740}
4741
4742cmdret *
4743cmd_nextscreen(int interactive, struct cmdarg **args)
4744{
4745 rp_screen *new_screen;
4746 rp_frame *new_frame;
4747
4748 new_screen = screen_next();
4749
4750 /* No need to go through the motions when we don't have to. */
4751 if (screen_count() <= 1 || new_screen == rp_current_screen)
4752 return cmdret_new(RET_FAILURE, "nextscreen: no other screen");
4753
4754 new_frame = vscreen_get_frame(new_screen->current_vscreen,
4755 new_screen->current_vscreen->current_frame);
4756
4757 set_active_frame(new_frame, 1);
4758
4759 return cmdret_new(RET_SUCCESS, NULL);
4760}
4761
4762cmdret *
4763cmd_prevscreen(int interactive, struct cmdarg **args)
4764{
4765 rp_screen *new_screen;
4766 rp_frame *new_frame;
4767
4768 new_screen = screen_prev();
4769
4770 /* No need to go through the motions when we don't have to. */
4771 if (screen_count() <= 1 || new_screen == rp_current_screen)
4772 return cmdret_new(RET_SUCCESS, "prevscreen: no other screen");
4773
4774 new_frame = vscreen_get_frame(new_screen->current_vscreen,
4775 new_screen->current_vscreen->current_frame);
4776
4777 set_active_frame(new_frame, 1);
4778
4779 return cmdret_new(RET_SUCCESS, NULL);
4780}
4781
4782cmdret *
4783cmd_smove(int interactive, struct cmdarg **args)
4784{
4785 rp_window *w;
4786 rp_screen *screen;
4787 int new_screen;
4788
4789 if ((w = current_window()) == NULL)
4790 return cmdret_new(RET_FAILURE, "smove: no focused window");
4791
4792 new_screen = ARG(0, number);
4793 if (new_screen < 0)
4794 return cmdret_new(RET_FAILURE, "smove: out of range");
4795
4796 screen = screen_number(new_screen);
4797 if (!screen)
4798 return cmdret_new(RET_FAILURE, "smove: screen %d not found",
4799 new_screen);
4800
4801 if (screen == rp_current_screen)
4802 return cmdret_new(RET_SUCCESS, NULL);
4803
4804 vscreen_move_window(screen->current_vscreen, w);
4805 set_active_frame(current_frame(screen->current_vscreen), 1);
4806 set_active_window(w);
4807 return cmdret_new(RET_SUCCESS, NULL);
4808}
4809
4810cmdret *
4811cmd_sselect(int interactive, struct cmdarg **args)
4812{
4813 int new_screen;
4814 rp_frame *new_frame;
4815 rp_screen *screen;
4816
4817 new_screen = ARG(0, number);
4818 if (new_screen < 0)
4819 return cmdret_new(RET_FAILURE, "sselect: out of range");
4820
4821 screen = screen_number(new_screen);
4822 if (!screen)
4823 return cmdret_new(RET_FAILURE, "sselect: screen %d not found",
4824 new_screen);
4825
4826 if (screen == rp_current_screen)
4827 return cmdret_new(RET_SUCCESS, NULL);
4828
4829 new_frame = vscreen_get_frame(screen->current_vscreen,
4830 screen->current_vscreen->current_frame);
4831 set_active_frame(new_frame, 1);
4832
4833 return cmdret_new(RET_SUCCESS, NULL);
4834}
4835
4836static cmdret *
4837set_warp(struct cmdarg **args)
4838{
4839 if (args[0] == NULL)
4840 return cmdret_new(RET_SUCCESS, "%d", defaults.warp);
4841
4842 if (ARG(0, number) != 0 && ARG(0, number) != 1)
4843 return cmdret_new(RET_FAILURE, "set warp: invalid argument");
4844
4845 defaults.warp = ARG(0, number);
4846 return cmdret_new(RET_SUCCESS, NULL);
4847}
4848
4849/*
4850 * Return a new string with the frame selector or it as a string if no selector
4851 * exists for the number.
4852 */
4853
4854/* Select a frame by number. */
4855cmdret *
4856cmd_fselect(int interactive, struct cmdarg **args)
4857{
4858 set_active_frame(ARG(0, frame), 1);
4859 return cmdret_new(RET_SUCCESS, NULL);
4860}
4861
4862char *
4863fdump(rp_vscreen *vscreen)
4864{
4865 struct sbuf *dump;
4866 rp_frame *cur;
4867
4868 dump = sbuf_new(0);
4869
4870 list_for_each_entry(cur, &(vscreen->frames), node) {
4871 char *frameset;
4872
4873 frameset = frame_dump(cur, vscreen);
4874 sbuf_concat(dump, frameset);
4875 sbuf_concat(dump, ",");
4876 free(frameset);
4877 }
4878 sbuf_chop(dump);
4879
4880 return sbuf_free_struct(dump);
4881}
4882
4883cmdret *
4884cmd_fdump(int interactively, struct cmdarg **args)
4885{
4886 rp_screen *screen;
4887 cmdret *ret;
4888 char *dump;
4889
4890 if (args[0] == NULL)
4891 screen = rp_current_screen;
4892 else {
4893 int snum;
4894 snum = ARG(0, number);
4895
4896 if (snum < 0)
4897 return cmdret_new(RET_FAILURE,
4898 "fdump: invalid negative screen number");
4899 else {
4900 screen = screen_number(snum);
4901 if (!screen)
4902 return cmdret_new(RET_FAILURE,
4903 "fdump: screen %d not found", snum);
4904 }
4905 }
4906
4907 dump = fdump(screen->current_vscreen);
4908 ret = cmdret_new(RET_SUCCESS, "%s", dump);
4909 free(dump);
4910
4911 return ret;
4912}
4913
4914cmdret *
4915frestore(char *data, rp_vscreen *v)
4916{
4917 char *token;
4918 char *d;
4919 rp_frame *new, *cur;
4920 rp_window *win;
4921 struct list_head fset;
4922 int max = -1;
4923 char *nexttok = NULL;
4924
4925 INIT_LIST_HEAD(&fset);
4926
4927 d = xstrdup(data);
4928 token = strtok_r(d, ",", &nexttok);
4929 if (token == NULL) {
4930 free(d);
4931 return cmdret_new(RET_FAILURE,
4932 "frestore: invalid frame format");
4933 }
4934
4935 /* Build the new frame set. */
4936 while (token != NULL) {
4937 new = frame_read(token, v);
4938 if (new == NULL) {
4939 free(d);
4940 return cmdret_new(RET_FAILURE,
4941 "frestore: invalid frame format");
4942 }
4943 list_add_tail(&new->node, &fset);
4944 token = strtok_r(NULL, ",", &nexttok);
4945 }
4946
4947 free(d);
4948
4949 /* Clear all the frames. */
4950 list_for_each_entry(cur, &v->frames, node) {
4951 PRINT_DEBUG(("blank %d\n", cur->number));
4952 blank_frame(cur);
4953 }
4954
4955 /* Get rid of the frames' numbers */
4956 vscreen_free_nums(v);
4957
4958 /* Splice in our new frameset. */
4959 vscreen_restore_frameset(v, &fset);
4960
4961 /* Process the frames a bit to make sure everything lines up. */
4962 list_for_each_entry(cur, &v->frames, node) {
4963 PRINT_DEBUG(("restore %d %d\n", cur->number, cur->win_number));
4964
4965 /*
4966 * Grab the frame's number, but if it already exists request a
4967 * new one.
4968 */
4969 if (!numset_add_num(v->frames_numset, cur->number)) {
4970 cur->number = numset_request(v->frames_numset);
4971 }
4972 /* Find the current frame based on last_access. */
4973 if (cur->last_access > max) {
4974 v->current_frame = cur->number;
4975 max = cur->last_access;
4976 }
4977 /* Update the window the frame points to. */
4978 if (cur->win_number != EMPTY) {
4979 set_frames_window(cur,
4980 find_window_number(cur->win_number));
4981 }
4982 }
4983
4984 /* Show the windows in the frames. */
4985 list_for_each_entry(win, &rp_mapped_window, node) {
4986 if (win->frame_number != EMPTY) {
4987 maximize(win);
4988 unhide_window(win);
4989 }
4990 }
4991
4992 set_active_frame(current_frame(v), 0);
4993 update_bar(v->screen);
4994 show_frame_indicator(0);
4995
4996 PRINT_DEBUG(("Done.\n"));
4997 return cmdret_new(RET_SUCCESS, NULL);
4998}
4999
5000cmdret *
5001cmd_frestore(int interactively, struct cmdarg **args)
5002{
5003 push_frame_undo(rp_current_vscreen); /* fdump to stack */
5004 return frestore(ARG_STRING(0), rp_current_vscreen);
5005}
5006
5007cmdret *
5008cmd_verbexec(int interactive, struct cmdarg **args)
5009{
5010 marked_message_printf(0, 0, "Running %s", ARG_STRING(0));
5011 spawn(ARG_STRING(0), current_frame(rp_current_vscreen));
5012 return cmdret_new(RET_SUCCESS, NULL);
5013}
5014
5015static cmdret *
5016set_winliststyle(struct cmdarg **args)
5017{
5018 if (args[0] == NULL)
5019 return cmdret_new(RET_SUCCESS, "%s",
5020 defaults.window_list_style ? "column" : "row");
5021
5022 if (!strcmp("column", ARG_STRING(0)))
5023 defaults.window_list_style = STYLE_COLUMN;
5024 else if (!strcmp("row", ARG_STRING(0)))
5025 defaults.window_list_style = STYLE_ROW;
5026 else
5027 return cmdret_new(RET_FAILURE,
5028 "set winliststyle: invalid argument");
5029
5030 return cmdret_new(RET_SUCCESS, NULL);
5031}
5032
5033cmdret *
5034cmd_vrename(int interactive, struct cmdarg **args)
5035{
5036 if (screen_find_vscreen_by_name(rp_current_screen, ARG_STRING(0), 1))
5037 return cmdret_new(RET_FAILURE, "vrename: duplicate vscreen name");
5038 vscreen_rename(rp_current_vscreen, ARG_STRING(0));
5039
5040 /* Update the vscreen list. */
5041 update_vscreen_names(rp_current_screen);
5042
5043 return cmdret_new(RET_SUCCESS, NULL);
5044}
5045
5046cmdret *
5047cmd_vselect(int interactive, struct cmdarg **args)
5048{
5049 rp_vscreen *v;
5050
5051 v = find_vscreen(ARG_STRING(0));
5052 if (v)
5053 set_current_vscreen(v);
5054 else
5055 return cmd_vscreens(interactive, NULL);
5056
5057 return cmdret_new(RET_SUCCESS, NULL);
5058}
5059
5060/* Show all the vscreens, with the current one highlighted. */
5061cmdret *
5062cmd_vscreens(int interactive, struct cmdarg **args)
5063{
5064 struct sbuf *vscreen_list = NULL;
5065 int dummy;
5066 cmdret *ret;
5067
5068 if (interactive) {
5069 rp_screen *s;
5070 s = rp_current_screen;
5071 ret = cmdret_new(RET_SUCCESS, NULL);
5072 if (defaults.bar_timeout == 0 && s->bar_is_raised) {
5073 hide_bar(s, 0);
5074 return ret;
5075 }
5076 show_vscreen_bar(s);
5077 } else {
5078 vscreen_list = sbuf_new(0);
5079 get_vscreen_list(rp_current_screen, "\n", vscreen_list, &dummy,
5080 &dummy);
5081 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(vscreen_list));
5082 sbuf_free(vscreen_list);
5083 }
5084 return ret;
5085}
5086
5087cmdret *
5088cmd_vnext(int interactive, struct cmdarg **args)
5089{
5090 rp_vscreen *v;
5091 v = vscreen_next_vscreen(rp_current_vscreen);
5092 if (!v)
5093 return cmdret_new(RET_FAILURE, "%s", "next vscreen failed");
5094
5095 set_current_vscreen(v);
5096 return cmdret_new(RET_SUCCESS, NULL);
5097}
5098
5099cmdret *
5100cmd_vprev(int interactive, struct cmdarg **args)
5101{
5102 rp_vscreen *v;
5103 v = vscreen_prev_vscreen(rp_current_vscreen);
5104 if (!v)
5105 return cmdret_new(RET_FAILURE, "%s", "prev vscreen failed");
5106
5107 set_current_vscreen(v);
5108 return cmdret_new(RET_SUCCESS, NULL);
5109}
5110
5111cmdret *
5112cmd_vother(int interactive, struct cmdarg **args)
5113{
5114 rp_vscreen *v;
5115 v = screen_last_vscreen(rp_current_screen);
5116 if (!v)
5117 /* todo: should we just return success here so it's a no-op? */
5118 return cmdret_new(RET_FAILURE, "%s", "last vscreen failed");
5119
5120 set_current_vscreen(v);
5121 return cmdret_new(RET_SUCCESS, NULL);
5122}
5123
5124cmdret *
5125cmd_addhook(int interactive, struct cmdarg **args)
5126{
5127 struct list_head *hook;
5128 struct sbuf *cmd;
5129
5130 hook = hook_lookup(ARG_STRING(0));
5131 if (hook == NULL)
5132 return cmdret_new(RET_FAILURE, "addhook: unknown hook '%s'",
5133 ARG_STRING(0));
5134
5135 /* Add the command to the hook */
5136 cmd = sbuf_new(0);
5137 sbuf_copy(cmd, ARG_STRING(1));
5138 hook_add(hook, cmd);
5139
5140 return cmdret_new(RET_SUCCESS, NULL);
5141}
5142
5143cmdret *
5144cmd_remhook(int interactive, struct cmdarg **args)
5145{
5146 struct sbuf *cmd;
5147
5148 /* Remove the command from the hook */
5149 cmd = sbuf_new(0);
5150 sbuf_copy(cmd, ARG_STRING(1));
5151 hook_remove(ARG(0, hook), cmd);
5152 sbuf_free(cmd);
5153
5154 return cmdret_new(RET_SUCCESS, NULL);
5155}
5156
5157cmdret *
5158cmd_listhook(int interactive, struct cmdarg **args)
5159{
5160 cmdret *ret;
5161 struct sbuf *buffer;
5162 struct list_head *hook;
5163 struct sbuf *cur;
5164
5165 hook = hook_lookup(ARG_STRING(0));
5166 if (hook == NULL)
5167 return cmdret_new(RET_FAILURE, "listhook: unknown hook '%s'",
5168 ARG_STRING(0));
5169
5170 if (list_empty(hook))
5171 return cmdret_new(RET_FAILURE, " Nothing defined for %s ",
5172 ARG_STRING(0));
5173
5174 buffer = sbuf_new(0);
5175
5176 list_for_each_entry(cur, hook, node) {
5177 sbuf_printf_concat(buffer, "%s", sbuf_get(cur));
5178 if (cur->node.next != hook)
5179 sbuf_printf_concat(buffer, "\n");
5180 }
5181
5182 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(buffer));
5183 sbuf_free(buffer);
5184 return ret;
5185}
5186
5187cmdret *
5188cmd_readkey(int interactive, struct cmdarg **args)
5189{
5190 char *keysym_name;
5191 rp_action *key_action;
5192 KeySym keysym; /* Key pressed */
5193 unsigned int mod; /* Modifiers */
5194 int rat_grabbed = 0;
5195 rp_keymap *map;
5196 cmdret *ret;
5197
5198 map = ARG(0, keymap);
5199
5200 /*
5201 * Change the mouse icon to indicate to the user we are waiting for
5202 * more keystrokes
5203 */
5204 if (defaults.wait_for_key_cursor) {
5205 grab_rat();
5206 rat_grabbed = 1;
5207 }
5208 read_single_key(&keysym, &mod, NULL, 0);
5209
5210 if (rat_grabbed)
5211 ungrab_rat();
5212
5213 if ((key_action = find_keybinding(keysym, x11_mask_to_rp_mask(mod), map))) {
5214 return command(1, key_action->data);
5215 }
5216
5217 /* No key match, notify user. */
5218 keysym_name = keysym_to_string(keysym, x11_mask_to_rp_mask(mod));
5219 ret = cmdret_new(RET_FAILURE, "readkey: unbound key '%s'", keysym_name);
5220 free(keysym_name);
5221 return ret;
5222}
5223
5224cmdret *
5225cmd_newkmap(int interactive, struct cmdarg **args)
5226{
5227 rp_keymap *map;
5228
5229 map = find_keymap(ARG_STRING(0));
5230 if (map)
5231 return cmdret_new(RET_FAILURE,
5232 "newkmap: keymap '%s' already exists", ARG_STRING(0));
5233
5234 map = keymap_new(ARG_STRING(0));
5235 list_add_tail(&map->node, &rp_keymaps);
5236
5237 return cmdret_new(RET_SUCCESS, NULL);
5238}
5239
5240cmdret *
5241cmd_delkmap(int interactive, struct cmdarg **args)
5242{
5243 rp_keymap *map, *top, *root;
5244
5245 top = find_keymap(defaults.top_kmap);
5246 root = find_keymap(ROOT_KEYMAP);
5247
5248 map = ARG(0, keymap);
5249 if (map == root || map == top)
5250 return cmdret_new(RET_FAILURE,
5251 "delkmap: cannot delete keymap '%s'", ARG_STRING(0));
5252
5253 list_del(&map->node);
5254
5255 return cmdret_new(RET_SUCCESS, NULL);
5256}
5257
5258static cmdret *
5259set_framesels(struct cmdarg **args)
5260{
5261 if (args[0] == NULL)
5262 return cmdret_new(RET_SUCCESS, "%s", defaults.frame_selectors);
5263
5264 free(defaults.frame_selectors);
5265 defaults.frame_selectors = xstrdup(ARG_STRING(0));
5266 return cmdret_new(RET_SUCCESS, NULL);
5267}
5268
5269cmdret *
5270cmd_set(int interactive, struct cmdarg **args)
5271{
5272 struct sbuf *scur;
5273 struct cmdarg *acur;
5274 struct list_head *iter, *tmp;
5275 struct list_head head, arglist;
5276 int i, nargs = 0, raw = 0;
5277 int parsed_args;
5278 cmdret *result = NULL;
5279 struct cmdarg **cmdargs;
5280 char *input;
5281
5282 if (args[0] == NULL) {
5283 /* List all the settings. */
5284 cmdret *ret;
5285 struct sbuf *s = sbuf_new(0);
5286 struct set_var *cur, *last;
5287
5288 list_last(last, &set_vars, node);
5289 list_for_each_entry(cur, &set_vars, node) {
5290 cmdret *r;
5291 r = cur->set_fn(args);
5292 sbuf_printf_concat(s, "%s: %s", cur->var, r->output);
5293 /* Skip a newline on the last line. */
5294 if (cur != last)
5295 sbuf_concat(s, "\n");
5296 cmdret_free(r);
5297 }
5298
5299 /* Return the accumulated string. */
5300 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(s));
5301 sbuf_free(s);
5302 return ret;
5303 }
5304
5305 INIT_LIST_HEAD(&arglist);
5306 INIT_LIST_HEAD(&head);
5307
5308 /* We need to tell parse_args about arg_REST and arg_SHELLCMD. */
5309 for (i = 0; i < ARG(0, variable)->nargs; i++)
5310 if (ARG(0, variable)->args[i].type == arg_REST
5311 || ARG(0, variable)->args[i].type == arg_COMMAND
5312 || ARG(0, variable)->args[i].type == arg_SHELLCMD
5313 || ARG(0, variable)->args[i].type == arg_RAW) {
5314 raw = 1;
5315 nargs = i;
5316 break;
5317 }
5318
5319 /* Parse the arguments and call the function. */
5320 if (args[1])
5321 input = xstrdup(args[1]->string);
5322 else
5323 input = xstrdup("");
5324
5325 result = parse_args(input, &head, nargs, raw);
5326 free(input);
5327 if (result)
5328 goto failed;
5329
5330 result = parsed_input_to_args(ARG(0, variable)->nargs,
5331 ARG(0, variable)->args, &head, &arglist, &parsed_args, NULL);
5332 if (result)
5333 goto failed;
5334
5335 /* 0 or nargs is acceptable */
5336 if (list_size(&arglist) > 0 &&
5337 list_size(&arglist) < ARG(0, variable)->nargs) {
5338 result = cmdret_new(RET_FAILURE, "not enough arguments.");
5339 goto failed;
5340 } else if (list_size(&head) > ARG(0, variable)->nargs) {
5341 result = cmdret_new(RET_FAILURE, "cmd_set: too many args.");
5342 goto failed;
5343 }
5344
5345 cmdargs = arg_array(&arglist);
5346 result = ARG(0, variable)->set_fn(cmdargs);
5347 free(cmdargs);
5348
5349 /* Free the lists. */
5350failed:
5351 /* Free the parsed strings */
5352 list_for_each_safe_entry(scur, iter, tmp, &head, node) {
5353 sbuf_free(scur);
5354 }
5355
5356 /* Free the args */
5357 list_for_each_safe_entry(acur, iter, tmp, &arglist, node) {
5358 arg_free(acur);
5359 }
5360
5361 return result;
5362}
5363
5364cmdret *
5365cmd_sfdump(int interactively, struct cmdarg **args)
5366{
5367 char screen_suffix[16];
5368 cmdret *ret;
5369 struct sbuf *dump;
5370 rp_frame *cur_frame;
5371 rp_screen *cur_screen;
5372
5373 dump = sbuf_new(0);
5374
5375 list_for_each_entry(cur_screen, &rp_screens, node) {
5376 snprintf(screen_suffix, sizeof(screen_suffix), " %d,",
5377 cur_screen->number);
5378
5379 list_for_each_entry(cur_frame,
5380 &(cur_screen->current_vscreen->frames), node) {
5381 char *frameset;
5382
5383 frameset = frame_dump(cur_frame,
5384 cur_screen->current_vscreen);
5385 sbuf_concat(dump, frameset);
5386 sbuf_concat(dump, screen_suffix);
5387 free(frameset);
5388 }
5389 }
5390
5391 sbuf_chop(dump);
5392 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(dump));
5393 sbuf_free(dump);
5394 return ret;
5395}
5396
5397cmdret *
5398cmd_sfrestore(int interactively, struct cmdarg **args)
5399{
5400 char *copy, *ptr, *token;
5401 rp_screen *screen;
5402 int out_of_screen = 0, restored = 0;
5403
5404 list_for_each_entry(screen, &rp_screens, node) {
5405 sbuf_free(screen->scratch_buffer);
5406 screen->scratch_buffer = sbuf_new(0);
5407 }
5408
5409 copy = xstrdup(ARG_STRING(0));
5410
5411 token = strtok(copy, ",");
5412 if (token == NULL) {
5413 free(copy);
5414 return cmdret_new(RET_FAILURE,
5415 "sfrestore: invalid frame format");
5416 }
5417
5418 while (token != NULL) {
5419 int snum;
5420
5421 /* search for end of frameset */
5422 ptr = token;
5423 while (*ptr != ')')
5424 ptr++;
5425 ptr++;
5426
5427 snum = string_to_positive_int(ptr);
5428 screen = screen_number(snum);
5429 if (screen) {
5430 /*
5431 * clobber screen number here, frestore() doesn't need
5432 * it
5433 */
5434 *ptr = '\0';
5435 sbuf_concat(screen->scratch_buffer, token);
5436 sbuf_concat(screen->scratch_buffer, ",");
5437 } else
5438 out_of_screen++;
5439
5440 /* continue with next frameset */
5441 token = strtok(NULL, ",");
5442 }
5443
5444 free(copy);
5445
5446 /* now restore the frames for each screen */
5447 list_for_each_entry(screen, &rp_screens, node) {
5448 cmdret *ret;
5449
5450 if (strlen(sbuf_get(screen->scratch_buffer)) == 0)
5451 continue;
5452
5453 push_frame_undo(screen->current_vscreen); /* fdump to stack */
5454
5455 /*
5456 * XXX save the failure of each frestore and display it in case
5457 * of error
5458 */
5459 ret = frestore(sbuf_get(screen->scratch_buffer),
5460 screen->current_vscreen);
5461 if (ret->success)
5462 restored++;
5463 cmdret_free(ret);
5464
5465 sbuf_free(screen->scratch_buffer);
5466 screen->scratch_buffer = NULL;
5467 }
5468
5469 if (!out_of_screen)
5470 return cmdret_new(RET_SUCCESS, "screens restored: %d", restored);
5471
5472 return cmdret_new(RET_SUCCESS,
5473 "screens restored: %d, frames out of screen: %d",
5474 restored, out_of_screen);
5475}
5476
5477cmdret *
5478cmd_sdump(int interactive, struct cmdarg **args)
5479{
5480 cmdret *ret;
5481 struct sbuf *s;
5482 char *tmp;
5483 rp_screen *cur_screen;
5484
5485 s = sbuf_new(0);
5486 list_for_each_entry(cur_screen, &rp_screens, node) {
5487 tmp = screen_dump(cur_screen);
5488 sbuf_concat(s, tmp);
5489 sbuf_concat(s, ",");
5490 free(tmp);
5491 }
5492 sbuf_chop(s);
5493
5494 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(s));
5495 sbuf_free(s);
5496 return ret;
5497}
5498
5499static cmdret *
5500set_maxundos(struct cmdarg **args)
5501{
5502 rp_frame_undo *cur;
5503
5504 if (args[0] == NULL)
5505 return cmdret_new(RET_SUCCESS, "%d", defaults.maxundos);
5506
5507 if (ARG(0, number) < 0)
5508 return cmdret_new(RET_FAILURE, "set maxundos: %s",
5509 invalid_negative_arg);
5510
5511 defaults.maxundos = ARG(0, number);
5512
5513 /* Delete any superfluous undos */
5514 while (list_size(&rp_frame_undos) > defaults.maxundos) {
5515 /* Delete the oldest node */
5516 list_last(cur, &rp_frame_undos, node);
5517 del_frame_undo(cur);
5518 }
5519
5520 return cmdret_new(RET_SUCCESS, NULL);
5521}
5522
5523cmdret *
5524cmd_cnext(int interactive, struct cmdarg **args)
5525{
5526 rp_window *cur, *last, *win;
5527
5528 cur = current_window();
5529 if (!cur || !cur->res_class) /* Can't be done. */
5530 return cmd_next(interactive, args);
5531
5532 /* CUR !in cycle list, so LAST marks last node. */
5533 last = vscreen_prev_window(rp_current_vscreen, cur);
5534
5535 if (last)
5536 for (win = vscreen_next_window(rp_current_vscreen, cur);
5537 win;
5538 win = vscreen_next_window(rp_current_vscreen, win)) {
5539 if (win->res_class
5540 && strcmp(cur->res_class, win->res_class)) {
5541 set_active_window_force(win);
5542 return cmdret_new(RET_SUCCESS, NULL);
5543 }
5544 if (win == last)
5545 break;
5546 }
5547
5548 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
5549}
5550
5551cmdret *
5552cmd_cprev(int interactive, struct cmdarg **args)
5553{
5554 rp_window *cur, *last, *win;
5555
5556 cur = current_window();
5557 if (!cur || !cur->res_class) /* Can't be done. */
5558 return cmd_next(interactive, args);
5559
5560 /* CUR !in cycle list, so LAST marks last node. */
5561 last = vscreen_next_window(rp_current_vscreen, cur);
5562
5563 if (last)
5564 for (win = vscreen_prev_window(rp_current_vscreen, cur);
5565 win;
5566 win = vscreen_prev_window(rp_current_vscreen, win)) {
5567 if (win->res_class
5568 && strcmp(cur->res_class, win->res_class)) {
5569 set_active_window_force(win);
5570 return cmdret_new(RET_SUCCESS, NULL);
5571 }
5572 if (win == last)
5573 break;
5574 }
5575
5576 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
5577}
5578
5579cmdret *
5580cmd_inext(int interactive, struct cmdarg **args)
5581{
5582 rp_window *cur, *last, *win;
5583
5584 cur = current_window();
5585 if (!cur || !cur->res_class) /* Can't be done. */
5586 return cmd_next(interactive, args);
5587
5588 /* CUR !in cycle list, so LAST marks last node. */
5589 last = vscreen_prev_window(rp_current_vscreen, cur);
5590
5591 if (last)
5592 for (win = vscreen_next_window(rp_current_vscreen, cur);
5593 win;
5594 win = vscreen_next_window(rp_current_vscreen, win)) {
5595 if (win->res_class
5596 && !strcmp(cur->res_class, win->res_class)) {
5597 set_active_window_force(win);
5598 return cmdret_new(RET_SUCCESS, NULL);
5599 }
5600 if (win == last)
5601 break;
5602 }
5603
5604 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
5605}
5606
5607cmdret *
5608cmd_iprev(int interactive, struct cmdarg **args)
5609{
5610 rp_window *cur, *last, *win;
5611
5612 cur = current_window();
5613 if (!cur || !cur->res_class) /* Can't be done. */
5614 return cmd_next(interactive, args);
5615
5616 /* CUR !in cycle list, so LAST marks last node. */
5617 last = vscreen_next_window(rp_current_vscreen, cur);
5618
5619 if (last)
5620 for (win = vscreen_prev_window(rp_current_vscreen, cur);
5621 win;
5622 win = vscreen_prev_window(rp_current_vscreen, win)) {
5623 if (win->res_class
5624 && !strcmp(cur->res_class, win->res_class)) {
5625 set_active_window_force(win);
5626 return cmdret_new(RET_SUCCESS, NULL);
5627 }
5628 if (win == last)
5629 break;
5630 }
5631
5632 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
5633}
5634
5635cmdret *
5636cmd_cother(int interactive, struct cmdarg **args)
5637{
5638 rp_window *cur, *w = NULL;
5639
5640 cur = current_window();
5641 if (cur)
5642 w = vscreen_last_window_by_class(rp_current_vscreen,
5643 cur->res_class);
5644
5645 if (!w)
5646 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
5647 else
5648 set_active_window_force(w);
5649
5650 return cmdret_new(RET_SUCCESS, NULL);
5651}
5652
5653cmdret *
5654cmd_iother(int interactive, struct cmdarg **args)
5655{
5656 rp_window *cur, *w = NULL;
5657
5658 cur = current_window();
5659 if (cur)
5660 w = vscreen_last_window_by_class_complement(rp_current_vscreen,
5661 cur->res_class);
5662
5663 if (!w)
5664 return cmdret_new(RET_FAILURE, "%s", MESSAGE_NO_OTHER_WINDOW);
5665 else
5666 set_active_window_force(w);
5667
5668 return cmdret_new(RET_SUCCESS, NULL);
5669}
5670
5671cmdret *
5672cmd_undo(int interactive, struct cmdarg **args)
5673{
5674 rp_frame_undo *cur;
5675
5676 cur = pop_frame_undo();
5677 if (!cur)
5678 return cmdret_new(RET_FAILURE,
5679 "No more undo information available");
5680 else {
5681 cmdret *ret;
5682
5683 ret = frestore(cur->frames, cur->vscreen);
5684 return ret;
5685 }
5686}
5687
5688cmdret *
5689cmd_redo(int interactive, struct cmdarg **args)
5690{
5691 rp_frame_undo *cur;
5692 cmdret *ret;
5693
5694 /* The current layout goes on the undo. */
5695 cur = pop_frame_redo();
5696 if (!cur)
5697 return cmdret_new(RET_FAILURE,
5698 "No more redo information available");
5699
5700 ret = frestore(cur->frames, cur->vscreen);
5701 return ret;
5702}
5703
5704cmdret *
5705cmd_prompt(int interactive, struct cmdarg **args)
5706{
5707 cmdret *ret;
5708 char *output;
5709
5710 if (args[0] == NULL)
5711 output = get_input(MESSAGE_PROMPT_COMMAND, hist_PROMPT,
5712 trivial_completions);
5713 else {
5714 char *arg_str, *prefix;
5715
5716 arg_str = ARG_STRING(0);
5717 prefix = strchr(arg_str, ':');
5718
5719 if (prefix) {
5720 struct sbuf *query;
5721
5722 prefix++; /* Don't return the colon. */
5723 query = sbuf_new(prefix - arg_str);
5724 sbuf_nconcat(query, arg_str, prefix - arg_str);
5725 output = get_more_input(sbuf_get(query), prefix,
5726 hist_PROMPT, BASIC, trivial_completions);
5727 sbuf_free(query);
5728 } else {
5729 output = get_input(arg_str, hist_PROMPT,
5730 trivial_completions);
5731 }
5732 }
5733
5734 if (output == NULL)
5735 return cmdret_new(RET_FAILURE, NULL); /* User aborted */
5736
5737 ret = cmdret_new(RET_SUCCESS, "%s", output);
5738 free(output);
5739
5740 return ret;
5741}
5742
5743cmdret *
5744cmd_describekey(int interactive, struct cmdarg **args)
5745{
5746 char *keysym_name;
5747 rp_action *key_action;
5748 KeySym keysym; /* Key pressed */
5749 unsigned int mod; /* Modifiers */
5750 int rat_grabbed = 0;
5751 rp_keymap *map;
5752
5753 map = ARG(0, keymap);
5754
5755 /*
5756 * Change the mouse icon to indicate to the user we are waiting for
5757 * more keystrokes
5758 */
5759 if (defaults.wait_for_key_cursor) {
5760 grab_rat();
5761 rat_grabbed = 1;
5762 }
5763 read_single_key(&keysym, &mod, NULL, 0);
5764
5765 if (rat_grabbed)
5766 ungrab_rat();
5767
5768 if ((key_action = find_keybinding(keysym, x11_mask_to_rp_mask(mod),
5769 map))) {
5770 cmdret *ret;
5771 keysym_name = keysym_to_string(keysym,
5772 x11_mask_to_rp_mask(mod));
5773 ret = cmdret_new(RET_SUCCESS, "%s bound to '%s'", keysym_name,
5774 key_action->data);
5775 free(keysym_name);
5776 return ret;
5777 } else {
5778 cmdret *ret;
5779 /* No key match, notify user. */
5780 keysym_name = keysym_to_string(keysym,
5781 x11_mask_to_rp_mask(mod));
5782 ret = cmdret_new(RET_SUCCESS, "describekey: unbound key '%s'",
5783 keysym_name);
5784 free(keysym_name);
5785 return ret;
5786 }
5787}
5788
5789cmdret *
5790cmd_dedicate(int interactive, struct cmdarg **args)
5791{
5792 rp_frame *f;
5793
5794 f = current_frame(rp_current_vscreen);
5795 if (f == NULL)
5796 return cmdret_new(RET_SUCCESS, NULL);
5797
5798 if (args[0] != NULL) {
5799 int dedicated;
5800
5801 dedicated = ARG(0, number);
5802 if (dedicated != 0 && dedicated != 1)
5803 return cmdret_new(RET_FAILURE,
5804 "Invalid \"dedicate\" value, use 0 or 1.");
5805 f->dedicated = dedicated;
5806 } else
5807 /* Just toggle it, rather than on or off. */
5808 f->dedicated = !(f->dedicated);
5809
5810 return cmdret_new(RET_SUCCESS, "Consider this frame %s.",
5811 f->dedicated ? "chaste" : "promiscuous");
5812}
5813
5814cmdret *
5815cmd_putsel(int interactive, struct cmdarg **args)
5816{
5817 set_selection(ARG_STRING(0));
5818 return cmdret_new(RET_SUCCESS, NULL);
5819}
5820
5821cmdret *
5822cmd_getsel(int interactive, struct cmdarg **args)
5823{
5824 char *sel;
5825 cmdret *ret;
5826 sel = get_selection();
5827 if (sel != NULL) {
5828 ret = cmdret_new(RET_SUCCESS, "%s", sel);
5829 free(sel);
5830 return ret;
5831 }
5832
5833 return cmdret_new(RET_FAILURE, "getsel: no X11 selection");
5834}
5835
5836cmdret *
5837cmd_vmove(int interactive, struct cmdarg **args)
5838{
5839 rp_vscreen *v;
5840 rp_window *w;
5841
5842 if ((w = current_window()) == NULL)
5843 return cmdret_new(RET_FAILURE, "vmove: no focused window");
5844
5845 if (!(v = find_vscreen(ARG_STRING(0))))
5846 return cmdret_new(RET_FAILURE, "vmove: invalid vscreen");
5847
5848 vscreen_move_window(v, w);
5849 set_current_vscreen(v);
5850 set_active_window(w);
5851 return cmdret_new(RET_SUCCESS, NULL);
5852}
5853
5854cmdret *
5855cmd_stick(int interactive, struct cmdarg **args)
5856{
5857 rp_window *cur = current_window();
5858
5859 if (cur == NULL)
5860 return cmdret_new(RET_FAILURE, NULL);
5861
5862 cur->sticky_frame = cur->frame_number;
5863
5864 return cmdret_new(RET_SUCCESS, NULL);
5865}
5866
5867cmdret *
5868cmd_unstick(int interactive, struct cmdarg **args)
5869{
5870 rp_window *cur = current_window();
5871
5872 if (cur == NULL)
5873 return cmdret_new(RET_FAILURE, NULL);
5874
5875 cur->sticky_frame = EMPTY;
5876
5877 return cmdret_new(RET_SUCCESS, NULL);
5878}
5879
5880cmdret *
5881cmd_commands(int interactive, struct cmdarg **args)
5882{
5883 struct sbuf *sb;
5884 struct user_command *cur;
5885 cmdret *ret;
5886
5887 sb = sbuf_new(0);
5888 list_for_each_entry(cur, &user_commands, node) {
5889 sbuf_printf_concat(sb, "%s\n", cur->name);
5890 }
5891 sbuf_chop(sb);
5892
5893 ret = cmdret_new(RET_SUCCESS, "%s", sbuf_get(sb));
5894 sbuf_free(sb);
5895 return ret;
5896}