Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/bindings/keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,20 @@ bindr=Super,Super_L,spawn,rofi -show run

| Command | Param | Description |
| :--- | :--- | :--- |
| `view` | `-1/0/1-9` or `mask [,synctag]` | View tag. `-1` = previous tagset, `0` = all tags, `1-9` = specific tag, mask e.g. `1\|3\|5`. Optional `synctag` (0/1) syncs the action to all monitors. |
| `view` | `-1/0/[1, tag_count]` or `mask [,synctag]` | View tag. `-1` = previous tagset, `0` = all tags, mask e.g. `1\|3\|5`. Optional `synctag` (0/1) syncs the action to all monitors. |
| `viewtoleft` | `[synctag]` | View previous tag. Optional `synctag` (0/1) syncs to all monitors. |
| `viewtoright` | `[synctag]` | View next tag. Optional `synctag` (0/1) syncs to all monitors. |
| `viewtoleft_have_client` | `[synctag]` | View left tag and focus client if present. Optional `synctag` (0/1). |
| `viewtoright_have_client` | `[synctag]` | View right tag and focus client if present. Optional `synctag` (0/1). |
| `viewcrossmon` | `tag,monitor_spec` | View specified tag on specified monitor. |
| `tag` | `1-9 [,synctag]` | Move window to tag. Optional `synctag` (0/1) syncs to all monitors. |
| `tagsilent` | `1-9` | Move window to tag without focusing it. |
| `tag` | `[1, tag_count] [,synctag]` | Move window to tag. Optional `synctag` (0/1) syncs to all monitors. |
| `tagsilent` | `[1, tag_count]` | Move window to tag without focusing it. |
| `tagtoleft` | `[synctag]` | Move window to left tag. Optional `synctag` (0/1). |
| `tagtoright` | `[synctag]` | Move window to right tag. Optional `synctag` (0/1). |
| `tagcrossmon` | `tag,monitor_spec` | Move window to specified tag on specified monitor. |
| `toggletag` | `0-9` | Toggle tag on window (0 means all tags). |
| `toggleview` | `1-9` | Toggle tag view. |
| `comboview` | `1-9` | View multi tags pressed simultaneously. |
| `toggletag` | `[0, tag_count]` | Toggle tag on window (0 means all tags). |
| `toggleview` | `[1, tag_count]` | Toggle tag view. |
| `comboview` | `[1, tag_count]` | View multi tags pressed simultaneously. |
| `focusmon` | `left/right/up/down/monitor_spec` | Focus monitor by direction or [monitor spec](/docs/configuration/monitors#monitor-spec-format). |
| `tagmon` | `left/right/up/down/monitor_spec,[keeptag]` | Move window to monitor by direction or [monitor spec](/docs/configuration/monitors#monitor-spec-format). `keeptag` is 0 or 1. |

Expand Down
4 changes: 3 additions & 1 deletion docs/configuration/miscellaneous.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ description: Advanced settings for XWayland, focus behavior, and system integrat

| Setting | Default | Description |
| :--- | :--- | :--- |
| `tag_count` | `9` | Number of tags/workspaces available. Supports range `[1–32]`. **Requires restart to take effect.** |
| `focus_cross_monitor` | `0` | Allow directional focus to cross monitor boundaries. |
| `exchange_cross_monitor` | `0` | Allow exchanging clients across monitor boundaries. |
| `focus_cross_tag` | `0` | Allow directional focus to cross into other tags. |
Expand All @@ -48,4 +49,5 @@ description: Advanced settings for XWayland, focus behavior, and system integrat
| `no_border_when_single` | `0` | Remove window borders when only one window is visible on the tag. |
| `idleinhibit_ignore_visible` | `0` | Allow invisible clients (e.g., background audio players) to inhibit idle. |
| `drag_tile_refresh_interval` | `8.0` | Interval (1.0–16.0) to refresh tiled window resize during drag. Too small may cause application lag. |
| `drag_floating_refresh_interval` | `8.0` | Interval (1.0–16.0) to refresh floating window resize during drag. Too small may cause application lag. |
| `drag_floating_refresh_interval` | `8.0` | Interval (1.0–16.0) to refresh floating window resize during drag. Too small may cause application lag. |

4 changes: 2 additions & 2 deletions docs/window-management/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ windowrule=Parameter:Values,Parameter:Values,appid:Values,title:Values
| `offsetx` | integer | -999-999 | X offset from center (%), 100 is the edge of screen with outer gap |
| `offsety` | integer | -999-999 | Y offset from center (%), 100 is the edge of screen with outer gap |
| `monitor` | string | Any | Assign to monitor by [monitor spec](/docs/configuration/monitors#monitor-spec-format) (name, make, model, or serial) |
| `tags` | integer | 1-9 | Assign to specific tag |
| `tags` | integer | `[1, tag_count]` | Assign to specific tag |
| `no_force_center` | integer | `0` / `1` | Window does not force center |
| `isnosizehint` | integer | `0` / `1` | Don't use min size and max size for size hints |

Expand Down Expand Up @@ -172,7 +172,7 @@ tagrule=id:Values,monitor_make:xxx,monitor_model:xxx,Parameter:Values

| Parameter | Type | Values | Description |
| :--- | :--- | :--- | :--- |
| `id` | integer | 0-9 | Match by tag id, 0 means the ~0 tag |
| `id` | integer | `[1, tag_count]` | Match by tag id, 0 means the ~0 tag |
| `monitor_name` | string | monitor name | Match by monitor name |
| `monitor_make` | string | monitor make | Match by monitor manufacturer |
| `monitor_model` | string | monitor model | Match by monitor model |
Expand Down
38 changes: 22 additions & 16 deletions mmsg/mmsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static int32_t Aflag;
static uint32_t occ, seltags, total_clients, urg;

static char *output_name;
static int32_t tagcount;
static uint32_t tag_count;
static char *tagset;
static char *layout_name;
static int32_t layoutcount, layout_idx;
Expand Down Expand Up @@ -85,20 +85,25 @@ static void noop_scale(void *data, struct wl_output *wl_output,
static void noop_description(void *data, struct wl_output *wl_output,
const char *description) {}

// 将 n 转换为 9 位二进制字符串,结果存入 buf(至少长度 10)
void bin_str_9bits(char *buf, uint32_t n) {
for (int32_t i = 8; i >= 0; i--) {
*buf++ = ((n >> i) & 1) ? '1' : '0';
// Convert num to an N-bit binary string, store result in buf (minimum length
// nbits+1)
void bin_str_nbits(char *buf, uint32_t num, uint32_t nbits) {
if (nbits == 0) {
*buf = '\0';
return;
}
for (int32_t i = nbits - 1; i >= 0; i--) {
*buf++ = ((num >> i) & 1) ? '1' : '0';
}
*buf = '\0'; // 字符串结尾
*buf = '\0';
}

static void dwl_ipc_tags(void *data,
struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
uint32_t count) {
tagcount = count;
tag_count = count;
if (Tflag && mode & GET)
printf("%d\n", tagcount);
printf("%u\n", tag_count);
}

static void dwl_ipc_layout(void *data,
Expand Down Expand Up @@ -324,7 +329,7 @@ static void dwl_ipc_output_frame(void *data,
if (tflag) {
uint32_t mask = seltags;
char *t = tagset;
int32_t i = 0;
uint32_t i = 0;

for (; *t && *t >= '0' && *t <= '9'; t++)
i = *t - '0' + i * 10;
Expand All @@ -346,15 +351,15 @@ static void dwl_ipc_output_frame(void *data,
}
}

if ((i - 1) > tagcount)
if (i == 0 || (i - 1) > tag_count)
die("bad tagset %s", tagset);

zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, 0);
}
if (cflag) {
uint32_t and = ~0, xor = 0;
char *t = client_tags;
int32_t i = 0;
uint32_t i = 0;

for (; *t && *t >= '0' && *t <= '9'; t++)
i = *t - '0' + i * 10;
Expand All @@ -376,7 +381,7 @@ static void dwl_ipc_output_frame(void *data,
break;
}
}
if ((i - 1) > tagcount)
if (i == 0 || (i - 1) > tag_count)
die("bad client tagset %s", client_tags);

zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor);
Expand All @@ -395,11 +400,12 @@ static void dwl_ipc_output_frame(void *data,

printf("%s clients %u\n", output_name, total_clients);

char occ_str[10], seltags_str[10], urg_str[10];
char occ_str[tag_count + 1], seltags_str[tag_count + 1],
urg_str[tag_count + 1];

bin_str_9bits(occ_str, occ);
bin_str_9bits(seltags_str, seltags);
bin_str_9bits(urg_str, urg);
bin_str_nbits(occ_str, occ, tag_count);
bin_str_nbits(seltags_str, seltags, tag_count);
bin_str_nbits(urg_str, urg, tag_count);
printf("%s tags %u %u %u\n", output_name, occ, seltags, urg);
printf("%s tags %s %s %s\n", output_name, occ_str, seltags_str,
urg_str);
Expand Down
57 changes: 44 additions & 13 deletions src/config/parse_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,29 @@
#define SYSCONFDIR "/etc"
#endif

// We don't want to allow config hot-reloading to change the tag_count and we
// want to require a compositor reload. The code to support hot-reloading a
// change in tag_count is a lot more involved than this minimal solution we have
// for configuring the number of tags available in Mango.
static uint32_t active_tag_count = 0;

// Clamps value in range while preserving numeric type
#define CLAMP(x, min, max) \
({ \
__typeof__(x) _x = (x); \
__typeof__(min) _min = (min); \
__typeof__(max) _max = (max); \
_x < _min ? _min : (_x > _max ? _max : _x); \
})

// 整数版本 - 截断小数部分
// Deprecated: use CLAMP or CLAMP with explicit casts instead
#define CLAMP_INT(x, min, max) \
((int32_t)(x) < (int32_t)(min) \
? (int32_t)(min) \
: ((int32_t)(x) > (int32_t)(max) ? (int32_t)(max) : (int32_t)(x)))
CLAMP((int32_t)(x), (int32_t)(min), (int32_t)(max))

// 浮点数版本 - 保留小数部分
#define CLAMP_FLOAT(x, min, max) \
((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
// Deprecated: use CLAMP instead
#define CLAMP_FLOAT(x, min, max) CLAMP(x, min, max)

enum { NUM_TYPE_MINUS, NUM_TYPE_PLUS, NUM_TYPE_DEFAULT };

Expand Down Expand Up @@ -159,7 +173,7 @@ typedef struct {
} GestureBinding;

typedef struct {
int32_t id;
uint32_t id;
char *layout_name;
char *monitor_name;
char *monitor_make;
Expand Down Expand Up @@ -317,6 +331,7 @@ typedef struct {
int32_t log_level;
uint32_t capslock;

uint32_t tag_count;
ConfigTagRule *tag_rules; // 动态数组
int32_t tag_rules_count; // 数量

Expand Down Expand Up @@ -1131,7 +1146,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,

while (token != NULL) {
int32_t num = atoi(token);
if (num > 0 && num <= LENGTH(tags)) {
if (num > 0 && num <= config.tag_count) {
mask |= (1 << (num - 1));
}
token = strtok_r(NULL, "|", &saveptr);
Expand Down Expand Up @@ -1599,6 +1614,20 @@ bool parse_option(Config *config, char *key, char *value) {
config->default_mfact = atof(value);
} else if (strcmp(key, "default_nmaster") == 0) {
config->default_nmaster = atoi(value);
} else if (strcmp(key, "tag_count") == 0) {
uint32_t requested = CLAMP(atoi(value), 1, 32);
bool is_initial_config_load = active_tag_count == 0;
if (is_initial_config_load) {
config->tag_count = requested;
} else {
config->tag_count = active_tag_count;
if (active_tag_count != requested) {
wlr_log(WLR_INFO,
"tag_count change requires restart (current: %u, "
"requested: %u)",
active_tag_count, requested);
}
}
} else if (strcmp(key, "center_master_overspread") == 0) {
config->center_master_overspread = atoi(value);
} else if (strcmp(key, "center_when_single_stack") == 0) {
Expand Down Expand Up @@ -1923,7 +1952,7 @@ bool parse_option(Config *config, char *key, char *value) {
trim_whitespace(val);

if (strcmp(key, "id") == 0) {
rule->id = CLAMP_INT(atoi(val), 0, LENGTH(tags));
rule->id = CLAMP_INT(atoi(val), 0, config->tag_count);
} else if (strcmp(key, "layout_name") == 0) {
rule->layout_name = strdup(val);
} else if (strcmp(key, "monitor_name") == 0) {
Expand Down Expand Up @@ -3278,6 +3307,7 @@ void set_value_default() {
config.new_is_master = 1;
config.default_mfact = 0.55f;
config.default_nmaster = 1;
config.tag_count = 9;
config.center_master_overspread = 0;
config.center_when_single_stack = 1;

Expand Down Expand Up @@ -3515,6 +3545,7 @@ bool parse_config(void) {
config.scroller_proportion_preset_count = 0;
config.circle_layout = NULL;
config.circle_layout_count = 0;
config.tag_count = 0;
config.tag_rules = NULL;
config.tag_rules_count = 0;
config.cursor_theme = NULL;
Expand Down Expand Up @@ -3713,9 +3744,9 @@ void reapply_pointer(void) {

void reapply_master(void) {

int32_t i;
uint32_t i;
Monitor *m = NULL;
for (i = 0; i <= LENGTH(tags); i++) {
for (i = 0; i <= config.tag_count; i++) {
wl_list_for_each(m, &mons, link) {
if (!m->wlr_output->enabled) {
continue;
Expand All @@ -3731,12 +3762,12 @@ void reapply_master(void) {
}

void parse_tagrule(Monitor *m) {
int32_t i, jk;
uint32_t i, jk;
ConfigTagRule tr;
Client *c = NULL;
bool match_rule = false;

for (i = 0; i <= LENGTH(tags); i++) {
for (i = 0; i <= config.tag_count; i++) {
m->pertag->nmasters[i] = config.default_nmaster;
m->pertag->mfacts[i] = config.default_mfact;
}
Expand Down Expand Up @@ -3796,7 +3827,7 @@ void parse_tagrule(Monitor *m) {
}
}

for (i = 1; i <= LENGTH(tags); i++) {
for (i = 1; i <= config.tag_count; i++) {
wl_list_for_each(c, &clients, link) {
if ((c->tags & (1 << (i - 1)) & TAGMASK) && ISTILED(c)) {
if (m->pertag->mfacts[i] > 0.0f)
Expand Down
3 changes: 0 additions & 3 deletions src/config/preset.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#define MODKEY WLR_MODIFIER_ALT

static const char *tags[] = {
"1", "2", "3", "4", "5", "6", "7", "8", "9",
};

static const struct xkb_rule_names xkb_fallback_rules = {
.layout = "us",
Expand Down
4 changes: 2 additions & 2 deletions src/dispatch/bind_define.h
Original file line number Diff line number Diff line change
Expand Up @@ -1539,10 +1539,10 @@ int32_t viewtoright_have_client(const Arg *arg) {
return 0;
}

if (current >= LENGTH(tags))
if (current >= config.tag_count)
return 0;

for (n = current + 1; n <= LENGTH(tags); n++) {
for (n = current + 1; n <= config.tag_count; n++) {
if (get_tag_status(n, selmon)) {
found = true;
break;
Expand Down
10 changes: 5 additions & 5 deletions src/ext-protocol/dwl-ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ void dwl_ipc_manager_bind(struct wl_client *client, void *data,
&dwl_manager_implementation, NULL,
dwl_ipc_manager_destroy);

zdwl_ipc_manager_v2_send_tags(manager_resource, LENGTH(tags));
zdwl_ipc_manager_v2_send_tags(manager_resource, config.tag_count);

for (uint32_t i = 0; i < LENGTH(layouts); i++)
zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol);
Expand Down Expand Up @@ -112,15 +112,15 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
Client *c = NULL, *focused = NULL;
struct wlr_keyboard *keyboard;
xkb_layout_index_t current;
int32_t tagmask, state, numclients, focused_client, tag;
uint32_t tagmask, state, numclients, focused_client, tag_idx;
const char *title, *appid, *symbol;
char kb_layout[32];
focused = focustop(monitor);
zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon);

for (tag = 0; tag < LENGTH(tags); tag++) {
for (tag_idx = 0; tag_idx < config.tag_count; tag_idx++) {
numclients = state = focused_client = 0;
tagmask = 1 << tag;
tagmask = 1 << tag_idx;
if ((tagmask & monitor->tagset[monitor->seltags]) != 0)
state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE;
wl_list_for_each(c, &clients, link) {
Expand All @@ -134,7 +134,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT;
numclients++;
}
zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state,
zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag_idx, state,
numclients, focused_client);
}

Expand Down
Loading