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) - Ipc support(get/send message from/to compositor by external program)
- Hycov-like overview - Hycov-like overview
- Window effects from scenefx (blur, shadow, corner radius, opacity) - 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 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 # Our discord
[mangowc](https://discord.gg/CPjbDxesh5) [mangowc](https://discord.gg/CPjbDxesh5)
# Supported layouts # Supported layouts
- tile - tile
- scroller - scroller
- monocle - monocle
@ -40,21 +48,20 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
- vertical_tile - vertical_tile
- vertical_grid - vertical_grid
- vertical_scroller - vertical_scroller
- tgmix
# Installation # Installation
[![Packaging status](https://repology.org/badge/vertical-allrepos/mangowc.svg)](https://repology.org/project/mangowc/versions)
## Dependencies ## Dependencies
- glibc
- wayland - wayland
- wayland-protocols - wayland-protocols
- libinput - libinput
- libdrm - libdrm
- libxkbcommon - libxkbcommon
- pixman - pixman
- git
- meson
- ninja
- libdisplay-info - libdisplay-info
- libliftoff - libliftoff
- hwdata - hwdata
@ -98,6 +105,35 @@ Then, install the package:
dnf install mangowc 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 ## Other
```bash ```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'], project('mango', ['c', 'cpp'],
version : '0.12.1', version : '0.12.3',
) )
subdir('protocols') subdir('protocols')
@ -56,7 +56,7 @@ endif
if is_git_repo if is_git_repo
# 如果是 Git 目录,获取 Commit Hash 和最新的 tag # 如果是 Git 目录,获取 Commit Hash 和最新的 tag
commit_hash = run_command(git, 'rev-parse', '--short', 'HEAD', check : false).stdout().strip() 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) version_with_hash = '@0@(@1@)'.format(latest_tag, commit_hash)
else else
# 如果不是 Git 目录,使用项目版本号和 "release" 字符串 # 如果不是 Git 目录,使用项目版本号和 "release" 字符串
@ -147,5 +147,7 @@ executable('mmsg',
) )
desktop_install_dir = join_paths(prefix, 'share/wayland-sessions') desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
install_data('mango.desktop', install_dir : desktop_install_dir) portal_install_dir = join_paths(prefix, 'share/xdg-desktop-portal')
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango')) 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 = { options = {
programs.mango = { programs.mango = {
enable = lib.mkEnableOption "mango, a wayland compositor based on dwl"; 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 { package = lib.mkOption {
type = lib.types.package; type = lib.types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango; default = self.packages.${pkgs.stdenv.hostPlatform.system}.mango;
@ -55,7 +59,7 @@ in {
programs.xwayland.enable = lib.mkDefault true; programs.xwayland.enable = lib.mkDefault true;
services = { services = {
displayManager.sessionPackages = [cfg.package]; displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [ cfg.package ];
graphical-desktop.enable = lib.mkDefault true; graphical-desktop.enable = lib.mkDefault true;
}; };

View file

@ -1,6 +1,6 @@
wayland_scanner = find_program('wayland-scanner') wayland_scanner = find_program('wayland-scanner')
wayland_protos_dep = dependency('wayland-protocols') 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_code = generator(
wayland_scanner, wayland_scanner,
output: '@BASENAME@-protocol.c', 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->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 2 * c->bw); c->geom.height - 2 * c->bw);
if (c->configure_serial != 0) {
c->mon->resizing_count_pending++;
}
if (c == grabc) { if (c == grabc) {
c->animation.running = false; c->animation.running = false;
c->need_output_flush = 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; 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) { static inline int32_t client_is_unmanaged(Client *c) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
@ -319,9 +294,24 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
uint32_t height) { uint32_t height) {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { 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, wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x + c->bw,
c->geom.y + c->bw, width, height); c->geom.y + c->bw, width, height);
return 0; return 1;
} }
#endif #endif
if ((int32_t)width == c->surface.xdg->toplevel->current.width && 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) { uint32_t timespec_to_ms(struct timespec *ts) {
return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000; 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); int32_t regex_match(const char *pattern_mb, const char *str_mb);
void wl_list_append(struct wl_list *list, struct wl_list *object); void wl_list_append(struct wl_list *list, struct wl_list *object);
uint32_t get_now_in_ms(void); 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; uint32_t tags;
int32_t isfloating; int32_t isfloating;
int32_t isfullscreen; int32_t isfullscreen;
int32_t isfakefullscreen;
float scroller_proportion; float scroller_proportion;
const char *animation_type_open; const char *animation_type_open;
const char *animation_type_close; const char *animation_type_close;
@ -89,6 +90,7 @@ typedef struct {
int32_t isterm; int32_t isterm;
int32_t allow_csd; int32_t allow_csd;
int32_t force_maximize; int32_t force_maximize;
int32_t force_tiled_state;
int32_t force_tearing; int32_t force_tearing;
int32_t noswallow; int32_t noswallow;
int32_t noblur; int32_t noblur;
@ -209,6 +211,7 @@ typedef struct {
int32_t scroller_ignore_proportion_single; int32_t scroller_ignore_proportion_single;
int32_t scroller_focus_center; int32_t scroller_focus_center;
int32_t scroller_prefer_center; int32_t scroller_prefer_center;
int32_t scroller_prefer_overspread;
int32_t edge_scroller_pointer_focus; int32_t edge_scroller_pointer_focus;
int32_t focus_cross_monitor; int32_t focus_cross_monitor;
int32_t exchange_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); (*arg).v = strdup(arg_value);
} else if (strcmp(func_name, "switch_keyboard_layout") == 0) { } else if (strcmp(func_name, "switch_keyboard_layout") == 0) {
func = switch_keyboard_layout; func = switch_keyboard_layout;
(*arg).i = CLAMP_INT(atoi(arg_value), 0, 100);
} else if (strcmp(func_name, "setlayout") == 0) { } else if (strcmp(func_name, "setlayout") == 0) {
func = setlayout; func = setlayout;
(*arg).v = strdup(arg_value); (*arg).v = strdup(arg_value);
@ -1335,6 +1339,8 @@ bool parse_option(Config *config, char *key, char *value) {
config->scroller_focus_center = atoi(value); config->scroller_focus_center = atoi(value);
} else if (strcmp(key, "scroller_prefer_center") == 0) { } else if (strcmp(key, "scroller_prefer_center") == 0) {
config->scroller_prefer_center = atoi(value); 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) { } else if (strcmp(key, "edge_scroller_pointer_focus") == 0) {
config->edge_scroller_pointer_focus = atoi(value); config->edge_scroller_pointer_focus = atoi(value);
} else if (strcmp(key, "focus_cross_monitor") == 0) { } 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 // int32_t rule value, relay to a client property
rule->isfloating = -1; rule->isfloating = -1;
rule->isfullscreen = -1; rule->isfullscreen = -1;
rule->isfakefullscreen = -1;
rule->isnoborder = -1; rule->isnoborder = -1;
rule->isnoshadow = -1; rule->isnoshadow = -1;
rule->isnoradius = -1; rule->isnoradius = -1;
@ -2011,6 +2018,7 @@ bool parse_option(Config *config, char *key, char *value) {
rule->isterm = -1; rule->isterm = -1;
rule->allow_csd = -1; rule->allow_csd = -1;
rule->force_maximize = -1; rule->force_maximize = -1;
rule->force_tiled_state = -1;
rule->force_tearing = -1; rule->force_tearing = -1;
rule->noswallow = -1; rule->noswallow = -1;
rule->noblur = -1; rule->noblur = -1;
@ -2123,6 +2131,8 @@ bool parse_option(Config *config, char *key, char *value) {
rule->allow_csd = atoi(val); rule->allow_csd = atoi(val);
} else if (strcmp(key, "force_maximize") == 0) { } else if (strcmp(key, "force_maximize") == 0) {
rule->force_maximize = atoi(val); 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) { } else if (strcmp(key, "force_tearing") == 0) {
rule->force_tearing = atoi(val); rule->force_tearing = atoi(val);
} else if (strcmp(key, "noswallow") == 0) { } else if (strcmp(key, "noswallow") == 0) {
@ -2133,6 +2143,8 @@ bool parse_option(Config *config, char *key, char *value) {
rule->scroller_proportion = atof(val); rule->scroller_proportion = atof(val);
} else if (strcmp(key, "isfullscreen") == 0) { } else if (strcmp(key, "isfullscreen") == 0) {
rule->isfullscreen = atoi(val); rule->isfullscreen = atoi(val);
} else if (strcmp(key, "isfakefullscreen") == 0) {
rule->isfakefullscreen = atoi(val);
} else if (strcmp(key, "globalkeybinding") == 0) { } else if (strcmp(key, "globalkeybinding") == 0) {
char mod_str[256], keysym_str[256]; char mod_str[256], keysym_str[256];
sscanf(val, "%255[^-]-%255[a-zA-Z]", mod_str, keysym_str); 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); CLAMP_INT(config.scroller_ignore_proportion_single, 0, 1);
scroller_focus_center = CLAMP_INT(config.scroller_focus_center, 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_center = CLAMP_INT(config.scroller_prefer_center, 0, 1);
scroller_prefer_overspread =
CLAMP_INT(config.scroller_prefer_overspread, 0, 1);
edge_scroller_pointer_focus = edge_scroller_pointer_focus =
CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1); CLAMP_INT(config.edge_scroller_pointer_focus, 0, 1);
scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000); scroller_structs = CLAMP_INT(config.scroller_structs, 0, 1000);
@ -3293,6 +3307,7 @@ void set_value_default() {
scroller_ignore_proportion_single; scroller_ignore_proportion_single;
config.scroller_focus_center = scroller_focus_center; config.scroller_focus_center = scroller_focus_center;
config.scroller_prefer_center = scroller_prefer_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.edge_scroller_pointer_focus = edge_scroller_pointer_focus;
config.focus_cross_monitor = focus_cross_monitor; config.focus_cross_monitor = focus_cross_monitor;
config.exchange_cross_monitor = exchange_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); 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; Monitor *m = NULL;
wl_list_for_each(m, &mons, link) { wl_list_for_each(m, &mons, link) {
wlr_xcursor_manager_load(cursor_mgr, m->wlr_output->scale); 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_ignore_proportion_single = 1;
int32_t scroller_focus_center = 0; int32_t scroller_focus_center = 0;
int32_t scroller_prefer_center = 0; int32_t scroller_prefer_center = 0;
int32_t scroller_prefer_overspread = 1;
int32_t focus_cross_monitor = 0; int32_t focus_cross_monitor = 0;
int32_t focus_cross_tag = 0; int32_t focus_cross_tag = 0;
int32_t exchange_cross_monitor = 0; int32_t exchange_cross_monitor = 0;

View file

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

View file

@ -83,6 +83,9 @@ setclient_coordinate_center(Client *c, Monitor *tm, struct wlr_box geom,
int32_t len = 0; int32_t len = 0;
Monitor *m = tm ? tm : selmon; Monitor *m = tm ? tm : selmon;
if (!m)
return geom;
uint32_t cbw = check_hit_no_border(c) ? c->bw : 0; uint32_t cbw = check_hit_no_border(c) ? c->bw : 0;
if (!c->no_force_center && m) { 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 || if (id == TILE || id == VERTICAL_TILE || id == DECK ||
id == VERTICAL_DECK || id == RIGHT_TILE) { id == VERTICAL_DECK || id == RIGHT_TILE) {
if (fc && !fc->ismaster) if (tc->ismaster ^ sc->ismaster)
return false; return false;
else if (!sc->ismaster) if (fc && !(fc->ismaster ^ sc->ismaster))
return false;
else
return true; return true;
} }
if (id == TGMIX) { if (id == TGMIX) {
if (fc && !fc->ismaster) if (tc->ismaster ^ sc->ismaster)
return false;
if (fc && !(fc->ismaster ^ sc->ismaster))
return false; return false;
if (!sc->ismaster && sc->mon->visible_tiling_clients <= 3) if (!sc->ismaster && sc->mon->visible_tiling_clients <= 3)
return true; return true;
} }
if (id == CENTER_TILE) { if (id == CENTER_TILE) {
if (fc && !fc->ismaster) if (tc->ismaster ^ sc->ismaster)
return false; 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; return true;
else else
return false; 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( surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node)) wlr_scene_buffer_from_node(node))
->surface; ->surface;
else if (node->type == WLR_SCENE_NODE_RECT) {
surface = NULL;
break;
}
/* start from the topmost layer, /* start from the topmost layer,
find a sureface that can be focused by pointer, 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; l = pnode->data;
} }
} }
if (node->type == WLR_SCENE_NODE_RECT) {
if (c) {
surface = client_surface(c);
}
break;
}
} }
if (psurface) 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); struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
return o ? o->data : NULL; 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) { void set_size_per(Monitor *m, Client *c) {
Client *fc = NULL; Client *fc = NULL;
bool found = false; bool found = false;
@ -5,8 +47,13 @@ void set_size_per(Monitor *m, Client *c) {
if (!m || !c) if (!m || !c)
return; return;
const Layout *current_layout = m->pertag->ltidxs[m->pertag->curtag];
wl_list_for_each(fc, &clients, link) { wl_list_for_each(fc, &clients, link) {
if (VISIBLEON(fc, m) && ISTILED(fc) && fc != c) { 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_mfact_per = fc->master_mfact_per;
c->master_inner_per = fc->master_inner_per; c->master_inner_per = fc->master_inner_per;
c->stack_inner_per = fc->stack_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; struct wlr_box target_geom;
int32_t focus_client_index = 0; int32_t focus_client_index = 0;
bool need_scroller = false; bool need_scroller = false;
bool over_overspread_to_left = false;
int32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 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) { if (n == 1 && scroller_ignore_proportion_single) {
need_scroller = true; need_scroller = true;
} }
@ -394,18 +435,26 @@ void scroller(Monitor *m) {
tempClients[focus_client_index], &target_geom); tempClients[focus_client_index], &target_geom);
arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv); arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv);
} else if (need_scroller) { } else if (need_scroller) {
if (scroller_focus_center || if (need_apply_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)) {
target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; 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 { } 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 - ? m->w.x + (m->w.width -
root_client->scroller_proportion * tempClients[focus_client_index]
->scroller_proportion *
max_client_width - max_client_width -
scroller_structs) scroller_structs)
: m->w.x + scroller_structs; : m->w.x + scroller_structs;

View file

@ -270,6 +270,7 @@ void vertical_scroller(Monitor *m) {
struct wlr_box target_geom; struct wlr_box target_geom;
int32_t focus_client_index = 0; int32_t focus_client_index = 0;
bool need_scroller = false; bool need_scroller = false;
bool over_overspread_to_up = false;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0; int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 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) { if (n == 1 && scroller_ignore_proportion_single) {
need_scroller = true; need_scroller = true;
} }
@ -381,18 +422,24 @@ void vertical_scroller(Monitor *m) {
arrange_stack_vertical(tempClients[focus_client_index], target_geom, arrange_stack_vertical(tempClients[focus_client_index], target_geom,
cur_gappih); cur_gappih);
} else if (need_scroller) { } else if (need_scroller) {
if (scroller_focus_center || if (need_apply_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)) {
target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; 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 { } else {
target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2 target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2
? m->w.y + (m->w.height - ? m->w.y + (m->w.height -
root_client->scroller_proportion * tempClients[focus_client_index]
->scroller_proportion *
max_client_height - max_client_height -
scroller_structs) scroller_structs)
: m->w.y + scroller_structs; : m->w.y + scroller_structs;

View file

@ -3,6 +3,7 @@
*/ */
#include "wlr-layer-shell-unstable-v1-protocol.h" #include "wlr-layer-shell-unstable-v1-protocol.h"
#include "wlr/util/box.h" #include "wlr/util/box.h"
#include "wlr/util/edges.h"
#include <getopt.h> #include <getopt.h>
#include <libinput.h> #include <libinput.h>
#include <limits.h> #include <limits.h>
@ -337,6 +338,7 @@ struct Client {
struct wl_listener configure; struct wl_listener configure;
struct wl_listener set_hints; struct wl_listener set_hints;
struct wl_listener set_geometry; struct wl_listener set_geometry;
struct wl_listener commmitx11;
#endif #endif
uint32_t bw; uint32_t bw;
uint32_t tags, oldtags, mini_restore_tag; uint32_t tags, oldtags, mini_restore_tag;
@ -390,6 +392,7 @@ struct Client {
int32_t isterm, noswallow; int32_t isterm, noswallow;
int32_t allow_csd; int32_t allow_csd;
int32_t force_maximize; int32_t force_maximize;
int32_t force_tiled_state;
pid_t pid; pid_t pid;
Client *swallowing, *swallowedby; Client *swallowing, *swallowedby;
bool is_clip_to_hide; bool is_clip_to_hide;
@ -483,6 +486,14 @@ typedef struct {
bool being_unmapped; bool being_unmapped;
} LayerSurface; } 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 { typedef struct {
const char *symbol; const char *symbol;
void (*arrange)(Monitor *); void (*arrange)(Monitor *);
@ -500,11 +511,15 @@ struct Monitor {
struct wl_listener request_state; struct wl_listener request_state;
struct wl_listener destroy_lock_surface; struct wl_listener destroy_lock_surface;
struct wlr_session_lock_surface_v1 *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 m; /* monitor area, layout-relative */
struct wlr_box w; /* window area, layout-relative */ struct wlr_box w; /* window area, layout-relative */
struct wl_list layers[4]; /* LayerSurface::link */ struct wl_list layers[4]; /* LayerSurface::link */
uint32_t seltags; uint32_t seltags;
uint32_t tagset[2]; uint32_t tagset[2];
bool skiping_frame;
uint32_t resizing_count_pending;
uint32_t resizing_count_current;
struct wl_list dwl_ipc_outputs; struct wl_list dwl_ipc_outputs;
int32_t gappih; /* horizontal gap between windows */ int32_t gappih; /* horizontal gap between windows */
@ -512,6 +527,8 @@ struct Monitor {
int32_t gappoh; /* horizontal outer gaps */ int32_t gappoh; /* horizontal outer gaps */
int32_t gappov; /* vertical outer gaps */ int32_t gappov; /* vertical outer gaps */
Pertag *pertag; Pertag *pertag;
uint32_t ovbk_current_tagset;
uint32_t ovbk_prev_tagset;
Client *sel, *prevsel; Client *sel, *prevsel;
int32_t isoverview; int32_t isoverview;
int32_t is_in_hotarea; 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 bool client_only_in_one_tag(Client *c);
static Client *get_focused_stack_client(Client *sc); static Client *get_focused_stack_client(Client *sc);
static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc); 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 "data/static_keymap.h"
#include "dispatch/bind_declare.h" #include "dispatch/bind_declare.h"
@ -883,6 +904,9 @@ static KeyMode keymode = {
.mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'}, .mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'},
.isdefault = true, .isdefault = true,
}; };
static char *env_vars[] = {"DISPLAY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP",
"XDG_SESSION_TYPE", NULL};
static struct { static struct {
enum wp_cursor_shape_device_v1_shape shape; enum wp_cursor_shape_device_v1_shape shape;
struct wlr_surface *surface; struct wlr_surface *surface;
@ -942,11 +966,13 @@ static struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor = {
.notify = handle_keyboard_shortcuts_inhibit_new_inhibitor}; .notify = handle_keyboard_shortcuts_inhibit_new_inhibitor};
#ifdef XWAYLAND #ifdef XWAYLAND
static void fix_xwayland_unmanaged_coordinate(Client *c);
static int32_t synckeymap(void *data); static int32_t synckeymap(void *data);
static void activatex11(struct wl_listener *listener, void *data); static void activatex11(struct wl_listener *listener, void *data);
static void configurex11(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 createnotifyx11(struct wl_listener *listener, void *data);
static void dissociatex11(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 associatex11(struct wl_listener *listener, void *data);
static void sethints(struct wl_listener *listener, void *data); static void sethints(struct wl_listener *listener, void *data);
static void xwaylandready(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, isterm);
APPLY_INT_PROP(c, r, allow_csd); APPLY_INT_PROP(c, r, allow_csd);
APPLY_INT_PROP(c, r, force_maximize); 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, force_tearing);
APPLY_INT_PROP(c, r, noswallow); APPLY_INT_PROP(c, r, noswallow);
APPLY_INT_PROP(c, r, nofocus); 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, no_force_center);
APPLY_INT_PROP(c, r, isfloating); APPLY_INT_PROP(c, r, isfloating);
APPLY_INT_PROP(c, r, isfullscreen); APPLY_INT_PROP(c, r, isfullscreen);
APPLY_INT_PROP(c, r, isfakefullscreen);
APPLY_INT_PROP(c, r, isnoborder); APPLY_INT_PROP(c, r, isnoborder);
APPLY_INT_PROP(c, r, isnoshadow); APPLY_INT_PROP(c, r, isnoshadow);
APPLY_INT_PROP(c, r, isnoradius); APPLY_INT_PROP(c, r, isnoradius);
@ -1366,11 +1394,22 @@ void applyrules(Client *c) {
Client *fc = NULL; Client *fc = NULL;
Client *parent = NULL; Client *parent = NULL;
if (!c)
return;
parent = client_get_parent(c); parent = client_get_parent(c);
Monitor *mon = parent && parent->mon ? parent->mon : selmon; Monitor *mon = parent && parent->mon ? parent->mon : selmon;
c->isfloating = client_is_float_type(c) || parent; 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))) if (!(appid = client_get_appid(c)))
appid = broken; appid = broken;
if (!(title = client_get_title(c))) if (!(title = client_get_title(c)))
@ -1446,7 +1485,8 @@ void applyrules(Client *c) {
/*-----------------------apply rule action-------------------------*/ /*-----------------------apply rule action-------------------------*/
// rule action only apply after map not apply in the init commit // 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; return;
// apply swallow rule // apply swallow rule
@ -1472,6 +1512,7 @@ void applyrules(Client *c) {
setmon(c, mon, newtags, setmon(c, mon, newtags,
!c->isopensilent && !c->isopensilent &&
!(client_is_x11_popup(c) && client_should_ignore_focus(c)) && !(client_is_x11_popup(c) && client_should_ignore_focus(c)) &&
mon &&
(!c->istagsilent || !newtags || (!c->istagsilent || !newtags ||
newtags & mon->tagset[mon->seltags])); newtags & mon->tagset[mon->seltags]));
@ -1484,12 +1525,16 @@ void applyrules(Client *c) {
setfullscreen(c, fullscreen_state_backup); 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 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 window in the current tag will exit fullscreen and participate in tiling
*/ */
wl_list_for_each(fc, &clients, 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) && VISIBLEON(fc, c->mon) && ISFULLSCREEN(fc) &&
!c->isfloating) { !c->isfloating) {
clear_fullscreen_flag(fc); clear_fullscreen_flag(fc);
@ -1508,7 +1553,7 @@ void applyrules(Client *c) {
} }
// apply overlay rule // apply overlay rule
if (c->isoverlay) { if (c->isoverlay && c->scene) {
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]); wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
wlr_scene_node_raise_to_top(&c->scene->node); 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); wlr_scene_node_destroy(&m->blur->node);
m->blur = NULL; 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; m->wlr_output->data = NULL;
free(m->pertag); free(m->pertag);
free(m); 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) { void maplayersurfacenotify(struct wl_listener *listener, void *data) {
LayerSurface *l = wl_container_of(listener, l, map); LayerSurface *l = wl_container_of(listener, l, map);
struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
@ -2390,15 +2453,7 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
} }
} }
if (blur) { layer_flush_blur_background(l);
// 如果背景层发生变化,标记优化的模糊背景缓存需要更新
if (layer_surface->current.layer ==
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
if (l->mon) {
wlr_scene_optimized_blur_mark_dirty(l->mon->blur);
}
}
}
if (layer_surface == exclusive_focus && if (layer_surface == exclusive_focus &&
layer_surface->current.keyboard_interactive != layer_surface->current.keyboard_interactive !=
@ -2438,9 +2493,6 @@ void commitnotify(struct wl_listener *listener, void *data) {
setmon(c, NULL, 0, setmon(c, NULL, 0,
true); /* Make sure to reapply rules in mapnotify() */ 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); uint32_t serial = wlr_xdg_surface_schedule_configure(c->surface.xdg);
if (serial > 0) { if (serial > 0) {
c->configure_serial = serial; c->configure_serial = serial;
@ -2500,37 +2552,105 @@ void destroydecoration(struct wl_listener *listener, void *data) {
wl_list_remove(&c->set_decoration_mode.link); 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_surface *surface = data;
struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface); struct wlr_xdg_popup *wkr_popup =
LayerSurface *l = NULL; wlr_xdg_popup_try_from_wlr_surface(surface);
Client *c = NULL; Client *c = NULL;
struct wlr_box box; LayerSurface *l = NULL;
int32_t type = -1; int32_t type = -1;
if (!popup || !popup->base->initial_commit) if (!wkr_popup || !wkr_popup->base->initial_commit)
goto commitpopup_listen_free; goto commitpopup_listen_free;
type = toplevel_from_wlr_surface(popup->base->surface, &c, &l); type = toplevel_from_wlr_surface(wkr_popup->base->surface, &c, &l);
if (!popup->parent || !popup->parent->data || type < 0) if (!wkr_popup->parent || !wkr_popup->parent->data || type < 0) {
goto commitpopup_listen_free; wlr_xdg_popup_destroy(wkr_popup);
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);
goto commitpopup_listen_free; goto commitpopup_listen_free;
} }
box = type == LayerShell ? l->mon->m : c->mon->w;
box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x); wlr_scene_node_raise_to_top(wkr_popup->parent->data);
box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
wlr_xdg_popup_unconstrain_from_box(popup, &box); 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: commitpopup_listen_free:
wl_list_remove(&listener->link); wl_list_remove(&popup->commit.link);
free(listener); 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) { void createdecoration(struct wl_listener *listener, void *data) {
@ -2750,7 +2870,15 @@ void createmon(struct wl_listener *listener, void *data) {
return; return;
} }
struct wl_event_loop *loop = wl_display_get_event_loop(dpy);
m = wlr_output->data = ecalloc(1, sizeof(*m)); 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 = wlr_output;
m->wlr_output->data = m; m->wlr_output->data = m;
@ -2768,6 +2896,8 @@ void createmon(struct wl_listener *listener, void *data) {
m->isoverview = 0; m->isoverview = 0;
m->sel = NULL; m->sel = NULL;
m->is_in_hotarea = 0; m->is_in_hotarea = 0;
m->m.x = INT32_MAX;
m->m.y = INT32_MAX;
float scale = 1; float scale = 1;
enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL; enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL;
wlr_output_state_set_scale(&state, scale); wlr_output_state_set_scale(&state, scale);
@ -3110,13 +3240,6 @@ void createpointerconstraint(struct wl_listener *listener, void *data) {
&pointer_constraint->destroy, destroypointerconstraint); &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) { void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) {
if (active_constraint == constraint) if (active_constraint == constraint)
return; 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) { void init_client_properties(Client *c) {
c->isfocusing = false; 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->ismaximizescreen = 0;
c->isfullscreen = 0; c->isfullscreen = 0;
c->need_float_size_reduce = 0; c->need_float_size_reduce = 0;
@ -3856,6 +3996,7 @@ void init_client_properties(Client *c) {
c->isterm = 0; c->isterm = 0;
c->allow_csd = 0; c->allow_csd = 0;
c->force_maximize = 0; c->force_maximize = 0;
c->force_tiled_state = 1;
c->force_tearing = 0; c->force_tearing = 0;
c->allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; c->allow_shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE;
c->scroller_proportion_single = 0.0f; c->scroller_proportion_single = 0.0f;
@ -3866,6 +4007,7 @@ void init_client_properties(Client *c) {
c->stack_proportion = 0.0f; c->stack_proportion = 0.0f;
c->next_in_stack = NULL; c->next_in_stack = NULL;
c->prev_in_stack = NULL; c->prev_in_stack = NULL;
memset(c->oldmonname, 0, sizeof(c->oldmonname));
memcpy(c->opacity_animation.initial_border_color, bordercolor, memcpy(c->opacity_animation.initial_border_color, bordercolor,
sizeof(c->opacity_animation.initial_border_color)); sizeof(c->opacity_animation.initial_border_color));
memcpy(c->opacity_animation.current_border_color, bordercolor, memcpy(c->opacity_animation.current_border_color, bordercolor,
@ -3913,18 +4055,19 @@ mapnotify(struct wl_listener *listener, void *data) {
*/ */
if (client_is_unmanaged(c)) { if (client_is_unmanaged(c)) {
/* Unmanaged clients always are floating */ /* 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_reparent(&c->scene->node, layers[LyrOverlay]);
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
if (client_wants_focus(c)) { if (client_wants_focus(c)) {
focusclient(c, 1); focusclient(c, 1);
exclusive_focus = c; exclusive_focus = c;
} }
#ifdef XWAYLAND
if (client_is_x11(c)) {
LISTEN(&c->surface.xwayland->events.set_geometry, &c->set_geometry,
setgeometrynotify);
}
#endif
return; return;
} }
@ -3970,8 +4113,10 @@ mapnotify(struct wl_listener *listener, void *data) {
applyrules(c); applyrules(c);
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | if (!c->isfloating || c->force_tiled_state) {
WLR_EDGE_RIGHT); client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
}
// apply buffer effects of client // apply buffer effects of client
wlr_scene_node_for_each_buffer(&c->scene_surface->node, 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); 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) { void rendermon(struct wl_listener *listener, void *data) {
Monitor *m = wl_container_of(listener, m, frame); Monitor *m = wl_container_of(listener, m, frame);
Client *c = NULL, *tmp = NULL; Client *c = NULL, *tmp = NULL;
struct wlr_output_state pending = {0};
LayerSurface *l = NULL, *tmpl = NULL; LayerSurface *l = NULL, *tmpl = NULL;
int32_t i; int32_t i;
struct wl_list *layer_list; struct wl_list *layer_list;
@ -4415,13 +4592,17 @@ void rendermon(struct wl_listener *listener, void *data) {
// 绘制客户端 // 绘制客户端
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
need_more_frames = client_draw_frame(c) || need_more_frames; need_more_frames = client_draw_frame(c) || need_more_frames;
if (!animations && !(allow_tearing && frame_allow_tearing) && if (!animations && !grabc && c->configure_serial &&
c->configure_serial && !c->isfloating && client_is_rendered_on_mon(c, m)) {
client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) { monitor_check_skip_frame_timeout(m);
goto skip; goto skip;
} }
} }
if (m->skiping_frame) {
monitor_stop_skip_frame_timer(m);
}
// 只有在需要帧时才构建和提交状态 // 只有在需要帧时才构建和提交状态
if (allow_tearing && frame_allow_tearing) { if (allow_tearing && frame_allow_tearing) {
apply_tear_state(m); apply_tear_state(m);
@ -4432,12 +4613,7 @@ void rendermon(struct wl_listener *listener, void *data) {
skip: skip:
// 发送帧完成通知 // 发送帧完成通知
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
if (allow_tearing && frame_allow_tearing) { wlr_scene_output_send_frame_done(m->scene_output, &now);
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);
}
// 如果需要更多帧,确保安排下一帧 // 如果需要更多帧,确保安排下一帧
if (need_more_frames && allow_frame_scheduling) { 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 void // 17
run(char *startup_cmd) { run(char *startup_cmd) {
@ -4676,6 +4887,8 @@ run(char *startup_cmd) {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr");
handlecursoractivity(); handlecursoractivity();
set_activation_env();
run_exec(); run_exec();
run_exec_once(); run_exec_once();
@ -4790,12 +5003,19 @@ setfloating(Client *c, int32_t floating) {
} }
if (!c->isfloating && old_floating_state) { if (!c->isfloating && old_floating_state) {
set_size_per(c->mon, c); restore_size_per(c->mon, c);
} }
if (!c->force_maximize) if (!c->force_maximize)
client_set_maximized(c, false); 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); arrange(c->mon, false, false);
setborder_color(c); setborder_color(c);
printstatus(); printstatus();
@ -4869,7 +5089,7 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) {
wlr_scene_node_reparent(&c->scene->node, wlr_scene_node_reparent(&c->scene->node,
layers[c->isfloating ? LyrTop : LyrTile]); layers[c->isfloating ? LyrTop : LyrTile]);
if (!c->ismaximizescreen && old_maximizescreen_state) { if (!c->ismaximizescreen && old_maximizescreen_state) {
set_size_per(c->mon, c); restore_size_per(c->mon, c);
} }
if (!c->force_maximize && !c->ismaximizescreen) { if (!c->force_maximize && !c->ismaximizescreen) {
@ -4940,7 +5160,7 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
} }
if (!c->isfullscreen && old_fullscreen_state) { if (!c->isfullscreen && old_fullscreen_state) {
set_size_per(c->mon, c); restore_size_per(c->mon, c);
} }
arrange(c->mon, false, false); 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 */ /* Make sure window actually overlaps with the monitor */
reset_foreign_tolevel(c); reset_foreign_tolevel(c);
resize(c, c->geom, 0); resize(c, c->geom, 0);
c->tags = if (!newtags && !m->isoverview) {
newtags ? newtags c->tags = m->tagset[m->seltags];
: m->tagset[m->seltags]; /* assign tags of target monitor */ } 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); setfloating(c, c->isfloating);
setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
} }
@ -5159,6 +5385,7 @@ void setup(void) {
setenv("XCURSOR_SIZE", "24", 1); setenv("XCURSOR_SIZE", "24", 1);
setenv("XDG_CURRENT_DESKTOP", "mango", 1); setenv("XDG_CURRENT_DESKTOP", "mango", 1);
parse_config(); parse_config();
init_baked_points(); init_baked_points();
@ -5531,6 +5758,9 @@ void overview_backup(Client *c) {
c->ismaximizescreen = 0; c->ismaximizescreen = 0;
} }
c->bw = c->isnoborder ? 0 : borderpx; c->bw = c->isnoborder ? 0 : borderpx;
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
WLR_EDGE_RIGHT);
} }
// overview切回到普通视图还原窗口的状态 // overview切回到普通视图还原窗口的状态
@ -5570,6 +5800,10 @@ void overview_restore(Client *c, const Arg *arg) {
!c->isfullscreen) { // 如果是在ov模式中创建的窗口,没有bw记录 !c->isfullscreen) { // 如果是在ov模式中创建的窗口,没有bw记录
c->bw = c->isnoborder ? 0 : borderpx; c->bw = c->isnoborder ? 0 : borderpx;
} }
if (c->isfloating && !c->force_tiled_state) {
client_set_tiled(c, WLR_EDGE_NONE);
}
} }
void handlecursoractivity(void) { void handlecursoractivity(void) {
@ -5616,6 +5850,7 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) {
focusclient(focustop(selmon), 1); focusclient(focustop(selmon), 1);
motionnotify(0, NULL, 0, 0, 0, 0); motionnotify(0, NULL, 0, 0, 0, 0);
l->being_unmapped = false; l->being_unmapped = false;
layer_flush_blur_background(l);
wlr_scene_node_destroy(&l->shadow->node); wlr_scene_node_destroy(&l->shadow->node);
l->shadow = NULL; l->shadow = NULL;
} }
@ -5838,6 +6073,10 @@ void updatemons(struct wl_listener *listener, void *data) {
c->mon = selmon; c->mon = selmon;
reset_foreign_tolevel(c); 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); focusclient(focustop(selmon), 1);
if (selmon->lock_surface) { if (selmon->lock_surface) {
@ -6055,6 +6294,18 @@ void virtualpointer(struct wl_listener *listener, void *data) {
} }
#ifdef XWAYLAND #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) { int32_t synckeymap(void *data) {
reset_keyboard_layout(); reset_keyboard_layout();
// we only need to sync keymap once // 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); 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) { void associatex11(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, associate); Client *c = wl_container_of(listener, c, associate);
LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); LISTEN(&client_surface(c)->events.map, &c->map, mapnotify);
LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); 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) { void dissociatex11(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, dissociate); Client *c = wl_container_of(listener, c, dissociate);
wl_list_remove(&c->map.link); wl_list_remove(&c->map.link);
wl_list_remove(&c->unmap.link); wl_list_remove(&c->unmap.link);
wl_list_remove(&c->commmitx11.link);
} }
void sethints(struct wl_listener *listener, void *data) { void sethints(struct wl_listener *listener, void *data) {