From: Michael Stapelberg Date: Mon, 23 May 2011 16:41:17 +0000 (+0200) Subject: Implement assignments for (named) workspaces, with '~' compatibility (floating) X-Git-Tag: tree-pr3~11 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=2c68c234ea99f210ab2c8a47124f23849b5d6037;p=i3%2Fi3 Implement assignments for (named) workspaces, with '~' compatibility (floating) --- diff --git a/include/assignments.h b/include/assignments.h index ecd8b808..f72dd2e5 100644 --- a/include/assignments.h +++ b/include/assignments.h @@ -12,4 +12,10 @@ */ void run_assignments(i3Window *window); +/** + * Returns the first matching assignment for the given window. + * + */ +Assignment *assignment_for(i3Window *window, int type); + #endif diff --git a/include/data.h b/include/data.h index 832200ee..fed4420c 100644 --- a/include/data.h +++ b/include/data.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) * * include/data.h: This file defines all data structures used by i3 * @@ -300,8 +300,6 @@ struct Match { Con *con_id; enum { M_ANY = 0, M_TILING, M_FLOATING } floating; - char *target_ws; - /* Where the window looking for a match should be inserted: * * M_HERE = the matched container will be replaced by the window @@ -314,9 +312,16 @@ struct Match { enum { M_HERE = 0, M_ASSIGN_WS, M_BELOW } insert_where; TAILQ_ENTRY(Match) matches; - TAILQ_ENTRY(Match) assignments; }; +/** + * An Assignment makes specific windows go to a specific workspace/output or + * run a command for that window. With this mechanism, the user can -- for + * example -- make specific windows floating or assign his browser to workspace + * "www". Checking if a window is assigned works by comparing the Match data + * structure with the window (see match_matches_window()). + * + */ struct Assignment { /** type of this assignment: * @@ -324,8 +329,17 @@ struct Assignment { * A_TO_WORKSPACE = assign the matching window to the specified workspace * A_TO_OUTPUT = assign the matching window to the specified output * + * While the type is a bitmask, only one value can be set at a time. It is + * a bitmask to allow filtering for multiple types, for example in the + * assignment_for() function. + * */ - enum { A_COMMAND = 0, A_TO_WORKSPACE = 1, A_TO_OUTPUT = 2 } type; + enum { + A_ANY = 0, + A_COMMAND = (1 << 0), + A_TO_WORKSPACE = (1 << 1), + A_TO_OUTPUT = (1 << 2) + } type; /** the criteria to check if a window matches */ Match match; @@ -337,7 +351,7 @@ struct Assignment { char *output; } dest; - TAILQ_ENTRY(Assignment) real_assignments; + TAILQ_ENTRY(Assignment) assignments; }; struct Con { diff --git a/include/i3.h b/include/i3.h index 6aeea847..7eb48ecc 100644 --- a/include/i3.h +++ b/include/i3.h @@ -26,9 +26,8 @@ extern Display *xlibdpy, *xkbdpy; extern int xkb_current_group; extern TAILQ_HEAD(bindings_head, Binding) *bindings; extern TAILQ_HEAD(autostarts_head, Autostart) autostarts; -extern TAILQ_HEAD(assignments_head, Match) assignments; extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments; -extern TAILQ_HEAD(real_assignments_head, Assignment) real_assignments; +extern TAILQ_HEAD(assignments_head, Assignment) assignments; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; extern uint8_t root_depth; extern bool xcursor_supported, xkb_supported; diff --git a/include/match.h b/include/match.h index 4f0e9bdc..2786c66a 100644 --- a/include/match.h +++ b/include/match.h @@ -17,15 +17,15 @@ void match_init(Match *match); bool match_is_empty(Match *match); /** - * Check if a match data structure matches the given window. + * Copies the data of a match from src to dest. * */ -bool match_matches_window(Match *match, i3Window *window); +void match_copy(Match *dest, Match *src); /** - * Returns the first match in 'assignments' that matches the given window. + * Check if a match data structure matches the given window. * */ -Match *match_by_assignment(i3Window *window); +bool match_matches_window(Match *match, i3Window *window); #endif diff --git a/src/assignments.c b/src/assignments.c index f41877f0..f171dc3b 100644 --- a/src/assignments.c +++ b/src/assignments.c @@ -17,7 +17,7 @@ void run_assignments(i3Window *window) { /* Check if any assignments match */ Assignment *current; - TAILQ_FOREACH(current, &real_assignments, real_assignments) { + TAILQ_FOREACH(current, &assignments, assignments) { if (!match_matches_window(&(current->match), window)) continue; @@ -48,3 +48,21 @@ void run_assignments(i3Window *window) { window->ran_assignments[window->nr_assignments-1] = current; } } + +/* + * Returns the first matching assignment for the given window. + * + */ +Assignment *assignment_for(i3Window *window, int type) { + Assignment *assignment; + + TAILQ_FOREACH(assignment, &assignments, assignments) { + if ((type != A_ANY && (assignment->type & type) == 0) || + !match_matches_window(&(assignment->match), window)) + continue; + DLOG("got a matching assignment (to %s)\n", assignment->dest.workspace); + return assignment; + } + + return NULL; +} diff --git a/src/cfgparse.l b/src/cfgparse.l index 41445944..fd9613f0 100644 --- a/src/cfgparse.l +++ b/src/cfgparse.l @@ -44,6 +44,7 @@ EOL (\r?\n) %s WANT_QSTRING %s BINDSYM_COND %s ASSIGN_COND +%s ASSIGN_TARGET_COND %s COLOR_COND %s OUTPUT_COND %s FOR_WINDOW_COND @@ -87,6 +88,8 @@ EOL (\r?\n) [a-zA-Z0-9_-]+ { yylval.string = sstrdup(yytext); return OUTPUT; } ^[ \t]*#[^\n]* { return TOKCOMMENT; } [0-9a-fA-F]+ { yylval.string = sstrdup(yytext); return HEX; } +[ \t]*→[ \t]* { BEGIN(WANT_STRING); } +[ \t]+ { BEGIN(WANT_STRING); } [0-9]+ { yylval.number = atoi(yytext); return NUMBER; } mode { return TOKMODE; } bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; } @@ -104,7 +107,7 @@ screen { } terminal { WS_STRING; return TOKTERMINAL; } font { WS_STRING; return TOKFONT; } -assign { BEGIN(ASSIGN_COND); return TOKASSIGN; } +assign { yy_push_state(ASSIGN_TARGET_COND); yy_push_state(ASSIGN_COND); return TOKASSIGN; } set[^\n]* { return TOKCOMMENT; } ipc-socket { WS_STRING; return TOKIPCSOCKET; } ipc_socket { WS_STRING; return TOKIPCSOCKET; } @@ -160,7 +163,6 @@ Mode_switch { yylval.number = BIND_MODE_SWITCH; return MODIF control { return TOKCONTROL; } ctrl { return TOKCONTROL; } shift { return TOKSHIFT; } -→ { return TOKARROW; } class { yy_push_state(WANT_QSTRING); return TOK_CLASS; } id { yy_push_state(WANT_QSTRING); return TOK_ID; } @@ -179,14 +181,16 @@ title { yy_push_state(WANT_QSTRING); return TOK_TITLE; [ \t]+ { /* ignore whitespace */ ; } \"[^\"]+\" { /* if ASSIGN_COND then */ - BEGIN(INITIAL); + if (yy_start_stack_ptr > 0) + yy_pop_state(); + else BEGIN(INITIAL); /* yylval will be the string, but without quotes */ char *copy = sstrdup(yytext+1); copy[strlen(copy)-1] = '\0'; yylval.string = copy; return QUOTEDSTRING; } -[^ \t]+ { BEGIN(INITIAL); yylval.string = sstrdup(yytext); return STR_NG; } +[^ \t\"]+ { BEGIN(ASSIGN_TARGET_COND); yylval.string = sstrdup(yytext); return STR_NG; } [a-zA-Z0-9_]+ { yylval.string = sstrdup(yytext); return WORD; } [a-zA-Z]+ { yylval.string = sstrdup(yytext); return WORD; } . { return (int)yytext[0]; } diff --git a/src/cfgparse.y b/src/cfgparse.y index 346f9476..d6eb12cd 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -268,7 +268,6 @@ void parse_file(const char *f) { %type optional_workspace_name %type workspace_name %type window_class -%type assign_target %% @@ -356,7 +355,7 @@ for_window: assignment->type = A_COMMAND; assignment->match = current_match; assignment->dest.command = $3; - TAILQ_INSERT_TAIL(&real_assignments, assignment, real_assignments); + TAILQ_INSERT_TAIL(&assignments, assignment, assignments); } ; @@ -651,57 +650,54 @@ workspace_name: ; assign: - TOKASSIGN window_class optional_arrow assign_target + TOKASSIGN window_class STR { - printf("assignment of %s\n", $2); + printf("assignment of %s to *%s*\n", $2, $3); + char *workspace = $3; + char *criteria = $2; - struct Match *match = $4; + Assignment *assignment = scalloc(sizeof(Assignment)); + Match *match = &(assignment->match); + match_init(match); char *separator = NULL; - if ((separator = strchr($2, '/')) != NULL) { + if ((separator = strchr(criteria, '/')) != NULL) { *(separator++) = '\0'; match->title = sstrdup(separator); } - if (*$2 != '\0') - match->class = sstrdup($2); - free($2); + if (*criteria != '\0') + match->class = sstrdup(criteria); + free(criteria); printf(" class = %s\n", match->class); printf(" title = %s\n", match->title); - if (match->insert_where == M_ASSIGN_WS) - printf(" to ws %s\n", match->target_ws); - TAILQ_INSERT_TAIL(&assignments, match, assignments); - } - ; -assign_target: - NUMBER - { - /* TODO: named workspaces */ - Match *match = smalloc(sizeof(Match)); - match_init(match); - match->insert_where = M_ASSIGN_WS; - asprintf(&(match->target_ws), "%d", $1); - $$ = match; - } - | '~' - { - /* TODO: compatiblity */ -#if 0 - struct Assignment *new = scalloc(sizeof(struct Assignment)); - new->floating = ASSIGN_FLOATING_ONLY; - $$ = new; -#endif - } - | '~' NUMBER - { - /* TODO: compatiblity */ -#if 0 - struct Assignment *new = scalloc(sizeof(struct Assignment)); - new->workspace = $2; - new->floating = ASSIGN_FLOATING; - $$ = new; -#endif + /* Compatibility with older versions: If the assignment target starts + * with ~, we create the equivalent of: + * + * for_window [class="foo"] mode floating + */ + if (*workspace == '~') { + workspace++; + if (*workspace == '\0') { + /* This assignment was *only* for floating */ + assignment->type = A_COMMAND; + assignment->dest.command = sstrdup("mode floating"); + TAILQ_INSERT_TAIL(&assignments, assignment, assignments); + break; + } else { + /* Create a new assignment and continue afterwards */ + Assignment *floating = scalloc(sizeof(Assignment)); + match_copy(&(floating->match), match); + floating->type = A_COMMAND; + floating->dest.command = sstrdup("mode floating"); + TAILQ_INSERT_TAIL(&assignments, floating, assignments); + } + } + + assignment->type = A_TO_WORKSPACE; + assignment->dest.workspace = workspace; + TAILQ_INSERT_TAIL(&assignments, assignment, assignments); } ; @@ -710,11 +706,6 @@ window_class: | STR_NG ; -optional_arrow: - /* NULL */ - | TOKARROW - ; - ipcsocket: TOKIPCSOCKET STR { diff --git a/src/main.c b/src/main.c index 39702223..c079d8dc 100644 --- a/src/main.c +++ b/src/main.c @@ -37,8 +37,6 @@ struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments); * output) */ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignments); -struct real_assignments_head real_assignments = TAILQ_HEAD_INITIALIZER(real_assignments); - /* We hope that those are supported and set them to true */ bool xcursor_supported = true; bool xkb_supported = true; diff --git a/src/manage.c b/src/manage.c index 7d240f42..9ccdc19a 100644 --- a/src/manage.c +++ b/src/manage.c @@ -205,17 +205,19 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki Con *nc = NULL; Match *match; + Assignment *assignment; /* check assignments first */ - if ((match = match_by_assignment(cwindow))) { + if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE | A_TO_OUTPUT))) { DLOG("Assignment matches (%p)\n", match); - if (match->insert_where == M_ASSIGN_WS) { - nc = con_descend_focused(workspace_get(match->target_ws, NULL)); - DLOG("focused on ws %s: %p / %s\n", match->target_ws, nc, nc->name); + if (assignment->type == A_TO_WORKSPACE) { + nc = con_descend_focused(workspace_get(assignment->dest.workspace, NULL)); + DLOG("focused on ws %s: %p / %s\n", assignment->dest.workspace, nc, nc->name); if (nc->type == CT_WORKSPACE) nc = tree_open_con(nc); else nc = tree_open_con(nc->parent); } + /* TODO: handle assignments with type == A_TO_OUTPUT */ } else { /* TODO: two matches for one container */ diff --git a/src/match.c b/src/match.c index fd024d59..2449bad7 100644 --- a/src/match.c +++ b/src/match.c @@ -45,6 +45,25 @@ bool match_is_empty(Match *match) { match->floating == M_ANY); } +/* + * Copies the data of a match from src to dest. + * + */ +void match_copy(Match *dest, Match *src) { + memcpy(dest, src, sizeof(Match)); + +#define STRDUP(field) do { \ + if (src->field != NULL) \ + dest->field = sstrdup(src->field); \ +} while (0) + + STRDUP(title); + STRDUP(mark); + STRDUP(application); + STRDUP(class); + STRDUP(instance); +} + /* * Check if a match data structure matches the given window. * @@ -87,20 +106,3 @@ bool match_matches_window(Match *match, i3Window *window) { return false; } - -/* - * Returns the first match in 'assignments' that matches the given window. - * - */ -Match *match_by_assignment(i3Window *window) { - Match *match; - - TAILQ_FOREACH(match, &assignments, assignments) { - if (!match_matches_window(match, window)) - continue; - DLOG("got a matching assignment (to %s)\n", match->target_ws); - return match; - } - - return NULL; -}