2012-01-14 16:02:30 +01:00
|
|
|
|
/*
|
|
|
|
|
* vim:ts=4:sw=4:expandtab
|
|
|
|
|
*
|
|
|
|
|
* i3 - an improved dynamic tiling window manager
|
2015-04-04 02:17:56 +02:00
|
|
|
|
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
|
2012-01-14 16:02:30 +01:00
|
|
|
|
*
|
|
|
|
|
* commands.c: all command functions (see commands_parser.c)
|
|
|
|
|
*
|
|
|
|
|
*/
|
2016-10-11 09:13:35 +02:00
|
|
|
|
#include "all.h"
|
|
|
|
|
|
2016-04-15 09:27:43 +02:00
|
|
|
|
#include <stdint.h>
|
2012-01-14 16:02:30 +01:00
|
|
|
|
#include <float.h>
|
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 20:53:29 +01:00
|
|
|
|
#include <stdarg.h>
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2016-01-09 17:18:05 +01:00
|
|
|
|
#ifdef I3_ASAN_ENABLED
|
|
|
|
|
#include <sanitizer/lsan_interface.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2013-06-05 15:04:57 +02:00
|
|
|
|
#include "shmlog.h"
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-05-02 22:01:50 +02:00
|
|
|
|
// Macros to make the YAJL API a bit easier to use.
|
2014-06-15 19:07:02 +02:00
|
|
|
|
#define y(x, ...) (cmd_output->json_gen != NULL ? yajl_gen_##x(cmd_output->json_gen, ##__VA_ARGS__) : 0)
|
|
|
|
|
#define ystr(str) (cmd_output->json_gen != NULL ? yajl_gen_string(cmd_output->json_gen, (unsigned char *)str, strlen(str)) : 0)
|
|
|
|
|
#define ysuccess(success) \
|
|
|
|
|
do { \
|
|
|
|
|
if (cmd_output->json_gen != NULL) { \
|
|
|
|
|
y(map_open); \
|
|
|
|
|
ystr("success"); \
|
|
|
|
|
y(bool, success); \
|
|
|
|
|
y(map_close); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
2015-03-04 14:01:42 +01:00
|
|
|
|
#define yerror(format, ...) \
|
|
|
|
|
do { \
|
|
|
|
|
if (cmd_output->json_gen != NULL) { \
|
|
|
|
|
char *message; \
|
|
|
|
|
sasprintf(&message, format, ##__VA_ARGS__); \
|
|
|
|
|
y(map_open); \
|
|
|
|
|
ystr("success"); \
|
|
|
|
|
y(bool, false); \
|
|
|
|
|
ystr("error"); \
|
|
|
|
|
ystr(message); \
|
|
|
|
|
y(map_close); \
|
|
|
|
|
free(message); \
|
|
|
|
|
} \
|
2014-06-15 19:07:02 +02:00
|
|
|
|
} while (0)
|
2012-05-02 22:01:50 +02:00
|
|
|
|
|
2016-04-04 09:33:59 +02:00
|
|
|
|
/** If an error occurred during parsing of the criteria, we want to exit instead
|
2015-12-09 13:39:08 +01:00
|
|
|
|
* of relying on fallback behavior. See #2091. */
|
|
|
|
|
#define HANDLE_INVALID_MATCH \
|
|
|
|
|
do { \
|
|
|
|
|
if (current_match->error != NULL) { \
|
|
|
|
|
yerror("Invalid match: %s", current_match->error); \
|
|
|
|
|
return; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
/** When the command did not include match criteria (!), we use the currently
|
2012-05-09 23:39:44 +02:00
|
|
|
|
* focused container. Do not confuse this case with a command which included
|
2012-01-14 16:02:30 +01:00
|
|
|
|
* criteria but which did not match any windows. This macro has to be called in
|
|
|
|
|
* every command.
|
|
|
|
|
*/
|
2014-06-15 19:07:02 +02:00
|
|
|
|
#define HANDLE_EMPTY_MATCH \
|
|
|
|
|
do { \
|
2015-12-09 13:39:08 +01:00
|
|
|
|
HANDLE_INVALID_MATCH; \
|
|
|
|
|
\
|
2014-06-15 19:07:02 +02:00
|
|
|
|
if (match_is_empty(current_match)) { \
|
2016-01-09 13:39:00 +01:00
|
|
|
|
while (!TAILQ_EMPTY(&owindows)) { \
|
|
|
|
|
owindow *ow = TAILQ_FIRST(&owindows); \
|
|
|
|
|
TAILQ_REMOVE(&owindows, ow, owindows); \
|
|
|
|
|
free(ow); \
|
|
|
|
|
} \
|
2014-06-15 19:07:02 +02:00
|
|
|
|
owindow *ow = smalloc(sizeof(owindow)); \
|
|
|
|
|
ow->con = focused; \
|
|
|
|
|
TAILQ_INIT(&owindows); \
|
|
|
|
|
TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-05-02 16:05:27 +02:00
|
|
|
|
/*
|
|
|
|
|
* Checks whether we switched to a new workspace and returns false in that case,
|
|
|
|
|
* signaling that further workspace switching should be done by the calling function
|
|
|
|
|
* If not, calls workspace_back_and_forth() if workspace_auto_back_and_forth is set
|
|
|
|
|
* and return true, signaling that no further workspace switching should occur in the calling function.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
static bool maybe_back_and_forth(struct CommandResultIR *cmd_output, const char *name) {
|
2012-05-02 16:05:27 +02:00
|
|
|
|
Con *ws = con_get_workspace(focused);
|
|
|
|
|
|
|
|
|
|
/* If we switched to a different workspace, do nothing */
|
|
|
|
|
if (strcmp(ws->name, name) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
DLOG("This workspace is already focused.\n");
|
2012-05-02 22:27:41 +02:00
|
|
|
|
if (config.workspace_auto_back_and_forth) {
|
|
|
|
|
workspace_back_and_forth();
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2012-05-02 16:05:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 10:51:18 +02:00
|
|
|
|
/*
|
|
|
|
|
* Return the passed workspace unless it is the current one and auto back and
|
|
|
|
|
* forth is enabled, in which case the back_and_forth workspace is returned.
|
|
|
|
|
*/
|
|
|
|
|
static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
|
|
|
|
|
Con *current, *baf;
|
|
|
|
|
|
|
|
|
|
if (!config.workspace_auto_back_and_forth)
|
|
|
|
|
return workspace;
|
|
|
|
|
|
|
|
|
|
current = con_get_workspace(focused);
|
|
|
|
|
|
|
|
|
|
if (current == workspace) {
|
|
|
|
|
baf = workspace_back_and_forth_get();
|
|
|
|
|
if (baf != NULL) {
|
|
|
|
|
DLOG("Substituting workspace with back_and_forth, as it is focused.\n");
|
|
|
|
|
return baf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return workspace;
|
|
|
|
|
}
|
|
|
|
|
|
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 20:53:29 +01:00
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* Criteria functions.
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
2012-10-08 13:26:24 +02:00
|
|
|
|
/*
|
|
|
|
|
* Helper data structure for an operation window (window on which the operation
|
|
|
|
|
* will be performed). Used to build the TAILQ owindows.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
typedef struct owindow {
|
|
|
|
|
Con *con;
|
2016-11-08 22:46:43 +01:00
|
|
|
|
|
|
|
|
|
TAILQ_ENTRY(owindow)
|
|
|
|
|
owindows;
|
2012-10-08 13:26:24 +02:00
|
|
|
|
} owindow;
|
|
|
|
|
|
|
|
|
|
typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
|
|
|
|
|
|
|
|
|
|
static owindows_head owindows;
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Initializes the specified 'Match' data structure and the initial state of
|
|
|
|
|
* commands.c for matching target windows of a command.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_criteria_init(I3_CMD) {
|
2012-01-27 22:47:55 +01:00
|
|
|
|
Con *con;
|
|
|
|
|
owindow *ow;
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("Initializing criteria, current_match = %p\n", current_match);
|
2014-03-19 09:34:42 +01:00
|
|
|
|
match_free(current_match);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
match_init(current_match);
|
2012-01-27 22:47:55 +01:00
|
|
|
|
while (!TAILQ_EMPTY(&owindows)) {
|
|
|
|
|
ow = TAILQ_FIRST(&owindows);
|
|
|
|
|
TAILQ_REMOVE(&owindows, ow, owindows);
|
|
|
|
|
free(ow);
|
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
TAILQ_INIT(&owindows);
|
|
|
|
|
/* copy all_cons */
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(con, &all_cons, all_cons) {
|
2012-01-27 22:47:55 +01:00
|
|
|
|
ow = smalloc(sizeof(owindow));
|
2012-01-14 16:02:30 +01:00
|
|
|
|
ow->con = con;
|
|
|
|
|
TAILQ_INSERT_TAIL(&owindows, ow, owindows);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* A match specification just finished (the closing square bracket was found),
|
|
|
|
|
* so we filter the list of owindows.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_criteria_match_windows(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
owindow *next, *current;
|
|
|
|
|
|
|
|
|
|
DLOG("match specification finished, matching...\n");
|
|
|
|
|
/* copy the old list head to iterate through it and start with a fresh
|
|
|
|
|
* list which will contain only matching windows */
|
|
|
|
|
struct owindows_head old = owindows;
|
|
|
|
|
TAILQ_INIT(&owindows);
|
|
|
|
|
for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
|
|
|
|
|
/* make a copy of the next pointer and advance the pointer to the
|
|
|
|
|
* next element as we are going to invalidate the element’s
|
|
|
|
|
* next/prev pointers by calling TAILQ_INSERT_TAIL later */
|
|
|
|
|
current = next;
|
|
|
|
|
next = TAILQ_NEXT(next, owindows);
|
|
|
|
|
|
|
|
|
|
DLOG("checking if con %p / %s matches\n", current->con, current->con->name);
|
2015-12-15 13:38:56 +01:00
|
|
|
|
|
|
|
|
|
/* We use this flag to prevent matching on window-less containers if
|
|
|
|
|
* only window-specific criteria were specified. */
|
|
|
|
|
bool accept_match = false;
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
if (current_match->con_id != NULL) {
|
2015-12-15 13:38:56 +01:00
|
|
|
|
accept_match = true;
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
if (current_match->con_id == current->con) {
|
2015-12-15 13:38:56 +01:00
|
|
|
|
DLOG("con_id matched.\n");
|
2014-03-19 09:56:34 +01:00
|
|
|
|
} else {
|
2015-12-15 13:38:56 +01:00
|
|
|
|
DLOG("con_id does not match.\n");
|
|
|
|
|
FREE(current);
|
|
|
|
|
continue;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
2015-12-15 13:38:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current_match->mark != NULL && !TAILQ_EMPTY(&(current->con->marks_head))) {
|
|
|
|
|
accept_match = true;
|
|
|
|
|
bool matched_by_mark = false;
|
|
|
|
|
|
2015-10-19 18:10:20 +02:00
|
|
|
|
mark_t *mark;
|
|
|
|
|
TAILQ_FOREACH(mark, &(current->con->marks_head), marks) {
|
|
|
|
|
if (!regex_matches(current_match->mark, mark->name))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
DLOG("match by mark\n");
|
2015-12-15 13:38:56 +01:00
|
|
|
|
matched_by_mark = true;
|
2015-10-19 18:10:20 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2015-12-15 13:38:56 +01:00
|
|
|
|
|
|
|
|
|
if (!matched_by_mark) {
|
|
|
|
|
DLOG("mark does not match.\n");
|
|
|
|
|
FREE(current);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current->con->window != NULL) {
|
|
|
|
|
if (match_matches_window(current_match, current->con->window)) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("matches window!\n");
|
2015-12-15 13:38:56 +01:00
|
|
|
|
accept_match = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
} else {
|
2016-02-01 09:42:55 +01:00
|
|
|
|
DLOG("doesn't match\n");
|
2015-12-15 13:38:56 +01:00
|
|
|
|
FREE(current);
|
|
|
|
|
continue;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-12-15 13:38:56 +01:00
|
|
|
|
|
|
|
|
|
if (accept_match) {
|
|
|
|
|
TAILQ_INSERT_TAIL(&owindows, current, owindows);
|
|
|
|
|
} else {
|
|
|
|
|
FREE(current);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Interprets a ctype=cvalue pair and adds it to the current match
|
|
|
|
|
* specification.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_criteria_add(I3_CMD, const char *ctype, const char *cvalue) {
|
2015-09-27 09:42:26 +02:00
|
|
|
|
match_parse_property(current_match, ctype, cvalue);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-10 21:06:29 +01:00
|
|
|
|
static void move_matches_to_workspace(Con *ws) {
|
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
|
|
|
|
con_move_to_workspace(current->con, ws, true, false, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-11 06:44:40 +02:00
|
|
|
|
#define CHECK_MOVE_CON_TO_WORKSPACE \
|
|
|
|
|
do { \
|
|
|
|
|
HANDLE_EMPTY_MATCH; \
|
|
|
|
|
if (TAILQ_EMPTY(&owindows)) { \
|
|
|
|
|
yerror("Nothing to move: specified criteria don't match any window"); \
|
|
|
|
|
return; \
|
|
|
|
|
} else { \
|
|
|
|
|
bool found = false; \
|
|
|
|
|
owindow *current = TAILQ_FIRST(&owindows); \
|
|
|
|
|
while (current) { \
|
|
|
|
|
owindow *next = TAILQ_NEXT(current, owindows); \
|
|
|
|
|
\
|
|
|
|
|
if (current->con->type == CT_WORKSPACE && !con_has_children(current->con)) { \
|
|
|
|
|
TAILQ_REMOVE(&owindows, current, owindows); \
|
|
|
|
|
} else { \
|
|
|
|
|
found = true; \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
current = next; \
|
|
|
|
|
} \
|
|
|
|
|
if (!found) { \
|
|
|
|
|
yerror("Nothing to move: workspace empty"); \
|
|
|
|
|
return; \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move [window|container] [to] workspace
|
2012-05-09 23:45:12 +02:00
|
|
|
|
* next|prev|next_on_output|prev_on_output|current'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_move_con_to_workspace(I3_CMD, const char *which) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("which=%s\n", which);
|
|
|
|
|
|
2018-09-11 06:44:40 +02:00
|
|
|
|
CHECK_MOVE_CON_TO_WORKSPACE;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
|
|
|
|
/* get the workspace */
|
|
|
|
|
Con *ws;
|
|
|
|
|
if (strcmp(which, "next") == 0)
|
|
|
|
|
ws = workspace_next();
|
|
|
|
|
else if (strcmp(which, "prev") == 0)
|
|
|
|
|
ws = workspace_prev();
|
|
|
|
|
else if (strcmp(which, "next_on_output") == 0)
|
|
|
|
|
ws = workspace_next_on_output();
|
|
|
|
|
else if (strcmp(which, "prev_on_output") == 0)
|
|
|
|
|
ws = workspace_prev_on_output();
|
2012-05-09 23:45:12 +02:00
|
|
|
|
else if (strcmp(which, "current") == 0)
|
|
|
|
|
ws = con_get_workspace(focused);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
else {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("BUG: called with which=%s", which);
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-10 21:06:29 +01:00
|
|
|
|
move_matches_to_workspace(ws);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-21 12:02:14 +02:00
|
|
|
|
/*
|
2012-09-04 10:51:18 +02:00
|
|
|
|
* Implementation of 'move [window|container] [to] workspace back_and_forth'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
|
2017-12-10 21:06:29 +01:00
|
|
|
|
Con *ws = workspace_back_and_forth_get();
|
2012-09-04 10:51:18 +02:00
|
|
|
|
if (ws == NULL) {
|
2013-09-25 23:17:38 +02:00
|
|
|
|
yerror("No workspace was previously active.");
|
2012-09-04 10:51:18 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2017-12-10 21:06:29 +01:00
|
|
|
|
move_matches_to_workspace(ws);
|
2012-09-04 10:51:18 +02:00
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2015-10-26 22:38:06 +01:00
|
|
|
|
* Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace <name>'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2018-09-11 06:44:40 +02:00
|
|
|
|
void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth) {
|
2014-04-30 11:12:22 +02:00
|
|
|
|
if (strncasecmp(name, "__", strlen("__")) == 0) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("You cannot move containers to i3-internal workspaces (\"%s\").", name);
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-11 06:44:40 +02:00
|
|
|
|
CHECK_MOVE_CON_TO_WORKSPACE;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
|
|
|
|
LOG("should move window to workspace %s\n", name);
|
|
|
|
|
/* get the workspace */
|
2015-03-24 09:27:42 +01:00
|
|
|
|
Con *ws = workspace_get(name, NULL);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2018-09-11 06:44:40 +02:00
|
|
|
|
if (no_auto_back_and_forth == NULL) {
|
2015-10-26 22:38:06 +01:00
|
|
|
|
ws = maybe_auto_back_and_forth_workspace(ws);
|
2018-09-11 06:44:40 +02:00
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2017-12-10 21:06:29 +01:00
|
|
|
|
move_matches_to_workspace(ws);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-08 20:33:46 +02:00
|
|
|
|
/*
|
2015-10-26 22:38:06 +01:00
|
|
|
|
* Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace number <name>'.
|
2012-04-08 20:33:46 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2018-09-11 06:44:40 +02:00
|
|
|
|
void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth) {
|
|
|
|
|
CHECK_MOVE_CON_TO_WORKSPACE;
|
2012-04-08 20:33:46 +02:00
|
|
|
|
|
2012-08-05 16:34:38 +02:00
|
|
|
|
LOG("should move window to workspace %s\n", which);
|
2012-04-08 20:33:46 +02:00
|
|
|
|
|
2014-06-22 17:12:51 +02:00
|
|
|
|
long parsed_num = ws_name_to_number(which);
|
|
|
|
|
if (parsed_num == -1) {
|
2012-08-31 20:19:27 +02:00
|
|
|
|
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
2015-03-04 14:01:42 +01:00
|
|
|
|
yerror("Could not parse number \"%s\"", which);
|
2012-04-08 20:33:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 21:36:51 +02:00
|
|
|
|
Con *ws = get_existing_workspace_by_num(parsed_num);
|
2017-12-10 21:08:46 +01:00
|
|
|
|
if (!ws) {
|
|
|
|
|
ws = workspace_get(which, NULL);
|
2012-04-08 20:33:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-11 06:44:40 +02:00
|
|
|
|
if (no_auto_back_and_forth == NULL) {
|
2017-12-10 21:08:46 +01:00
|
|
|
|
ws = maybe_auto_back_and_forth_workspace(ws);
|
2018-09-11 06:44:40 +02:00
|
|
|
|
}
|
2012-04-08 20:33:46 +02:00
|
|
|
|
|
2017-12-10 21:08:46 +01:00
|
|
|
|
move_matches_to_workspace(ws);
|
2012-04-08 20:33:46 +02:00
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-04-08 20:33:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 14:36:50 +02:00
|
|
|
|
/*
|
|
|
|
|
* Convert a string direction ("left", "right", etc.) to a direction_t. Assumes
|
|
|
|
|
* valid direction string.
|
|
|
|
|
*/
|
|
|
|
|
static direction_t parse_direction(const char *str) {
|
|
|
|
|
if (strcmp(str, "left") == 0) {
|
|
|
|
|
return D_LEFT;
|
|
|
|
|
} else if (strcmp(str, "right") == 0) {
|
|
|
|
|
return D_RIGHT;
|
|
|
|
|
} else if (strcmp(str, "up") == 0) {
|
|
|
|
|
return D_UP;
|
|
|
|
|
} else if (strcmp(str, "down") == 0) {
|
|
|
|
|
return D_DOWN;
|
|
|
|
|
} else {
|
|
|
|
|
ELOG("Invalid direction. This is a parser bug.\n");
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cmd_resize_floating(I3_CMD, const char *way, const char *direction_str, Con *floating_con, int px) {
|
2012-11-27 09:26:31 +01:00
|
|
|
|
Rect old_rect = floating_con->rect;
|
2013-04-15 20:30:22 +02:00
|
|
|
|
Con *focused_con = con_descend_focused(floating_con);
|
|
|
|
|
|
2018-08-23 14:36:50 +02:00
|
|
|
|
direction_t direction;
|
|
|
|
|
if (strcmp(direction_str, "height") == 0) {
|
|
|
|
|
direction = D_DOWN;
|
|
|
|
|
} else if (strcmp(direction_str, "width") == 0) {
|
|
|
|
|
direction = D_RIGHT;
|
|
|
|
|
} else {
|
|
|
|
|
direction = parse_direction(direction_str);
|
|
|
|
|
}
|
|
|
|
|
orientation_t orientation = orientation_from_direction(direction);
|
|
|
|
|
|
2013-04-15 20:30:22 +02:00
|
|
|
|
/* ensure that resize will take place even if pixel increment is smaller than
|
|
|
|
|
* height increment or width increment.
|
|
|
|
|
* fixes #1011 */
|
2015-08-30 23:04:20 +02:00
|
|
|
|
const i3Window *window = focused_con->window;
|
|
|
|
|
if (window != NULL) {
|
2018-08-23 14:36:50 +02:00
|
|
|
|
if (orientation == VERT) {
|
|
|
|
|
if (px < 0) {
|
2015-08-30 23:04:20 +02:00
|
|
|
|
px = (-px < window->height_increment) ? -window->height_increment : px;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
} else {
|
2015-08-30 23:04:20 +02:00
|
|
|
|
px = (px < window->height_increment) ? window->height_increment : px;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (px < 0) {
|
2015-08-30 23:04:20 +02:00
|
|
|
|
px = (-px < window->width_increment) ? -window->width_increment : px;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
} else {
|
2015-08-30 23:04:20 +02:00
|
|
|
|
px = (px < window->width_increment) ? window->width_increment : px;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
}
|
2015-08-30 23:04:20 +02:00
|
|
|
|
}
|
2013-04-15 20:30:22 +02:00
|
|
|
|
}
|
2012-11-27 09:26:31 +01:00
|
|
|
|
|
2018-08-23 14:36:50 +02:00
|
|
|
|
if (orientation == VERT) {
|
2012-04-08 15:59:49 +02:00
|
|
|
|
floating_con->rect.height += px;
|
|
|
|
|
} else {
|
|
|
|
|
floating_con->rect.width += px;
|
|
|
|
|
}
|
2012-11-25 20:55:49 +01:00
|
|
|
|
floating_check_size(floating_con);
|
2012-11-27 09:26:31 +01:00
|
|
|
|
|
|
|
|
|
/* Did we actually resize anything or did the size constraints prevent us?
|
|
|
|
|
* If we could not resize, exit now to not move the window. */
|
2018-08-23 14:36:50 +02:00
|
|
|
|
if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0) {
|
2012-11-27 09:26:31 +01:00
|
|
|
|
return;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
}
|
2012-11-27 09:26:31 +01:00
|
|
|
|
|
2018-08-23 14:36:50 +02:00
|
|
|
|
if (direction == D_UP) {
|
2013-02-08 17:41:41 +01:00
|
|
|
|
floating_con->rect.y -= (floating_con->rect.height - old_rect.height);
|
2018-08-23 14:36:50 +02:00
|
|
|
|
} else if (direction == D_LEFT) {
|
2013-02-08 17:41:41 +01:00
|
|
|
|
floating_con->rect.x -= (floating_con->rect.width - old_rect.width);
|
2012-11-27 09:26:31 +01:00
|
|
|
|
}
|
2012-09-24 21:14:40 +02:00
|
|
|
|
|
|
|
|
|
/* If this is a scratchpad window, don't auto center it from now on. */
|
2018-08-23 14:36:50 +02:00
|
|
|
|
if (floating_con->scratchpad_state == SCRATCHPAD_FRESH) {
|
2012-09-24 21:14:40 +02:00
|
|
|
|
floating_con->scratchpad_state = SCRATCHPAD_CHANGED;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
}
|
2012-04-08 15:59:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 02:03:26 +02:00
|
|
|
|
static bool cmd_resize_tiling_direction(I3_CMD, Con *current, const char *direction, int px, int ppt) {
|
2013-09-24 15:46:58 +02:00
|
|
|
|
Con *second = NULL;
|
|
|
|
|
Con *first = current;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
direction_t search_direction = parse_direction(direction);
|
2013-09-24 15:46:58 +02:00
|
|
|
|
|
2018-01-06 17:59:27 +01:00
|
|
|
|
bool res = resize_find_tiling_participants(&first, &second, search_direction, false);
|
2013-09-24 15:46:58 +02:00
|
|
|
|
if (!res) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("No second container found in this direction.");
|
2012-07-22 21:25:38 +02:00
|
|
|
|
return false;
|
2012-04-08 15:59:49 +02:00
|
|
|
|
}
|
2012-07-22 21:37:26 +02:00
|
|
|
|
|
2018-08-23 21:09:52 +02:00
|
|
|
|
if (ppt) {
|
|
|
|
|
/* For backwards compatibility, 'X px or Y ppt' means that ppt is
|
|
|
|
|
* preferred. */
|
|
|
|
|
px = 0;
|
2012-04-08 15:59:49 +02:00
|
|
|
|
}
|
2018-08-23 21:09:52 +02:00
|
|
|
|
return resize_neighboring_cons(first, second, px, ppt);
|
2012-04-08 15:59:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 02:50:23 +02:00
|
|
|
|
static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, const char *direction, int px, double ppt) {
|
2012-04-08 15:59:49 +02:00
|
|
|
|
LOG("width/height resize\n");
|
|
|
|
|
|
2018-01-06 17:59:27 +01:00
|
|
|
|
/* get the appropriate current container (skip stacked/tabbed cons) */
|
|
|
|
|
Con *dummy = NULL;
|
|
|
|
|
direction_t search_direction = (strcmp(direction, "width") == 0 ? D_LEFT : D_DOWN);
|
|
|
|
|
bool search_result = resize_find_tiling_participants(¤t, &dummy, search_direction, true);
|
|
|
|
|
if (search_result == false) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Failed to find appropriate tiling containers for resize operation");
|
2018-01-06 17:59:27 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-04-08 15:59:49 +02:00
|
|
|
|
|
|
|
|
|
/* get the default percentage */
|
|
|
|
|
int children = con_num_children(current->parent);
|
|
|
|
|
LOG("ins. %d children\n", children);
|
|
|
|
|
double percentage = 1.0 / children;
|
|
|
|
|
LOG("default percentage = %f\n", percentage);
|
|
|
|
|
|
|
|
|
|
/* Ensure all the other children have a percentage set. */
|
|
|
|
|
Con *child;
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
|
2012-04-08 15:59:49 +02:00
|
|
|
|
LOG("child->percent = %f (child %p)\n", child->percent, child);
|
|
|
|
|
if (child->percent == 0.0)
|
|
|
|
|
child->percent = percentage;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 01:14:11 +02:00
|
|
|
|
double new_current_percent;
|
|
|
|
|
double subtract_percent;
|
2018-08-24 02:50:23 +02:00
|
|
|
|
if (ppt != 0.0) {
|
2018-08-24 01:14:11 +02:00
|
|
|
|
new_current_percent = current->percent + ppt;
|
|
|
|
|
} else {
|
|
|
|
|
new_current_percent = px_resize_to_percent(current, px);
|
|
|
|
|
ppt = new_current_percent - current->percent;
|
|
|
|
|
}
|
|
|
|
|
subtract_percent = ppt / (children - 1);
|
2018-09-06 03:56:31 +02:00
|
|
|
|
if (ppt < 0.0 && new_current_percent < percent_for_1px(current)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Not resizing, container would end with less than 1px");
|
2018-09-06 03:56:31 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-08-24 01:14:11 +02:00
|
|
|
|
|
2012-04-08 15:59:49 +02:00
|
|
|
|
LOG("new_current_percent = %f\n", new_current_percent);
|
|
|
|
|
LOG("subtract_percent = %f\n", subtract_percent);
|
2017-12-26 09:37:22 +01:00
|
|
|
|
/* Ensure that the new percentages are positive. */
|
2018-09-06 03:56:31 +02:00
|
|
|
|
if (subtract_percent >= 0.0) {
|
|
|
|
|
TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
|
|
|
|
|
if (child == current) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (child->percent - subtract_percent < percent_for_1px(child)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Not resizing, already at minimum size (child %p would end up with a size of %.f", child, child->percent - subtract_percent);
|
2018-09-06 03:56:31 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-04-08 15:59:49 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 09:37:22 +01:00
|
|
|
|
current->percent = new_current_percent;
|
2012-04-08 15:59:49 +02:00
|
|
|
|
LOG("current->percent after = %f\n", current->percent);
|
|
|
|
|
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
|
2012-04-08 15:59:49 +02:00
|
|
|
|
if (child == current)
|
|
|
|
|
continue;
|
|
|
|
|
child->percent -= subtract_percent;
|
|
|
|
|
LOG("child->percent after (%p) = %f\n", child, child->percent);
|
|
|
|
|
}
|
2012-07-22 21:25:38 +02:00
|
|
|
|
|
|
|
|
|
return true;
|
2012-04-08 15:59:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'resize grow|shrink <direction> [<px> px] [or <ppt> ppt]'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_resize(I3_CMD, const char *way, const char *direction, long resize_px, long resize_ppt) {
|
2015-09-27 16:32:54 +02:00
|
|
|
|
DLOG("resizing in way %s, direction %s, px %ld or ppt %ld\n", way, direction, resize_px, resize_ppt);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
if (strcmp(way, "shrink") == 0) {
|
2015-09-27 16:32:54 +02:00
|
|
|
|
resize_px *= -1;
|
|
|
|
|
resize_ppt *= -1;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-28 20:36:25 +02:00
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
owindow *current;
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2014-03-15 03:59:08 +01:00
|
|
|
|
/* Don't handle dock windows (issue #1201) */
|
2014-04-18 20:14:22 +02:00
|
|
|
|
if (current->con->window && current->con->window->dock) {
|
2014-03-15 03:59:08 +01:00
|
|
|
|
DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-28 20:36:25 +02:00
|
|
|
|
Con *floating_con;
|
|
|
|
|
if ((floating_con = con_inside_floating(current->con))) {
|
2015-09-27 16:32:54 +02:00
|
|
|
|
cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, resize_px);
|
2012-07-22 21:25:38 +02:00
|
|
|
|
} else {
|
2012-09-28 20:36:25 +02:00
|
|
|
|
if (strcmp(direction, "width") == 0 ||
|
|
|
|
|
strcmp(direction, "height") == 0) {
|
2018-08-24 02:50:23 +02:00
|
|
|
|
const double ppt = (double)resize_ppt / 100.0;
|
2015-09-27 16:32:54 +02:00
|
|
|
|
if (!cmd_resize_tiling_width_height(current_match, cmd_output,
|
2018-08-24 02:03:26 +02:00
|
|
|
|
current->con, direction,
|
2018-08-24 02:50:23 +02:00
|
|
|
|
resize_px, ppt))
|
2012-09-28 20:36:25 +02:00
|
|
|
|
return;
|
|
|
|
|
} else {
|
2015-09-27 16:32:54 +02:00
|
|
|
|
if (!cmd_resize_tiling_direction(current_match, cmd_output,
|
2018-08-24 02:03:26 +02:00
|
|
|
|
current->con, direction,
|
2018-08-23 21:09:52 +02:00
|
|
|
|
resize_px, resize_ppt))
|
2012-09-28 20:36:25 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2012-07-22 21:25:38 +02:00
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 02:06:52 +02:00
|
|
|
|
static bool resize_set_tiling(I3_CMD, Con *target, orientation_t resize_orientation, bool is_ppt, long target_size) {
|
|
|
|
|
direction_t search_direction;
|
|
|
|
|
char *mode;
|
|
|
|
|
if (resize_orientation == HORIZ) {
|
|
|
|
|
search_direction = D_LEFT;
|
|
|
|
|
mode = "width";
|
|
|
|
|
} else {
|
|
|
|
|
search_direction = D_DOWN;
|
|
|
|
|
mode = "height";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the appropriate current container (skip stacked/tabbed cons) */
|
|
|
|
|
Con *dummy;
|
|
|
|
|
resize_find_tiling_participants(&target, &dummy, search_direction, true);
|
|
|
|
|
|
|
|
|
|
/* Calculate new size for the target container */
|
2018-08-24 02:50:23 +02:00
|
|
|
|
double ppt = 0.0;
|
2018-08-24 02:06:52 +02:00
|
|
|
|
int px = 0;
|
|
|
|
|
if (is_ppt) {
|
2018-08-24 02:50:23 +02:00
|
|
|
|
ppt = (double)target_size / 100.0 - target->percent;
|
2018-08-24 02:06:52 +02:00
|
|
|
|
} else {
|
|
|
|
|
px = target_size - (resize_orientation == HORIZ ? target->rect.width : target->rect.height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perform resizing and report failure if not possible */
|
|
|
|
|
return cmd_resize_tiling_width_height(current_match, cmd_output,
|
|
|
|
|
target, mode, px, ppt);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-05 08:31:45 +02:00
|
|
|
|
/*
|
2017-10-18 01:04:42 +02:00
|
|
|
|
* Implementation of 'resize set <width> [px | ppt] <height> [px | ppt]'.
|
2015-09-05 08:31:45 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2017-10-18 01:04:42 +02:00
|
|
|
|
void cmd_resize_set(I3_CMD, long cwidth, const char *mode_width, long cheight, const char *mode_height) {
|
|
|
|
|
DLOG("resizing to %ld %s x %ld %s\n", cwidth, mode_width, cheight, mode_height);
|
2018-01-08 23:25:08 +01:00
|
|
|
|
if (cwidth < 0 || cheight < 0) {
|
2017-10-18 01:04:42 +02:00
|
|
|
|
ELOG("Resize failed: dimensions cannot be negative (was %ld %s x %ld %s)\n", cwidth, mode_width, cheight, mode_height);
|
2015-09-05 08:31:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
owindow *current;
|
2018-01-08 23:25:08 +01:00
|
|
|
|
bool success = true;
|
2015-09-05 08:31:45 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
Con *floating_con;
|
|
|
|
|
if ((floating_con = con_inside_floating(current->con))) {
|
2017-10-18 01:04:42 +02:00
|
|
|
|
Con *output = con_get_output(floating_con);
|
2018-01-08 23:25:08 +01:00
|
|
|
|
if (cwidth == 0) {
|
2018-05-02 16:43:43 +02:00
|
|
|
|
cwidth = floating_con->rect.width;
|
2018-01-08 23:25:08 +01:00
|
|
|
|
} else if (mode_width && strcmp(mode_width, "ppt") == 0) {
|
2017-10-18 01:04:42 +02:00
|
|
|
|
cwidth = output->rect.width * ((double)cwidth / 100.0);
|
|
|
|
|
}
|
2018-01-08 23:25:08 +01:00
|
|
|
|
if (cheight == 0) {
|
2018-05-02 16:43:43 +02:00
|
|
|
|
cheight = floating_con->rect.height;
|
2018-01-08 23:25:08 +01:00
|
|
|
|
} else if (mode_height && strcmp(mode_height, "ppt") == 0) {
|
2017-10-18 01:04:42 +02:00
|
|
|
|
cheight = output->rect.height * ((double)cheight / 100.0);
|
|
|
|
|
}
|
2015-09-27 16:32:54 +02:00
|
|
|
|
floating_resize(floating_con, cwidth, cheight);
|
2015-09-05 08:31:45 +02:00
|
|
|
|
} else {
|
2018-01-08 23:25:08 +01:00
|
|
|
|
if (current->con->window && current->con->window->dock) {
|
|
|
|
|
DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-06 04:58:51 +02:00
|
|
|
|
if (cwidth > 0) {
|
|
|
|
|
bool is_ppt = mode_width && strcmp(mode_width, "ppt") == 0;
|
2018-08-24 02:06:52 +02:00
|
|
|
|
success &= resize_set_tiling(current_match, cmd_output, current->con,
|
2018-09-06 04:58:51 +02:00
|
|
|
|
HORIZ, is_ppt, cwidth);
|
2018-01-08 23:25:08 +01:00
|
|
|
|
}
|
2018-09-06 04:58:51 +02:00
|
|
|
|
if (cheight > 0) {
|
|
|
|
|
bool is_ppt = mode_height && strcmp(mode_height, "ppt") == 0;
|
2018-08-24 02:06:52 +02:00
|
|
|
|
success &= resize_set_tiling(current_match, cmd_output, current->con,
|
2018-09-06 04:58:51 +02:00
|
|
|
|
VERT, is_ppt, cheight);
|
2018-01-08 23:25:08 +01:00
|
|
|
|
}
|
2015-09-05 08:31:45 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
2018-01-08 23:25:08 +01:00
|
|
|
|
ysuccess(success);
|
2015-09-05 08:31:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-21 14:21:04 +02:00
|
|
|
|
static int border_width_from_style(border_style_t border_style, long border_width, Con *con) {
|
|
|
|
|
if (border_style == BS_NONE) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (border_width >= 0) {
|
|
|
|
|
return logical_px(border_width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool is_floating = con_inside_floating(con) != NULL;
|
|
|
|
|
/* Load the configured defaults. */
|
|
|
|
|
if (is_floating && border_style == config.default_floating_border) {
|
|
|
|
|
return config.default_floating_border_width;
|
|
|
|
|
} else if (!is_floating && border_style == config.default_border) {
|
|
|
|
|
return config.default_border_width;
|
|
|
|
|
} else {
|
|
|
|
|
/* Use some hardcoded values. */
|
|
|
|
|
return logical_px(border_style == BS_NORMAL ? 2 : 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2015-06-09 23:13:40 +02:00
|
|
|
|
* Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2016-02-09 21:03:44 +01:00
|
|
|
|
void cmd_border(I3_CMD, const char *border_style_str, long border_width) {
|
|
|
|
|
DLOG("border style should be changed to %s with border width %ld\n", border_style_str, border_width);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
owindow *current;
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
2016-02-09 21:03:44 +01:00
|
|
|
|
|
2018-04-21 14:21:04 +02:00
|
|
|
|
border_style_t border_style;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
if (strcmp(border_style_str, "toggle") == 0) {
|
2018-04-21 14:21:04 +02:00
|
|
|
|
border_style = (current->con->border_style + 1) % 3;
|
|
|
|
|
} else if (strcmp(border_style_str, "normal") == 0) {
|
|
|
|
|
border_style = BS_NORMAL;
|
|
|
|
|
} else if (strcmp(border_style_str, "pixel") == 0) {
|
|
|
|
|
border_style = BS_PIXEL;
|
|
|
|
|
} else if (strcmp(border_style_str, "none") == 0) {
|
|
|
|
|
border_style = BS_NONE;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
} else {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("BUG: called with border_style=%s", border_style_str);
|
2018-04-21 14:21:04 +02:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
2016-02-09 21:03:44 +01:00
|
|
|
|
|
2018-04-21 14:21:04 +02:00
|
|
|
|
const int con_border_width = border_width_from_style(border_style, border_width, current->con);
|
|
|
|
|
con_set_border_style(current->con, border_style, con_border_width);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'nop <comment>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_nop(I3_CMD, const char *comment) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
LOG("-------------------------------------------------\n");
|
|
|
|
|
LOG(" NOP: %s\n", comment);
|
|
|
|
|
LOG("-------------------------------------------------\n");
|
2018-01-06 23:24:33 +01:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'append_layout <path>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_append_layout(I3_CMD, const char *cpath) {
|
2018-03-27 02:36:54 +02:00
|
|
|
|
LOG("Appending layout \"%s\"\n", cpath);
|
2014-07-15 10:15:04 +02:00
|
|
|
|
|
2015-03-01 00:13:37 +01:00
|
|
|
|
/* Make sure we allow paths like '~/.i3/layout.json' */
|
2018-03-27 02:36:54 +02:00
|
|
|
|
char *path = resolve_tilde(cpath);
|
2015-03-01 00:13:37 +01:00
|
|
|
|
|
2017-09-13 17:14:51 +02:00
|
|
|
|
char *buf = NULL;
|
|
|
|
|
ssize_t len;
|
|
|
|
|
if ((len = slurp(path, &buf)) < 0) {
|
|
|
|
|
/* slurp already logged an error. */
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!json_validate(buf, len)) {
|
|
|
|
|
ELOG("Could not parse \"%s\" as JSON, not loading.\n", path);
|
|
|
|
|
yerror("Could not parse \"%s\" as JSON.", path);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_content_t content = json_determine_content(buf, len);
|
2014-07-15 10:15:04 +02:00
|
|
|
|
LOG("JSON content = %d\n", content);
|
|
|
|
|
if (content == JSON_CONTENT_UNKNOWN) {
|
|
|
|
|
ELOG("Could not determine the contents of \"%s\", not loading.\n", path);
|
2015-03-04 22:45:39 +01:00
|
|
|
|
yerror("Could not determine the contents of \"%s\".", path);
|
2017-09-13 17:14:51 +02:00
|
|
|
|
goto out;
|
2014-07-15 10:15:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-14 22:49:33 +01:00
|
|
|
|
Con *parent = focused;
|
2014-07-15 10:15:04 +02:00
|
|
|
|
if (content == JSON_CONTENT_WORKSPACE) {
|
|
|
|
|
parent = output_get_content(con_get_output(parent));
|
|
|
|
|
} else {
|
|
|
|
|
/* We need to append the layout to a split container, since a leaf
|
|
|
|
|
* container must not have any children (by definition).
|
|
|
|
|
* Note that we explicitly check for workspaces, since they are okay for
|
|
|
|
|
* this purpose, but con_accepts_window() returns false for workspaces. */
|
|
|
|
|
while (parent->type != CT_WORKSPACE && !con_accepts_window(parent))
|
|
|
|
|
parent = parent->parent;
|
|
|
|
|
}
|
|
|
|
|
DLOG("Appending to parent=%p instead of focused=%p\n", parent, focused);
|
2014-01-04 21:39:13 +01:00
|
|
|
|
char *errormsg = NULL;
|
2017-09-13 17:14:51 +02:00
|
|
|
|
tree_append_json(parent, buf, len, &errormsg);
|
2014-01-04 21:39:13 +01:00
|
|
|
|
if (errormsg != NULL) {
|
|
|
|
|
yerror(errormsg);
|
|
|
|
|
free(errormsg);
|
|
|
|
|
/* Note that we continue executing since tree_append_json() has
|
|
|
|
|
* side-effects — user-provided layouts can be partly valid, partly
|
|
|
|
|
* invalid, leading to half of the placeholder containers being
|
|
|
|
|
* created. */
|
|
|
|
|
} else {
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2013-12-14 22:49:33 +01:00
|
|
|
|
// XXX: This is a bit of a kludge. Theoretically, render_con(parent,
|
|
|
|
|
// false); should be enough, but when sending 'workspace 4; append_layout
|
|
|
|
|
// /tmp/foo.json', the needs_tree_render == true of the workspace command
|
|
|
|
|
// is not executed yet and will be batched with append_layout’s
|
|
|
|
|
// needs_tree_render after the parser finished. We should check if that is
|
|
|
|
|
// necessary at all.
|
|
|
|
|
render_con(croot, false);
|
|
|
|
|
|
|
|
|
|
restore_open_placeholder_windows(parent);
|
|
|
|
|
|
2014-07-15 10:15:04 +02:00
|
|
|
|
if (content == JSON_CONTENT_WORKSPACE)
|
2014-12-20 05:43:47 +01:00
|
|
|
|
ipc_send_workspace_event("restored", parent, NULL);
|
2014-07-15 10:15:04 +02:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2017-09-13 17:14:51 +02:00
|
|
|
|
out:
|
|
|
|
|
free(path);
|
|
|
|
|
free(buf);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_workspace(I3_CMD, const char *which) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
Con *ws;
|
|
|
|
|
|
|
|
|
|
DLOG("which=%s\n", which);
|
|
|
|
|
|
2014-11-29 18:22:56 +01:00
|
|
|
|
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Cannot switch workspace while in global fullscreen");
|
2014-11-29 18:22:56 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
if (strcmp(which, "next") == 0)
|
|
|
|
|
ws = workspace_next();
|
|
|
|
|
else if (strcmp(which, "prev") == 0)
|
|
|
|
|
ws = workspace_prev();
|
|
|
|
|
else if (strcmp(which, "next_on_output") == 0)
|
|
|
|
|
ws = workspace_next_on_output();
|
|
|
|
|
else if (strcmp(which, "prev_on_output") == 0)
|
|
|
|
|
ws = workspace_prev_on_output();
|
|
|
|
|
else {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("BUG: called with which=%s", which);
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
workspace_show(ws);
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-08 19:17:46 +02:00
|
|
|
|
/*
|
2015-10-23 23:36:37 +02:00
|
|
|
|
* Implementation of 'workspace [--no-auto-back-and-forth] number <name>'
|
2012-04-08 19:17:46 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-10-23 23:36:37 +02:00
|
|
|
|
void cmd_workspace_number(I3_CMD, const char *which, const char *_no_auto_back_and_forth) {
|
|
|
|
|
const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
|
2012-04-08 19:17:46 +02:00
|
|
|
|
|
2014-11-29 18:22:56 +01:00
|
|
|
|
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Cannot switch workspace while in global fullscreen");
|
2014-11-29 18:22:56 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-22 17:12:51 +02:00
|
|
|
|
long parsed_num = ws_name_to_number(which);
|
|
|
|
|
if (parsed_num == -1) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Could not parse initial part of \"%s\" as a number.", which);
|
2012-04-08 19:17:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 21:36:51 +02:00
|
|
|
|
Con *workspace = get_existing_workspace_by_num(parsed_num);
|
2012-04-08 19:17:46 +02:00
|
|
|
|
if (!workspace) {
|
2012-08-05 16:34:38 +02:00
|
|
|
|
LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
|
2012-05-02 16:05:27 +02:00
|
|
|
|
ysuccess(true);
|
|
|
|
|
workspace_show_by_name(which);
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-04-08 19:17:46 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-11-15 18:26:53 +01:00
|
|
|
|
if (!no_auto_back_and_forth && maybe_back_and_forth(cmd_output, workspace->name)) {
|
|
|
|
|
ysuccess(true);
|
2012-05-02 16:05:27 +02:00
|
|
|
|
return;
|
2016-11-15 18:26:53 +01:00
|
|
|
|
}
|
2012-04-08 19:17:46 +02:00
|
|
|
|
workspace_show(workspace);
|
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-04-08 19:17:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'workspace back_and_forth'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_workspace_back_and_forth(I3_CMD) {
|
2014-11-29 18:22:56 +01:00
|
|
|
|
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Cannot switch workspace while in global fullscreen");
|
2014-11-29 18:22:56 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
workspace_back_and_forth();
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2015-10-23 23:36:37 +02:00
|
|
|
|
* Implementation of 'workspace [--no-auto-back-and-forth] <name>'
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-10-23 23:36:37 +02:00
|
|
|
|
void cmd_workspace_name(I3_CMD, const char *name, const char *_no_auto_back_and_forth) {
|
|
|
|
|
const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
|
|
|
|
|
|
2014-04-30 11:12:22 +02:00
|
|
|
|
if (strncasecmp(name, "__", strlen("__")) == 0) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("You cannot switch to the i3-internal workspaces (\"%s\").", name);
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-29 18:22:56 +01:00
|
|
|
|
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Cannot switch workspace while in global fullscreen");
|
2014-11-29 18:22:56 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("should switch to workspace %s\n", name);
|
2016-11-15 18:26:53 +01:00
|
|
|
|
if (!no_auto_back_and_forth && maybe_back_and_forth(cmd_output, name)) {
|
|
|
|
|
ysuccess(true);
|
2014-06-15 19:07:02 +02:00
|
|
|
|
return;
|
2016-11-15 18:26:53 +01:00
|
|
|
|
}
|
2015-03-24 09:27:42 +01:00
|
|
|
|
workspace_show_by_name(name);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2015-10-19 18:31:21 +02:00
|
|
|
|
* Implementation of 'mark [--add|--replace] [--toggle] <mark>'
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-10-19 18:31:21 +02:00
|
|
|
|
void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle) {
|
2015-03-28 19:12:25 +01:00
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2015-03-31 20:47:22 +02:00
|
|
|
|
owindow *current = TAILQ_FIRST(&owindows);
|
|
|
|
|
if (current == NULL) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Given criteria don't match a window");
|
2015-03-31 20:47:22 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Marks must be unique, i.e., no two windows must have the same mark. */
|
|
|
|
|
if (current != TAILQ_LAST(&owindows, owindows_head)) {
|
|
|
|
|
yerror("A mark must not be put onto more than one window");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
2015-10-19 18:31:21 +02:00
|
|
|
|
|
|
|
|
|
mark_mode_t mark_mode = (mode == NULL || strcmp(mode, "--replace") == 0) ? MM_REPLACE : MM_ADD;
|
2015-09-25 19:17:46 +02:00
|
|
|
|
if (toggle != NULL) {
|
2015-10-19 18:31:21 +02:00
|
|
|
|
con_mark_toggle(current->con, mark, mark_mode);
|
2015-03-31 20:47:22 +02:00
|
|
|
|
} else {
|
2015-10-19 18:31:21 +02:00
|
|
|
|
con_mark(current->con, mark, mark_mode);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-16 00:33:14 +02:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'unmark [mark]'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_unmark(I3_CMD, const char *mark) {
|
2015-10-20 08:14:42 +02:00
|
|
|
|
if (match_is_empty(current_match)) {
|
|
|
|
|
con_unmark(NULL, mark);
|
|
|
|
|
} else {
|
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
con_unmark(current->con, mark);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-07-16 00:33:14 +02:00
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'mode <string>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_mode(I3_CMD, const char *mode) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("mode=%s\n", mode);
|
|
|
|
|
switch_mode(mode);
|
|
|
|
|
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move [window|container] [to] output <str>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_move_con_to_output(I3_CMD, const char *name) {
|
2015-09-26 21:31:28 +02:00
|
|
|
|
DLOG("Should move window to output \"%s\".\n", name);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2015-09-26 21:31:28 +02:00
|
|
|
|
owindow *current;
|
|
|
|
|
bool had_error = false;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2017-09-16 19:14:35 +02:00
|
|
|
|
had_error |= !con_move_to_output_name(current->con, name, true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2015-09-26 21:31:28 +02:00
|
|
|
|
ysuccess(!had_error);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-12 17:30:31 +02:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move [container|window] [to] mark <str>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_move_con_to_mark(I3_CMD, const char *mark) {
|
2015-04-14 17:57:33 +02:00
|
|
|
|
DLOG("moving window to mark \"%s\"\n", mark);
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
bool result = true;
|
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
DLOG("moving matched window %p / %s to mark \"%s\"\n", current->con, current->con->name, mark);
|
|
|
|
|
result &= con_move_to_mark(current->con, mark);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
ysuccess(result);
|
2015-04-12 17:30:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'floating enable|disable|toggle'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_floating(I3_CMD, const char *floating_mode) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
owindow *current;
|
|
|
|
|
|
|
|
|
|
DLOG("floating_mode=%s\n", floating_mode);
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
|
|
|
|
if (strcmp(floating_mode, "toggle") == 0) {
|
|
|
|
|
DLOG("should toggle mode\n");
|
|
|
|
|
toggle_floating_mode(current->con, false);
|
|
|
|
|
} else {
|
|
|
|
|
DLOG("should switch mode to %s\n", floating_mode);
|
|
|
|
|
if (strcmp(floating_mode, "enable") == 0) {
|
|
|
|
|
floating_enable(current->con, false);
|
|
|
|
|
} else {
|
|
|
|
|
floating_disable(current->con, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move workspace to [output] <str>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_move_workspace_to_output(I3_CMD, const char *name) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("should move workspace to output %s\n", name);
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
owindow *current;
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2015-03-04 09:22:25 +01:00
|
|
|
|
Con *ws = con_get_workspace(current->con);
|
2017-12-01 15:12:43 +01:00
|
|
|
|
if (con_is_internal(ws)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-31 21:56:59 +02:00
|
|
|
|
Output *current_output = get_output_for_con(ws);
|
|
|
|
|
if (current_output == NULL) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Cannot get current output. This is a bug in i3.");
|
2018-03-31 21:56:59 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Output *target_output = get_output_from_string(current_output, name);
|
|
|
|
|
if (!target_output) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Could not get output from string \"%s\"", name);
|
2018-03-31 21:56:59 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool success = workspace_move_to_output(ws, target_output);
|
2015-03-04 09:22:25 +01:00
|
|
|
|
if (!success) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Failed to move workspace to output.");
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2016-01-04 17:31:47 +01:00
|
|
|
|
* Implementation of 'split v|h|t|vertical|horizontal|toggle'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_split(I3_CMD, const char *direction) {
|
2015-10-26 18:16:21 +01:00
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
con_set_layout: always use the parent container, handle workspaces properly
Previously, in case 'layout stacked' (for example) had been called
interactively, con_set_layout would be called with focused->parent,
while with for_window, it’d be called on the actual matching container.
This difference in behavior was the cause for the inability to use
'for_window [class="XTerm"] layout tabbed', which now works \o/, but
more on that below.
The change also allows us to handle the case of the user selecting a
CT_WORKSPACE container properly, that is, by using the special case and
creating a new split container on the workspace which gets all the
contents, but a new layout.
Now, before you are enthusiastic about the change and try to use
for_window magic in your config file, keep in mind: The 'layout' command
acts on the parent split container. That is, when using a line such as
this one:
for_window [class="XTerm"] layout tabbed
…and opening an XTerm when on a workspace with one single other window,
the whole workspace will be set tabbed (just as previously when you
opened an XTerm and sent 'layout tabbed' manually).
Therefore, to open XTerm in its own tabbed split container, you need to
split before:
for_window [class="XTerm"] split v, layout tabbed
The comma here is important! It says that the second command should not
be treated as an entirely unrelated command, but it should also relate
the matching window (while it does work with a ';', that is prone to
race-conditions and should be avoided).
fixes #358
2012-09-05 00:22:38 +02:00
|
|
|
|
owindow *current;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
LOG("splitting in direction %c\n", direction[0]);
|
2015-10-26 18:16:21 +01:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
if (con_is_docked(current->con)) {
|
|
|
|
|
ELOG("Cannot split a docked container, skipping.\n");
|
|
|
|
|
continue;
|
con_set_layout: always use the parent container, handle workspaces properly
Previously, in case 'layout stacked' (for example) had been called
interactively, con_set_layout would be called with focused->parent,
while with for_window, it’d be called on the actual matching container.
This difference in behavior was the cause for the inability to use
'for_window [class="XTerm"] layout tabbed', which now works \o/, but
more on that below.
The change also allows us to handle the case of the user selecting a
CT_WORKSPACE container properly, that is, by using the special case and
creating a new split container on the workspace which gets all the
contents, but a new layout.
Now, before you are enthusiastic about the change and try to use
for_window magic in your config file, keep in mind: The 'layout' command
acts on the parent split container. That is, when using a line such as
this one:
for_window [class="XTerm"] layout tabbed
…and opening an XTerm when on a workspace with one single other window,
the whole workspace will be set tabbed (just as previously when you
opened an XTerm and sent 'layout tabbed' manually).
Therefore, to open XTerm in its own tabbed split container, you need to
split before:
for_window [class="XTerm"] split v, layout tabbed
The comma here is important! It says that the second command should not
be treated as an entirely unrelated command, but it should also relate
the matching window (while it does work with a ';', that is prone to
race-conditions and should be avoided).
fixes #358
2012-09-05 00:22:38 +02:00
|
|
|
|
}
|
2015-10-26 18:16:21 +01:00
|
|
|
|
|
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
2016-01-04 17:31:47 +01:00
|
|
|
|
if (direction[0] == 't') {
|
|
|
|
|
layout_t current_layout;
|
|
|
|
|
if (current->con->type == CT_WORKSPACE) {
|
|
|
|
|
current_layout = current->con->layout;
|
|
|
|
|
} else {
|
|
|
|
|
current_layout = current->con->parent->layout;
|
|
|
|
|
}
|
|
|
|
|
/* toggling split orientation */
|
|
|
|
|
if (current_layout == L_SPLITH) {
|
|
|
|
|
tree_split(current->con, VERT);
|
|
|
|
|
} else {
|
|
|
|
|
tree_split(current->con, HORIZ);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
|
|
|
|
|
}
|
con_set_layout: always use the parent container, handle workspaces properly
Previously, in case 'layout stacked' (for example) had been called
interactively, con_set_layout would be called with focused->parent,
while with for_window, it’d be called on the actual matching container.
This difference in behavior was the cause for the inability to use
'for_window [class="XTerm"] layout tabbed', which now works \o/, but
more on that below.
The change also allows us to handle the case of the user selecting a
CT_WORKSPACE container properly, that is, by using the special case and
creating a new split container on the workspace which gets all the
contents, but a new layout.
Now, before you are enthusiastic about the change and try to use
for_window magic in your config file, keep in mind: The 'layout' command
acts on the parent split container. That is, when using a line such as
this one:
for_window [class="XTerm"] layout tabbed
…and opening an XTerm when on a workspace with one single other window,
the whole workspace will be set tabbed (just as previously when you
opened an XTerm and sent 'layout tabbed' manually).
Therefore, to open XTerm in its own tabbed split container, you need to
split before:
for_window [class="XTerm"] split v, layout tabbed
The comma here is important! It says that the second command should not
be treated as an entirely unrelated command, but it should also relate
the matching window (while it does work with a ';', that is prone to
race-conditions and should be avoided).
fixes #358
2012-09-05 00:22:38 +02:00
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2012-10-24 07:58:03 +02:00
|
|
|
|
* Implementation of 'kill [window|client]'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_kill(I3_CMD, const char *kill_mode_str) {
|
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 20:53:29 +01:00
|
|
|
|
if (kill_mode_str == NULL)
|
|
|
|
|
kill_mode_str = "window";
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
|
|
|
|
DLOG("kill_mode=%s\n", kill_mode_str);
|
|
|
|
|
|
|
|
|
|
int kill_mode;
|
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 20:53:29 +01:00
|
|
|
|
if (strcmp(kill_mode_str, "window") == 0)
|
2012-01-14 16:02:30 +01:00
|
|
|
|
kill_mode = KILL_WINDOW;
|
|
|
|
|
else if (strcmp(kill_mode_str, "client") == 0)
|
|
|
|
|
kill_mode = KILL_CLIENT;
|
|
|
|
|
else {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("BUG: called with kill_mode=%s", kill_mode_str);
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-28 04:11:51 +01:00
|
|
|
|
HANDLE_EMPTY_MATCH;
|
2015-12-09 13:39:08 +01:00
|
|
|
|
|
2015-12-28 04:11:51 +01:00
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
con_close(current->con, kill_mode);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'exec [--no-startup-id] <command>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_exec(I3_CMD, const char *nosn, const char *command) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
bool no_startup_id = (nosn != NULL);
|
|
|
|
|
|
|
|
|
|
DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id);
|
|
|
|
|
start_application(command, no_startup_id);
|
|
|
|
|
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'focus left|right|up|down'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_focus_direction(I3_CMD, const char *direction) {
|
2018-08-23 14:36:50 +02:00
|
|
|
|
switch (parse_direction(direction)) {
|
|
|
|
|
case D_LEFT:
|
|
|
|
|
tree_next('p', HORIZ);
|
|
|
|
|
break;
|
|
|
|
|
case D_RIGHT:
|
|
|
|
|
tree_next('n', HORIZ);
|
|
|
|
|
break;
|
|
|
|
|
case D_UP:
|
|
|
|
|
tree_next('p', VERT);
|
|
|
|
|
break;
|
|
|
|
|
case D_DOWN:
|
|
|
|
|
tree_next('n', VERT);
|
|
|
|
|
break;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-17 01:20:48 +02:00
|
|
|
|
/*
|
|
|
|
|
* Focus a container and disable any other fullscreen container not permitting the focus.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static void cmd_focus_force_focus(Con *con) {
|
|
|
|
|
/* Disable fullscreen container in workspace with container to be focused. */
|
|
|
|
|
Con *ws = con_get_workspace(con);
|
2018-08-22 13:02:27 +02:00
|
|
|
|
Con *fullscreen_on_ws = con_get_fullscreen_covering_ws(ws);
|
2017-09-17 01:20:48 +02:00
|
|
|
|
if (fullscreen_on_ws && fullscreen_on_ws != con && !con_has_parent(con, fullscreen_on_ws)) {
|
|
|
|
|
con_disable_fullscreen(fullscreen_on_ws);
|
|
|
|
|
}
|
2017-12-06 00:58:47 +01:00
|
|
|
|
con_activate(con);
|
2017-09-17 01:20:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'focus tiling|floating|mode_toggle'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_focus_window_mode(I3_CMD, const char *window_mode) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("window_mode = %s\n", window_mode);
|
|
|
|
|
|
2017-09-19 13:52:02 +02:00
|
|
|
|
bool to_floating = false;
|
|
|
|
|
if (strcmp(window_mode, "mode_toggle") == 0) {
|
|
|
|
|
to_floating = !con_inside_floating(focused);
|
|
|
|
|
} else if (strcmp(window_mode, "floating") == 0) {
|
|
|
|
|
to_floating = true;
|
|
|
|
|
} else if (strcmp(window_mode, "tiling") == 0) {
|
|
|
|
|
to_floating = false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
Con *ws = con_get_workspace(focused);
|
2017-09-19 13:52:02 +02:00
|
|
|
|
Con *current;
|
|
|
|
|
bool success = false;
|
|
|
|
|
TAILQ_FOREACH(current, &(ws->focus_head), focused) {
|
|
|
|
|
if ((to_floating && current->type != CT_FLOATING_CON) ||
|
|
|
|
|
(!to_floating && current->type == CT_FLOATING_CON))
|
|
|
|
|
continue;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2017-09-17 01:20:48 +02:00
|
|
|
|
cmd_focus_force_focus(con_descend_focused(current));
|
2017-09-19 13:52:02 +02:00
|
|
|
|
success = true;
|
|
|
|
|
break;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:52:02 +02:00
|
|
|
|
if (success) {
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
} else {
|
|
|
|
|
yerror("Failed to find a %s container in workspace.", to_floating ? "floating" : "tiling");
|
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'focus parent|child'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_focus_level(I3_CMD, const char *level) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("level = %s\n", level);
|
2012-05-26 23:36:25 +02:00
|
|
|
|
bool success = false;
|
|
|
|
|
|
|
|
|
|
/* Focusing the parent can only be allowed if the newly
|
|
|
|
|
* focused container won't escape the fullscreen container. */
|
|
|
|
|
if (strcmp(level, "parent") == 0) {
|
|
|
|
|
if (focused && focused->parent) {
|
|
|
|
|
if (con_fullscreen_permits_focusing(focused->parent))
|
|
|
|
|
success = level_up();
|
|
|
|
|
else
|
2012-08-05 20:56:33 +02:00
|
|
|
|
ELOG("'focus parent': Currently in fullscreen, not going up\n");
|
2012-05-26 23:36:25 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-05-26 23:36:25 +02:00
|
|
|
|
/* Focusing a child should always be allowed. */
|
2014-06-15 19:07:02 +02:00
|
|
|
|
else
|
|
|
|
|
success = level_down();
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-05-26 23:36:25 +02:00
|
|
|
|
cmd_output->needs_tree_render = success;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-26 23:36:25 +02:00
|
|
|
|
ysuccess(success);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'focus'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_focus(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("current_match = %p\n", current_match);
|
|
|
|
|
|
|
|
|
|
if (match_is_empty(current_match)) {
|
|
|
|
|
ELOG("You have to specify which window/container should be focused.\n");
|
|
|
|
|
ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
|
|
|
|
|
|
2013-09-25 23:17:38 +02:00
|
|
|
|
yerror("You have to specify which window/container should be focused");
|
2012-05-02 22:01:50 +02:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-25 16:10:37 +01:00
|
|
|
|
Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
int count = 0;
|
2012-04-21 21:34:25 +02:00
|
|
|
|
owindow *current;
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
Con *ws = con_get_workspace(current->con);
|
|
|
|
|
/* If no workspace could be found, this was a dock window.
|
|
|
|
|
* Just skip it, you cannot focus dock windows. */
|
|
|
|
|
if (!ws)
|
|
|
|
|
continue;
|
|
|
|
|
|
2013-01-25 16:10:37 +01:00
|
|
|
|
/* In case this is a scratchpad window, call scratchpad_show(). */
|
|
|
|
|
if (ws == __i3_scratch) {
|
|
|
|
|
scratchpad_show(current->con);
|
|
|
|
|
count++;
|
|
|
|
|
/* While for the normal focus case we can change focus multiple
|
|
|
|
|
* times and only a single window ends up focused, we could show
|
|
|
|
|
* multiple scratchpad windows. So, rather break here. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
/* If the container is not on the current workspace,
|
|
|
|
|
* workspace_show() will switch to a different workspace and (if
|
|
|
|
|
* enabled) trigger a mouse pointer warp to the currently focused
|
|
|
|
|
* container (!) on the target workspace.
|
|
|
|
|
*
|
|
|
|
|
* Therefore, before calling workspace_show(), we make sure that
|
|
|
|
|
* 'current' will be focused on the workspace. However, we cannot
|
|
|
|
|
* just con_focus(current) because then the pointer will not be
|
|
|
|
|
* warped at all (the code thinks we are already there).
|
|
|
|
|
*
|
|
|
|
|
* So we focus 'current' to make it the currently focused window of
|
|
|
|
|
* the target workspace, then revert focus. */
|
|
|
|
|
Con *currently_focused = focused;
|
2017-09-17 01:20:48 +02:00
|
|
|
|
cmd_focus_force_focus(current->con);
|
2017-12-06 00:58:47 +01:00
|
|
|
|
con_activate(currently_focused);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
|
|
|
|
/* Now switch to the workspace, then focus */
|
|
|
|
|
workspace_show(ws);
|
|
|
|
|
LOG("focusing %p / %s\n", current->con, current->con->name);
|
2017-12-06 00:58:47 +01:00
|
|
|
|
con_activate(current->con);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count > 1)
|
|
|
|
|
LOG("WARNING: Your criteria for the focus command matches %d containers, "
|
2014-06-15 19:07:02 +02:00
|
|
|
|
"while only exactly one container can be focused at a time.\n",
|
|
|
|
|
count);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2015-09-11 08:26:33 +02:00
|
|
|
|
ysuccess(count > 0);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2014-10-26 19:33:09 +01:00
|
|
|
|
* Implementation of 'fullscreen enable|toggle [global]' and
|
|
|
|
|
* 'fullscreen disable'
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_fullscreen(I3_CMD, const char *action, const char *fullscreen_mode) {
|
2014-10-26 19:33:09 +01:00
|
|
|
|
fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
|
|
|
|
|
DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
owindow *current;
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2013-08-20 02:07:31 +02:00
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
2014-10-26 19:33:09 +01:00
|
|
|
|
if (strcmp(action, "toggle") == 0) {
|
|
|
|
|
con_toggle_fullscreen(current->con, mode);
|
|
|
|
|
} else if (strcmp(action, "enable") == 0) {
|
|
|
|
|
con_enable_fullscreen(current->con, mode);
|
|
|
|
|
} else if (strcmp(action, "disable") == 0) {
|
|
|
|
|
con_disable_fullscreen(current->con);
|
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-22 23:37:41 +02:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'sticky enable|disable|toggle'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_sticky(I3_CMD, const char *action) {
|
2015-08-22 23:37:41 +02:00
|
|
|
|
DLOG("%s sticky on window\n", action);
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
if (current->con->window == NULL) {
|
|
|
|
|
ELOG("only containers holding a window can be made sticky, skipping con = %p\n", current->con);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
DLOG("setting sticky for container = %p / %s\n", current->con, current->con->name);
|
|
|
|
|
|
|
|
|
|
bool sticky = false;
|
|
|
|
|
if (strcmp(action, "enable") == 0)
|
|
|
|
|
sticky = true;
|
|
|
|
|
else if (strcmp(action, "disable") == 0)
|
|
|
|
|
sticky = false;
|
|
|
|
|
else if (strcmp(action, "toggle") == 0)
|
|
|
|
|
sticky = !current->con->sticky;
|
|
|
|
|
|
|
|
|
|
current->con->sticky = sticky;
|
|
|
|
|
ewmh_update_sticky(current->con->window->id, sticky);
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-26 21:06:53 +02:00
|
|
|
|
/* A window we made sticky might not be on a visible workspace right now, so we need to make
|
|
|
|
|
* sure it gets pushed to the front now. */
|
2015-09-17 22:52:56 +02:00
|
|
|
|
output_push_sticky_windows(focused);
|
2015-08-26 21:06:53 +02:00
|
|
|
|
|
Handle the EWMH atom _NET_WM_DESKTOP.
We already claim _NET_WM_DESKTOP support in _NET_SUPPORTED since around 2009,
but haven't actually done anything with it. However, especially pagers like
gnome-panel rely on this property to be updated and many tools, like GTK, want
to use the corresponding client messages to make a window sticky, move it
around etc.
This patch implements full support according to the EWMH spec. This means:
* We set the property on all windows when managing it.
* We keep the property updated on all windows at all times.
* We read and respect the property upon managing a window if it was set before
mapping the window.
* We react to client messages for it.
* We remove the property on withdrawn windows.
Note that the special value 0xFFFFFFFF, according to the spec, means that the
window shall be shown on all workspaces. We do this by making it sticky and
float it. This shows it on all workspaces at least on the output it is on.
Furthermore, the spec gives us the freedom to ignore _NET_WM_DESKTOP when
managing a window if we have good reason to. In our case, we give window
swallowing a higher priority since the user would likely expect that and we
want to keep placeholder windows only around for as long as we have to.
However, we do prioritize this property over, for example, startup
notifications.
fixes #2153
fixes #1507
fixes #938
2016-01-11 20:53:26 +01:00
|
|
|
|
ewmh_update_wm_desktop();
|
|
|
|
|
|
2015-08-26 21:06:53 +02:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2015-08-22 23:37:41 +02:00
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move <direction> [<pixels> [px]]'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2018-08-23 14:36:50 +02:00
|
|
|
|
void cmd_move_direction(I3_CMD, const char *direction_str, long move_px) {
|
2014-06-19 14:09:31 +02:00
|
|
|
|
owindow *current;
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
Con *initially_focused = focused;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
direction_t direction = parse_direction(direction_str);
|
2014-06-19 14:09:31 +02:00
|
|
|
|
|
2014-06-23 21:29:42 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2018-08-23 14:36:50 +02:00
|
|
|
|
DLOG("moving in direction %s, px %ld\n", direction_str, move_px);
|
2014-06-19 14:09:31 +02:00
|
|
|
|
if (con_is_floating(current->con)) {
|
2015-09-27 16:59:36 +02:00
|
|
|
|
DLOG("floating move with %ld pixels\n", move_px);
|
2014-06-19 14:09:31 +02:00
|
|
|
|
Rect newrect = current->con->parent->rect;
|
2018-08-23 14:36:50 +02:00
|
|
|
|
|
|
|
|
|
switch (direction) {
|
|
|
|
|
case D_LEFT:
|
|
|
|
|
newrect.x -= move_px;
|
|
|
|
|
break;
|
|
|
|
|
case D_RIGHT:
|
|
|
|
|
newrect.x += move_px;
|
|
|
|
|
break;
|
|
|
|
|
case D_UP:
|
|
|
|
|
newrect.y -= move_px;
|
|
|
|
|
break;
|
|
|
|
|
case D_DOWN:
|
|
|
|
|
newrect.y += move_px;
|
|
|
|
|
break;
|
2014-06-19 14:09:31 +02:00
|
|
|
|
}
|
2018-08-23 14:36:50 +02:00
|
|
|
|
|
2014-06-19 14:09:31 +02:00
|
|
|
|
floating_reposition(current->con->parent, newrect);
|
|
|
|
|
} else {
|
2018-08-23 14:36:50 +02:00
|
|
|
|
tree_move(current->con, direction);
|
2014-06-19 14:09:31 +02:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-19 14:09:31 +02:00
|
|
|
|
/* the move command should not disturb focus */
|
|
|
|
|
if (focused != initially_focused)
|
2017-12-06 00:58:47 +01:00
|
|
|
|
con_activate(initially_focused);
|
2014-06-19 14:09:31 +02:00
|
|
|
|
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
* Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_layout(I3_CMD, const char *layout_str) {
|
2015-10-26 18:16:21 +01:00
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2013-05-22 06:28:13 +02:00
|
|
|
|
layout_t layout;
|
2017-02-21 02:12:39 +01:00
|
|
|
|
if (!layout_from_name(layout_str, &layout)) {
|
2012-08-05 16:34:38 +02:00
|
|
|
|
ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
|
|
|
|
|
return;
|
|
|
|
|
}
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
|
|
|
|
|
DLOG("changing layout to %s (%d)\n", layout_str, layout);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2015-10-26 18:16:21 +01:00
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
if (con_is_docked(current->con)) {
|
|
|
|
|
ELOG("cannot change layout of a docked container, skipping it.\n");
|
|
|
|
|
continue;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
2015-10-26 18:16:21 +01:00
|
|
|
|
|
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
|
|
|
|
con_set_layout(current->con, layout);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'layout toggle [all|split]'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_layout_toggle(I3_CMD, const char *toggle_mode) {
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
owindow *current;
|
|
|
|
|
|
|
|
|
|
if (toggle_mode == NULL)
|
|
|
|
|
toggle_mode = "default";
|
|
|
|
|
|
|
|
|
|
DLOG("toggling layout (mode = %s)\n", toggle_mode);
|
|
|
|
|
|
|
|
|
|
/* check if the match is empty, not if the result is empty */
|
|
|
|
|
if (match_is_empty(current_match))
|
con_set_layout: always use the parent container, handle workspaces properly
Previously, in case 'layout stacked' (for example) had been called
interactively, con_set_layout would be called with focused->parent,
while with for_window, it’d be called on the actual matching container.
This difference in behavior was the cause for the inability to use
'for_window [class="XTerm"] layout tabbed', which now works \o/, but
more on that below.
The change also allows us to handle the case of the user selecting a
CT_WORKSPACE container properly, that is, by using the special case and
creating a new split container on the workspace which gets all the
contents, but a new layout.
Now, before you are enthusiastic about the change and try to use
for_window magic in your config file, keep in mind: The 'layout' command
acts on the parent split container. That is, when using a line such as
this one:
for_window [class="XTerm"] layout tabbed
…and opening an XTerm when on a workspace with one single other window,
the whole workspace will be set tabbed (just as previously when you
opened an XTerm and sent 'layout tabbed' manually).
Therefore, to open XTerm in its own tabbed split container, you need to
split before:
for_window [class="XTerm"] split v, layout tabbed
The comma here is important! It says that the second command should not
be treated as an entirely unrelated command, but it should also relate
the matching window (while it does work with a ';', that is prone to
race-conditions and should be avoided).
fixes #358
2012-09-05 00:22:38 +02:00
|
|
|
|
con_toggle_layout(focused, toggle_mode);
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
else {
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
|
|
|
|
con_toggle_layout(current->con, toggle_mode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2012-10-24 07:58:03 +02:00
|
|
|
|
* Implementation of 'exit'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_exit(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
LOG("Exiting due to user command.\n");
|
2016-01-09 17:18:05 +01:00
|
|
|
|
#ifdef I3_ASAN_ENABLED
|
|
|
|
|
__lsan_do_leak_check();
|
|
|
|
|
#endif
|
2017-01-22 23:08:32 +01:00
|
|
|
|
ipc_shutdown(SHUTDOWN_REASON_EXIT);
|
2014-06-08 22:13:43 +02:00
|
|
|
|
unlink(config.ipc_socket_path);
|
2012-06-29 23:13:25 +02:00
|
|
|
|
xcb_disconnect(conn);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
exit(0);
|
|
|
|
|
|
|
|
|
|
/* unreached */
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2012-10-24 07:58:03 +02:00
|
|
|
|
* Implementation of 'reload'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_reload(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
LOG("reloading\n");
|
2012-12-24 16:53:20 +01:00
|
|
|
|
kill_nagbar(&config_error_nagbar_pid, false);
|
|
|
|
|
kill_nagbar(&command_error_nagbar_pid, false);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
load_configuration(conn, NULL, true);
|
|
|
|
|
x_set_i3_atoms();
|
|
|
|
|
/* Send an IPC event just in case the ws names have changed */
|
2014-12-20 05:43:47 +01:00
|
|
|
|
ipc_send_workspace_event("reload", NULL, NULL);
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
/* Send an update event for the barconfig just in case it has changed */
|
|
|
|
|
update_barconfig();
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2012-10-24 07:58:03 +02:00
|
|
|
|
* Implementation of 'restart'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_restart(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
LOG("restarting i3\n");
|
2017-01-22 23:08:32 +01:00
|
|
|
|
ipc_shutdown(SHUTDOWN_REASON_RESTART);
|
2014-06-27 09:04:51 +02:00
|
|
|
|
unlink(config.ipc_socket_path);
|
2014-06-11 09:17:43 +02:00
|
|
|
|
/* We need to call this manually since atexit handlers don’t get called
|
|
|
|
|
* when exec()ing */
|
|
|
|
|
purge_zerobyte_logfile();
|
2012-01-14 16:02:30 +01:00
|
|
|
|
i3_restart(false);
|
|
|
|
|
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
2012-10-24 07:58:03 +02:00
|
|
|
|
* Implementation of 'open'.
|
2012-01-27 23:32:40 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_open(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
LOG("opening new container\n");
|
|
|
|
|
Con *con = tree_open_con(NULL, NULL);
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
con->layout = L_SPLITH;
|
2017-12-06 00:58:47 +01:00
|
|
|
|
con_activate(con);
|
2012-05-02 22:01:50 +02:00
|
|
|
|
|
|
|
|
|
y(map_open);
|
|
|
|
|
ystr("success");
|
|
|
|
|
y(bool, true);
|
|
|
|
|
ystr("id");
|
2016-04-15 09:27:43 +02:00
|
|
|
|
y(integer, (uintptr_t)con);
|
2012-05-02 22:01:50 +02:00
|
|
|
|
y(map_close);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'focus output <output>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_focus_output(I3_CMD, const char *name) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
owindow *current;
|
|
|
|
|
|
|
|
|
|
DLOG("name = %s\n", name);
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
/* get the output */
|
|
|
|
|
Output *current_output = NULL;
|
|
|
|
|
Output *output;
|
|
|
|
|
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows)
|
2016-09-30 17:28:02 +02:00
|
|
|
|
current_output = get_output_for_con(current->con);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
assert(current_output != NULL);
|
|
|
|
|
|
|
|
|
|
output = get_output_from_string(current_output, name);
|
|
|
|
|
|
|
|
|
|
if (!output) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("No such output found.");
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get visible workspace on output */
|
|
|
|
|
Con *ws = NULL;
|
|
|
|
|
GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
|
2012-02-07 23:38:21 +01:00
|
|
|
|
if (!ws) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("BUG: No workspace found on output.");
|
2012-02-07 23:38:21 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
|
|
|
|
workspace_show(ws);
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-03-23 13:39:17 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
|
|
|
|
|
*
|
|
|
|
|
*/
|
2018-03-18 01:08:16 +01:00
|
|
|
|
void cmd_move_window_to_position(I3_CMD, long x, long y) {
|
2014-07-01 08:12:28 +02:00
|
|
|
|
bool has_error = false;
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2014-07-01 08:12:28 +02:00
|
|
|
|
owindow *current;
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
if (!con_is_floating(current->con)) {
|
|
|
|
|
ELOG("Cannot change position. The window/container is not floating\n");
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2014-07-01 08:12:28 +02:00
|
|
|
|
if (!has_error) {
|
|
|
|
|
yerror("Cannot change position of a window/container because it is not floating.");
|
|
|
|
|
has_error = true;
|
|
|
|
|
}
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2014-07-01 08:12:28 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-18 01:08:16 +01:00
|
|
|
|
Rect newrect = current->con->parent->rect;
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2018-03-18 01:08:16 +01:00
|
|
|
|
DLOG("moving to position %ld %ld\n", x, y);
|
|
|
|
|
newrect.x = x;
|
|
|
|
|
newrect.y = y;
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2018-03-18 01:41:12 +01:00
|
|
|
|
if (!floating_reposition(current->con->parent, newrect)) {
|
|
|
|
|
yerror("Cannot move window/container out of bounds.");
|
|
|
|
|
has_error = true;
|
|
|
|
|
}
|
2012-03-23 13:39:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-01 08:12:28 +02:00
|
|
|
|
if (!has_error)
|
|
|
|
|
ysuccess(true);
|
2012-03-23 13:39:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move [window|container] [to] [absolute] position center
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_move_window_to_center(I3_CMD, const char *method) {
|
2015-12-03 13:24:39 +01:00
|
|
|
|
bool has_error = false;
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2015-12-03 13:24:39 +01:00
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
Con *floating_con = con_inside_floating(current->con);
|
|
|
|
|
if (floating_con == NULL) {
|
|
|
|
|
ELOG("con %p / %s is not floating, cannot move it to the center.\n",
|
|
|
|
|
current->con, current->con->name);
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2015-12-03 13:24:39 +01:00
|
|
|
|
if (!has_error) {
|
|
|
|
|
yerror("Cannot change position of a window/container because it is not floating.");
|
|
|
|
|
has_error = true;
|
|
|
|
|
}
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2015-12-03 13:24:39 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-03-23 13:39:17 +01:00
|
|
|
|
|
2015-12-03 13:24:39 +01:00
|
|
|
|
if (strcmp(method, "absolute") == 0) {
|
|
|
|
|
DLOG("moving to absolute center\n");
|
|
|
|
|
floating_center(floating_con, croot->rect);
|
|
|
|
|
|
|
|
|
|
floating_maybe_reassign_ws(floating_con);
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(method, "position") == 0) {
|
|
|
|
|
DLOG("moving to center\n");
|
|
|
|
|
floating_center(floating_con, con_get_workspace(floating_con)->rect);
|
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
}
|
2012-03-23 13:39:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2015-12-03 13:24:39 +01:00
|
|
|
|
if (!has_error)
|
|
|
|
|
ysuccess(true);
|
2012-03-23 13:39:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-03 00:19:26 +02:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move [window|container] [to] position mouse'
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void cmd_move_window_to_mouse(I3_CMD) {
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
Con *floating_con = con_inside_floating(current->con);
|
|
|
|
|
if (floating_con == NULL) {
|
|
|
|
|
DLOG("con %p / %s is not floating, cannot move it to the mouse position.\n",
|
|
|
|
|
current->con, current->con->name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DLOG("moving floating container %p / %s to cursor position\n", floating_con, floating_con->name);
|
|
|
|
|
floating_move_to_pointer(floating_con);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'move scratchpad'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_move_scratchpad(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("should move window to scratchpad\n");
|
|
|
|
|
owindow *current;
|
|
|
|
|
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
|
|
|
|
scratchpad_move(current->con);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:32:40 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'scratchpad show'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-02-07 23:38:21 +01:00
|
|
|
|
void cmd_scratchpad_show(I3_CMD) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("should show scratchpad window\n");
|
|
|
|
|
owindow *current;
|
2018-04-06 18:00:38 +02:00
|
|
|
|
bool result = false;
|
2012-01-14 16:02:30 +01:00
|
|
|
|
|
|
|
|
|
if (match_is_empty(current_match)) {
|
2018-04-06 18:00:38 +02:00
|
|
|
|
result = scratchpad_show(NULL);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
} else {
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
2012-01-14 16:02:30 +01:00
|
|
|
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
2018-04-06 18:00:38 +02:00
|
|
|
|
result |= scratchpad_show(current->con);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2018-04-06 18:00:38 +02:00
|
|
|
|
|
|
|
|
|
ysuccess(result);
|
2012-01-14 16:02:30 +01:00
|
|
|
|
}
|
2012-04-08 20:34:31 +02:00
|
|
|
|
|
2016-02-26 22:39:44 +01:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'swap [container] [with] id|con_id|mark <arg>'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void cmd_swap(I3_CMD, const char *mode, const char *arg) {
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
owindow *match = TAILQ_FIRST(&owindows);
|
|
|
|
|
if (match == NULL) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("No match found for swapping.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (match->con == NULL) {
|
|
|
|
|
yerror("Match %p has no container.", match);
|
2016-02-26 22:39:44 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Con *con;
|
|
|
|
|
if (strcmp(mode, "id") == 0) {
|
|
|
|
|
long target;
|
|
|
|
|
if (!parse_long(arg, &target, 0)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Failed to parse %s into a window id.", arg);
|
2016-02-26 22:39:44 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
con = con_by_window_id(target);
|
|
|
|
|
} else if (strcmp(mode, "con_id") == 0) {
|
|
|
|
|
long target;
|
|
|
|
|
if (!parse_long(arg, &target, 0)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Failed to parse %s into a container id.", arg);
|
2016-02-26 22:39:44 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-06 07:34:14 +02:00
|
|
|
|
con = con_by_con_id(target);
|
2016-02-26 22:39:44 +01:00
|
|
|
|
} else if (strcmp(mode, "mark") == 0) {
|
|
|
|
|
con = con_by_mark(arg);
|
|
|
|
|
} else {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Unhandled swap mode \"%s\". This is a bug.", mode);
|
2016-02-26 22:39:44 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (con == NULL) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Could not find container for %s = %s", mode, arg);
|
2016-02-26 22:39:44 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-05 09:01:53 +02:00
|
|
|
|
if (match != TAILQ_LAST(&owindows, owindows_head)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
LOG("More than one container matched the swap command, only using the first one.");
|
2016-02-26 22:39:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DLOG("Swapping %p with %p.\n", match->con, con);
|
|
|
|
|
bool result = con_swap(match->con, con);
|
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
2018-09-11 06:44:40 +02:00
|
|
|
|
// XXX: default reply for now, make this a better reply
|
2016-02-26 22:39:44 +01:00
|
|
|
|
ysuccess(result);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-10 18:59:45 +02:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'title_format <format>'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_title_format(I3_CMD, const char *format) {
|
2015-06-10 19:01:05 +02:00
|
|
|
|
DLOG("setting title_format to \"%s\"\n", format);
|
|
|
|
|
HANDLE_EMPTY_MATCH;
|
|
|
|
|
|
|
|
|
|
owindow *current;
|
|
|
|
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
|
|
|
|
DLOG("setting title_format for %p / %s\n", current->con, current->con->name);
|
2015-12-29 18:01:51 +01:00
|
|
|
|
FREE(current->con->title_format);
|
2015-06-10 19:01:05 +02:00
|
|
|
|
|
|
|
|
|
/* If we only display the title without anything else, we can skip the parsing step,
|
|
|
|
|
* so we remove the title format altogether. */
|
2015-08-30 10:10:37 +02:00
|
|
|
|
if (strcasecmp(format, "%title") != 0) {
|
2015-12-29 18:01:51 +01:00
|
|
|
|
current->con->title_format = sstrdup(format);
|
2015-06-10 19:01:05 +02:00
|
|
|
|
|
2015-12-29 18:01:51 +01:00
|
|
|
|
if (current->con->window != NULL) {
|
|
|
|
|
i3String *formatted_title = con_parse_title_format(current->con);
|
|
|
|
|
ewmh_update_visible_name(current->con->window->id, i3string_as_utf8(formatted_title));
|
|
|
|
|
I3STRING_FREE(formatted_title);
|
|
|
|
|
}
|
2015-08-30 10:10:37 +02:00
|
|
|
|
} else {
|
2015-12-29 18:01:51 +01:00
|
|
|
|
if (current->con->window != NULL) {
|
|
|
|
|
/* We can remove _NET_WM_VISIBLE_NAME since we don't display a custom title. */
|
|
|
|
|
ewmh_update_visible_name(current->con->window->id, NULL);
|
|
|
|
|
}
|
2015-08-30 10:10:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-29 18:01:51 +01:00
|
|
|
|
if (current->con->window != NULL) {
|
|
|
|
|
/* Make sure the window title is redrawn immediately. */
|
|
|
|
|
current->con->window->name_x_changed = true;
|
|
|
|
|
} else {
|
|
|
|
|
/* For windowless containers we also need to force the redrawing. */
|
|
|
|
|
FREE(current->con->deco_render_params);
|
|
|
|
|
}
|
2015-06-10 19:01:05 +02:00
|
|
|
|
}
|
2015-06-10 18:59:45 +02:00
|
|
|
|
|
|
|
|
|
cmd_output->needs_tree_render = true;
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-08 20:34:31 +02:00
|
|
|
|
/*
|
2012-10-29 22:42:20 +01:00
|
|
|
|
* Implementation of 'rename workspace [<name>] to <name>'
|
2012-04-08 20:34:31 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
|
2014-04-30 11:12:22 +02:00
|
|
|
|
if (strncasecmp(new_name, "__", strlen("__")) == 0) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name);
|
2014-04-30 11:12:22 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2012-10-29 22:42:20 +01:00
|
|
|
|
if (old_name) {
|
|
|
|
|
LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
|
|
|
|
|
} else {
|
|
|
|
|
LOG("Renaming current workspace to \"%s\"\n", new_name);
|
|
|
|
|
}
|
2012-04-08 20:34:31 +02:00
|
|
|
|
|
2018-03-27 21:18:17 +02:00
|
|
|
|
Con *workspace;
|
2012-10-29 22:42:20 +01:00
|
|
|
|
if (old_name) {
|
2018-03-27 21:18:17 +02:00
|
|
|
|
workspace = get_existing_workspace_by_name(old_name);
|
2012-10-29 22:42:20 +01:00
|
|
|
|
} else {
|
|
|
|
|
workspace = con_get_workspace(focused);
|
2015-03-30 08:23:00 +02:00
|
|
|
|
old_name = workspace->name;
|
2012-10-29 22:42:20 +01:00
|
|
|
|
}
|
2012-04-08 20:34:31 +02:00
|
|
|
|
|
|
|
|
|
if (!workspace) {
|
2015-03-04 14:01:42 +01:00
|
|
|
|
yerror("Old workspace \"%s\" not found", old_name);
|
2012-04-08 20:34:31 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 21:18:17 +02:00
|
|
|
|
Con *check_dest = get_existing_workspace_by_name(new_name);
|
2012-04-08 20:34:31 +02:00
|
|
|
|
|
2016-02-15 08:34:03 +01:00
|
|
|
|
/* If check_dest == workspace, the user might be changing the case of the
|
|
|
|
|
* workspace, or it might just be a no-op. */
|
|
|
|
|
if (check_dest != NULL && check_dest != workspace) {
|
2015-03-04 14:01:42 +01:00
|
|
|
|
yerror("New workspace \"%s\" already exists", new_name);
|
2012-04-08 20:34:31 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Change the name and try to parse it as a number. */
|
2016-01-08 20:21:54 +01:00
|
|
|
|
/* old_name might refer to workspace->name, so copy it before free()ing */
|
|
|
|
|
char *old_name_copy = sstrdup(old_name);
|
2012-04-08 20:34:31 +02:00
|
|
|
|
FREE(workspace->name);
|
|
|
|
|
workspace->name = sstrdup(new_name);
|
2014-06-22 17:12:51 +02:00
|
|
|
|
|
|
|
|
|
workspace->num = ws_name_to_number(new_name);
|
2012-04-08 20:34:31 +02:00
|
|
|
|
LOG("num = %d\n", workspace->num);
|
|
|
|
|
|
|
|
|
|
/* By re-attaching, the sort order will be correct afterwards. */
|
|
|
|
|
Con *previously_focused = focused;
|
2018-05-01 10:25:13 +02:00
|
|
|
|
Con *previously_focused_content = focused->type == CT_WORKSPACE ? focused->parent : NULL;
|
2012-04-08 20:34:31 +02:00
|
|
|
|
Con *parent = workspace->parent;
|
|
|
|
|
con_detach(workspace);
|
|
|
|
|
con_attach(workspace, parent, false);
|
2018-09-06 02:11:47 +02:00
|
|
|
|
ipc_send_workspace_event("rename", workspace, NULL);
|
2015-03-04 09:06:44 +01:00
|
|
|
|
|
|
|
|
|
/* Move the workspace to the correct output if it has an assignment */
|
|
|
|
|
struct Workspace_Assignment *assignment = NULL;
|
|
|
|
|
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
|
|
|
|
|
if (assignment->output == NULL)
|
|
|
|
|
continue;
|
2015-03-14 22:31:58 +01:00
|
|
|
|
if (strcmp(assignment->name, workspace->name) != 0 && (!name_is_digits(assignment->name) || ws_name_to_number(assignment->name) != workspace->num)) {
|
2015-03-04 09:06:44 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-31 21:56:59 +02:00
|
|
|
|
Output *target_output = get_output_by_name(assignment->output, true);
|
|
|
|
|
if (!target_output) {
|
|
|
|
|
LOG("Could not get output named \"%s\"\n", assignment->output);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-03-27 23:55:20 +02:00
|
|
|
|
if (!output_triggers_assignment(target_output, assignment)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-03-31 21:56:59 +02:00
|
|
|
|
workspace_move_to_output(workspace, target_output);
|
2015-03-16 04:30:00 +01:00
|
|
|
|
|
2018-07-11 23:12:27 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2018-05-01 10:25:13 +02:00
|
|
|
|
|
2018-07-11 23:12:27 +02:00
|
|
|
|
bool can_restore_focus = previously_focused != NULL;
|
|
|
|
|
/* NB: If previously_focused is a workspace we can't work directly with it
|
|
|
|
|
* since it might have been cleaned up by workspace_show() already,
|
|
|
|
|
* depending on the focus order/number of other workspaces on the output.
|
|
|
|
|
* Instead, we loop through the available workspaces and only focus
|
|
|
|
|
* previously_focused if we still find it. */
|
|
|
|
|
if (previously_focused_content) {
|
|
|
|
|
Con *workspace = NULL;
|
|
|
|
|
GREP_FIRST(workspace, previously_focused_content, child == previously_focused);
|
|
|
|
|
can_restore_focus &= (workspace != NULL);
|
|
|
|
|
}
|
2015-03-16 04:30:00 +01:00
|
|
|
|
|
2018-07-11 23:12:27 +02:00
|
|
|
|
if (can_restore_focus) {
|
|
|
|
|
/* Restore the previous focus since con_attach messes with the focus. */
|
|
|
|
|
workspace_show(con_get_workspace(previously_focused));
|
|
|
|
|
con_focus(previously_focused);
|
2015-03-04 09:06:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-08 20:34:31 +02:00
|
|
|
|
cmd_output->needs_tree_render = true;
|
2012-05-02 22:01:50 +02:00
|
|
|
|
ysuccess(true);
|
2012-04-08 20:34:31 +02:00
|
|
|
|
|
2014-06-23 23:44:24 +02:00
|
|
|
|
ewmh_update_desktop_names();
|
|
|
|
|
ewmh_update_desktop_viewport();
|
|
|
|
|
ewmh_update_current_desktop();
|
2015-03-07 00:13:54 +01:00
|
|
|
|
|
2016-01-08 20:21:54 +01:00
|
|
|
|
startup_sequence_rename_workspace(old_name_copy, new_name);
|
|
|
|
|
free(old_name_copy);
|
2012-04-08 20:34:31 +02:00
|
|
|
|
}
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2018-04-21 01:28:31 +02:00
|
|
|
|
static bool cmd_bar_mode(const char *bar_mode, const char *bar_id) {
|
2013-08-18 18:12:19 +02:00
|
|
|
|
int mode = M_DOCK;
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
bool toggle = false;
|
|
|
|
|
if (strcmp(bar_mode, "dock") == 0)
|
|
|
|
|
mode = M_DOCK;
|
|
|
|
|
else if (strcmp(bar_mode, "hide") == 0)
|
|
|
|
|
mode = M_HIDE;
|
|
|
|
|
else if (strcmp(bar_mode, "invisible") == 0)
|
|
|
|
|
mode = M_INVISIBLE;
|
|
|
|
|
else if (strcmp(bar_mode, "toggle") == 0)
|
|
|
|
|
toggle = true;
|
|
|
|
|
else {
|
|
|
|
|
ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool changed_sth = false;
|
|
|
|
|
Barconfig *current = NULL;
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
if (bar_id && strcmp(current->id, bar_id) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (toggle)
|
|
|
|
|
mode = (current->mode + 1) % 2;
|
|
|
|
|
|
|
|
|
|
DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode);
|
|
|
|
|
current->mode = mode;
|
|
|
|
|
changed_sth = true;
|
|
|
|
|
|
|
|
|
|
if (bar_id)
|
2014-06-15 19:07:02 +02:00
|
|
|
|
break;
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bar_id && !changed_sth) {
|
|
|
|
|
DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2018-04-21 01:28:31 +02:00
|
|
|
|
static bool cmd_bar_hidden_state(const char *bar_hidden_state, const char *bar_id) {
|
2013-08-18 18:12:19 +02:00
|
|
|
|
int hidden_state = S_SHOW;
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
bool toggle = false;
|
|
|
|
|
if (strcmp(bar_hidden_state, "hide") == 0)
|
|
|
|
|
hidden_state = S_HIDE;
|
|
|
|
|
else if (strcmp(bar_hidden_state, "show") == 0)
|
|
|
|
|
hidden_state = S_SHOW;
|
|
|
|
|
else if (strcmp(bar_hidden_state, "toggle") == 0)
|
|
|
|
|
toggle = true;
|
|
|
|
|
else {
|
|
|
|
|
ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool changed_sth = false;
|
|
|
|
|
Barconfig *current = NULL;
|
2014-06-19 11:20:32 +02:00
|
|
|
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
if (bar_id && strcmp(current->id, bar_id) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (toggle)
|
|
|
|
|
hidden_state = (current->hidden_state + 1) % 2;
|
|
|
|
|
|
|
|
|
|
DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state);
|
|
|
|
|
current->hidden_state = hidden_state;
|
|
|
|
|
changed_sth = true;
|
|
|
|
|
|
|
|
|
|
if (bar_id)
|
2014-06-15 19:07:02 +02:00
|
|
|
|
break;
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bar_id && !changed_sth) {
|
|
|
|
|
DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_bar(I3_CMD, const char *bar_type, const char *bar_value, const char *bar_id) {
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
bool ret;
|
|
|
|
|
if (strcmp(bar_type, "mode") == 0)
|
|
|
|
|
ret = cmd_bar_mode(bar_value, bar_id);
|
|
|
|
|
else if (strcmp(bar_type, "hidden_state") == 0)
|
|
|
|
|
ret = cmd_bar_hidden_state(bar_value, bar_id);
|
|
|
|
|
else {
|
|
|
|
|
ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type);
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ysuccess(ret);
|
|
|
|
|
if (!ret)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
update_barconfig();
|
|
|
|
|
}
|
2013-06-05 15:04:57 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'shmlog <size>|toggle|on|off'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_shmlog(I3_CMD, const char *argument) {
|
2014-06-15 19:07:02 +02:00
|
|
|
|
if (!strcmp(argument, "toggle"))
|
2013-06-05 15:04:57 +02:00
|
|
|
|
/* Toggle shm log, if size is not 0. If it is 0, set it to default. */
|
|
|
|
|
shmlog_size = shmlog_size ? -shmlog_size : default_shmlog_size;
|
|
|
|
|
else if (!strcmp(argument, "on"))
|
|
|
|
|
shmlog_size = default_shmlog_size;
|
|
|
|
|
else if (!strcmp(argument, "off"))
|
|
|
|
|
shmlog_size = 0;
|
|
|
|
|
else {
|
2018-04-26 22:47:45 +02:00
|
|
|
|
long new_size = 0;
|
|
|
|
|
if (!parse_long(argument, &new_size, 0)) {
|
2018-09-11 06:44:40 +02:00
|
|
|
|
yerror("Failed to parse %s into a shmlog size.", argument);
|
2018-04-26 22:47:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2013-06-05 15:04:57 +02:00
|
|
|
|
/* If shm logging now, restart logging with the new size. */
|
|
|
|
|
if (shmlog_size > 0) {
|
|
|
|
|
shmlog_size = 0;
|
|
|
|
|
LOG("Restarting shm logging...\n");
|
|
|
|
|
init_logging();
|
|
|
|
|
}
|
2018-04-26 22:47:45 +02:00
|
|
|
|
shmlog_size = (int)new_size;
|
2013-06-05 15:04:57 +02:00
|
|
|
|
}
|
|
|
|
|
LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling");
|
|
|
|
|
init_logging();
|
|
|
|
|
update_shmlog_atom();
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|
2013-06-05 15:06:53 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Implementation of 'debuglog toggle|on|off'
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-09-27 23:42:58 +02:00
|
|
|
|
void cmd_debuglog(I3_CMD, const char *argument) {
|
2013-06-05 15:06:53 +02:00
|
|
|
|
bool logging = get_debug_logging();
|
2014-06-15 19:07:02 +02:00
|
|
|
|
if (!strcmp(argument, "toggle")) {
|
2013-06-05 15:06:53 +02:00
|
|
|
|
LOG("%s debug logging\n", logging ? "Disabling" : "Enabling");
|
|
|
|
|
set_debug_logging(!logging);
|
|
|
|
|
} else if (!strcmp(argument, "on") && !logging) {
|
|
|
|
|
LOG("Enabling debug logging\n");
|
|
|
|
|
set_debug_logging(true);
|
|
|
|
|
} else if (!strcmp(argument, "off") && logging) {
|
|
|
|
|
LOG("Disabling debug logging\n");
|
|
|
|
|
set_debug_logging(false);
|
|
|
|
|
}
|
|
|
|
|
// XXX: default reply for now, make this a better reply
|
|
|
|
|
ysuccess(true);
|
|
|
|
|
}
|