%token TOK_ENABLE "enable"
%token TOK_DISABLE "disable"
%token TOK_WORKSPACE "workspace"
+%token TOK_OUTPUT "output"
%token TOK_TOGGLE "toggle"
%token TOK_FOCUS "focus"
%token TOK_MOVE "move"
%token TOK_CLASS "class"
%token TOK_INSTANCE "instance"
+%token TOK_WINDOW_ROLE "window_role"
%token TOK_ID "id"
%token TOK_CON_ID "con_id"
%token TOK_TITLE "title"
}
} else if (current_match.mark != NULL && current->con->mark != NULL &&
- strcasecmp(current_match.mark, current->con->mark) == 0) {
+ regex_matches(current_match.mark, current->con->mark)) {
printf("match by mark\n");
- TAILQ_INSERT_TAIL(&owindows, current, owindows);
-
+ TAILQ_INSERT_TAIL(&owindows, current, owindows);
} else {
if (current->con->window == NULL)
continue;
TOK_CLASS '=' STR
{
printf("criteria: class = %s\n", $3);
- current_match.class = $3;
+ current_match.class = regex_new($3);
+ free($3);
}
| TOK_INSTANCE '=' STR
{
printf("criteria: instance = %s\n", $3);
- current_match.instance = $3;
+ current_match.instance = regex_new($3);
+ free($3);
+ }
+ | TOK_WINDOW_ROLE '=' STR
+ {
+ printf("criteria: window_role = %s\n", $3);
+ current_match.role = regex_new($3);
+ free($3);
}
| TOK_CON_ID '=' STR
{
| TOK_MARK '=' STR
{
printf("criteria: mark = %s\n", $3);
- current_match.mark = $3;
+ current_match.mark = regex_new($3);
+ free($3);
}
| TOK_TITLE '=' STR
{
printf("criteria: title = %s\n", $3);
- current_match.title = $3;
+ current_match.title = regex_new($3);
+ free($3);
}
;
else {
TAILQ_FOREACH(current, &owindows, owindows) {
printf("matching: %p / %s\n", current->con, current->con->name);
- tree_close(current->con, $2, false);
+ tree_close(current->con, $2, false, false);
}
}
tree_render();
}
+ | TOK_MOVE TOK_OUTPUT STR
+ {
+ owindow *current;
+
+ printf("should move window to output %s", $3);
+
+ HANDLE_EMPTY_MATCH;
+
+ /* get the output */
+ Output *current_output = NULL;
+ Output *output;
+
+ TAILQ_FOREACH(current, &owindows, owindows)
+ current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
+
+ assert(current_output != NULL);
+
+ if (strcasecmp($3, "up") == 0)
+ output = get_output_next(D_UP, current_output);
+ else if (strcasecmp($3, "down") == 0)
+ output = get_output_next(D_DOWN, current_output);
+ else if (strcasecmp($3, "left") == 0)
+ output = get_output_next(D_LEFT, current_output);
+ else if (strcasecmp($3, "right") == 0)
+ output = get_output_next(D_RIGHT, current_output);
+ else
+ output = get_output_by_name($3);
+ free($3);
+
+ if (!output)
+ break;
+
+ /* get visible workspace on output */
+ Con *ws = NULL;
+ GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
+ if (!ws)
+ break;
+
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ printf("matching: %p / %s\n", current->con, current->con->name);
+ con_move_to_workspace(current->con, ws, true, false);
+ }
+
+ tree_render();
+ }
;
append_layout:
double percentage = 1.0 / children;
LOG("default percentage = %f\n", percentage);
+ orientation_t orientation = current->parent->orientation;
+
+ if ((orientation == HORIZ &&
+ (direction == TOK_UP || direction == TOK_DOWN)) ||
+ (orientation == VERT &&
+ (direction == TOK_LEFT || direction == TOK_RIGHT))) {
+ LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
+ (orientation == HORIZ ? "horizontal" : "vertical"));
+ break;
+ }
+
if (direction == TOK_UP || direction == TOK_LEFT) {
other = TAILQ_PREV(current, nodes_head, nodes);
} else {
}
if (other == TAILQ_END(workspaces)) {
LOG("No other container in this direction found, cannot resize.\n");
- return 0;
+ break;
}
LOG("other->percent = %f\n", other->percent);
LOG("current->percent before = %f\n", current->percent);
/* check if the parent container is empty and close it if so */
if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) && con_num_children(con->parent) == 0) {
DLOG("Old container empty after setting this child to floating, closing\n");
- tree_close(con->parent, DONT_KILL_WINDOW, false);
+ tree_close(con->parent, DONT_KILL_WINDOW, false, false);
}
char *name;
con->percent = 1.0;
con->floating = FLOATING_USER_ON;
+ /* 4: set the border style as specified with new_float */
+ if (automatic)
+ con->border_style = config.default_floating_border;
+
TAILQ_INSERT_TAIL(&(nc->nodes_head), con, nodes);
TAILQ_INSERT_TAIL(&(nc->focus_head), con, focused);
/* 2: kill parent container */
TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows);
TAILQ_REMOVE(&(con->parent->parent->focus_head), con->parent, focused);
- tree_close(con->parent, DONT_KILL_WINDOW, false);
+ tree_close(con->parent, DONT_KILL_WINDOW, false, false);
/* 3: re-attach to the parent of the currently focused con on the workspace
* this floating con was on */
}
#if 0
-/*
- * Changes focus in the given direction for floating clients.
- *
- * Changing to the left/right means going to the previous/next floating client,
- * changing to top/bottom means cycling through the Z-index.
- *
- */
-void floating_focus_direction(xcb_connection_t *conn, Client *currently_focused, direction_t direction) {
- DLOG("floating focus\n");
-
- if (direction == D_LEFT || direction == D_RIGHT) {
- /* Go to the next/previous floating client */
- Client *client;
-
- while ((client = (direction == D_LEFT ? TAILQ_PREV(currently_focused, floating_clients_head, floating_clients) :
- TAILQ_NEXT(currently_focused, floating_clients))) !=
- TAILQ_END(&(currently_focused->workspace->floating_clients))) {
- if (!client->floating)
- continue;
- set_focus(conn, client, true);
- return;
- }
- }
-}
-
/*
* Moves the client 10px to the specified direction.
*
return 1;
}
- tree_close(con, DONT_KILL_WINDOW, false);
+ tree_close(con, DONT_KILL_WINDOW, false, false);
tree_render();
x_push_changes(croot);
return 1;
return true;
}
+/*
+ * Called when a window changes its WM_WINDOW_ROLE.
+ *
+ */
+static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t state,
+ xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
+ Con *con;
+ if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
+ return false;
+
+ window_update_role(con->window, prop, false);
+
+ return true;
+}
+
#if 0
/*
* Updates the client’s WM_CLASS property
{ 0, 128, handle_windowname_change_legacy },
{ 0, UINT_MAX, handle_normal_hints },
{ 0, UINT_MAX, handle_clientleader_change },
- { 0, UINT_MAX, handle_transient_for }
+ { 0, UINT_MAX, handle_transient_for },
+ { 0, 128, handle_windowrole_change }
};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
property_handlers[3].atom = XCB_ATOM_WM_NORMAL_HINTS;
property_handlers[4].atom = A_WM_CLIENT_LEADER;
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
+ property_handlers[6].atom = A_WM_WINDOW_ROLE;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
if (!exists) {
/* Set ->num to the number of the workspace, if the name actually
* is a number or starts with a number */
- long parsed_num = strtol(ws->name, NULL, 10);
+ char *endptr = NULL;
+ long parsed_num = strtol(ws->name, &endptr, 10);
if (parsed_num == LONG_MIN ||
parsed_num == LONG_MAX ||
- parsed_num <= 0)
+ parsed_num < 0 ||
+ endptr == ws->name)
ws->num = -1;
else ws->num = parsed_num;
LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
}
DLOG("destroying disappearing con %p\n", output->con);
- tree_close(output->con, DONT_KILL_WINDOW, true);
+ tree_close(output->con, DONT_KILL_WINDOW, true, false);
DLOG("Done. Should be fine now\n");
output->con = NULL;
}
* Returns true if the container was killed or false if just WM_DELETE was sent
* and the window is expected to kill itself.
*
+ * The dont_kill_parent flag is specified when the function calls itself
+ * recursively while deleting a containers children.
+ *
+ * The force_set_focus flag is specified in the case of killing a floating
+ * window: tree_close() will be invoked for the CT_FLOATINGCON (the parent
+ * container) and focus should be set there.
+ *
*/
- bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent) {
+ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus) {
bool was_mapped = con->mapped;
Con *parent = con->parent;
for (child = TAILQ_FIRST(&(con->nodes_head)); child; ) {
nextchild = TAILQ_NEXT(child, nodes);
DLOG("killing child=%p\n", child);
- if (!tree_close(child, kill_window, true))
+ if (!tree_close(child, kill_window, true, false))
abort_kill = true;
child = nextchild;
}
if (con_is_floating(con)) {
Con *ws = con_get_workspace(con);
DLOG("Container was floating, killing floating container\n");
- tree_close(parent, DONT_KILL_WINDOW, false);
+ tree_close(parent, DONT_KILL_WINDOW, false, (con == focused));
DLOG("parent container killed\n");
if (con == focused) {
DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws);
/* Instead of focusing the dockarea, we need to restore focus to the workspace */
con_focus(con_descend_focused(output_get_content(next->parent)));
} else {
- if (con != focused)
+ if (!force_set_focus && con != focused)
DLOG("not changing focus, the container was not focused before\n");
else con_focus(next);
}
assert(focused->type != CT_ROOT);
/* Kill con */
- tree_close(focused, kill_window, false);
+ tree_close(focused, kill_window, false, false);
}
/*
return true;
}
+ Con *parent = con->parent;
+
if (con->type == CT_FLOATING_CON) {
- /* TODO: implement focus for floating windows */
- return false;
- }
+ /* left/right focuses the previous/next floating container */
+ if (orientation == HORIZ) {
+ Con *next;
+ if (way == 'n')
+ next = TAILQ_NEXT(con, floating_windows);
+ else next = TAILQ_PREV(con, floating_head, floating_windows);
+
+ /* If there is no next/previous container, wrap */
+ if (!next) {
+ if (way == 'n')
+ next = TAILQ_FIRST(&(parent->floating_head));
+ else next = TAILQ_LAST(&(parent->floating_head), floating_head);
+ }
- Con *parent = con->parent;
+ /* Still no next/previous container? bail out */
+ if (!next)
+ return false;
+
+ con_focus(con_descend_focused(next));
+ return true;
+ } else {
+ /* up/down cycles through the Z-index */
+ /* TODO: implement cycling through the z-index */
+ return false;
+ }
+ }
/* If the orientation does not match or there is no other con to focus, we
* need to go higher in the hierarchy */
/* 4: close the redundant cons */
DLOG("closing redundant cons\n");
- tree_close(con, DONT_KILL_WINDOW, true);
+ tree_close(con, DONT_KILL_WINDOW, true, false);
/* Well, we got to abort the recursion here because we destroyed the
* container. However, if tree_flatten() is called sufficiently often,
workspace->name = sstrdup(num);
/* We set ->num to the number if this workspace’s name consists only of
* a positive number. Otherwise it’s a named ws and num will be -1. */
- char *end;
- long parsed_num = strtol(num, &end, 10);
+ char *endptr = NULL;
+ long parsed_num = strtol(num, &endptr, 10);
if (parsed_num == LONG_MIN ||
parsed_num == LONG_MAX ||
parsed_num < 0 ||
- (end && *end != '\0'))
+ endptr == num)
workspace->num = -1;
else workspace->num = parsed_num;
LOG("num = %d\n", workspace->num);
/* check if this workspace is currently visible */
if (!workspace_is_visible(old)) {
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
- tree_close(old, DONT_KILL_WINDOW, false);
+ tree_close(old, DONT_KILL_WINDOW, false, false);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
changed_num_workspaces = true;
}