Merge branch 'DreamMaoMao:main' into validate-config-hm

This commit is contained in:
Tomate 06 13 2026-02-25 14:40:00 +01:00 committed by GitHub
commit f5372f160c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 920 additions and 155 deletions

View file

@ -23,14 +23,22 @@ This project's development is based on [dwl](https://codeberg.org/dwl/dwl/).
- Ipc support(get/send message from/to compositor by external program)
- Hycov-like overview
- Window effects from scenefx (blur, shadow, corner radius, opacity)
- Zero flickering - every frame is perfect.
https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
# Mango's Vision
**Mango's primary goal is stability**: After months of testing and development—and aside from a few lingering GPU compatibility issues—it should now be stable enough. I don't plan on making many breaking changes.
**Mango's preference is practicality**: I tend to add features that genuinely help with daily workflows—things that make our work more convenient.
**Mango won't cater to every user preference**: For niche feature requests, I'll take a wait-and-see approach. I'll only consider adding them if they get a significant number of upvotes.
# Our discord
[mangowc](https://discord.gg/CPjbDxesh5)
# Supported layouts
- tile
- scroller
- monocle
@ -40,21 +48,20 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
- vertical_tile
- vertical_grid
- vertical_scroller
- tgmix
# Installation
[![Packaging status](https://repology.org/badge/vertical-allrepos/mangowc.svg)](https://repology.org/project/mangowc/versions)
## Dependencies
- glibc
- wayland
- wayland-protocols
- libinput
- libdrm
- libxkbcommon
- pixman
- git
- meson
- ninja
- libdisplay-info
- libliftoff
- hwdata
@ -98,6 +105,35 @@ Then, install the package:
dnf install mangowc
```
## Guix System
The package definition is described in the source repository.
First, add `mangowc` channel to `channels.scm` file:
```scheme
;; In $HOME/.config/guix/channels.scm
(cons (channel
(name 'mangowc)
(url "https://github.com/DreamMaoMao/mangowc.git")
(branch "main"))
... ;; Your other channels
%default-channels)
```
Then, run `guix pull` and after update you can either run
`guix install mangowc` or add it to your configuration via:
```scheme
(use-modules (mangowc)) ;; Add mangowc module
;; Add mangowc to packages list
(packages (cons*
mangowc-git
... ;; Other packages you specified
%base-packages))
```
And then rebuild your system.
## Other
```bash

View file

@ -0,0 +1,5 @@
[preferred]
default=gtk
org.freedesktop.impl.portal.ScreenCast=wlr
org.freedesktop.impl.portal.Screenshot=wlr
org.freedesktop.impl.portal.Inhibit=none

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'],
version : '0.12.1',
version : '0.12.3',
)
subdir('protocols')
@ -56,7 +56,7 @@ endif
if is_git_repo
# 如果是 Git 目录,获取 Commit Hash 和最新的 tag
commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip()
latest_tag = run_command(git, 'describe', '--tags', '--abbrev=0', check : false).stdout().strip()
latest_tag = meson.project_version()
version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
else
# 如果不是 Git 目录,使用项目版本号和 "release" 字符串
@ -147,5 +147,7 @@ executable('mmsg',
)
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
install_data('mango.desktop', install_dir : desktop_install_dir)
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
portal_install_dir = join_paths(prefix, 'share/xdg-desktop-portal')
install_data('assets/mango.desktop', install_dir : desktop_install_dir)
install_data('assets/mango-portals.conf', install_dir : portal_install_dir)
install_data('assets/config.conf', install_dir : join_paths(sysconfdir, 'mango'))

View file

@ -9,6 +9,10 @@ in {
options = {
programs.mango = {
enable = lib.mkEnableOption "mango, a wayland compositor based on dwl";
addLoginEntry = lib.mkEnableOption {
default = true;
description = "Whether to add a login entry to the display manager for mango";
};
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango;
@ -55,7 +59,7 @@ in {
programs.xwayland.enable = lib.mkDefault true;
services = {
displayManager.sessionPackages = [cfg.package];
displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [ cfg.package ];
graphical-desktop.enable = lib.mkDefault true;
};

View file

@ -1,6 +1,6 @@
wayland_scanner = find_program('wayland-scanner')
wayland_protos_dep = dependency('wayland-protocols')
wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir')
wl_protocol_dir = wayland_protos_dep.get_variable(pkgconfig:'pkgdatadir')
wayland_scanner_code = generator(
wayland_scanner,
output: '@BASENAME@-protocol.c',

View file

@ -990,6 +990,10 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 2 * c->bw);
if (c->configure_serial != 0) {
c->mon->resizing_count_pending++;
}
if (c == grabc) {
c->animation.running = false;
c->need_output_flush = false;

View file

@ -243,31 +243,6 @@ static inline int32_t client_is_rendered_on_mon(Client *c, Monitor *m) {
return 0;
}
static inline int32_t client_is_stopped(Client *c) {
int32_t pid;
siginfo_t in = {0};
#ifdef XWAYLAND
if (client_is_x11(c))
return 0;
#endif
wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL);
if (waitid(P_PID, pid, &in, WNOHANG | WCONTINUED | WSTOPPED | WNOWAIT) <
0) {
/* This process is not our child process, while is very unlikely that
* it is stopped, in order to do not skip frames assume that it is. */
if (errno == ECHILD)
return 1;
} else if (in.si_pid) {
if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
return 1;
if (in.si_code == CLD_CONTINUED)
return 0;
}
return 0;
}
static inline int32_t client_is_unmanaged(Client *c) {
#ifdef XWAYLAND
if (client_is_x11(c))
@ -319,9 +294,24 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
uint32_t height) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
struct wlr_surface_state *state =
&c->surface.xwayland->surface->current;
if ((int32_t)c->geom.width - 2 * (int32_t)c->bw ==
(int32_t)state->width &&
(int32_t)c->geom.height - 2 * (int32_t)c->bw ==
(int32_t)state->height &&
(int32_t)c->surface.xwayland->x ==
(int32_t)c->geom.x + (int32_t)c->bw &&
(int32_t)c->surface.xwayland->y ==
(int32_t)c->geom.y + (int32_t)c->bw) {
return 0;
}
wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw,
c->geom.y + c->bw, width, height);
return 0;
return 1;
}
#endif
if ((int32_t)width == c->surface.xdg->toplevel->current.width &&

View file

@ -92,3 +92,85 @@ uint32_t get_now_in_ms(void) {
uint32_t timespec_to_ms(struct timespec *ts) {
return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000;
}
char *join_strings(char *arr[], const char *sep) {
if (!arr || !arr[0]) {
char *empty = malloc(1);
if (empty)
empty[0] = '\0';
return empty;
}
size_t total_len = 0;
int count = 0;
for (int i = 0; arr[i] != NULL; i++) {
total_len += strlen(arr[i]);
count++;
}
if (count > 0) {
total_len += strlen(sep) * (count - 1);
}
char *result = malloc(total_len + 1);
if (!result)
return NULL;
result[0] = '\0';
for (int i = 0; arr[i] != NULL; i++) {
if (i > 0)
strcat(result, sep);
strcat(result, arr[i]);
}
return result;
}
char *join_strings_with_suffix(char *arr[], const char *suffix,
const char *sep) {
if (!arr || !arr[0]) {
char *empty = malloc(1);
if (empty)
empty[0] = '\0';
return empty;
}
size_t total_len = 0;
int count = 0;
for (int i = 0; arr[i] != NULL; i++) {
total_len += strlen(arr[i]) + strlen(suffix);
count++;
}
if (count > 0) {
total_len += strlen(sep) * (count - 1);
}
char *result = malloc(total_len + 1);
if (!result)
return NULL;
result[0] = '\0';
for (int i = 0; arr[i] != NULL; i++) {
if (i > 0)
strcat(result, sep);
strcat(result, arr[i]);
strcat(result, suffix);
}
return result;
}
char *string_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
if (len < 0)
return NULL;
char *str = malloc(len + 1);
if (!str)
return NULL;
va_start(args, fmt);
vsnprintf(str, len + 1, fmt, args);
va_end(args);
return str;
}

View file

@ -7,4 +7,8 @@ int32_t fd_set_nonblock(int32_t fd);
int32_t regex_match(const char *pattern_mb, const char *str_mb);
void wl_list_append(struct wl_list *list, struct wl_list *object);
uint32_t get_now_in_ms(void);
uint32_t timespec_to_ms(struct timespec *ts);
uint32_t timespec_to_ms(struct timespec *ts);
char *join_strings(char *arr[], const char *sep);
char *join_strings_with_suffix(char *arr[], const char *suffix,
const char *sep);
char *string_printf(const char *fmt, ...);

View file

@ -58,6 +58,7 @@ typedef struct {
uint32_t tags;
int32_t isfloating;
int32_t isfullscreen;
int32_t isfakefullscreen;
float scroller_proportion;
const char *animation_type_open;
const char *animation_type_close;
@ -89,6 +90,7 @@ typedef struct {
int32_t isterm;
int32_t allow_csd;
int32_t force_maximize;
int32_t force_tiled_state;
int32_t force_tearing;
int32_t noswallow;
int32_t noblur;
@ -209,6 +211,7 @@ typedef struct {
int32_t scroller_ignore_proportion_single;
int32_t scroller_focus_center;
int32_t scroller_prefer_center;
int32_t scroller_prefer_overspread;
int32_t edge_scroller_pointer_focus;
int32_t focus_cross_monitor;
int32_t exchange_cross_monitor;
@ -1014,6 +1017,7 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
(*arg).v = strdup(arg_value);
} else if (strcmp(func_name, "switch_keyboard_layout") == 0) {
func = switch_keyboard_layout;
(*arg).i = CLAMP_INT(atoi(arg_value), 0, 100);
} else if (strcmp(func_name, "setlayout") == 0) {
func = setlayout;
(*arg).v = strdup(arg_value);
@ -1335,6 +1339,8 @@ bool parse_option(Config *config, char *key, char *value) {
config->scroller_focus_center = atoi(value);
} else if (strcmp(key, "scroller_prefer_center") == 0) {
config->scroller_prefer_center = atoi(value);
} else if (strcmp(key, "scroller_prefer_overspread") == 0) {
config->scroller_prefer_overspread = atoi(value);
} else if (strcmp(key, "edge_scroller_pointer_focus") == 0) {
config->edge_scroller_pointer_focus = atoi(value);
} else if (strcmp(key, "focus_cross_monitor") == 0) {
@ -1994,6 +2000,7 @@ bool parse_option(Config *config, char *key, char *value) {
// int32_t rule value, relay to a client property
rule->isfloating = -1;
rule->isfullscreen = -1;
rule->isfakefullscreen = -1;
rule->isnoborder = -1;
rule->isnoshadow = -1;
rule->isnoradius = -1;
@ -2011,6 +2018,7 @@ bool parse_option(Config *config, char *key, char *value) {
rule->isterm = -1;
rule->allow_csd = -1;
rule->force_maximize = -1;
rule->force_tiled_state = -1;
rule->force_tearing = -1;
rule->noswallow = -1;
rule->noblur = -1;
@ -2123,6 +2131,8 @@ bool parse_option(Config *config, char *key, char *value) {
rule->allow_csd = atoi(val);
} else if (strcmp(key, "force_maximize") == 0) {
rule->force_maximize = atoi(val);
} else if (strcmp(key, "force_tiled_state") == 0) {
rule->force_tiled_state = atoi(val);
} else if (strcmp(key, "force_tearing") == 0) {
rule->force_tearing = atoi(val);
} else if (strcmp(key, "noswallow") == 0) {
@ -2133,6 +2143,8 @@ bool parse_option(Config *config, char *key, char *value) {
rule->scroller_proportion = atof(val);
} else if (strcmp(key, "isfullscreen") == 0) {
rule->isfullscreen = atoi(val);
} else if (strcmp(key, "isfakefullscreen") == 0) {
rule->isfakefullscreen = atoi(val);
} else if (strcmp(key, "globalkeybinding") == 0) {
char mod_str[256], keysym_str[256];
sscanf(val, "%255[^-]-%255[a-zA-Z]", mod_str, keysym_str);
@ -3094,6 +3106,8 @@ void override_config(void) {
CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1);
scroller_focus_center = CLAMP_INT(config.scroller_focus_center, 0, 1);
scroller_prefer_center = CLAMP_INT(config.scroller_prefer_center, 0, 1);
scroller_prefer_overspread =
CLAMP_INT(config.scroller_prefer_overspread, 0, 1);
edge_scroller_pointer_focus =
CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1);
scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000);
@ -3293,6 +3307,7 @@ void set_value_default() {
scroller_ignore_proportion_single;
config.scroller_focus_center = scroller_focus_center;
config.scroller_prefer_center = scroller_prefer_center;
config.scroller_prefer_overspread = scroller_prefer_overspread;
config.edge_scroller_pointer_focus = edge_scroller_pointer_focus;
config.focus_cross_monitor = focus_cross_monitor;
config.exchange_cross_monitor = exchange_cross_monitor;
@ -3632,6 +3647,16 @@ void reapply_cursor_style(void) {
cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size);
if (cursor_size > 0) {
char size_str[16];
snprintf(size_str, sizeof(size_str), "%d", cursor_size);
setenv("XCURSOR_SIZE", size_str, 1);
}
if (config.cursor_theme) {
setenv("XCURSOR_THEME", config.cursor_theme, 1);
}
Monitor *m = NULL;
wl_list_for_each(m, &mons, link) {
wlr_xcursor_manager_load(cursor_mgr, m->wlr_output->scale);

View file

@ -66,6 +66,7 @@ float scroller_default_proportion_single = 1.0;
int32_t scroller_ignore_proportion_single = 1;
int32_t scroller_focus_center = 0;
int32_t scroller_prefer_center = 0;
int32_t scroller_prefer_overspread = 1;
int32_t focus_cross_monitor = 0;
int32_t focus_cross_tag = 0;
int32_t exchange_cross_monitor = 0;

View file

@ -1,5 +1,6 @@
int32_t bind_to_view(const Arg *arg) {
if (!selmon)
return 0;
uint32_t target = arg->ui;
if (view_current_to_back && selmon->pertag->curtag &&
@ -100,6 +101,8 @@ int32_t defaultgaps(const Arg *arg) {
}
int32_t exchange_client(const Arg *arg) {
if (!selmon)
return 0;
Client *c = selmon->sel;
if (!c || c->isfloating)
return 0;
@ -112,6 +115,9 @@ int32_t exchange_client(const Arg *arg) {
}
int32_t exchange_stack_client(const Arg *arg) {
if (!selmon)
return 0;
Client *c = selmon->sel;
Client *tc = NULL;
if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen)
@ -204,7 +210,7 @@ int32_t focusmon(const Arg *arg) {
if (!m->wlr_output->enabled) {
continue;
}
if (regex_match(arg->v, m->wlr_output->name)) {
if (match_monitor_spec(arg->v, m)) {
tm = m;
break;
}
@ -265,42 +271,56 @@ int32_t incnmaster(const Arg *arg) {
}
int32_t incgaps(const Arg *arg) {
if (!selmon)
return 0;
setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i,
selmon->gappih + arg->i, selmon->gappiv + arg->i);
return 0;
}
int32_t incigaps(const Arg *arg) {
if (!selmon)
return 0;
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i,
selmon->gappiv + arg->i);
return 0;
}
int32_t incogaps(const Arg *arg) {
if (!selmon)
return 0;
setgaps(selmon->gappoh + arg->i, selmon->gappov + arg->i, selmon->gappih,
selmon->gappiv);
return 0;
}
int32_t incihgaps(const Arg *arg) {
if (!selmon)
return 0;
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih + arg->i,
selmon->gappiv);
return 0;
}
int32_t incivgaps(const Arg *arg) {
if (!selmon)
return 0;
setgaps(selmon->gappoh, selmon->gappov, selmon->gappih,
selmon->gappiv + arg->i);
return 0;
}
int32_t incohgaps(const Arg *arg) {
if (!selmon)
return 0;
setgaps(selmon->gappoh + arg->i, selmon->gappov, selmon->gappih,
selmon->gappiv);
return 0;
}
int32_t incovgaps(const Arg *arg) {
if (!selmon)
return 0;
setgaps(selmon->gappoh, selmon->gappov + arg->i, selmon->gappih,
selmon->gappiv);
return 0;
@ -330,6 +350,8 @@ int32_t setmfact(const Arg *arg) {
int32_t killclient(const Arg *arg) {
Client *c = NULL;
if (!selmon)
return 0;
c = selmon->sel;
if (c) {
pending_kill_client(c);
@ -399,6 +421,8 @@ int32_t moveresize(const Arg *arg) {
int32_t movewin(const Arg *arg) {
Client *c = NULL;
if (!selmon)
return 0;
c = selmon->sel;
if (!c || c->isfullscreen)
return 0;
@ -442,6 +466,8 @@ int32_t quit(const Arg *arg) {
int32_t resizewin(const Arg *arg) {
Client *c = NULL;
if (!selmon)
return 0;
c = selmon->sel;
int32_t offsetx = 0, offsety = 0;
@ -546,6 +572,8 @@ int32_t restore_minimized(const Arg *arg) {
int32_t setlayout(const Arg *arg) {
int32_t jk;
if (!selmon)
return 0;
for (jk = 0; jk < LENGTH(layouts); jk++) {
if (strcmp(layouts[jk].name, arg->v) == 0) {
@ -571,6 +599,8 @@ int32_t setkeymode(const Arg *arg) {
}
int32_t set_proportion(const Arg *arg) {
if (!selmon)
return 0;
if (selmon->isoverview || !is_scroller_layout(selmon))
return 0;
@ -596,6 +626,8 @@ int32_t smartmovewin(const Arg *arg) {
Client *c = NULL, *tc = NULL;
int32_t nx, ny;
int32_t buttom, top, left, right, tar;
if (!selmon)
return 0;
c = selmon->sel;
if (!c || c->isfullscreen)
return 0;
@ -697,6 +729,8 @@ int32_t smartresizewin(const Arg *arg) {
Client *c = NULL, *tc = NULL;
int32_t nw, nh;
int32_t buttom, top, left, right, tar;
if (!selmon)
return 0;
c = selmon->sel;
if (!c || c->isfullscreen)
return 0;
@ -765,6 +799,8 @@ int32_t smartresizewin(const Arg *arg) {
int32_t centerwin(const Arg *arg) {
Client *c = NULL;
if (!selmon)
return 0;
c = selmon->sel;
if (!c || c->isfullscreen || c->ismaximizescreen)
@ -900,7 +936,13 @@ int32_t switch_keyboard_layout(const Arg *arg) {
wlr_log(WLR_INFO, "Only one layout available");
return 0;
}
xkb_layout_index_t next = (current + 1) % num_layouts;
xkb_layout_index_t next = 0;
if (arg->i > 0 && arg->i <= num_layouts) {
next = arg->i - 1;
} else {
next = (current + 1) % num_layouts;
}
// 6. 应用新 keymap
uint32_t depressed = keyboard->modifiers.depressed;
@ -937,6 +979,9 @@ int32_t switch_layout(const Arg *arg) {
char *target_layout_name = NULL;
uint32_t len;
if (!selmon)
return 0;
if (config.circle_layout_count != 0) {
for (jk = 0; jk < config.circle_layout_count; jk++) {
@ -988,6 +1033,8 @@ int32_t switch_layout(const Arg *arg) {
int32_t switch_proportion_preset(const Arg *arg) {
float target_proportion = 0;
if (!selmon)
return 0;
if (config.scroller_proportion_preset_count == 0) {
return 0;
@ -1032,6 +1079,8 @@ int32_t switch_proportion_preset(const Arg *arg) {
}
int32_t tag(const Arg *arg) {
if (!selmon)
return 0;
Client *target_client = selmon->sel;
tag_client(arg, target_client);
return 0;
@ -1039,6 +1088,8 @@ int32_t tag(const Arg *arg) {
int32_t tagmon(const Arg *arg) {
Monitor *m = NULL, *cm = NULL;
if (!selmon)
return 0;
Client *c = focustop(selmon);
if (!c)
@ -1051,7 +1102,7 @@ int32_t tagmon(const Arg *arg) {
if (!cm->wlr_output->enabled) {
continue;
}
if (regex_match(arg->v, cm->wlr_output->name)) {
if (match_monitor_spec(arg->v, cm)) {
m = cm;
break;
}
@ -1130,6 +1181,9 @@ int32_t tagsilent(const Arg *arg) {
}
int32_t tagtoleft(const Arg *arg) {
if (!selmon)
return 0;
if (selmon->sel != NULL &&
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 &&
selmon->tagset[selmon->seltags] > 1) {
@ -1139,6 +1193,9 @@ int32_t tagtoleft(const Arg *arg) {
}
int32_t tagtoright(const Arg *arg) {
if (!selmon)
return 0;
if (selmon->sel != NULL &&
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 &&
selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) {
@ -1167,6 +1224,8 @@ int32_t toggle_named_scratchpad(const Arg *arg) {
}
int32_t toggle_render_border(const Arg *arg) {
if (!selmon)
return 0;
render_border = !render_border;
arrange(selmon, false, false);
return 0;
@ -1202,6 +1261,8 @@ int32_t toggle_scratchpad(const Arg *arg) {
}
int32_t togglefakefullscreen(const Arg *arg) {
if (!selmon)
return 0;
Client *sel = focustop(selmon);
if (sel)
setfakefullscreen(sel, !sel->isfakefullscreen);
@ -1209,6 +1270,9 @@ int32_t togglefakefullscreen(const Arg *arg) {
}
int32_t togglefloating(const Arg *arg) {
if (!selmon)
return 0;
Client *sel = focustop(selmon);
if (selmon && selmon->isoverview)
@ -1217,17 +1281,22 @@ int32_t togglefloating(const Arg *arg) {
if (!sel)
return 0;
bool isfloating = sel->isfloating;
if ((sel->isfullscreen || sel->ismaximizescreen)) {
sel->isfloating = 1;
isfloating = 1;
} else {
sel->isfloating = !sel->isfloating;
isfloating = !sel->isfloating;
}
setfloating(sel, sel->isfloating);
setfloating(sel, isfloating);
return 0;
}
int32_t togglefullscreen(const Arg *arg) {
if (!selmon)
return 0;
Client *sel = focustop(selmon);
if (!sel)
return 0;
@ -1244,6 +1313,9 @@ int32_t togglefullscreen(const Arg *arg) {
}
int32_t toggleglobal(const Arg *arg) {
if (!selmon)
return 0;
if (!selmon->sel)
return 0;
if (selmon->sel->is_in_scratchpad) {
@ -1262,12 +1334,18 @@ int32_t toggleglobal(const Arg *arg) {
}
int32_t togglegaps(const Arg *arg) {
if (!selmon)
return 0;
enablegaps ^= 1;
arrange(selmon, false, false);
return 0;
}
int32_t togglemaximizescreen(const Arg *arg) {
if (!selmon)
return 0;
Client *sel = focustop(selmon);
if (!sel)
return 0;
@ -1286,6 +1364,9 @@ int32_t togglemaximizescreen(const Arg *arg) {
}
int32_t toggleoverlay(const Arg *arg) {
if (!selmon)
return 0;
if (!selmon->sel || !selmon->sel->mon || selmon->sel->isfullscreen) {
return 0;
}
@ -1307,6 +1388,9 @@ int32_t toggleoverlay(const Arg *arg) {
}
int32_t toggletag(const Arg *arg) {
if (!selmon)
return 0;
uint32_t newtags;
Client *sel = focustop(selmon);
if (!sel)
@ -1330,13 +1414,15 @@ int32_t toggletag(const Arg *arg) {
}
int32_t toggleview(const Arg *arg) {
if (!selmon)
return 0;
uint32_t newtagset;
uint32_t target;
target = arg->ui == 0 ? ~0 & TAGMASK : arg->ui;
newtagset =
selmon ? selmon->tagset[selmon->seltags] ^ (target & TAGMASK) : 0;
newtagset = selmon->tagset[selmon->seltags] ^ (target & TAGMASK);
if (newtagset) {
selmon->tagset[selmon->seltags] = newtagset;
@ -1348,6 +1434,9 @@ int32_t toggleview(const Arg *arg) {
}
int32_t viewtoleft(const Arg *arg) {
if (!selmon)
return 0;
uint32_t target = selmon->tagset[selmon->seltags];
if (selmon->isoverview || selmon->pertag->curtag == 0) {
@ -1368,6 +1457,9 @@ int32_t viewtoleft(const Arg *arg) {
}
int32_t viewtoright(const Arg *arg) {
if (!selmon)
return 0;
if (selmon->isoverview || selmon->pertag->curtag == 0) {
return 0;
}
@ -1385,6 +1477,9 @@ int32_t viewtoright(const Arg *arg) {
}
int32_t viewtoleft_have_client(const Arg *arg) {
if (!selmon)
return 0;
uint32_t n;
uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]);
bool found = false;
@ -1409,6 +1504,9 @@ int32_t viewtoleft_have_client(const Arg *arg) {
}
int32_t viewtoright_have_client(const Arg *arg) {
if (!selmon)
return 0;
uint32_t n;
uint32_t current = get_tags_first_tag_num(selmon->tagset[selmon->seltags]);
bool found = false;
@ -1433,6 +1531,9 @@ int32_t viewtoright_have_client(const Arg *arg) {
}
int32_t viewcrossmon(const Arg *arg) {
if (!selmon)
return 0;
focusmon(&(Arg){.v = arg->v, .i = UNDIR});
view_in_mon(arg, true, selmon, true);
return 0;
@ -1442,7 +1543,7 @@ int32_t tagcrossmon(const Arg *arg) {
if (!selmon || !selmon->sel)
return 0;
if (regex_match(selmon->wlr_output->name, arg->v)) {
if (match_monitor_spec(arg->v, selmon)) {
tag_client(arg, selmon->sel);
return 0;
}
@ -1511,6 +1612,8 @@ int32_t setoption(const Arg *arg) {
}
int32_t minimized(const Arg *arg) {
if (!selmon)
return 0;
if (selmon && selmon->isoverview)
return 0;
@ -1521,8 +1624,20 @@ int32_t minimized(const Arg *arg) {
return 0;
}
void fix_mon_tagset_from_overview(Monitor *m) {
if (m->tagset[m->seltags] == (m->ovbk_prev_tagset & TAGMASK)) {
m->tagset[m->seltags ^ 1] = m->ovbk_current_tagset;
m->pertag->prevtag = get_tags_first_tag_num(m->ovbk_current_tagset);
} else {
m->tagset[m->seltags ^ 1] = m->ovbk_prev_tagset;
m->pertag->prevtag = get_tags_first_tag_num(m->ovbk_prev_tagset);
}
}
int32_t toggleoverview(const Arg *arg) {
Client *c = NULL;
if (!selmon)
return 0;
if (selmon->isoverview && ov_tab_mode && arg->i != 1 && selmon->sel) {
focusstack(&(Arg){.i = 1});
@ -1542,6 +1657,8 @@ int32_t toggleoverview(const Arg *arg) {
visible_client_number++;
}
if (visible_client_number > 0) {
selmon->ovbk_current_tagset = selmon->tagset[selmon->seltags];
selmon->ovbk_prev_tagset = selmon->tagset[selmon->seltags ^ 1];
target = ~0 & TAGMASK;
} else {
selmon->isoverview ^= 1;
@ -1552,6 +1669,7 @@ int32_t toggleoverview(const Arg *arg) {
} else if (!selmon->isoverview && !selmon->sel) {
target = (1 << (selmon->pertag->prevtag - 1));
view(&(Arg){.ui = target}, false);
fix_mon_tagset_from_overview(selmon);
refresh_monitors_workspaces_status(selmon);
return 0;
}
@ -1574,7 +1692,7 @@ int32_t toggleoverview(const Arg *arg) {
}
view(&(Arg){.ui = target}, false);
fix_mon_tagset_from_overview(selmon);
refresh_monitors_workspaces_status(selmon);
return 0;
}
@ -1583,7 +1701,7 @@ int32_t disable_monitor(const Arg *arg) {
Monitor *m = NULL;
struct wlr_output_state state = {0};
wl_list_for_each(m, &mons, link) {
if (regex_match(arg->v, m->wlr_output->name)) {
if (match_monitor_spec(arg->v, m)) {
wlr_output_state_set_enabled(&state, false);
wlr_output_commit_state(m->wlr_output, &state);
m->asleep = 1;
@ -1598,7 +1716,7 @@ int32_t enable_monitor(const Arg *arg) {
Monitor *m = NULL;
struct wlr_output_state state = {0};
wl_list_for_each(m, &mons, link) {
if (regex_match(arg->v, m->wlr_output->name)) {
if (match_monitor_spec(arg->v, m)) {
wlr_output_state_set_enabled(&state, true);
wlr_output_commit_state(m->wlr_output, &state);
m->asleep = 0;
@ -1613,7 +1731,7 @@ int32_t toggle_monitor(const Arg *arg) {
Monitor *m = NULL;
struct wlr_output_state state = {0};
wl_list_for_each(m, &mons, link) {
if (regex_match(arg->v, m->wlr_output->name)) {
if (match_monitor_spec(arg->v, m)) {
wlr_output_state_set_enabled(&state, !m->wlr_output->enabled);
wlr_output_commit_state(m->wlr_output, &state);
m->asleep = !m->wlr_output->enabled;
@ -1625,6 +1743,8 @@ int32_t toggle_monitor(const Arg *arg) {
}
int32_t scroller_stack(const Arg *arg) {
if (!selmon)
return 0;
Client *c = selmon->sel;
Client *stack_head = NULL;
Client *source_stack_head = NULL;

View file

@ -77,15 +77,6 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) {
return NULL;
}
Monitor *output_nearest_to(int32_t lx, int32_t ly) {
double closest_x, closest_y;
wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
&closest_y);
return output_from_wlr_output(
wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}
bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; }
static bool
@ -255,7 +246,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
cursor_rect = (struct wlr_box){0};
}
output = output_nearest_to(cursor_rect.x, cursor_rect.y);
output = get_monitor_nearest_to(cursor_rect.x, cursor_rect.y);
if (!output_is_usable(output)) {
return;
}

View file

@ -83,6 +83,9 @@ setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom,
int32_t len = 0;
Monitor *m = tm ? tm : selmon;
if (!m)
return geom;
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
if (!c->no_force_center && m) {
@ -574,23 +577,29 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
if (id == TILE || id == VERTICAL_TILE || id == DECK ||
id == VERTICAL_DECK || id == RIGHT_TILE) {
if (fc && !fc->ismaster)
if (tc->ismaster ^ sc->ismaster)
return false;
else if (!sc->ismaster)
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
else
return true;
}
if (id == TGMIX) {
if (fc && !fc->ismaster)
if (tc->ismaster ^ sc->ismaster)
return false;
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
if (!sc->ismaster && sc->mon->visible_tiling_clients <= 3)
return true;
}
if (id == CENTER_TILE) {
if (fc && !fc->ismaster)
if (tc->ismaster ^ sc->ismaster)
return false;
if (!sc->ismaster && sc->geom.x == tc->geom.x)
if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
if (sc->geom.x == tc->geom.x)
return true;
else
return false;

View file

@ -100,10 +100,6 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node))
->surface;
else if (node->type == WLR_SCENE_NODE_RECT) {
surface = NULL;
break;
}
/* start from the topmost layer,
find a sureface that can be focused by pointer,
@ -119,6 +115,13 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
l = pnode->data;
}
}
if (node->type == WLR_SCENE_NODE_RECT) {
if (c) {
surface = client_surface(c);
}
break;
}
}
if (psurface)

View file

@ -96,3 +96,81 @@ Monitor *xytomon(double x, double y) {
struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
return o ? o->data : NULL;
}
Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly) {
double closest_x, closest_y;
wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
&closest_y);
return output_from_wlr_output(
wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}
bool match_monitor_spec(char *spec, Monitor *m) {
if (!spec || !m)
return false;
// if the spec does not contain a colon, treat it as a match on the monitor
// name
if (strchr(spec, ':') == NULL) {
return regex_match(spec, m->wlr_output->name);
}
char *spec_copy = strdup(spec);
if (!spec_copy)
return false;
char *name_rule = NULL;
char *make_rule = NULL;
char *model_rule = NULL;
char *serial_rule = NULL;
char *token = strtok(spec_copy, "&&");
while (token) {
char *colon = strchr(token, ':');
if (colon) {
*colon = '\0';
char *key = token;
char *value = colon + 1;
if (strcmp(key, "name") == 0)
name_rule = strdup(value);
else if (strcmp(key, "make") == 0)
make_rule = strdup(value);
else if (strcmp(key, "model") == 0)
model_rule = strdup(value);
else if (strcmp(key, "serial") == 0)
serial_rule = strdup(value);
}
token = strtok(NULL, "&&");
}
bool match = true;
if (name_rule) {
if (!regex_match(name_rule, m->wlr_output->name))
match = false;
}
if (make_rule) {
if (!m->wlr_output->make || strcmp(make_rule, m->wlr_output->make) != 0)
match = false;
}
if (model_rule) {
if (!m->wlr_output->model ||
strcmp(model_rule, m->wlr_output->model) != 0)
match = false;
}
if (serial_rule) {
if (!m->wlr_output->serial ||
strcmp(serial_rule, m->wlr_output->serial) != 0)
match = false;
}
free(spec_copy);
free(name_rule);
free(make_rule);
free(model_rule);
free(serial_rule);
return match;
}

View file

@ -1,3 +1,45 @@
void restore_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
double total_master_inner_per = 0;
double total_stack_inner_per = 0;
if (!m || !c)
return;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
if (current_layout->id == SCROLLER ||
current_layout->id == VERTICAL_SCROLLER || current_layout->id == GRID ||
current_layout->id == VERTICAL_GRID || current_layout->id == DECK ||
current_layout->id == VERTICAL_DECK || current_layout->id == MONOCLE) {
return;
}
if (current_layout->id == CENTER_TILE || c->ismaster) {
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && !c->ismaster) {
set_size_per(m, fc);
}
}
return;
}
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) {
if (fc->ismaster) {
total_master_inner_per += fc->master_inner_per;
} else {
total_stack_inner_per += fc->stack_inner_per;
}
}
}
if (!c->ismaster && total_stack_inner_per) {
c->stack_inner_per = total_stack_inner_per * c->stack_inner_per /
(1 - c->stack_inner_per);
}
}
void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL;
bool found = false;
@ -5,8 +47,13 @@ void set_size_per(Monitor *m, Client *c) {
if (!m || !c)
return;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) {
if (current_layout->id == CENTER_TILE &&
!(fc->isleftstack ^ c->isleftstack))
continue;
c->master_mfact_per = fc->master_mfact_per;
c->master_inner_per = fc->master_inner_per;
c->stack_inner_per = fc->stack_inner_per;

View file

@ -283,6 +283,7 @@ void scroller(Monitor *m) {
struct wlr_box target_geom;
int32_t focus_client_index = 0;
bool need_scroller = false;
bool over_overspread_to_left = false;
int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
@ -371,6 +372,46 @@ void scroller(Monitor *m) {
}
}
bool need_apply_overspread =
scroller_prefer_overspread && m->visible_scroll_tiling_clients > 1 &&
(focus_client_index == 0 || focus_client_index == n - 1) &&
tempClients[focus_client_index]->scroller_proportion < 1.0f;
if (need_apply_overspread) {
if (focus_client_index == 0) {
over_overspread_to_left = true;
} else {
over_overspread_to_left = false;
}
if (over_overspread_to_left &&
(!INSIDEMON(tempClients[1]) ||
(tempClients[1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else if (!over_overspread_to_left &&
(!INSIDEMON(tempClients[focus_client_index - 1]) ||
(tempClients[focus_client_index - 1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else {
need_apply_overspread = false;
}
}
bool need_apply_center =
scroller_focus_center || m->visible_scroll_tiling_clients == 1 ||
(scroller_prefer_center && !need_apply_overspread &&
(!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_width) +
(tempClients[focus_client_index]->scroller_proportion *
max_client_width) >
m->w.width - 2 * scroller_structs - cur_gappih)));
if (n == 1 && scroller_ignore_proportion_single) {
need_scroller = true;
}
@ -394,18 +435,26 @@ void scroller(Monitor *m) {
tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} else if (need_scroller) {
if (scroller_focus_center ||
((!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_width) +
(root_client->scroller_proportion * max_client_width) >
m->w.width - 2 * scroller_structs - cur_gappih)) &&
scroller_prefer_center)) {
if (need_apply_center) {
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2;
} else if (need_apply_overspread) {
if (over_overspread_to_left) {
target_geom.x = m->w.x + scroller_structs;
} else {
target_geom.x =
m->w.x +
(m->w.width -
tempClients[focus_client_index]->scroller_proportion *
max_client_width -
scroller_structs);
}
} else {
target_geom.x = root_client->geom.x > m->w.x + (m->w.width) / 2
target_geom.x = tempClients[focus_client_index]->geom.x >
m->w.x + (m->w.width) / 2
? m->w.x + (m->w.width -
root_client->scroller_proportion *
tempClients[focus_client_index]
->scroller_proportion *
max_client_width -
scroller_structs)
: m->w.x + scroller_structs;

View file

@ -270,6 +270,7 @@ void vertical_scroller(Monitor *m) {
struct wlr_box target_geom;
int32_t focus_client_index = 0;
bool need_scroller = false;
bool over_overspread_to_up = false;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
@ -355,6 +356,46 @@ void vertical_scroller(Monitor *m) {
}
}
bool need_apply_overspread =
scroller_prefer_overspread && m->visible_scroll_tiling_clients > 1 &&
(focus_client_index == 0 || focus_client_index == n - 1) &&
tempClients[focus_client_index]->scroller_proportion < 1.0f;
if (need_apply_overspread) {
if (focus_client_index == 0) {
over_overspread_to_up = true;
} else {
over_overspread_to_up = false;
}
if (over_overspread_to_up &&
(!INSIDEMON(tempClients[1]) ||
(tempClients[1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else if (!over_overspread_to_up &&
(!INSIDEMON(tempClients[focus_client_index - 1]) ||
(tempClients[focus_client_index - 1]->scroller_proportion +
tempClients[focus_client_index]->scroller_proportion >=
1.0f))) {
need_scroller = true;
} else {
need_apply_overspread = false;
}
}
bool need_apply_center =
scroller_focus_center || m->visible_scroll_tiling_clients == 1 ||
(scroller_prefer_center && !need_apply_overspread &&
(!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_height) +
(tempClients[focus_client_index]->scroller_proportion *
max_client_height) >
m->w.height - 2 * scroller_structs - cur_gappiv)));
if (n == 1 && scroller_ignore_proportion_single) {
need_scroller = true;
}
@ -381,18 +422,24 @@ void vertical_scroller(Monitor *m) {
arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih);
} else if (need_scroller) {
if (scroller_focus_center ||
((!m->prevsel ||
(ISSCROLLTILED(m->prevsel) &&
(m->prevsel->scroller_proportion * max_client_height) +
(root_client->scroller_proportion * max_client_height) >
m->w.height - 2 * scroller_structs - cur_gappiv)) &&
scroller_prefer_center)) {
if (need_apply_center) {
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2;
} else if (need_apply_overspread) {
if (over_overspread_to_up) {
target_geom.y = m->w.y + scroller_structs;
} else {
target_geom.y =
m->w.y +
(m->w.height -
tempClients[focus_client_index]->scroller_proportion *
max_client_height -
scroller_structs);
}
} else {
target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2
? m->w.y + (m->w.height -
root_client->scroller_proportion *
tempClients[focus_client_index]
->scroller_proportion *
max_client_height -
scroller_structs)
: m->w.y + scroller_structs;

View file

@ -3,6 +3,7 @@
*/
#include "wlr-layer-shell-unstable-v1-protocol.h"
#include "wlr/util/box.h"
#include "wlr/util/edges.h"
#include <getopt.h>
#include <libinput.h>
#include <limits.h>
@ -337,6 +338,7 @@ struct Client {
struct wl_listener configure;
struct wl_listener set_hints;
struct wl_listener set_geometry;
struct wl_listener commmitx11;
#endif
uint32_t bw;
uint32_t tags, oldtags, mini_restore_tag;
@ -390,6 +392,7 @@ struct Client {
int32_t isterm, noswallow;
int32_t allow_csd;
int32_t force_maximize;
int32_t force_tiled_state;
pid_t pid;
Client *swallowing, *swallowedby;
bool is_clip_to_hide;
@ -483,6 +486,14 @@ typedef struct {
bool being_unmapped;
} LayerSurface;
typedef struct {
struct wlr_xdg_popup *wlr_popup;
uint32_t type;
struct wl_listener destroy;
struct wl_listener commit;
struct wl_listener reposition;
} Popup;
typedef struct {
const char *symbol;
void (*arrange)(Monitor *);
@ -500,11 +511,15 @@ struct Monitor {
struct wl_listener request_state;
struct wl_listener destroy_lock_surface;
struct wlr_session_lock_surface_v1 *lock_surface;
struct wl_event_source *skip_frame_timeout;
struct wlr_box m; /* monitor area, layout-relative */
struct wlr_box w; /* window area, layout-relative */
struct wl_list layers[4]; /* LayerSurface::link */
uint32_t seltags;
uint32_t tagset[2];
bool skiping_frame;
uint32_t resizing_count_pending;
uint32_t resizing_count_current;
struct wl_list dwl_ipc_outputs;
int32_t gappih; /* horizontal gap between windows */
@ -512,6 +527,8 @@ struct Monitor {
int32_t gappoh; /* horizontal outer gaps */
int32_t gappov; /* vertical outer gaps */
Pertag *pertag;
uint32_t ovbk_current_tagset;
uint32_t ovbk_prev_tagset;
Client *sel, *prevsel;
int32_t isoverview;
int32_t is_in_hotarea;
@ -779,6 +796,10 @@ static Client *get_scroll_stack_head(Client *c);
static bool client_only_in_one_tag(Client *c);
static Client *get_focused_stack_client(Client *sc);
static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc);
static void monitor_stop_skip_frame_timer(Monitor *m);
static int monitor_skip_frame_timeout_callback(void *data);
static Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly);
static bool match_monitor_spec(char *spec, Monitor *m);
#include "data/static_keymap.h"
#include "dispatch/bind_declare.h"
@ -883,6 +904,9 @@ static KeyMode keymode = {
.mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'},
.isdefault = true,
};
static char *env_vars[] = {"DISPLAY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP",
"XDG_SESSION_TYPE", NULL};
static struct {
enum wp_cursor_shape_device_v1_shape shape;
struct wlr_surface *surface;
@ -942,11 +966,13 @@ static struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = {
.notify = handle_keyboard_shortcuts_inhibit_new_inhibitor};
#ifdef XWAYLAND
static void fix_xwayland_unmanaged_coordinate(Client *c);
static int32_t synckeymap(void *data);
static void activatex11(struct wl_listener *listener, void *data);
static void configurex11(struct wl_listener *listener, void *data);
static void createnotifyx11(struct wl_listener *listener, void *data);
static void dissociatex11(struct wl_listener *listener, void *data);
static void commitx11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data);
static void sethints(struct wl_listener *listener, void *data);
static void xwaylandready(struct wl_listener *listener, void *data);
@ -1283,6 +1309,7 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) {
APPLY_INT_PROP(c, r, isterm);
APPLY_INT_PROP(c, r, allow_csd);
APPLY_INT_PROP(c, r, force_maximize);
APPLY_INT_PROP(c, r, force_tiled_state);
APPLY_INT_PROP(c, r, force_tearing);
APPLY_INT_PROP(c, r, noswallow);
APPLY_INT_PROP(c, r, nofocus);
@ -1291,6 +1318,7 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) {
APPLY_INT_PROP(c, r, no_force_center);
APPLY_INT_PROP(c, r, isfloating);
APPLY_INT_PROP(c, r, isfullscreen);
APPLY_INT_PROP(c, r, isfakefullscreen);
APPLY_INT_PROP(c, r, isnoborder);
APPLY_INT_PROP(c, r, isnoshadow);
APPLY_INT_PROP(c, r, isnoradius);
@ -1366,11 +1394,22 @@ void applyrules(Client *c) {
Client *fc = NULL;
Client *parent = NULL;
if (!c)
return;
parent = client_get_parent(c);
Monitor *mon = parent && parent->mon ? parent->mon : selmon;
c->isfloating = client_is_float_type(c) || parent;
#ifdef XWAYLAND
if (c->isfloating && client_is_x11(c)) {
fix_xwayland_unmanaged_coordinate(c);
c->float_geom = c->geom;
}
#endif
if (!(appid = client_get_appid(c)))
appid = broken;
if (!(title = client_get_title(c)))
@ -1446,7 +1485,8 @@ void applyrules(Client *c) {
/*-----------------------apply rule action-------------------------*/
// rule action only apply after map not apply in the init commit
if (!client_surface(c)->mapped)
struct wlr_surface *surface = client_surface(c);
if (!surface || !surface->mapped)
return;
// apply swallow rule
@ -1472,6 +1512,7 @@ void applyrules(Client *c) {
setmon(c, mon, newtags,
!c->isopensilent &&
!(client_is_x11_popup(c) && client_should_ignore_focus(c)) &&
mon &&
(!c->istagsilent || !newtags ||
newtags & mon->tagset[mon->seltags]));
@ -1484,12 +1525,16 @@ void applyrules(Client *c) {
setfullscreen(c, fullscreen_state_backup);
if (c->isfakefullscreen) {
setfakefullscreen(c, 1);
}
/*
if there is a new non-floating window in the current tag, the fullscreen
window in the current tag will exit fullscreen and participate in tiling
*/
wl_list_for_each(fc, &clients,
link) if (fc && fc != c && c->tags & fc->tags &&
link) if (fc && fc != c && c->tags & fc->tags && c->mon &&
VISIBLEON(fc, c->mon) && ISFULLSCREEN(fc) &&
!c->isfloating) {
clear_fullscreen_flag(fc);
@ -1508,7 +1553,7 @@ void applyrules(Client *c) {
}
// apply overlay rule
if (c->isoverlay) {
if (c->isoverlay && c->scene) {
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
wlr_scene_node_raise_to_top(&c->scene->node);
}
@ -2204,6 +2249,11 @@ void cleanupmon(struct wl_listener *listener, void *data) {
wlr_scene_node_destroy(&m->blur->node);
m->blur = NULL;
}
if (m->skip_frame_timeout) {
monitor_stop_skip_frame_timer(m);
wl_event_source_remove(m->skip_frame_timeout);
m->skip_frame_timeout = NULL;
}
m->wlr_output->data = NULL;
free(m->pertag);
free(m);
@ -2263,6 +2313,19 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer,
}
}
void layer_flush_blur_background(LayerSurface *l) {
if (!blur)
return;
// 如果背景层发生变化,标记优化的模糊背景缓存需要更新
if (l->layer_surface->current.layer ==
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
if (l->mon) {
wlr_scene_optimized_blur_mark_dirty(l->mon->blur);
}
}
}
void maplayersurfacenotify(struct wl_listener *listener, void *data) {
LayerSurface *l = wl_container_of(listener, l, map);
struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
@ -2390,15 +2453,7 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
}
}
if (blur) {
// 如果背景层发生变化,标记优化的模糊背景缓存需要更新
if (layer_surface->current.layer ==
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
if (l->mon) {
wlr_scene_optimized_blur_mark_dirty(l->mon->blur);
}
}
}
layer_flush_blur_background(l);
if (layer_surface == exclusive_focus &&
layer_surface->current.keyboard_interactive !=
@ -2438,9 +2493,6 @@ void commitnotify(struct wl_listener *listener, void *data) {
setmon(c, NULL, 0,
true); /* Make sure to reapply rules in mapnotify() */
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
uint32_t serial = wlr_xdg_surface_schedule_configure(c->surface.xdg);
if (serial > 0) {
c->configure_serial = serial;
@ -2500,37 +2552,105 @@ void destroydecoration(struct wl_listener *listener, void *data) {
wl_list_remove(&c->set_decoration_mode.link);
}
void commitpopup(struct wl_listener *listener, void *data) {
static void popup_unconstrain(Popup *popup) {
struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
if (!wlr_popup || !wlr_popup->parent) {
return;
}
struct wlr_scene_node *parent_node = wlr_popup->parent->data;
if (!parent_node) {
wlr_log(WLR_ERROR, "Popup parent has no scene node");
return;
}
int parent_lx, parent_ly;
wlr_scene_node_coords(parent_node, &parent_lx, &parent_ly);
struct wlr_box *scheduled = &wlr_popup->scheduled.geometry;
int popup_lx = parent_lx + scheduled->x;
int popup_ly = parent_ly + scheduled->y;
Monitor *mon = get_monitor_nearest_to(popup_lx, popup_ly);
struct wlr_box usable = popup->type == LayerShell ? mon->m : mon->w;
struct wlr_box constraint_box = {
.x = usable.x - parent_lx,
.y = usable.y - parent_ly,
.width = usable.width,
.height = usable.height,
};
wlr_xdg_popup_unconstrain_from_box(wlr_popup, &constraint_box);
}
static void destroypopup(struct wl_listener *listener, void *data) {
Popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->reposition.link);
free(popup);
}
static void commitpopup(struct wl_listener *listener, void *data) {
Popup *popup = wl_container_of(listener, popup, commit);
struct wlr_surface *surface = data;
struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface);
LayerSurface *l = NULL;
struct wlr_xdg_popup *wkr_popup =
wlr_xdg_popup_try_from_wlr_surface(surface);
Client *c = NULL;
struct wlr_box box;
LayerSurface *l = NULL;
int32_t type = -1;
if (!popup || !popup->base->initial_commit)
if (!wkr_popup || !wkr_popup->base->initial_commit)
goto commitpopup_listen_free;
type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
if (!popup->parent || !popup->parent->data || type < 0)
goto commitpopup_listen_free;
wlr_scene_node_raise_to_top(popup->parent->data);
popup->base->surface->data =
wlr_scene_xdg_surface_create(popup->parent->data, popup->base);
if ((l && !l->mon) || (c && !c->mon)) {
wlr_xdg_popup_destroy(popup);
type = toplevel_from_wlr_surface(wkr_popup->base->surface, &c, &l);
if (!wkr_popup->parent || !wkr_popup->parent->data || type < 0) {
wlr_xdg_popup_destroy(wkr_popup);
goto commitpopup_listen_free;
}
box = type == LayerShell ? l->mon->m : c->mon->w;
box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x);
box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
wlr_xdg_popup_unconstrain_from_box(popup, &box);
wlr_scene_node_raise_to_top(wkr_popup->parent->data);
wkr_popup->base->surface->data =
wlr_scene_xdg_surface_create(wkr_popup->parent->data, wkr_popup->base);
if ((l && !l->mon) || (c && !c->mon)) {
wlr_xdg_popup_destroy(wkr_popup);
goto commitpopup_listen_free;
}
popup->type = type;
popup->wlr_popup = wkr_popup;
popup_unconstrain(popup);
commitpopup_listen_free:
wl_list_remove(&listener->link);
free(listener);
wl_list_remove(&popup->commit.link);
popup->commit.notify = NULL;
}
static void repositionpopup(struct wl_listener *listener, void *data) {
Popup *popup = wl_container_of(listener, popup, reposition);
popup_unconstrain(popup);
}
static void createpopup(struct wl_listener *listener, void *data) {
struct wlr_xdg_popup *wlr_popup = data;
Popup *popup = calloc(1, sizeof(Popup));
if (!popup)
return;
popup->destroy.notify = destroypopup;
wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
popup->commit.notify = commitpopup;
wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
popup->reposition.notify = repositionpopup;
wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
}
void createdecoration(struct wl_listener *listener, void *data) {
@ -2750,7 +2870,15 @@ void createmon(struct wl_listener *listener, void *data) {
return;
}
struct wl_event_loop *loop = wl_display_get_event_loop(dpy);
m = wlr_output->data = ecalloc(1, sizeof(*m));
m->skip_frame_timeout =
wl_event_loop_add_timer(loop, monitor_skip_frame_timeout_callback, m);
m->skiping_frame = false;
m->resizing_count_pending = 0;
m->resizing_count_current = 0;
m->wlr_output = wlr_output;
m->wlr_output->data = m;
@ -2768,6 +2896,8 @@ void createmon(struct wl_listener *listener, void *data) {
m->isoverview = 0;
m->sel = NULL;
m->is_in_hotarea = 0;
m->m.x = INT32_MAX;
m->m.y = INT32_MAX;
float scale = 1;
enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL;
wlr_output_state_set_scale(&state, scale);
@ -3110,13 +3240,6 @@ void createpointerconstraint(struct wl_listener *listener, void *data) {
&pointer_constraint->destroy, destroypointerconstraint);
}
void createpopup(struct wl_listener *listener, void *data) {
/* This event is raised when a client (either xdg-shell or layer-shell)
* creates a new popup. */
struct wlr_xdg_popup *popup = data;
LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
}
void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) {
if (active_constraint == constraint)
return;
@ -3814,6 +3937,23 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx,
void init_client_properties(Client *c) {
c->isfocusing = false;
c->isfloating = 0;
c->isfakefullscreen = 0;
c->isnoanimation = 0;
c->isopensilent = 0;
c->istagsilent = 0;
c->noswallow = 0;
c->isterm = 0;
c->noblur = 0;
c->tearing_hint = 0;
c->overview_isfullscreenbak = 0;
c->overview_ismaximizescreenbak = 0;
c->overview_isfloatingbak = 0;
c->pid = 0;
c->swallowing = NULL;
c->swallowedby = NULL;
c->ismaster = 0;
c->isleftstack = 0;
c->ismaximizescreen = 0;
c->isfullscreen = 0;
c->need_float_size_reduce = 0;
@ -3856,6 +3996,7 @@ void init_client_properties(Client *c) {
c->isterm = 0;
c->allow_csd = 0;
c->force_maximize = 0;
c->force_tiled_state = 1;
c->force_tearing = 0;
c->allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;
c->scroller_proportion_single = 0.0f;
@ -3866,6 +4007,7 @@ void init_client_properties(Client *c) {
c->stack_proportion = 0.0f;
c->next_in_stack = NULL;
c->prev_in_stack = NULL;
memset(c->oldmonname, 0, sizeof(c->oldmonname));
memcpy(c->opacity_animation.initial_border_color, bordercolor,
sizeof(c->opacity_animation.initial_border_color));
memcpy(c->opacity_animation.current_border_color, bordercolor,
@ -3913,18 +4055,19 @@ mapnotify(struct wl_listener *listener, void *data) {
*/
if (client_is_unmanaged(c)) {
/* Unmanaged clients always are floating */
#ifdef XWAYLAND
if (client_is_x11(c)) {
fix_xwayland_unmanaged_coordinate(c);
LISTEN(&c->surface.xwayland->events.set_geometry, &c->set_geometry,
setgeometrynotify);
}
#endif
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
if (client_wants_focus(c)) {
focusclient(c, 1);
exclusive_focus = c;
}
#ifdef XWAYLAND
if (client_is_x11(c)) {
LISTEN(&c->surface.xwayland->events.set_geometry, &c->set_geometry,
setgeometrynotify);
}
#endif
return;
}
@ -3970,8 +4113,10 @@ mapnotify(struct wl_listener *listener, void *data) {
applyrules(c);
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
if (!c->isfloating || c->force_tiled_state) {
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
}
// apply buffer effects of client
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
@ -4376,10 +4521,42 @@ void client_set_opacity(Client *c, double opacity) {
scene_buffer_apply_opacity, &opacity);
}
void monitor_stop_skip_frame_timer(Monitor *m) {
if (m->skip_frame_timeout)
wl_event_source_timer_update(m->skip_frame_timeout, 0);
m->skiping_frame = false;
m->resizing_count_pending = 0;
m->resizing_count_current = 0;
}
static int monitor_skip_frame_timeout_callback(void *data) {
Monitor *m = data;
Client *c, *tmp;
wl_list_for_each_safe(c, tmp, &clients, link) { c->configure_serial = 0; }
monitor_stop_skip_frame_timer(m);
wlr_output_schedule_frame(m->wlr_output);
return 1;
}
void monitor_check_skip_frame_timeout(Monitor *m) {
if (m->skiping_frame &&
m->resizing_count_pending == m->resizing_count_current) {
return;
}
if (m->skip_frame_timeout) {
m->resizing_count_current = m->resizing_count_pending;
m->skiping_frame = true;
wl_event_source_timer_update(m->skip_frame_timeout, 100); // 100ms
}
}
void rendermon(struct wl_listener *listener, void *data) {
Monitor *m = wl_container_of(listener, m, frame);
Client *c = NULL, *tmp = NULL;
struct wlr_output_state pending = {0};
LayerSurface *l = NULL, *tmpl = NULL;
int32_t i;
struct wl_list *layer_list;
@ -4415,13 +4592,17 @@ void rendermon(struct wl_listener *listener, void *data) {
// 绘制客户端
wl_list_for_each(c, &clients, link) {
need_more_frames = client_draw_frame(c) || need_more_frames;
if (!animations && !(allow_tearing && frame_allow_tearing) &&
c->configure_serial && !c->isfloating &&
client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) {
if (!animations && !grabc && c->configure_serial &&
client_is_rendered_on_mon(c, m)) {
monitor_check_skip_frame_timeout(m);
goto skip;
}
}
if (m->skiping_frame) {
monitor_stop_skip_frame_timer(m);
}
// 只有在需要帧时才构建和提交状态
if (allow_tearing && frame_allow_tearing) {
apply_tear_state(m);
@ -4432,12 +4613,7 @@ void rendermon(struct wl_listener *listener, void *data) {
skip:
// 发送帧完成通知
clock_gettime(CLOCK_MONOTONIC, &now);
if (allow_tearing && frame_allow_tearing) {
wlr_scene_output_send_frame_done(m->scene_output, &now);
} else {
wlr_scene_output_send_frame_done(m->scene_output, &now);
wlr_output_state_finish(&pending);
}
wlr_scene_output_send_frame_done(m->scene_output, &now);
// 如果需要更多帧,确保安排下一帧
if (need_more_frames && allow_frame_scheduling) {
@ -4619,6 +4795,41 @@ void exchange_two_client(Client *c1, Client *c2) {
}
}
void set_activation_env() {
if (!getenv("DBUS_SESSION_BUS_ADDRESS")) {
wlr_log(WLR_INFO, "Not updating dbus execution environment: "
"DBUS_SESSION_BUS_ADDRESS not set");
return;
}
wlr_log(WLR_INFO, "Updating dbus execution environment");
char *env_keys = join_strings(env_vars, " ");
// first command: dbus-update-activation-environment
const char *arg1 = env_keys;
char *cmd1 = string_printf("dbus-update-activation-environment %s", arg1);
if (!cmd1) {
wlr_log(WLR_ERROR, "Failed to allocate command string");
goto cleanup;
}
spawn(&(Arg){.v = cmd1});
free(cmd1);
// second command: systemctl --user
const char *action = "import-environment";
char *cmd2 = string_printf("systemctl --user %s %s", action, env_keys);
if (!cmd2) {
wlr_log(WLR_ERROR, "Failed to allocate command string");
goto cleanup;
}
spawn(&(Arg){.v = cmd2});
free(cmd2);
cleanup:
free(env_keys);
}
void // 17
run(char *startup_cmd) {
@ -4676,6 +4887,8 @@ run(char *startup_cmd) {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr");
handlecursoractivity();
set_activation_env();
run_exec();
run_exec_once();
@ -4790,12 +5003,19 @@ setfloating(Client *c, int32_t floating) {
}
if (!c->isfloating && old_floating_state) {
set_size_per(c->mon, c);
restore_size_per(c->mon, c);
}
if (!c->force_maximize)
client_set_maximized(c, false);
if (!c->isfloating || c->force_tiled_state) {
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
} else {
client_set_tiled(c, WLR_EDGE_NONE);
}
arrange(c->mon, false, false);
setborder_color(c);
printstatus();
@ -4869,7 +5089,7 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) {
wlr_scene_node_reparent(&c->scene->node,
layers[c->isfloating ? LyrTop : LyrTile]);
if (!c->ismaximizescreen && old_maximizescreen_state) {
set_size_per(c->mon, c);
restore_size_per(c->mon, c);
}
if (!c->force_maximize && !c->ismaximizescreen) {
@ -4940,7 +5160,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
}
if (!c->isfullscreen && old_fullscreen_state) {
set_size_per(c->mon, c);
restore_size_per(c->mon, c);
}
arrange(c->mon, false, false);
@ -5070,9 +5290,15 @@ 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);
c->tags =
newtags ? newtags
: m->tagset[m->seltags]; /* assign tags of target monitor */
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];
}
setfloating(c, c->isfloating);
setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
}
@ -5159,6 +5385,7 @@ void setup(void) {
setenv("XCURSOR_SIZE", "24", 1);
setenv("XDG_CURRENT_DESKTOP", "mango", 1);
parse_config();
init_baked_points();
@ -5531,6 +5758,9 @@ void overview_backup(Client *c) {
c->ismaximizescreen = 0;
}
c->bw = c->isnoborder ? 0 : borderpx;
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
}
// overview切回到普通视图还原窗口的状态
@ -5570,6 +5800,10 @@ void overview_restore(Client *c, const Arg *arg) {
!c->isfullscreen) { // 如果是在ov模式中创建的窗口,没有bw记录
c->bw = c->isnoborder ? 0 : borderpx;
}
if (c->isfloating && !c->force_tiled_state) {
client_set_tiled(c, WLR_EDGE_NONE);
}
}
void handlecursoractivity(void) {
@ -5616,6 +5850,7 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) {
focusclient(focustop(selmon), 1);
motionnotify(0, NULL, 0, 0, 0, 0);
l->being_unmapped = false;
layer_flush_blur_background(l);
wlr_scene_node_destroy(&l->shadow->node);
l->shadow = NULL;
}
@ -5838,6 +6073,10 @@ void updatemons(struct wl_listener *listener, void *data) {
c->mon = selmon;
reset_foreign_tolevel(c);
}
if (c->tags == 0 && !c->is_in_scratchpad) {
c->tags = selmon->tagset[selmon->seltags];
set_size_per(selmon, c);
}
}
focusclient(focustop(selmon), 1);
if (selmon->lock_surface) {
@ -6055,6 +6294,18 @@ void virtualpointer(struct wl_listener *listener, void *data) {
}
#ifdef XWAYLAND
void fix_xwayland_unmanaged_coordinate(Client *c) {
if (!selmon)
return;
// 1. 如果窗口已经在当前活动显示器内,直接返回
if (c->geom.x >= selmon->m.x && c->geom.x < selmon->m.x + selmon->m.width &&
c->geom.y >= selmon->m.y && c->geom.y < selmon->m.y + selmon->m.height)
return;
c->geom = setclient_coordinate_center(c, selmon, c->geom, 0, 0);
}
int32_t synckeymap(void *data) {
reset_keyboard_layout();
// we only need to sync keymap once
@ -6155,17 +6406,34 @@ void createnotifyx11(struct wl_listener *listener, void *data) {
LISTEN(&xsurface->events.request_minimize, &c->minimize, minimizenotify);
}
void commitx11(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, commmitx11);
struct wlr_surface_state *state = &c->surface.xwayland->surface->current;
if ((int32_t)c->geom.width - 2 * (int32_t)c->bw == (int32_t)state->width &&
(int32_t)c->geom.height - 2 * (int32_t)c->bw ==
(int32_t)state->height &&
(int32_t)c->surface.xwayland->x ==
(int32_t)c->geom.x + (int32_t)c->bw &&
(int32_t)c->surface.xwayland->y ==
(int32_t)c->geom.y + (int32_t)c->bw) {
c->configure_serial = 0;
}
}
void associatex11(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, associate);
LISTEN(&client_surface(c)->events.map, &c->map, mapnotify);
LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify);
LISTEN(&client_surface(c)->events.commit, &c->commmitx11, commitx11);
}
void dissociatex11(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, dissociate);
wl_list_remove(&c->map.link);
wl_list_remove(&c->unmap.link);
wl_list_remove(&c->commmitx11.link);
}
void sethints(struct wl_listener *listener, void *data) {