diff --git a/docs/bindings/keys.md b/docs/bindings/keys.md index 08cab9ad..225738b8 100644 --- a/docs/bindings/keys.md +++ b/docs/bindings/keys.md @@ -90,6 +90,7 @@ bindr=Super,Super_L,spawn,rofi -show run | :--- | :--- | :--- | | `killclient` | - | Close the focused window. | | `togglefloating` | - | Toggle floating state. | +| `toggle_all_floating` | - | Toggle all visible clients floating state. | | `togglefullscreen` | - | Toggle fullscreen. | | `togglefakefullscreen` | - | Toggle "fake" fullscreen (remains constrained). | | `togglemaximizescreen` | - | Maximize window (keep decoration/bar). | diff --git a/docs/window-management/rules.md b/docs/window-management/rules.md index d3e60029..195f7f44 100644 --- a/docs/window-management/rules.md +++ b/docs/window-management/rules.md @@ -179,6 +179,7 @@ tagrule=id:Values,monitor_make:xxx,monitor_model:xxx,Parameter:Values | `monitor_serial` | string | monitor serial | Match by monitor serial number | | `layout_name` | string | layout name | Layout name to set | | `no_render_border` | integer | `0` / `1` | Disable render border | +| `open_as_floating` | integer | `0` / `1` | New open window will be floating| | `no_hide` | integer | `0` / `1` | Not hide even if the tag is empty | | `nmaster` | integer | 0, 99 | Number of master windows | | `mfact` | float | 0.1–0.9 | Master area factor | diff --git a/meson.build b/meson.build index c538c927..5d09d536 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('mango', ['c', 'cpp'], - version : '0.12.6', + version : '0.12.7', ) subdir('protocols') diff --git a/nix/default.nix b/nix/default.nix index 87d4bfd0..cb6497b9 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -67,8 +67,8 @@ stdenv.mkDerivation { meta = { mainProgram = "mango"; - description = "A streamlined but feature-rich Wayland compositor"; - homepage = "https://github.com/DreamMaoMao/mango"; + description = "Practical and Powerful wayland compositor (dwm but wayland)"; + homepage = "https://github.com/mangowm/mango"; license = lib.licenses.gpl3Plus; maintainers = []; platforms = lib.platforms.unix; diff --git a/src/config/parse_config.h b/src/config/parse_config.h index e9397161..7dd13418 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -182,6 +182,7 @@ typedef struct { float mfact; int32_t nmaster; int32_t no_render_border; + int32_t open_as_floating; int32_t no_hide; } ConfigTagRule; @@ -649,9 +650,14 @@ uint32_t parse_mod(const char *mod_str) { // 分割处理每个部分 token = strtok_r(input_copy, "+", &saveptr); while (token != NULL) { - // 去除空白 - while (*token == ' ' || *token == '\t') - token++; + // 去除前后空白 + trim_whitespace(token); + + // 如果 token 变成空字符串则跳过 + if (*token == '\0') { + token = strtok_r(NULL, "+", &saveptr); + continue; + } if (strncmp(token, "code:", 5) == 0) { // 处理 code: 形式 @@ -1211,6 +1217,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "scroller_stack") == 0) { func = scroller_stack; (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "toggle_all_floating") == 0) { + func = toggle_all_floating; } else { return NULL; } @@ -1928,6 +1936,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->nmaster = 0; rule->mfact = 0.0f; rule->no_render_border = 0; + rule->open_as_floating = 0; rule->no_hide = 0; bool parse_error = false; @@ -1956,6 +1965,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->monitor_serial = strdup(val); } else if (strcmp(key, "no_render_border") == 0) { rule->no_render_border = CLAMP_INT(atoi(val), 0, 1); + } else if (strcmp(key, "open_as_floating") == 0) { + rule->open_as_floating = CLAMP_INT(atoi(val), 0, 1); } else if (strcmp(key, "no_hide") == 0) { rule->no_hide = CLAMP_INT(atoi(val), 0, 1); } else if (strcmp(key, "nmaster") == 0) { @@ -3811,6 +3822,8 @@ void parse_tagrule(Monitor *m) { m->pertag->mfacts[tr.id] = tr.mfact; if (tr.no_render_border >= 0) m->pertag->no_render_border[tr.id] = tr.no_render_border; + if (tr.open_as_floating >= 0) + m->pertag->open_as_floating[tr.id] = tr.open_as_floating; } } diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index 7dced532..dbeebd33 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -70,3 +70,4 @@ int32_t disable_monitor(const Arg *arg); int32_t enable_monitor(const Arg *arg); int32_t toggle_monitor(const Arg *arg); int32_t scroller_stack(const Arg *arg); +int32_t toggle_all_floating(const Arg *arg); \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index deec59d0..45ea836d 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1866,3 +1866,27 @@ int32_t scroller_stack(const Arg *arg) { arrange(selmon, false, false); return 0; } + +int32_t toggle_all_floating(const Arg *arg) { + if (!selmon || !selmon->sel) + return 0; + + Client *c = NULL; + bool should_floating = !selmon->sel->isfloating; + + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, selmon)) { + + if (c->isfloating && !should_floating) { + c->old_master_inner_per = 0.0f; + c->old_stack_inner_per = 0.0f; + set_size_per(selmon, c); + } + + if (c->isfloating != should_floating) { + setfloating(c, should_floating); + } + } + } + return 0; +} diff --git a/src/fetch/client.h b/src/fetch/client.h index 0b142847..8fe831be 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -1,15 +1,12 @@ bool check_hit_no_border(Client *c) { - int32_t i; bool hit_no_border = false; if (!render_border) { hit_no_border = true; } - for (i = 0; i < config.tag_rules_count; i++) { - if (c->tags & (1 << (config.tag_rules[i].id - 1)) && - config.tag_rules[i].no_render_border) { - hit_no_border = true; - } + if (c->mon && !c->mon->isoverview && + c->mon->pertag->no_render_border[get_tags_first_tag_num(c->tags)]) { + hit_no_border = true; } if (config.no_border_when_single && c && c->mon && @@ -19,6 +16,7 @@ bool check_hit_no_border(Client *c) { } return hit_no_border; } + Client *termforwin(Client *w) { Client *c = NULL; diff --git a/src/mango.c b/src/mango.c index 5314bd61..92f16aa4 100644 --- a/src/mango.c +++ b/src/mango.c @@ -930,12 +930,13 @@ static struct { #include "config/preset.h" struct Pertag { - uint32_t curtag, prevtag; /* current and previous tag */ - int32_t *nmasters; /* number of windows in master area */ - float *mfacts; /* mfacts per tag */ - bool *no_hide; /* no_hide per tag */ - bool *no_render_border; /* no_render_border per tag */ - const Layout **ltidxs; /* matrix of tags and layouts indexes */ + uint32_t curtag, prevtag; /* current and previous tag */ + int32_t *nmasters; /* number of windows in master area */ + float *mfacts; /* mfacts per tag */ + bool *no_hide; /* no_hide per tag */ + bool *no_render_border; /* no_render_border per tag */ + int32_t *open_as_floating; /* open_as_floating per tag */ + const Layout **ltidxs; /* matrix of tags and layouts indexes */ }; #include "config/parse_config.h" @@ -1401,6 +1402,25 @@ void set_float_malposition(Client *tc) { tc->float_geom.y = tc->geom.y = y; } +void client_reset_mon_tags(Client *c, Monitor *mon, uint32_t newtags) { + if (!newtags && mon && !mon->isoverview) { + c->tags = mon->tagset[mon->seltags]; + } else if (!newtags && mon && mon->isoverview) { + c->tags = mon->ovbk_current_tagset; + } else if (newtags) { + c->tags = newtags; + } else { + c->tags = mon->tagset[mon->seltags]; + } +} + +void check_match_tag_floating_rule(Client *c, Monitor *mon) { + if (c->tags && !c->isfloating && mon && !c->swallowedby && + mon->pertag->open_as_floating[get_tags_first_tag_num(c->tags)]) { + c->isfloating = 1; + } +} + void applyrules(Client *c) { /* rule matching */ const char *appid, *title; @@ -1525,6 +1545,7 @@ void applyrules(Client *c) { int32_t fullscreen_state_backup = c->isfullscreen || client_wants_fullscreen(c); + setmon(c, mon, newtags, !c->isopensilent && !(client_is_x11_popup(c) && client_should_ignore_focus(c)) && @@ -1532,6 +1553,11 @@ void applyrules(Client *c) { (!c->istagsilent || !newtags || newtags & mon->tagset[mon->seltags])); + if (!c->isfloating) { + c->old_stack_inner_per = c->stack_inner_per; + c->old_master_inner_per = c->master_inner_per; + } + if (c->mon && !(c->mon == selmon && c->tags & c->mon->tagset[c->mon->seltags]) && !c->isopensilent && !c->istagsilent) { @@ -2305,6 +2331,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { free(m->pertag->mfacts); free(m->pertag->no_hide); free(m->pertag->no_render_border); + free(m->pertag->open_as_floating); free(m->pertag->ltidxs); free(m->pertag); free(m); @@ -3050,9 +3077,10 @@ void createmon(struct wl_listener *listener, void *data) { m->pertag->mfacts = calloc(config.tag_count + 1, sizeof(float)); m->pertag->no_hide = calloc(config.tag_count + 1, sizeof(bool)); m->pertag->no_render_border = calloc(config.tag_count + 1, sizeof(bool)); + m->pertag->open_as_floating = calloc(config.tag_count + 1, sizeof(int32_t)); m->pertag->ltidxs = calloc(config.tag_count + 1, sizeof(const Layout *)); if (!m->pertag->nmasters || !m->pertag->mfacts || !m->pertag->no_hide || - !m->pertag->no_render_border || !m->pertag->ltidxs) + !m->pertag->no_render_border || !m->pertag->open_as_floating || !m->pertag->ltidxs) die("pertag member calloc failed"); if (chvt_backup_tag && regex_match(chvt_backup_selmon, m->wlr_output->name)) { @@ -4058,9 +4086,9 @@ void init_client_properties(Client *c) { c->master_mfact_per = 0.0f; c->master_inner_per = 0.0f; c->stack_inner_per = 0.0f; - c->old_stack_inner_per = 1.0f; - c->old_master_inner_per = 1.0f; - c->old_master_mfact_per = 1.0f; + c->old_stack_inner_per = 0.0f; + c->old_master_inner_per = 0.0f; + c->old_master_mfact_per = 0.0f; c->isterm = 0; c->allow_csd = 0; c->force_maximize = 0; @@ -5078,7 +5106,8 @@ setfloating(Client *c, int32_t floating) { // 让当前tag中的全屏窗口退出全屏参与平铺 wl_list_for_each(fc, &clients, link) if (fc && fc != c && VISIBLEON(fc, c->mon) && - c->tags & fc->tags && ISFULLSCREEN(fc)) { + c->tags & fc->tags && ISFULLSCREEN(fc) && + old_floating_state) { clear_fullscreen_flag(fc); } } @@ -5092,7 +5121,8 @@ setfloating(Client *c, int32_t floating) { layers[c->isfloating ? LyrTop : LyrTile]); } - if (!c->isfloating && old_floating_state) { + if (!c->isfloating && old_floating_state && + (c->old_stack_inner_per > 0.0f || c->old_master_inner_per > 0.0f)) { restore_size_per(c->mon, c); } @@ -5111,6 +5141,12 @@ setfloating(Client *c, int32_t floating) { } arrange(c->mon, false, false); + + if (!c->isfloating) { + c->old_master_inner_per = c->master_inner_per; + c->old_stack_inner_per = c->stack_inner_per; + } + setborder_color(c); printstatus(); } @@ -5391,15 +5427,8 @@ void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) { /* Make sure window actually overlaps with the monitor */ reset_foreign_tolevel(c); resize(c, c->geom, 0); - if (!newtags && !m->isoverview) { - c->tags = m->tagset[m->seltags]; - } else if (!newtags && m->isoverview) { - c->tags = m->ovbk_current_tagset; - } else if (newtags) { - c->tags = newtags; - } else { - c->tags = m->tagset[m->seltags]; - } + client_reset_mon_tags(c, m, newtags); + check_match_tag_floating_rule(c, m); setfloating(c, c->isfloating); setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ }