Merge branch 'mangowm:main' into main

This commit is contained in:
Eslam Mohamed 2026-05-11 01:09:56 +03:00 committed by GitHub
commit bd5b7b04f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1422 additions and 381 deletions

View file

@ -48,7 +48,7 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f
- vertical_tile - vertical_tile
- vertical_grid - vertical_grid
- vertical_scroller - vertical_scroller
- tgmix - dwindle
# Installation # Installation
@ -95,6 +95,12 @@ Finally, install the package:
emerge --ask --verbose gui-wm/mangowm emerge --ask --verbose gui-wm/mangowm
``` ```
## openSUSE
The package is in the community-maintained repository called obs.
```bash
sudo opi in mangowm
```
## Fedora Linux ## Fedora Linux
The package is in the third-party Terra repository. The package is in the third-party Terra repository.
First, add the [Terra Repository](https://terra.fyralabs.com/). First, add the [Terra Repository](https://terra.fyralabs.com/).
@ -148,7 +154,7 @@ meson build -Dprefix=/usr
sudo ninja -C build install sudo ninja -C build install
git clone https://github.com/mangowm/mango.git git clone https://github.com/mangowm/mango.git
cd mangowm cd mango
meson build -Dprefix=/usr meson build -Dprefix=/usr
sudo ninja -C build install sudo ninja -C build install
``` ```

View file

@ -86,6 +86,7 @@ enable_floating_snap=0
snap_distance=30 snap_distance=30
cursor_size=24 cursor_size=24
drag_tile_to_tile=1 drag_tile_to_tile=1
drag_tile_small=1
# keyboard # keyboard
repeat_rate=25 repeat_rate=25
@ -119,6 +120,7 @@ scratchpad_height_ratio=0.9
borderpx=4 borderpx=4
rootcolor=0x201b14ff rootcolor=0x201b14ff
bordercolor=0x444444ff bordercolor=0x444444ff
dropcolor=0x8FBA7C55
focuscolor=0xc9b890ff focuscolor=0xc9b890ff
maximizescreencolor=0x89aa61ff maximizescreencolor=0x89aa61ff
urgentcolor=0xad401fff urgentcolor=0xad401fff

View file

@ -35,6 +35,19 @@ xkb_rules_options=caps:escape,ctrl:nocaps
--- ---
### Mouse Settings
Configuration for external mice.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `mouse_natural_scrolling` | `0` | Invert scrolling direction. |
| `mouse_accel_profile` | `2` | `0` (None), `1` (Flat), `2` (Adaptive). |
| `mouse_accel_speed` | `0.0` | Speed adjustment (-1.0 to 1.0). |
| `left_handed` | `0` | Swap left and right buttons. |
| `axis_scroll_factor` | `1.0` | Scroll factor for axis scroll speed (0.110.0). |
---
### Trackpad Settings ### Trackpad Settings
Specific settings for laptop touchpads. Some settings may require a relogin to take effect. Specific settings for laptop touchpads. Some settings may require a relogin to take effect.
@ -45,15 +58,19 @@ Specific settings for laptop touchpads. Some settings may require a relogin to t
| `tap_to_click` | `1` | Tap to trigger a left click. | | `tap_to_click` | `1` | Tap to trigger a left click. |
| `tap_and_drag` | `1` | Tap and hold to drag items. | | `tap_and_drag` | `1` | Tap and hold to drag items. |
| `trackpad_natural_scrolling` | `0` | Invert scrolling direction (natural scrolling). | | `trackpad_natural_scrolling` | `0` | Invert scrolling direction (natural scrolling). |
| `trackpad_accel_profile` | `2` | `0` (None), `1` (Flat), `2` (Adaptive). |
| `trackpad_accel_speed` | `0.0` | Speed adjustment (-1.0 to 1.0). |
| `scroll_button` | `274` | The mouse button that use for scrolling(272 to 279). | `scroll_button` | `274` | The mouse button that use for scrolling(272 to 279).
| `scroll_method` | `1` | `1` (Two-finger), `2` (Edge), `4` (Button). | | `scroll_method` | `1` | `1` (Two-finger), `2` (Edge), `4` (Button). |
| `click_method` | `1` | `1` (Button areas), `2` (Clickfinger). | | `click_method` | `1` | `1` (Button areas), `2` (Clickfinger). |
| `send_events_mode` | `0` | `0` (Enabled), `1` (Disabled), `2` (Disabled on external mouse). |
| `drag_lock` | `1` | Lock dragging after tapping. | | `drag_lock` | `1` | Lock dragging after tapping. |
| `disable_while_typing` | `1` | Disable trackpad while typing. | | `disable_while_typing` | `1` | Disable trackpad while typing. |
| `left_handed` | `0` | Swap left/right buttons. | | `left_handed` | `0` | Swap left/right buttons. |
| `middle_button_emulation` | `0` | Emulate middle button. | | `middle_button_emulation` | `0` | Emulate middle button. |
| `swipe_min_threshold` | `1` | Minimum swipe threshold. | | `swipe_min_threshold` | `1` | Minimum swipe threshold when use gesture. |
| `button_map` | `0` | `0` (Left/right/middle), `1` (Left/middle/right). |
| `trackpad_scroll_factor` | `1.0` | Scroll factor for trackpad scroll speed (0.110.0). |
--- ---
**Detailed descriptions:** **Detailed descriptions:**
@ -79,9 +96,9 @@ Specific settings for laptop touchpads. Some settings may require a relogin to t
- `1` — Button areas: use software-defined areas on the touchpad to generate button events. - `1` — Button areas: use software-defined areas on the touchpad to generate button events.
- `2` — Clickfinger: the number of fingers determines which button is pressed. - `2` — Clickfinger: the number of fingers determines which button is pressed.
- `accel_profile` values: - `mouse_accel_profile` or `trackpad_scroll_profile` values:
- `0` — No acceleration. - `0` — No acceleration.
- `1` — Flat: no dynamic acceleration. Pointer speed = original input speed × (1 + `accel_speed`). - `1` — Flat: no dynamic acceleration. Pointer speed = original input speed × (1 + `mouse_accel_speed`).
- `2` — Adaptive: slow movement results in less acceleration, fast movement results in more. - `2` — Adaptive: slow movement results in less acceleration, fast movement results in more.
- `button_map` values: - `button_map` values:
@ -94,24 +111,6 @@ Specific settings for laptop touchpads. Some settings may require a relogin to t
- `2` — Disable this device when an external pointer device is plugged in. - `2` — Disable this device when an external pointer device is plugged in.
--- ---
### Mouse Settings
Configuration for external mice.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `mouse_natural_scrolling` | `0` | Invert scrolling direction. |
| `accel_profile` | `2` | `0` (None), `1` (Flat), `2` (Adaptive). |
| `accel_speed` | `0.0` | Speed adjustment (-1.0 to 1.0). |
| `left_handed` | `0` | Swap left and right buttons. |
| `middle_button_emulation` | `0` | Emulate middle button. |
| `swipe_min_threshold` | `1` | Minimum swipe threshold. |
| `send_events_mode` | `0` | `0` (Enabled), `1` (Disabled), `2` (Disabled on external mouse). |
| `button_map` | `0` | `0` (Left/right/middle), `1` (Left/middle/right). |
---
--- ---
## Keyboard Layout Switching ## Keyboard Layout Switching

View file

@ -22,10 +22,10 @@ description: Advanced settings for XWayland, focus behavior, and system integrat
| `warpcursor` | `1` | Warp the cursor to the center of the window when focus changes via keyboard. | | `warpcursor` | `1` | Warp the cursor to the center of the window when focus changes via keyboard. |
| `cursor_hide_timeout` | `0` | Hide the cursor after `N` seconds of inactivity (`0` to disable). | | `cursor_hide_timeout` | `0` | Hide the cursor after `N` seconds of inactivity (`0` to disable). |
| `drag_tile_to_tile` | `0` | Allow dragging a tiled window onto another to swap their positions. | | `drag_tile_to_tile` | `0` | Allow dragging a tiled window onto another to swap their positions. |
| `drag_tile_small` | `1` | Allow dragging a tiled window temporarily to small size.|
| `drag_corner` | `3` | Corner for drag-to-tile detection (0: none, 13: corners, 4: auto-detect). | | `drag_corner` | `3` | Corner for drag-to-tile detection (0: none, 13: corners, 4: auto-detect). |
| `drag_warp_cursor` | `1` | Warp cursor when dragging windows to tile. | | `drag_warp_cursor` | `1` | Warp cursor when dragging windows to tile. |
| `axis_bind_apply_timeout` | `100` | Timeout (ms) for detecting consecutive scroll events for axis bindings. | | `axis_bind_apply_timeout` | `100` | Timeout (ms) for detecting consecutive scroll events for axis bindings. |
| `axis_scroll_factor` | `1.0` | Scroll factor for axis scroll speed (0.110.0). |
## Multi-Monitor & Tags ## Multi-Monitor & Tags

View file

@ -41,11 +41,10 @@ blur_optimized=0
### My games are lagging or stuttering ### My games are lagging or stuttering
Try enabling **SyncObj** timeline support and **Adaptive Sync** (VRR) if your monitor supports it. Try enabling **SyncObj** timeline support.
```ini ```ini
syncobj_enable=1 syncobj_enable=1
adaptive_sync=1
``` ```
--- ---

View file

@ -64,7 +64,7 @@ mmsg -s -t 2^
### Layouts ### Layouts
Switch layouts programmatically. Layout codes: `S` (Scroller), `T` (Tile), `G` (Grid), `M` (Monocle), `K` (Deck), `CT` (Center Tile), `RT` (Right Tile), `VS` (Vertical Scroller), `VT` (Vertical Tile), `VG` (Vertical Grid), `VK` (Vertical Deck), `TG` (TGMix). Switch layouts programmatically. Layout codes: `S` (Scroller), `T` (Tile), `G` (Grid), `M` (Monocle), `K` (Deck), `CT` (Center Tile), `RT` (Right Tile), `VS` (Vertical Scroller), `VT` (Vertical Tile), `VG` (Vertical Grid), `VK` (Vertical Deck), `DW` (Dwindle).
```bash ```bash
# Switch to Scroller # Switch to Scroller

View file

@ -64,7 +64,7 @@ To get a fully functional desktop experience, we recommend installing the follow
| Terminal Emulator | foot, wezterm, alacritty, kitty, ghostty | | Terminal Emulator | foot, wezterm, alacritty, kitty, ghostty |
| Status Bar | waybar, eww, quickshell, ags | | Status Bar | waybar, eww, quickshell, ags |
| Desktop Shell | Noctalia, DankMaterialShell | | Desktop Shell | Noctalia, DankMaterialShell |
| Wallpaper Setup | swww, swaybg | | Wallpaper Setup | awww(swww), swaybg |
| Notification Daemon | swaync, dunst, mako | | Notification Daemon | swaync, dunst, mako |
| Desktop Portal | xdg-desktop-portal, xdg-desktop-portal-wlr, xdg-desktop-portal-gtk | | Desktop Portal | xdg-desktop-portal, xdg-desktop-portal-wlr, xdg-desktop-portal-gtk |
| Clipboard | wl-clipboard, wl-clip-persist, cliphist | | Clipboard | wl-clipboard, wl-clip-persist, cliphist |

View file

@ -26,6 +26,9 @@ rootcolor=0x323232ff
# Inactive window border # Inactive window border
bordercolor=0x444444ff bordercolor=0x444444ff
# Drop shadow when dragging windows
dropcolor=0x8FBA7C55
# Active window border # Active window border
focuscolor=0xc66b25ff focuscolor=0xc66b25ff

View file

@ -18,7 +18,7 @@ mangowm supports a variety of layouts that can be assigned per tag.
- `vertical_scroller` - `vertical_scroller`
- `vertical_grid` - `vertical_grid`
- `vertical_deck` - `vertical_deck`
- `tgmix` - `dwindle`
--- ---
@ -83,6 +83,35 @@ default_nmaster=1
--- ---
## Dwindle Layout
The Dwindle layout arranges windows as a binary tree of recursive splits. Each new window splits the focused window's container, producing a spiral-like tiling.
### Configuration
| Setting | Default | Description |
| :--- | :--- | :--- |
| `dwindle_split_ratio` | `0.5` | Ratio used for new splits (`0.05``0.95`). |
| `dwindle_smart_split` | `0` | Pick the split axis from the cursor's position inside the focused window. The new window appears on the cursor's side. |
| `dwindle_hsplit` | `0` | Side-by-side splits: where the new window goes. `0` = follow cursor, `1` = right, `2` = left. |
| `dwindle_vsplit` | `0` | Top/bottom splits: where the new window goes. `0` = follow cursor, `1` = below, `2` = above. |
| `dwindle_preserve_split` | `0` | Keep the sibling's split orientation when a window is closed. |
| `dwindle_smart_resize` | `0` | When dragging to resize, move the split toward the cursor regardless of which side was grabbed. |
| `dwindle_drop_simple_split` | `1` | Drag-to-tile drop preview. `1` = 2-zone preview matching `dwindle_split_ratio`, `0` = 4-quadrant preview. |
```ini
# Example dwindle configuration
dwindle_split_ratio=0.5
dwindle_smart_split=0
dwindle_hsplit=0
dwindle_vsplit=0
dwindle_preserve_split=0
dwindle_smart_resize=0
dwindle_drop_simple_split=1
```
---
## Switching Layouts ## Switching Layouts
You can switch layouts dynamically or set a default for specific tags using [Tag Rules](/docs/window-management/rules#tag-rules). You can switch layouts dynamically or set a default for specific tags using [Tag Rules](/docs/window-management/rules#tag-rules).

View file

@ -1,5 +1,5 @@
project('mango', ['c', 'cpp'], project('mango', ['c', 'cpp'],
version : '0.12.8', version : '0.12.9',
) )
subdir('protocols') subdir('protocols')

View file

@ -510,6 +510,163 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) {
return offset; return offset;
} }
void client_set_drop_area(Client *c) {
bool first_draw = false;
int32_t drop_direction = UNDIR;
if (!c || !c->mon)
return;
if (!c->enable_drop_area_draw && !c->droparea->node.enabled) {
return;
}
if (!c->enable_drop_area_draw && c->droparea->node.enabled) {
wlr_scene_node_lower_to_bottom(&c->droparea->node);
wlr_scene_node_set_enabled(&c->droparea->node, false);
return;
} else if (c->enable_drop_area_draw && !c->droparea->node.enabled) {
wlr_scene_node_raise_to_top(&c->droparea->node);
wlr_scene_node_set_enabled(&c->droparea->node, true);
first_draw = true;
}
int32_t bw = (int32_t)c->bw;
int32_t client_width = c->geom.width - 2 * bw;
int32_t client_height = c->geom.height - 2 * bw;
// 光标在窗口客户区内的相对坐标
double rel_x = cursor->x - c->geom.x - bw;
double rel_y = cursor->y - c->geom.y - bw;
struct wlr_box drop_box;
const Layout *cur_layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag];
bool dwindle_familiar =
cur_layout->id == DWINDLE && config.dwindle_drop_simple_split;
uint32_t nmaster = c->mon->pertag->nmasters[c->mon->pertag->curtag];
bool should_swap =
(cur_layout->id == DECK || cur_layout->id == VERTICAL_DECK ||
cur_layout->id == MONOCLE || cur_layout->id == GRID ||
cur_layout->id == VERTICAL_GRID) ||
((cur_layout->id == TILE || cur_layout->id == VERTICAL_TILE ||
cur_layout->id == CENTER_TILE || cur_layout->id == RIGHT_TILE) &&
nmaster == 1 && c->ismaster);
if (dwindle_familiar) {
bool split_h = c->geom.width >= c->geom.height;
float ratio = config.dwindle_split_ratio;
if (split_h) {
if (rel_x < client_width * 0.5) {
drop_direction = LEFT;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = (int32_t)(client_width * ratio);
drop_box.height = client_height;
} else {
drop_direction = RIGHT;
drop_box.x = bw + (int32_t)(client_width * ratio);
drop_box.y = bw;
drop_box.width = client_width - (int32_t)(client_width * ratio);
drop_box.height = client_height;
}
} else {
if (rel_y < client_height * 0.5) {
drop_direction = UP;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = (int32_t)(client_height * ratio);
} else {
drop_direction = DOWN;
drop_box.x = bw;
drop_box.y = bw + (int32_t)(client_height * ratio);
drop_box.width = client_width;
drop_box.height =
client_height - (int32_t)(client_height * ratio);
}
}
} else if (should_swap) {
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height;
drop_direction = UNDIR;
} else if (cur_layout->id == TILE || cur_layout->id == DECK ||
cur_layout->id == CENTER_TILE || cur_layout->id == RIGHT_TILE) {
if (rel_y < client_height * 0.5) {
drop_direction = UP;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height / 2;
} else {
drop_direction = DOWN;
drop_box.x = bw;
drop_box.y = bw + client_height / 2;
drop_box.width = client_width;
drop_box.height = client_height / 2;
}
} else if (cur_layout->id == VERTICAL_TILE ||
cur_layout->id == VERTICAL_DECK) {
if (rel_x < client_width * 0.5) {
drop_direction = LEFT;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width / 2;
drop_box.height = client_height;
} else {
drop_direction = RIGHT;
drop_box.x = bw + client_width / 2;
drop_box.y = bw;
drop_box.width = client_width / 2;
drop_box.height = client_height;
}
} else {
double dist_left = rel_x;
double dist_right = client_width - rel_x;
double dist_top = rel_y;
double dist_bottom = client_height - rel_y;
if (dist_left <= dist_right && dist_left <= dist_top &&
dist_left <= dist_bottom) {
drop_direction = LEFT;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width / 2;
drop_box.height = client_height;
} else if (dist_right <= dist_top && dist_right <= dist_bottom) {
drop_direction = RIGHT;
drop_box.x = bw + client_width / 2;
drop_box.y = bw;
drop_box.width = client_width / 2;
drop_box.height = client_height;
} else if (dist_top <= dist_bottom) {
drop_direction = UP;
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height / 2;
} else {
drop_direction = DOWN;
drop_box.x = bw;
drop_box.y = bw + client_height / 2;
drop_box.width = client_width;
drop_box.height = client_height / 2;
}
}
if (!first_draw && c->drop_direction == drop_direction) {
return;
}
c->drop_direction = drop_direction;
wlr_scene_node_set_position(&c->droparea->node, drop_box.x, drop_box.y);
wlr_scene_rect_set_size(c->droparea, drop_box.width, drop_box.height);
}
void client_apply_clip(Client *c, float factor) { void client_apply_clip(Client *c, float factor) {
if (c->iskilling || !client_surface(c)->mapped) if (c->iskilling || !client_surface(c)->mapped)

View file

@ -312,11 +312,6 @@ static inline uint32_t client_set_size(Client *c, uint32_t width,
int32_t width = c->geom.width - 2 * c->bw; int32_t width = c->geom.width - 2 * c->bw;
int32_t height = c->geom.height - 2 * c->bw; int32_t height = c->geom.height - 2 * c->bw;
if (c->mon && c->mon->isoverview && size_hints &&
c->geom.width - 2 * (int32_t)c->bw < size_hints->min_width &&
c->geom.height - 2 * (int32_t)c->bw < size_hints->min_height)
return 0;
if (size_hints && if (size_hints &&
c->geom.width - 2 * (int32_t)c->bw < size_hints->min_width) c->geom.width - 2 * (int32_t)c->bw < size_hints->min_width)
width = size_hints->min_width; width = size_hints->min_width;

View file

@ -226,6 +226,7 @@ typedef struct {
int32_t snap_distance; int32_t snap_distance;
int32_t enable_floating_snap; int32_t enable_floating_snap;
int32_t drag_tile_to_tile; int32_t drag_tile_to_tile;
int32_t drag_tile_small;
uint32_t swipe_min_threshold; uint32_t swipe_min_threshold;
float focused_opacity; float focused_opacity;
float unfocused_opacity; float unfocused_opacity;
@ -241,6 +242,15 @@ typedef struct {
int32_t center_master_overspread; int32_t center_master_overspread;
int32_t center_when_single_stack; int32_t center_when_single_stack;
/* dwindle layout */
int32_t dwindle_vsplit;
int32_t dwindle_hsplit;
int32_t dwindle_preserve_split;
int32_t dwindle_smart_split;
int32_t dwindle_smart_resize;
int32_t dwindle_drop_simple_split;
float dwindle_split_ratio;
uint32_t hotarea_size; uint32_t hotarea_size;
uint32_t hotarea_corner; uint32_t hotarea_corner;
uint32_t enable_hotarea; uint32_t enable_hotarea;
@ -262,26 +272,33 @@ typedef struct {
int32_t repeat_delay; int32_t repeat_delay;
uint32_t numlockon; uint32_t numlockon;
/* Trackpad */ /* common pointer */
int32_t disable_trackpad;
int32_t tap_to_click;
int32_t tap_and_drag;
int32_t drag_lock;
int32_t mouse_natural_scrolling;
int32_t trackpad_natural_scrolling;
int32_t disable_while_typing; int32_t disable_while_typing;
int32_t left_handed; int32_t left_handed;
int32_t middle_button_emulation; int32_t middle_button_emulation;
uint32_t accel_profile;
double accel_speed;
uint32_t scroll_method; uint32_t scroll_method;
uint32_t scroll_button; uint32_t scroll_button;
uint32_t click_method; uint32_t click_method;
uint32_t send_events_mode; uint32_t send_events_mode;
uint32_t button_map;
/* mouse */
int32_t mouse_natural_scrolling;
uint32_t mouse_accel_profile;
double mouse_accel_speed;
double axis_scroll_factor; double axis_scroll_factor;
/* Trackpad */
int32_t trackpad_natural_scrolling;
uint32_t trackpad_accel_profile;
double trackpad_accel_speed;
double trackpad_scroll_factor;
int32_t disable_trackpad;
int32_t tap_to_click;
int32_t tap_and_drag;
int32_t drag_lock;
uint32_t button_map;
/* window effects */
int32_t blur; int32_t blur;
int32_t blur_layer; int32_t blur_layer;
int32_t blur_optimized; int32_t blur_optimized;
@ -297,6 +314,7 @@ typedef struct {
int32_t shadows_position_y; int32_t shadows_position_y;
float shadowscolor[4]; float shadowscolor[4];
/* appearance */
int32_t smartgaps; int32_t smartgaps;
uint32_t gappih; uint32_t gappih;
uint32_t gappiv; uint32_t gappiv;
@ -307,6 +325,7 @@ typedef struct {
float scratchpad_height_ratio; float scratchpad_height_ratio;
float rootcolor[4]; float rootcolor[4];
float bordercolor[4]; float bordercolor[4];
float dropcolor[4];
float focuscolor[4]; float focuscolor[4];
float maximizescreencolor[4]; float maximizescreencolor[4];
float urgentcolor[4]; float urgentcolor[4];
@ -1440,6 +1459,8 @@ bool parse_option(Config *config, char *key, char *value) {
config->enable_floating_snap = atoi(value); config->enable_floating_snap = atoi(value);
} else if (strcmp(key, "drag_tile_to_tile") == 0) { } else if (strcmp(key, "drag_tile_to_tile") == 0) {
config->drag_tile_to_tile = atoi(value); config->drag_tile_to_tile = atoi(value);
} else if (strcmp(key, "drag_tile_small") == 0) {
config->drag_tile_small = atoi(value);
} else if (strcmp(key, "swipe_min_threshold") == 0) { } else if (strcmp(key, "swipe_min_threshold") == 0) {
config->swipe_min_threshold = atoi(value); config->swipe_min_threshold = atoi(value);
} else if (strcmp(key, "focused_opacity") == 0) { } else if (strcmp(key, "focused_opacity") == 0) {
@ -1607,6 +1628,20 @@ bool parse_option(Config *config, char *key, char *value) {
config->center_master_overspread = atoi(value); config->center_master_overspread = atoi(value);
} else if (strcmp(key, "center_when_single_stack") == 0) { } else if (strcmp(key, "center_when_single_stack") == 0) {
config->center_when_single_stack = atoi(value); config->center_when_single_stack = atoi(value);
} else if (strcmp(key, "dwindle_vsplit") == 0) {
config->dwindle_vsplit = atoi(value);
} else if (strcmp(key, "dwindle_hsplit") == 0) {
config->dwindle_hsplit = atoi(value);
} else if (strcmp(key, "dwindle_preserve_split") == 0) {
config->dwindle_preserve_split = atoi(value);
} else if (strcmp(key, "dwindle_smart_split") == 0) {
config->dwindle_smart_split = atoi(value);
} else if (strcmp(key, "dwindle_smart_resize") == 0) {
config->dwindle_smart_resize = atoi(value);
} else if (strcmp(key, "dwindle_drop_simple_split") == 0) {
config->dwindle_drop_simple_split = atoi(value);
} else if (strcmp(key, "dwindle_split_ratio") == 0) {
config->dwindle_split_ratio = atof(value);
} else if (strcmp(key, "hotarea_size") == 0) { } else if (strcmp(key, "hotarea_size") == 0) {
config->hotarea_size = atoi(value); config->hotarea_size = atoi(value);
} else if (strcmp(key, "hotarea_corner") == 0) { } else if (strcmp(key, "hotarea_corner") == 0) {
@ -1665,10 +1700,14 @@ bool parse_option(Config *config, char *key, char *value) {
config->left_handed = atoi(value); config->left_handed = atoi(value);
} else if (strcmp(key, "middle_button_emulation") == 0) { } else if (strcmp(key, "middle_button_emulation") == 0) {
config->middle_button_emulation = atoi(value); config->middle_button_emulation = atoi(value);
} else if (strcmp(key, "accel_profile") == 0) { } else if (strcmp(key, "mouse_accel_profile") == 0) {
config->accel_profile = atoi(value); config->mouse_accel_profile = atoi(value);
} else if (strcmp(key, "accel_speed") == 0) { } else if (strcmp(key, "mouse_accel_speed") == 0) {
config->accel_speed = atof(value); config->mouse_accel_speed = atof(value);
} else if (strcmp(key, "trackpad_accel_profile") == 0) {
config->trackpad_accel_profile = atoi(value);
} else if (strcmp(key, "trackpad_accel_speed") == 0) {
config->trackpad_accel_speed = atof(value);
} else if (strcmp(key, "scroll_method") == 0) { } else if (strcmp(key, "scroll_method") == 0) {
config->scroll_method = atoi(value); config->scroll_method = atoi(value);
} else if (strcmp(key, "scroll_button") == 0) { } else if (strcmp(key, "scroll_button") == 0) {
@ -1681,6 +1720,8 @@ bool parse_option(Config *config, char *key, char *value) {
config->button_map = atoi(value); config->button_map = atoi(value);
} else if (strcmp(key, "axis_scroll_factor") == 0) { } else if (strcmp(key, "axis_scroll_factor") == 0) {
config->axis_scroll_factor = atof(value); config->axis_scroll_factor = atof(value);
} else if (strcmp(key, "trackpad_scroll_factor") == 0) {
config->trackpad_scroll_factor = atof(value);
} else if (strcmp(key, "gappih") == 0) { } else if (strcmp(key, "gappih") == 0) {
config->gappih = atoi(value); config->gappih = atoi(value);
} else if (strcmp(key, "gappiv") == 0) { } else if (strcmp(key, "gappiv") == 0) {
@ -1730,6 +1771,17 @@ bool parse_option(Config *config, char *key, char *value) {
} else { } else {
convert_hex_to_rgba(config->bordercolor, color); convert_hex_to_rgba(config->bordercolor, color);
} }
} else if (strcmp(key, "dropcolor") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid dropcolor "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->dropcolor, color);
}
} else if (strcmp(key, "focuscolor") == 0) { } else if (strcmp(key, "focuscolor") == 0) {
int64_t color = parse_color(value); int64_t color = parse_color(value);
if (color == -1) { if (color == -1) {
@ -3154,6 +3206,16 @@ void override_config(void) {
config.center_when_single_stack = config.center_when_single_stack =
CLAMP_INT(config.center_when_single_stack, 0, 1); CLAMP_INT(config.center_when_single_stack, 0, 1);
config.new_is_master = CLAMP_INT(config.new_is_master, 0, 1); config.new_is_master = CLAMP_INT(config.new_is_master, 0, 1);
config.dwindle_vsplit = CLAMP_INT(config.dwindle_vsplit, 0, 2);
config.dwindle_hsplit = CLAMP_INT(config.dwindle_hsplit, 0, 2);
config.dwindle_preserve_split =
CLAMP_INT(config.dwindle_preserve_split, 0, 1);
config.dwindle_smart_split = CLAMP_INT(config.dwindle_smart_split, 0, 1);
config.dwindle_smart_resize = CLAMP_INT(config.dwindle_smart_resize, 0, 1);
config.dwindle_drop_simple_split =
CLAMP_INT(config.dwindle_drop_simple_split, 0, 1);
config.dwindle_split_ratio =
CLAMP_FLOAT(config.dwindle_split_ratio, 0.05f, 0.95f);
config.hotarea_size = CLAMP_INT(config.hotarea_size, 1, 1000); config.hotarea_size = CLAMP_INT(config.hotarea_size, 1, 1000);
config.hotarea_corner = CLAMP_INT(config.hotarea_corner, 0, 3); config.hotarea_corner = CLAMP_INT(config.hotarea_corner, 0, 3);
config.enable_hotarea = CLAMP_INT(config.enable_hotarea, 0, 1); config.enable_hotarea = CLAMP_INT(config.enable_hotarea, 0, 1);
@ -3167,6 +3229,7 @@ void override_config(void) {
config.drag_floating_refresh_interval = config.drag_floating_refresh_interval =
CLAMP_FLOAT(config.drag_floating_refresh_interval, 0.0f, 1000.0f); CLAMP_FLOAT(config.drag_floating_refresh_interval, 0.0f, 1000.0f);
config.drag_tile_to_tile = CLAMP_INT(config.drag_tile_to_tile, 0, 1); config.drag_tile_to_tile = CLAMP_INT(config.drag_tile_to_tile, 0, 1);
config.drag_tile_small = CLAMP_INT(config.drag_tile_small, 0, 1);
config.allow_tearing = CLAMP_INT(config.allow_tearing, 0, 2); config.allow_tearing = CLAMP_INT(config.allow_tearing, 0, 2);
config.allow_shortcuts_inhibit = config.allow_shortcuts_inhibit =
CLAMP_INT(config.allow_shortcuts_inhibit, 0, 1); CLAMP_INT(config.allow_shortcuts_inhibit, 0, 1);
@ -3214,8 +3277,13 @@ void override_config(void) {
config.swipe_min_threshold = CLAMP_INT(config.swipe_min_threshold, 1, 1000); config.swipe_min_threshold = CLAMP_INT(config.swipe_min_threshold, 1, 1000);
config.mouse_natural_scrolling = config.mouse_natural_scrolling =
CLAMP_INT(config.mouse_natural_scrolling, 0, 1); CLAMP_INT(config.mouse_natural_scrolling, 0, 1);
config.accel_profile = CLAMP_INT(config.accel_profile, 0, 2); config.mouse_accel_profile = CLAMP_INT(config.mouse_accel_profile, 0, 2);
config.accel_speed = CLAMP_FLOAT(config.accel_speed, -1.0f, 1.0f); config.mouse_accel_speed =
CLAMP_FLOAT(config.mouse_accel_speed, -1.0f, 1.0f);
config.trackpad_accel_profile =
CLAMP_INT(config.trackpad_accel_profile, 0, 2);
config.trackpad_accel_speed =
CLAMP_FLOAT(config.trackpad_accel_speed, -1.0f, 1.0f);
config.scroll_method = CLAMP_INT(config.scroll_method, 0, 4); config.scroll_method = CLAMP_INT(config.scroll_method, 0, 4);
config.scroll_button = CLAMP_INT(config.scroll_button, 272, 279); config.scroll_button = CLAMP_INT(config.scroll_button, 272, 279);
config.click_method = CLAMP_INT(config.click_method, 0, 2); config.click_method = CLAMP_INT(config.click_method, 0, 2);
@ -3223,6 +3291,8 @@ void override_config(void) {
config.button_map = CLAMP_INT(config.button_map, 0, 1); config.button_map = CLAMP_INT(config.button_map, 0, 1);
config.axis_scroll_factor = config.axis_scroll_factor =
CLAMP_FLOAT(config.axis_scroll_factor, 0.1f, 10.0f); CLAMP_FLOAT(config.axis_scroll_factor, 0.1f, 10.0f);
config.trackpad_scroll_factor =
CLAMP_FLOAT(config.trackpad_scroll_factor, 0.1f, 10.0f);
config.gappih = CLAMP_INT(config.gappih, 0, 1000); config.gappih = CLAMP_INT(config.gappih, 0, 1000);
config.gappiv = CLAMP_INT(config.gappiv, 0, 1000); config.gappiv = CLAMP_INT(config.gappiv, 0, 1000);
config.gappoh = CLAMP_INT(config.gappoh, 0, 1000); config.gappoh = CLAMP_INT(config.gappoh, 0, 1000);
@ -3285,6 +3355,14 @@ void set_value_default() {
config.center_master_overspread = 0; config.center_master_overspread = 0;
config.center_when_single_stack = 1; config.center_when_single_stack = 1;
config.dwindle_vsplit = 0;
config.dwindle_hsplit = 0;
config.dwindle_preserve_split = 0;
config.dwindle_smart_split = 0;
config.dwindle_smart_resize = 0;
config.dwindle_drop_simple_split = 1;
config.dwindle_split_ratio = 0.5f;
config.log_level = WLR_ERROR; config.log_level = WLR_ERROR;
config.numlockon = 0; config.numlockon = 0;
config.capslock = 0; config.capslock = 0;
@ -3315,6 +3393,7 @@ void set_value_default() {
config.scratchpad_cross_monitor = 0; config.scratchpad_cross_monitor = 0;
config.focus_cross_tag = 0; config.focus_cross_tag = 0;
config.axis_scroll_factor = 1.0; config.axis_scroll_factor = 1.0;
config.trackpad_scroll_factor = 1.0;
config.view_current_to_back = 0; config.view_current_to_back = 0;
config.single_scratchpad = 1; config.single_scratchpad = 1;
config.xwayland_persistence = 1; config.xwayland_persistence = 1;
@ -3328,6 +3407,7 @@ void set_value_default() {
config.no_radius_when_single = 0; config.no_radius_when_single = 0;
config.snap_distance = 30; config.snap_distance = 30;
config.drag_tile_to_tile = 0; config.drag_tile_to_tile = 0;
config.drag_tile_small = 1;
config.enable_floating_snap = 0; config.enable_floating_snap = 0;
config.swipe_min_threshold = 1; config.swipe_min_threshold = 1;
@ -3355,8 +3435,10 @@ void set_value_default() {
config.disable_while_typing = 1; config.disable_while_typing = 1;
config.left_handed = 0; config.left_handed = 0;
config.middle_button_emulation = 0; config.middle_button_emulation = 0;
config.accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; config.mouse_accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
config.accel_speed = 0.0; config.mouse_accel_speed = 0.0;
config.trackpad_accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
config.trackpad_accel_speed = 0.0;
config.scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; config.scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
config.scroll_button = 274; config.scroll_button = 274;
config.click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; config.click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
@ -3426,6 +3508,10 @@ void set_value_default() {
config.bordercolor[1] = 0x44 / 255.0f; config.bordercolor[1] = 0x44 / 255.0f;
config.bordercolor[2] = 0x44 / 255.0f; config.bordercolor[2] = 0x44 / 255.0f;
config.bordercolor[3] = 1.0f; config.bordercolor[3] = 1.0f;
config.dropcolor[0] = 0x8f / 255.0f;
config.dropcolor[1] = 0xba / 255.0f;
config.dropcolor[2] = 0x7c / 255.0f;
config.dropcolor[3] = 0.5f;
config.focuscolor[0] = 0xc6 / 255.0f; config.focuscolor[0] = 0xc6 / 255.0f;
config.focuscolor[1] = 0x6b / 255.0f; config.focuscolor[1] = 0x6b / 255.0f;
config.focuscolor[2] = 0x25 / 255.0f; config.focuscolor[2] = 0x25 / 255.0f;
@ -3675,7 +3761,7 @@ void reapply_rootbg(void) {
wlr_scene_rect_set_color(root_bg, config.rootcolor); wlr_scene_rect_set_color(root_bg, config.rootcolor);
} }
void reapply_border(void) { void reapply_property(void) {
Client *c = NULL; Client *c = NULL;
// reset border width when config change // reset border width when config change
@ -3684,6 +3770,8 @@ void reapply_border(void) {
if (!c->isnoborder && !c->isfullscreen) { if (!c->isnoborder && !c->isfullscreen) {
c->bw = config.borderpx; c->bw = config.borderpx;
} }
wlr_scene_rect_set_color(c->droparea, config.dropcolor);
} }
} }
} }
@ -3829,7 +3917,7 @@ void reset_option(void) {
run_exec(); run_exec();
reapply_cursor_style(); reapply_cursor_style();
reapply_border(); reapply_property();
reapply_rootbg(); reapply_rootbg();
reapply_keyboard(); reapply_keyboard();
reapply_pointer(); reapply_pointer();

View file

@ -112,6 +112,10 @@ int32_t exchange_client(const Arg *arg) {
Client *tc = direction_select(arg); Client *tc = direction_select(arg);
tc = get_focused_stack_client(tc); tc = get_focused_stack_client(tc);
if (!tc)
return 0;
exchange_two_client(c, tc); exchange_two_client(c, tc);
return 0; return 0;
} }
@ -378,11 +382,20 @@ int32_t moveresize(const Arg *arg) {
grabc->drag_to_tile = true; grabc->drag_to_tile = true;
exit_scroller_stack(grabc); exit_scroller_stack(grabc);
setfloating(grabc, 1); setfloating(grabc, 1);
grabc->drag_tile_float_backup_geom = grabc->float_geom;
grabc->old_stack_inner_per = 0.0f; grabc->old_stack_inner_per = 0.0f;
grabc->old_master_inner_per = 0.0f; grabc->old_master_inner_per = 0.0f;
set_size_per(grabc->mon, grabc); set_size_per(grabc->mon, grabc);
} }
if (grabc && grabc->drag_to_tile && config.drag_tile_small) {
grabc->geom.x = cursor->x - 150;
grabc->geom.y = cursor->y - 150;
grabc->geom.width = 300;
grabc->geom.height = 300;
resize(grabc, grabc->geom, 1);
}
switch (cursor_mode = arg->ui) { switch (cursor_mode = arg->ui) {
case CurMove: case CurMove:
@ -1124,7 +1137,7 @@ 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, *oldmon = NULL;
if (!selmon) if (!selmon)
return 0; return 0;
Client *c = focustop(selmon); Client *c = focustop(selmon);
@ -1132,6 +1145,8 @@ int32_t tagmon(const Arg *arg) {
if (!c) if (!c)
return 0; return 0;
oldmon = c->mon;
if (arg->i != UNDIR) { if (arg->i != UNDIR) {
m = dirtomon(arg->i); m = dirtomon(arg->i);
} else if (arg->v) { } else if (arg->v) {
@ -1166,12 +1181,12 @@ int32_t tagmon(const Arg *arg) {
setmon(c, m, newtags, true); setmon(c, m, newtags, true);
client_update_oldmonname_record(c, m); client_update_oldmonname_record(c, m);
reset_foreign_tolevel(c); reset_foreign_tolevel(c, oldmon, c->mon);
c->float_geom.width = c->float_geom.width =
(int32_t)(c->float_geom.width * c->mon->w.width / selmon->w.width); (int32_t)(c->float_geom.width * c->mon->w.width / oldmon->w.width);
c->float_geom.height = c->float_geom.height =
(int32_t)(c->float_geom.height * c->mon->w.height / selmon->w.height); (int32_t)(c->float_geom.height * c->mon->w.height / oldmon->w.height);
selmon = c->mon; selmon = c->mon;
c->float_geom = setclient_coordinate_center(c, c->mon, c->float_geom, 0, 0); c->float_geom = setclient_coordinate_center(c, c->mon, c->float_geom, 0, 0);
@ -1787,21 +1802,15 @@ int32_t toggle_monitor(const Arg *arg) {
return 0; return 0;
} }
int32_t scroller_stack(const Arg *arg) { int32_t scroller_apply_stack(Client *c, Client *target_client,
if (!selmon) int32_t direction) {
return 0;
Client *c = selmon->sel;
Client *stack_head = NULL;
Client *source_stack_head = NULL;
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
return 0;
Client *source_stack_head = NULL;
Client *stack_head = NULL;
bool is_horizontal_layout = bool is_horizontal_layout =
c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true
: false; : false;
Client *target_client = find_client_by_direction(c, arg, false, true);
if (target_client) { if (target_client) {
stack_head = get_scroll_stack_head(target_client); stack_head = get_scroll_stack_head(target_client);
} }
@ -1820,32 +1829,32 @@ int32_t scroller_stack(const Arg *arg) {
setmaximizescreen(c, 0); setmaximizescreen(c, 0);
} }
if (c->prev_in_stack) { if (c->prev_in_stack && direction != UNDIR) {
if ((is_horizontal_layout && arg->i == LEFT) || if ((is_horizontal_layout && direction == LEFT) ||
(!is_horizontal_layout && arg->i == UP)) { (!is_horizontal_layout && direction == UP)) {
exit_scroller_stack(c); exit_scroller_stack(c);
wl_list_remove(&c->link); wl_list_remove(&c->link);
wl_list_insert(source_stack_head->link.prev, &c->link); wl_list_insert(source_stack_head->link.prev, &c->link);
arrange(selmon, false, false); arrange(selmon, false, false);
} else if ((is_horizontal_layout && arg->i == RIGHT) || } else if ((is_horizontal_layout && direction == RIGHT) ||
(!is_horizontal_layout && arg->i == DOWN)) { (!is_horizontal_layout && direction == DOWN)) {
exit_scroller_stack(c); exit_scroller_stack(c);
wl_list_remove(&c->link); wl_list_remove(&c->link);
wl_list_insert(&source_stack_head->link, &c->link); wl_list_insert(&source_stack_head->link, &c->link);
arrange(selmon, false, false); arrange(selmon, false, false);
} }
return 0; return 0;
} else if (c->next_in_stack) { } else if (c->next_in_stack && direction != UNDIR) {
Client *next_in_stack = c->next_in_stack; Client *next_in_stack = c->next_in_stack;
if ((is_horizontal_layout && arg->i == LEFT) || if ((is_horizontal_layout && direction == LEFT) ||
(!is_horizontal_layout && arg->i == UP)) { (!is_horizontal_layout && direction == UP)) {
exit_scroller_stack(c); exit_scroller_stack(c);
wl_list_remove(&c->link); wl_list_remove(&c->link);
wl_list_insert(next_in_stack->link.prev, &c->link); wl_list_insert(next_in_stack->link.prev, &c->link);
arrange(selmon, false, false); arrange(selmon, false, false);
} else if ((is_horizontal_layout && arg->i == RIGHT) || } else if ((is_horizontal_layout && direction == RIGHT) ||
(!is_horizontal_layout && arg->i == DOWN)) { (!is_horizontal_layout && direction == DOWN)) {
exit_scroller_stack(c); exit_scroller_stack(c);
wl_list_remove(&c->link); wl_list_remove(&c->link);
wl_list_insert(&next_in_stack->link, &c->link); wl_list_insert(&next_in_stack->link, &c->link);
@ -1856,37 +1865,31 @@ int32_t scroller_stack(const Arg *arg) {
if (!target_client || target_client->mon != c->mon) { if (!target_client || target_client->mon != c->mon) {
return 0; return 0;
} else {
c->isglobal = target_client->isglobal = 0;
c->isunglobal = target_client->isglobal = 0;
c->tags = target_client->tags = get_tags_first_tag(target_client->tags);
} }
exit_scroller_stack(c);
// Find the tail of target_client's stack // Find the tail of target_client's stack
Client *stack_tail = target_client; Client *stack_tail = target_client;
while (stack_tail->next_in_stack) { while (stack_tail->next_in_stack) {
stack_tail = stack_tail->next_in_stack; stack_tail = stack_tail->next_in_stack;
} }
// Add c to the stack scroller_insert_stack(c, stack_tail, false);
stack_tail->next_in_stack = c;
c->prev_in_stack = stack_tail;
c->next_in_stack = NULL;
if (stack_head->ismaximizescreen) {
setmaximizescreen(stack_head, 0);
}
if (stack_head->isfullscreen) {
setfullscreen(stack_head, 0);
}
arrange(selmon, false, false);
return 0; return 0;
} }
int32_t scroller_stack(const Arg *arg) {
if (!selmon)
return 0;
Client *c = selmon->sel;
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
return 0;
Client *target_client = find_client_by_direction(c, arg, false, true);
return scroller_apply_stack(c, target_client, arg->i);
}
int32_t toggle_all_floating(const Arg *arg) { int32_t toggle_all_floating(const Arg *arg) {
if (!selmon || !selmon->sel) if (!selmon || !selmon->sel)
return 0; return 0;

View file

@ -7,15 +7,11 @@
typedef struct Monitor Monitor; typedef struct Monitor Monitor;
struct workspace { struct workspace {
struct wl_list link; // Link in global workspaces list struct wl_list link;
uint32_t tag; // Numeric identifier (1-9, 0=overview) uint32_t tag;
Monitor *m; // Associated monitor Monitor *m;
struct wlr_ext_workspace_handle_v1 *ext_workspace; // Protocol object struct wlr_ext_workspace_handle_v1 *ext_workspace;
/* Event listeners */ struct wl_listener commit;
struct wl_listener activate;
struct wl_listener deactivate;
struct wl_listener assign;
struct wl_listener remove;
}; };
struct wlr_ext_workspace_manager_v1 *ext_manager; struct wlr_ext_workspace_manager_v1 *ext_manager;
@ -43,30 +39,60 @@ void toggle_workspace(struct workspace *target) {
} }
} }
static void handle_ext_workspace_activate(struct wl_listener *listener, static void handle_ext_commit(struct wl_listener *listener, void *data) {
void *data) { struct wlr_ext_workspace_v1_commit_event *event = data;
struct workspace *workspace = struct wlr_ext_workspace_v1_request *request;
wl_container_of(listener, workspace, activate);
if (workspace->m->isoverview) { wl_list_for_each(request, event->requests, link) {
return; switch (request->type) {
case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: {
if (!request->activate.workspace) {
break;
}
struct workspace *workspace = NULL;
struct workspace *w;
wl_list_for_each(w, &workspaces, link) {
if (w->ext_workspace == request->activate.workspace) {
workspace = w;
break;
}
}
if (!workspace || workspace->m->isoverview) {
break;
}
goto_workspace(workspace);
wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag);
break;
}
case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE: {
if (!request->deactivate.workspace) {
break;
}
struct workspace *workspace = NULL;
struct workspace *w;
wl_list_for_each(w, &workspaces, link) {
if (w->ext_workspace == request->deactivate.workspace) {
workspace = w;
break;
}
}
if (!workspace || workspace->m->isoverview) {
break;
}
toggle_workspace(workspace);
wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag);
break;
}
default:
break;
}
} }
goto_workspace(workspace);
wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag);
}
static void handle_ext_workspace_deactivate(struct wl_listener *listener,
void *data) {
struct workspace *workspace =
wl_container_of(listener, workspace, deactivate);
if (workspace->m->isoverview) {
return;
}
toggle_workspace(workspace);
wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag);
} }
static const char *get_name_from_tag(uint32_t tag) { static const char *get_name_from_tag(uint32_t tag) {
@ -76,8 +102,6 @@ static const char *get_name_from_tag(uint32_t tag) {
} }
void destroy_workspace(struct workspace *workspace) { void destroy_workspace(struct workspace *workspace) {
wl_list_remove(&workspace->activate.link);
wl_list_remove(&workspace->deactivate.link);
wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace); wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace);
wl_list_remove(&workspace->link); wl_list_remove(&workspace->link);
free(workspace); free(workspace);
@ -112,17 +136,12 @@ static void add_workspace_by_tag(int32_t tag, Monitor *m) {
workspace->m = m; workspace->m = m;
workspace->ext_workspace = wlr_ext_workspace_handle_v1_create( workspace->ext_workspace = wlr_ext_workspace_handle_v1_create(
ext_manager, name, EXT_WORKSPACE_ENABLE_CAPS); ext_manager, name, EXT_WORKSPACE_ENABLE_CAPS);
workspace->ext_workspace->data = workspace;
wlr_ext_workspace_handle_v1_set_group(workspace->ext_workspace, wlr_ext_workspace_handle_v1_set_group(workspace->ext_workspace,
m->ext_group); m->ext_group);
wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name); wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name);
workspace->activate.notify = handle_ext_workspace_activate;
wl_signal_add(&workspace->ext_workspace->events.activate,
&workspace->activate);
workspace->deactivate.notify = handle_ext_workspace_deactivate;
wl_signal_add(&workspace->ext_workspace->events.deactivate,
&workspace->deactivate);
} }
void dwl_ext_workspace_printstatus(Monitor *m) { void dwl_ext_workspace_printstatus(Monitor *m) {
@ -180,8 +199,11 @@ void refresh_monitors_workspaces_status(Monitor *m) {
} }
void workspaces_init() { void workspaces_init() {
/* Create the global workspace manager with activation capability */
ext_manager = wlr_ext_workspace_manager_v1_create(dpy, 1); ext_manager = wlr_ext_workspace_manager_v1_create(dpy, 1);
/* Initialize the global workspaces list */
wl_list_init(&workspaces); wl_list_init(&workspaces);
static struct wl_listener commit_listener;
commit_listener.notify = handle_ext_commit;
wl_signal_add(&ext_manager->events.commit, &commit_listener);
} }

View file

@ -6,7 +6,7 @@ void handle_foreign_activate_request(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_activate_request); Client *c = wl_container_of(listener, c, foreign_activate_request);
uint32_t target; uint32_t target;
if (c->swallowing) if (c->swallowing || !c->mon)
return; return;
if (c->isminimized) { if (c->isminimized) {
@ -28,7 +28,7 @@ void handle_foreign_maximize_request(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_maximize_request); Client *c = wl_container_of(listener, c, foreign_maximize_request);
struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
if (c->swallowing) if (c->swallowing || !c->mon)
return; return;
if (c->ismaximizescreen && !event->maximized) { if (c->ismaximizescreen && !event->maximized) {
@ -46,7 +46,7 @@ void handle_foreign_minimize_request(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, foreign_minimize_request); Client *c = wl_container_of(listener, c, foreign_minimize_request);
struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data;
if (c->swallowing) if (c->swallowing || !c->mon)
return; return;
if (!c->isminimized && event->minimized) { if (!c->isminimized && event->minimized) {
@ -71,7 +71,7 @@ void handle_foreign_fullscreen_request(struct wl_listener *listener,
Client *c = wl_container_of(listener, c, foreign_fullscreen_request); Client *c = wl_container_of(listener, c, foreign_fullscreen_request);
struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
if (c->swallowing) if (c->swallowing || !c->mon)
return; return;
if (c->isfullscreen && !event->fullscreen) { if (c->isfullscreen && !event->fullscreen) {
@ -98,10 +98,6 @@ void handle_foreign_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&c->foreign_fullscreen_request.link); wl_list_remove(&c->foreign_fullscreen_request.link);
wl_list_remove(&c->foreign_close_request.link); wl_list_remove(&c->foreign_close_request.link);
wl_list_remove(&c->foreign_destroy.link); wl_list_remove(&c->foreign_destroy.link);
}
void remove_foreign_topleve(Client *c) {
wlr_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel);
c->foreign_toplevel = NULL; c->foreign_toplevel = NULL;
} }
@ -144,7 +140,23 @@ void add_foreign_toplevel(Client *c) {
} }
} }
void reset_foreign_tolevel(Client *c) { void reset_foreign_tolevel(Client *c, Monitor *oldmon, Monitor *newmon) {
remove_foreign_topleve(c); if (!c)
add_foreign_toplevel(c); return;
if (!c->foreign_toplevel) {
add_foreign_toplevel(c);
return;
}
if (oldmon == newmon)
return;
if (oldmon)
wlr_foreign_toplevel_handle_v1_output_leave(c->foreign_toplevel,
oldmon->wlr_output);
if (newmon)
wlr_foreign_toplevel_handle_v1_output_enter(c->foreign_toplevel,
newmon->wlr_output);
} }

View file

@ -1,7 +1,3 @@
// bash on: https://gitlab.freedesktop.org/tokyo4j/wlroots/-/tree/ext-workspace
// TODO: remove this file
// refer: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5115
#include "wlr_ext_workspace_v1.h" #include "wlr_ext_workspace_v1.h"
#include "ext-workspace-v1-protocol.h" #include "ext-workspace-v1-protocol.h"
#include <assert.h> #include <assert.h>
@ -11,27 +7,6 @@
#define EXT_WORKSPACE_V1_VERSION 1 #define EXT_WORKSPACE_V1_VERSION 1
enum wlr_ext_workspace_v1_request_type {
WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE,
WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN,
WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE,
};
struct wlr_ext_workspace_v1_request {
enum wlr_ext_workspace_v1_request_type type;
// CREATE_WORKSPACE
char *name;
// CREATE_WORKSPACE / ASSIGN
struct wlr_ext_workspace_group_handle_v1 *group;
// ACTIVATE / DEACTIVATE / ASSIGN / REMOVE
struct wlr_ext_workspace_handle_v1 *workspace;
struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests
};
struct wlr_ext_workspace_v1_group_output { struct wlr_ext_workspace_v1_group_output {
struct wlr_output *output; struct wlr_output *output;
struct wlr_ext_workspace_group_handle_v1 *group; struct wlr_ext_workspace_group_handle_v1 *group;
@ -117,7 +92,7 @@ static void workspace_handle_activate(struct wl_client *client,
return; return;
} }
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE; req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE;
req->workspace = workspace_res->workspace; req->activate.workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link); wl_list_insert(workspace_res->manager->requests.prev, &req->link);
} }
@ -136,7 +111,7 @@ workspace_handle_deactivate(struct wl_client *client,
return; return;
} }
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE; req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE;
req->workspace = workspace_res->workspace; req->deactivate.workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link); wl_list_insert(workspace_res->manager->requests.prev, &req->link);
} }
@ -157,8 +132,8 @@ static void workspace_handle_assign(struct wl_client *client,
return; return;
} }
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN; req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN;
req->group = group_res->group; req->assign.group = group_res->group;
req->workspace = workspace_res->workspace; req->assign.workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link); wl_list_insert(workspace_res->manager->requests.prev, &req->link);
} }
@ -176,7 +151,7 @@ static void workspace_handle_remove(struct wl_client *client,
return; return;
} }
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE; req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE;
req->workspace = workspace_res->workspace; req->remove.workspace = workspace_res->workspace;
wl_list_insert(workspace_res->manager->requests.prev, &req->link); wl_list_insert(workspace_res->manager->requests.prev, &req->link);
} }
@ -202,14 +177,14 @@ static void group_handle_create_workspace(struct wl_client *client,
wl_resource_post_no_memory(group_resource); wl_resource_post_no_memory(group_resource);
return; return;
} }
req->name = strdup(name); req->create_workspace.name = strdup(name);
if (!req->name) { if (!req->create_workspace.name) {
free(req); free(req);
wl_resource_post_no_memory(group_resource); wl_resource_post_no_memory(group_resource);
return; return;
} }
req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE; req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE;
req->group = group_res->group; req->create_workspace.group = group_res->group;
wl_list_insert(group_res->manager->requests.prev, &req->link); wl_list_insert(group_res->manager->requests.prev, &req->link);
} }
@ -313,10 +288,55 @@ static struct wlr_ext_workspace_group_v1_resource *create_group_resource(
return group_res; return group_res;
} }
static void destroy_request(struct wlr_ext_workspace_v1_request *req) { static void
wl_list_remove(&req->link); destroy_requests(struct wlr_ext_workspace_manager_v1_resource *manager_res) {
free(req->name); struct wlr_ext_workspace_v1_request *req, *tmp;
free(req); wl_list_for_each_safe(req, tmp, &manager_res->requests, link) {
if (req->type == WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE) {
free(req->create_workspace.name);
}
wl_list_remove(&req->link);
free(req);
}
}
static void
clear_requests_by(struct wlr_ext_workspace_manager_v1_resource *manager_res,
struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_ext_workspace_handle_v1 *workspace) {
struct wlr_ext_workspace_v1_request *req, *tmp;
wl_list_for_each_safe(req, tmp, &manager_res->requests, link) {
switch (req->type) {
case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE:
if (group && req->create_workspace.group == group) {
req->create_workspace.group = NULL;
}
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE:
if (workspace && req->activate.workspace == workspace) {
req->activate.workspace = NULL;
}
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE:
if (workspace && req->deactivate.workspace == workspace) {
req->deactivate.workspace = NULL;
}
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN:
if (workspace && req->assign.workspace == workspace) {
req->assign.workspace = NULL;
}
if (group && req->assign.group == group) {
req->assign.group = NULL;
}
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE:
if (workspace && req->remove.workspace == workspace) {
req->remove.workspace = NULL;
}
break;
}
}
} }
static void manager_handle_commit(struct wl_client *client, static void manager_handle_commit(struct wl_client *client,
@ -327,32 +347,11 @@ static void manager_handle_commit(struct wl_client *client,
return; return;
} }
struct wlr_ext_workspace_v1_request *req, *tmp; struct wlr_ext_workspace_v1_commit_event commit_event = {
wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { .requests = &manager_res->requests,
switch (req->type) { };
case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE:; wl_signal_emit_mutable(&manager_res->manager->events.commit, &commit_event);
struct wlr_ext_workspace_group_handle_v1_create_workspace_event destroy_requests(manager_res);
event = {
.name = req->name,
};
wl_signal_emit_mutable(&req->group->events.create_workspace,
&event);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE:
wl_signal_emit_mutable(&req->workspace->events.activate, NULL);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE:
wl_signal_emit_mutable(&req->workspace->events.deactivate, NULL);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN:
wl_signal_emit_mutable(&req->workspace->events.assign, req->group);
break;
case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE:
wl_signal_emit_mutable(&req->workspace->events.remove, NULL);
break;
}
destroy_request(req);
}
} }
static void handle_idle(void *data) { static void handle_idle(void *data) {
@ -406,10 +405,8 @@ static const struct ext_workspace_manager_v1_interface manager_impl = {
static void destroy_manager_resource( static void destroy_manager_resource(
struct wlr_ext_workspace_manager_v1_resource *manager_res) { struct wlr_ext_workspace_manager_v1_resource *manager_res) {
struct wlr_ext_workspace_v1_request *req, *tmp; destroy_requests(manager_res);
wl_list_for_each_safe(req, tmp, &manager_res->requests, link) {
destroy_request(req);
}
struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2; struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2;
wl_list_for_each_safe(workspace_res, tmp2, wl_list_for_each_safe(workspace_res, tmp2,
&manager_res->workspace_resources, &manager_res->workspace_resources,
@ -531,6 +528,7 @@ static void manager_handle_display_destroy(struct wl_listener *listener,
wl_container_of(listener, manager, display_destroy); wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL); wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.commit.listener_list));
assert(wl_list_empty(&manager->events.destroy.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list));
struct wlr_ext_workspace_group_handle_v1 *group, *tmp; struct wlr_ext_workspace_group_handle_v1 *group, *tmp;
@ -583,6 +581,7 @@ wlr_ext_workspace_manager_v1_create(struct wl_display *display,
wl_list_init(&manager->groups); wl_list_init(&manager->groups);
wl_list_init(&manager->workspaces); wl_list_init(&manager->workspaces);
wl_list_init(&manager->resources); wl_list_init(&manager->resources);
wl_signal_init(&manager->events.commit);
wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.destroy);
return manager; return manager;
@ -601,7 +600,6 @@ wlr_ext_workspace_group_handle_v1_create(
wl_list_init(&group->outputs); wl_list_init(&group->outputs);
wl_list_init(&group->resources); wl_list_init(&group->resources);
wl_signal_init(&group->events.create_workspace);
wl_signal_init(&group->events.destroy); wl_signal_init(&group->events.destroy);
wl_list_insert(manager->groups.prev, &group->link); wl_list_insert(manager->groups.prev, &group->link);
@ -689,7 +687,6 @@ void wlr_ext_workspace_group_handle_v1_destroy(
wl_signal_emit_mutable(&group->events.destroy, NULL); wl_signal_emit_mutable(&group->events.destroy, NULL);
assert(wl_list_empty(&group->events.create_workspace.listener_list));
assert(wl_list_empty(&group->events.destroy.listener_list)); assert(wl_list_empty(&group->events.destroy.listener_list));
struct wlr_ext_workspace_handle_v1 *workspace; struct wlr_ext_workspace_handle_v1 *workspace;
@ -708,12 +705,7 @@ void wlr_ext_workspace_group_handle_v1_destroy(
struct wlr_ext_workspace_manager_v1_resource *manager_res; struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &group->manager->resources, link) { wl_list_for_each(manager_res, &group->manager->resources, link) {
struct wlr_ext_workspace_v1_request *req, *tmp2; clear_requests_by(manager_res, group, NULL);
wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) {
if (req->group == group) {
destroy_request(req);
}
}
} }
struct wlr_ext_workspace_v1_group_output *group_output, *tmp3; struct wlr_ext_workspace_v1_group_output *group_output, *tmp3;
@ -822,13 +814,9 @@ wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager,
wl_list_init(&workspace->resources); wl_list_init(&workspace->resources);
wl_array_init(&workspace->coordinates); wl_array_init(&workspace->coordinates);
wl_signal_init(&workspace->events.activate);
wl_signal_init(&workspace->events.deactivate);
wl_signal_init(&workspace->events.remove);
wl_signal_init(&workspace->events.assign);
wl_signal_init(&workspace->events.destroy); wl_signal_init(&workspace->events.destroy);
wl_list_insert(&manager->workspaces, &workspace->link); wl_list_insert(manager->workspaces.prev, &workspace->link);
struct wlr_ext_workspace_manager_v1_resource *manager_res; struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &manager->resources, link) { wl_list_for_each(manager_res, &manager->resources, link) {
@ -855,10 +843,6 @@ void wlr_ext_workspace_handle_v1_destroy(
wl_signal_emit_mutable(&workspace->events.destroy, NULL); wl_signal_emit_mutable(&workspace->events.destroy, NULL);
assert(wl_list_empty(&workspace->events.activate.listener_list));
assert(wl_list_empty(&workspace->events.deactivate.listener_list));
assert(wl_list_empty(&workspace->events.remove.listener_list));
assert(wl_list_empty(&workspace->events.assign.listener_list));
assert(wl_list_empty(&workspace->events.destroy.listener_list)); assert(wl_list_empty(&workspace->events.destroy.listener_list));
if (workspace->group) { if (workspace->group) {
@ -873,12 +857,7 @@ void wlr_ext_workspace_handle_v1_destroy(
struct wlr_ext_workspace_manager_v1_resource *manager_res; struct wlr_ext_workspace_manager_v1_resource *manager_res;
wl_list_for_each(manager_res, &workspace->manager->resources, link) { wl_list_for_each(manager_res, &workspace->manager->resources, link) {
struct wlr_ext_workspace_v1_request *req, *tmp2; clear_requests_by(manager_res, NULL, workspace);
wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) {
if (req->workspace == workspace) {
destroy_request(req);
}
}
} }
manager_schedule_done(workspace->manager); manager_schedule_done(workspace->manager);
@ -929,23 +908,22 @@ void wlr_ext_workspace_handle_v1_set_name(
manager_schedule_done(workspace->manager); manager_schedule_done(workspace->manager);
} }
static bool array_equal(struct wl_array *a, struct wl_array *b) {
return (a->size == b->size) &&
(a->size == 0 || memcmp(a->data, b->data, a->size) == 0);
}
void wlr_ext_workspace_handle_v1_set_coordinates( void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace, struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords,
struct wl_array *coordinates) { size_t coords_len) {
assert(coordinates); size_t size = coords_len * sizeof(coords[0]);
if (size == workspace->coordinates.size &&
if (array_equal(&workspace->coordinates, coordinates)) { (size == 0 || memcmp(workspace->coordinates.data, coords, size) == 0)) {
return; return;
} }
wl_array_release(&workspace->coordinates); wl_array_release(&workspace->coordinates);
wl_array_init(&workspace->coordinates); wl_array_init(&workspace->coordinates);
wl_array_copy(&workspace->coordinates, coordinates); struct wl_array arr = {
.data = (void *)coords,
.size = size,
};
wl_array_copy(&workspace->coordinates, &arr);
struct wlr_ext_workspace_v1_resource *workspace_res; struct wlr_ext_workspace_v1_resource *workspace_res;
wl_list_for_each(workspace_res, &workspace->resources, link) { wl_list_for_each(workspace_res, &workspace->resources, link) {

View file

@ -1,21 +1,69 @@
// bash on: https://gitlab.freedesktop.org/tokyo4j/wlroots/-/tree/ext-workspace /*
// TODO: remove this file * This an unstable interface of wlroots. No guarantees are made regarding the
// refer: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5115 * future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_EXT_WORKSPACE_V1_H
#define WLR_TYPES_WLR_EXT_WORKSPACE_V1_H
#include <wayland-protocols/ext-workspace-v1-enum.h> #include <wayland-protocols/ext-workspace-v1-enum.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
struct wlr_output; struct wlr_output;
enum wlr_ext_workspace_v1_request_type {
WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE,
WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE,
WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN,
WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE,
};
struct wlr_ext_workspace_v1_request {
enum wlr_ext_workspace_v1_request_type type;
struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests
union {
struct {
char *name;
struct wlr_ext_workspace_group_handle_v1
*group; // NULL if destroyed
} create_workspace;
struct {
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
} activate;
struct {
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
} deactivate;
struct {
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
struct wlr_ext_workspace_group_handle_v1
*group; // NULL if destroyed
} assign;
struct {
struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed
} remove;
};
};
struct wlr_ext_workspace_v1_commit_event {
struct wl_list *requests; // wlr_ext_workspace_v1_request.link
};
struct wlr_ext_workspace_manager_v1 { struct wlr_ext_workspace_manager_v1 {
struct wl_global *global; struct wl_global *global;
struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link
struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link
struct { struct {
struct wl_signal commit; // wlr_ext_workspace_v1_commit_event
struct wl_signal destroy; struct wl_signal destroy;
} events; } events;
void *data;
struct { struct {
struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
struct wl_event_source *idle_source; struct wl_event_source *idle_source;
@ -24,21 +72,17 @@ struct wlr_ext_workspace_manager_v1 {
}; };
}; };
struct wlr_ext_workspace_group_handle_v1_create_workspace_event {
const char *name;
};
struct wlr_ext_workspace_group_handle_v1 { struct wlr_ext_workspace_group_handle_v1 {
struct wlr_ext_workspace_manager_v1 *manager; struct wlr_ext_workspace_manager_v1 *manager;
uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities
struct { struct {
struct wl_signal
create_workspace; // wlr_ext_workspace_group_handle_v1_create_workspace_event
struct wl_signal destroy; struct wl_signal destroy;
} events; } events;
struct wl_list link; // wlr_ext_workspace_manager_v1.groups struct wl_list link; // wlr_ext_workspace_manager_v1.groups
void *data;
struct { struct {
struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link
struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
@ -55,15 +99,13 @@ struct wlr_ext_workspace_handle_v1 {
uint32_t state; // ext_workspace_handle_v1_state uint32_t state; // ext_workspace_handle_v1_state
struct { struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal remove;
struct wl_signal assign; // wlr_ext_workspace_group_handle_v1
struct wl_signal destroy; struct wl_signal destroy;
} events; } events;
struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces
void *data;
struct { struct {
struct wl_list resources; // wlr_ext_workspace_v1_resource.link struct wl_list resources; // wlr_ext_workspace_v1_resource.link
}; };
@ -96,11 +138,13 @@ void wlr_ext_workspace_handle_v1_set_group(
void wlr_ext_workspace_handle_v1_set_name( void wlr_ext_workspace_handle_v1_set_name(
struct wlr_ext_workspace_handle_v1 *workspace, const char *name); struct wlr_ext_workspace_handle_v1 *workspace, const char *name);
void wlr_ext_workspace_handle_v1_set_coordinates( void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace, struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords,
struct wl_array *coordinates); size_t coords_len);
void wlr_ext_workspace_handle_v1_set_active( void wlr_ext_workspace_handle_v1_set_active(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
void wlr_ext_workspace_handle_v1_set_urgent( void wlr_ext_workspace_handle_v1_set_urgent(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
void wlr_ext_workspace_handle_v1_set_hidden( void wlr_ext_workspace_handle_v1_set_hidden(
struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
#endif

View file

@ -558,7 +558,7 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
if (id != SCROLLER && id != VERTICAL_SCROLLER && id != TILE && if (id != SCROLLER && id != VERTICAL_SCROLLER && id != TILE &&
id != VERTICAL_TILE && id != DECK && id != VERTICAL_DECK && id != VERTICAL_TILE && id != DECK && id != VERTICAL_DECK &&
id != CENTER_TILE && id != RIGHT_TILE && id != TGMIX) id != CENTER_TILE && id != RIGHT_TILE)
return false; return false;
if (id == SCROLLER || id == VERTICAL_SCROLLER) { if (id == SCROLLER || id == VERTICAL_SCROLLER) {
@ -583,15 +583,6 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
return true; return true;
} }
if (id == TGMIX) {
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 (id == CENTER_TILE) {
if (tc->ismaster ^ sc->ismaster) if (tc->ismaster ^ sc->ismaster)
return false; return false;

View file

@ -488,6 +488,21 @@ void resize_tile_master_vertical(Client *grabc, bool isdrag, int32_t offsetx,
} }
} }
void resize_tile_dwindle(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, bool isvertical) {
if (!isdrag) {
dwindle_resize_client_step(grabc->mon, grabc, offsetx, offsety);
return;
}
if (last_apply_drap_time == 0 ||
time - last_apply_drap_time > config.drag_tile_refresh_interval) {
dwindle_resize_client(grabc->mon, grabc);
last_apply_drap_time = time;
}
}
void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx,
int32_t offsety, uint32_t time, bool isvertical) { int32_t offsety, uint32_t time, bool isvertical) {
Client *tc = NULL; Client *tc = NULL;
@ -692,8 +707,7 @@ void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx,
const Layout *current_layout = const Layout *current_layout =
grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag]; grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag];
if (current_layout->id == TILE || current_layout->id == DECK || if (current_layout->id == TILE || current_layout->id == DECK ||
current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE || current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE
(current_layout->id == TGMIX && grabc->mon->visible_tiling_clients <= 3)
) { ) {
resize_tile_master_horizontal(grabc, isdrag, offsetx, offsety, time, resize_tile_master_horizontal(grabc, isdrag, offsetx, offsety, time,
@ -706,6 +720,8 @@ void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx,
resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, false); resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, false);
} else if (current_layout->id == VERTICAL_SCROLLER) { } else if (current_layout->id == VERTICAL_SCROLLER) {
resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, true); resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, true);
} else if (current_layout->id == DWINDLE) {
resize_tile_dwindle(grabc, isdrag, offsetx, offsety, time, true);
} }
} }

433
src/layout/dwindle.h Normal file
View file

@ -0,0 +1,433 @@
typedef struct DwindleNode DwindleNode;
struct DwindleNode {
bool is_split;
bool split_h;
bool split_locked;
float ratio;
float drag_init_ratio;
int32_t container_x;
int32_t container_y;
int32_t container_w;
int32_t container_h;
DwindleNode *parent;
DwindleNode *first;
DwindleNode *second;
Client *client;
};
static DwindleNode *dwindle_locked_h_node = NULL;
static DwindleNode *dwindle_locked_v_node = NULL;
static DwindleNode *dwindle_new_leaf(Client *c) {
DwindleNode *n = calloc(1, sizeof(DwindleNode));
n->client = c;
return n;
}
static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c) {
if (!node)
return NULL;
if (!node->is_split)
return node->client == c ? node : NULL;
DwindleNode *r = dwindle_find_leaf(node->first, c);
return r ? r : dwindle_find_leaf(node->second, c);
}
static DwindleNode *dwindle_first_leaf(DwindleNode *node) {
if (!node)
return NULL;
while (node->is_split)
node = node->first;
return node;
}
static void dwindle_free_tree(DwindleNode *node) {
if (!node)
return;
dwindle_free_tree(node->first);
dwindle_free_tree(node->second);
free(node);
}
static void dwindle_insert(DwindleNode **root, Client *new_c, Client *focused,
float ratio, bool as_first, bool split_h,
bool lock) {
DwindleNode *new_leaf = dwindle_new_leaf(new_c);
if (!*root) {
*root = new_leaf;
return;
}
DwindleNode *target = focused ? dwindle_find_leaf(*root, focused) : NULL;
if (!target)
target = dwindle_first_leaf(*root);
DwindleNode *split = calloc(1, sizeof(DwindleNode));
split->is_split = true;
split->ratio = ratio;
split->split_h = split_h;
split->split_locked = lock;
if (as_first) {
split->first = new_leaf;
split->second = target;
} else {
split->first = target;
split->second = new_leaf;
}
split->parent = target->parent;
target->parent = split;
new_leaf->parent = split;
if (!split->parent) {
*root = split;
} else {
if (split->parent->first == target)
split->parent->first = split;
else
split->parent->second = split;
}
}
static void dwindle_remove(DwindleNode **root, Client *c) {
DwindleNode *leaf = dwindle_find_leaf(*root, c);
if (!leaf)
return;
DwindleNode *parent = leaf->parent;
if (!parent) {
free(leaf);
*root = NULL;
return;
}
DwindleNode *sibling =
(parent->first == leaf) ? parent->second : parent->first;
DwindleNode *grandparent = parent->parent;
sibling->parent = grandparent;
/* Preserve split direction on sibling split-nodes when requested. */
if (!sibling->is_split ||
(!config.dwindle_preserve_split && !config.dwindle_smart_split)) {
sibling->container_w = 0;
sibling->container_h = 0;
}
if (!grandparent) {
*root = sibling;
} else {
if (grandparent->first == parent)
grandparent->first = sibling;
else
grandparent->second = sibling;
}
free(leaf);
free(parent);
}
static void dwindle_assign(DwindleNode *node, int32_t ax, int32_t ay,
int32_t aw, int32_t ah, int32_t gap_h,
int32_t gap_v) {
if (!node)
return;
if (!node->is_split) {
if (node->client) {
struct wlr_box box = {ax, ay, MAX(1, aw), MAX(1, ah)};
resize(node->client, box, 0);
}
return;
}
if (!node->split_locked && node->container_w == 0 && node->container_h == 0)
node->split_h = (aw >= ah);
node->container_x = ax;
node->container_y = ay;
node->container_w = aw;
node->container_h = ah;
if (node->split_h) {
int32_t w1 = MAX(1, (int32_t)(aw * node->ratio) - gap_h / 2);
dwindle_assign(node->first, ax, ay, w1, ah, gap_h, gap_v);
dwindle_assign(node->second, ax + w1 + gap_h, ay, aw - w1 - gap_h, ah,
gap_h, gap_v);
} else {
int32_t h1 = MAX(1, (int32_t)(ah * node->ratio) - gap_v / 2);
dwindle_assign(node->first, ax, ay, aw, h1, gap_h, gap_v);
dwindle_assign(node->second, ax, ay + h1 + gap_v, aw, ah - h1 - gap_v,
gap_h, gap_v);
}
}
static void dwindle_move_client(DwindleNode **root, Client *c, Client *target,
float ratio, int32_t dir) {
if (!c || !target || c == target)
return;
if (!dwindle_find_leaf(*root, c) || !dwindle_find_leaf(*root, target))
return;
dwindle_remove(root, c);
bool as_first = (dir == UP || dir == LEFT);
bool split_h = (dir == LEFT || dir == RIGHT);
dwindle_insert(root, c, target, ratio, as_first, split_h, true);
}
static void dwindle_swap_clients(DwindleNode **root, Client *a, Client *b) {
DwindleNode *la = dwindle_find_leaf(*root, a);
DwindleNode *lb = dwindle_find_leaf(*root, b);
if (!la || !lb || la == lb)
return;
la->client = b;
lb->client = a;
}
static void dwindle_resize_client(Monitor *m, Client *c) {
uint32_t tag = m->pertag->curtag;
DwindleNode *leaf = dwindle_find_leaf(m->pertag->dwindle_root[tag], c);
if (!leaf)
return;
if (!start_drag_window) {
start_drag_window = true;
dwindle_locked_h_node = NULL;
dwindle_locked_v_node = NULL;
drag_begin_cursorx = cursor->x;
drag_begin_cursory = cursor->y;
DwindleNode *node = leaf->parent;
while (node) {
if (node->split_h && !dwindle_locked_h_node) {
dwindle_locked_h_node = node;
node->drag_init_ratio = node->ratio;
}
if (!node->split_h && !dwindle_locked_v_node) {
dwindle_locked_v_node = node;
node->drag_init_ratio = node->ratio;
}
if (dwindle_locked_h_node && dwindle_locked_v_node)
break;
node = node->parent;
}
}
if (!dwindle_locked_h_node && !dwindle_locked_v_node)
return;
if (dwindle_locked_h_node) {
float cw = (float)MAX(1, dwindle_locked_h_node->container_w);
float ox = (float)(cursor->x - drag_begin_cursorx);
if (config.dwindle_smart_resize) {
/* Move the boundary toward the cursor: invert direction when
* the drag started on the right side of the split line. */
float split_x = dwindle_locked_h_node->container_x +
cw * dwindle_locked_h_node->drag_init_ratio;
if (drag_begin_cursorx >= split_x)
ox = -ox;
}
dwindle_locked_h_node->ratio =
dwindle_locked_h_node->drag_init_ratio + ox / cw;
dwindle_locked_h_node->ratio =
CLAMP_FLOAT(dwindle_locked_h_node->ratio, 0.05f, 0.95f);
}
if (dwindle_locked_v_node) {
float ch = (float)MAX(1, dwindle_locked_v_node->container_h);
float oy = (float)(cursor->y - drag_begin_cursory);
if (config.dwindle_smart_resize) {
/* Same logic for the vertical split line. */
float split_y = dwindle_locked_v_node->container_y +
ch * dwindle_locked_v_node->drag_init_ratio;
if (drag_begin_cursory >= split_y)
oy = -oy;
}
dwindle_locked_v_node->ratio =
dwindle_locked_v_node->drag_init_ratio + oy / ch;
dwindle_locked_v_node->ratio =
CLAMP_FLOAT(dwindle_locked_v_node->ratio, 0.05f, 0.95f);
}
int32_t n = m->visible_tiling_clients;
int32_t gap_ih = enablegaps ? m->gappih : 0;
int32_t gap_iv = enablegaps ? m->gappiv : 0;
int32_t gap_oh = enablegaps ? m->gappoh : 0;
int32_t gap_ov = enablegaps ? m->gappov : 0;
if (config.smartgaps && n == 1)
gap_ih = gap_iv = gap_oh = gap_ov = 0;
dwindle_assign(m->pertag->dwindle_root[tag], m->w.x + gap_oh,
m->w.y + gap_ov, m->w.width - 2 * gap_oh,
m->w.height - 2 * gap_ov, gap_ih, gap_iv);
}
static void dwindle_resize_client_step(Monitor *m, Client *c, int32_t dx,
int32_t dy) {
uint32_t tag = m->pertag->curtag;
DwindleNode *leaf = dwindle_find_leaf(m->pertag->dwindle_root[tag], c);
if (!leaf)
return;
DwindleNode *h_node = NULL;
DwindleNode *v_node = NULL;
DwindleNode *node = leaf->parent;
while (node) {
if (node->split_h && !h_node)
h_node = node;
if (!node->split_h && !v_node)
v_node = node;
if (h_node && v_node)
break;
node = node->parent;
}
if (!h_node && !v_node)
return;
if (h_node && dx) {
float cw = (float)MAX(1, h_node->container_w);
float delta = (float)dx / cw;
h_node->ratio = CLAMP_FLOAT(h_node->ratio + delta, 0.05f, 0.95f);
}
if (v_node && dy) {
float ch = (float)MAX(1, v_node->container_h);
float delta = (float)dy / ch;
v_node->ratio = CLAMP_FLOAT(v_node->ratio + delta, 0.05f, 0.95f);
}
int32_t n_clients = m->visible_tiling_clients;
int32_t gap_ih = enablegaps ? m->gappih : 0;
int32_t gap_iv = enablegaps ? m->gappiv : 0;
int32_t gap_oh = enablegaps ? m->gappoh : 0;
int32_t gap_ov = enablegaps ? m->gappov : 0;
if (config.smartgaps && n_clients == 1)
gap_ih = gap_iv = gap_oh = gap_ov = 0;
dwindle_assign(m->pertag->dwindle_root[tag], m->w.x + gap_oh,
m->w.y + gap_ov, m->w.width - 2 * gap_oh,
m->w.height - 2 * gap_ov, gap_ih, gap_iv);
}
static void dwindle_remove_client(Client *c) {
Monitor *m;
wl_list_for_each(m, &mons, link) {
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++)
dwindle_remove(&m->pertag->dwindle_root[t], c);
}
}
/* Insert a new client respecting dwindle_vsplit, dwindle_hsplit, and
* dwindle_smart_split config options. */
static void dwindle_insert_with_config(DwindleNode **root, Client *new_c,
Client *focused, float ratio) {
bool as_first = false;
bool split_h = false;
bool lock = false;
if (focused) {
struct wlr_box *fg = &focused->geom;
double fcx = fg->x + fg->width * 0.5;
double fcy = fg->y + fg->height * 0.5;
if (config.dwindle_smart_split) {
double nx = (cursor->x - fcx) / (fg->width * 0.5);
double ny = (cursor->y - fcy) / (fg->height * 0.5);
if (fabs(ny) > fabs(nx)) {
split_h = false; // vertical split
as_first = (ny < 0); // top → new window on top
} else {
split_h = true; // horizontal split
as_first = (nx < 0); // left → new window on left
}
lock = true; // lock split direction
} else {
// normal mode, auto split
bool likely_h = (fg->width >= fg->height);
if (likely_h) {
if (config.dwindle_hsplit == 0)
as_first = (cursor->x < fcx);
else
as_first = (config.dwindle_hsplit == 2);
} else {
if (config.dwindle_vsplit == 0)
as_first = (cursor->y < fcy);
else
as_first = (config.dwindle_vsplit == 2);
}
// split_h and lock are false, decided by width/height ratio
}
}
dwindle_insert(root, new_c, focused, ratio, as_first, split_h, lock);
}
void dwindle(Monitor *m) {
int32_t n = m->visible_tiling_clients;
if (n == 0)
return;
uint32_t tag = m->pertag->curtag;
DwindleNode **root = &m->pertag->dwindle_root[tag];
float ratio = config.dwindle_split_ratio;
Client *vis[512];
int32_t count = 0;
Client *c;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && ISTILED(c))
vis[count++] = c;
if (count >= 512)
break;
}
{
DwindleNode *leaves[512];
int32_t lc = 0;
DwindleNode *stack[1024];
int32_t sp = 0;
if (*root)
stack[sp++] = *root;
while (sp > 0) {
DwindleNode *nd = stack[--sp];
if (!nd->is_split) {
leaves[lc++] = nd;
} else {
if (nd->second)
stack[sp++] = nd->second;
if (nd->first)
stack[sp++] = nd->first;
}
}
for (int32_t i = 0; i < lc; i++) {
bool found = false;
for (int32_t j = 0; j < count; j++)
if (vis[j] == leaves[i]->client) {
found = true;
break;
}
if (!found)
dwindle_remove(root, leaves[i]->client);
}
}
Client *focused = focustop(m);
if (focused && !dwindle_find_leaf(*root, focused))
focused = m->sel;
for (int32_t i = 0; i < count; i++) {
if (!dwindle_find_leaf(*root, vis[i]))
dwindle_insert_with_config(root, vis[i], focused, ratio);
}
int32_t gap_ih = enablegaps ? m->gappih : 0;
int32_t gap_iv = enablegaps ? m->gappiv : 0;
int32_t gap_oh = enablegaps ? m->gappoh : 0;
int32_t gap_ov = enablegaps ? m->gappov : 0;
if (config.smartgaps && n == 1)
gap_ih = gap_iv = gap_oh = gap_ov = 0;
dwindle_assign(*root, m->w.x + gap_oh, m->w.y + gap_ov,
m->w.width - 2 * gap_oh, m->w.height - 2 * gap_ov, gap_ih,
gap_iv);
}

View file

@ -993,15 +993,4 @@ monocle(Monitor *m) {
} }
if ((c = focustop(m))) if ((c = focustop(m)))
wlr_scene_node_raise_to_top(&c->scene->node); wlr_scene_node_raise_to_top(&c->scene->node);
} }
void tgmix(Monitor *m) {
int32_t n = m->visible_tiling_clients;
if (n <= 3) {
tile(m);
return;
} else {
grid(m);
return;
}
}

View file

@ -11,7 +11,7 @@ static void vertical_overview(Monitor *m);
static void vertical_grid(Monitor *m); static void vertical_grid(Monitor *m);
static void vertical_scroller(Monitor *m); static void vertical_scroller(Monitor *m);
static void vertical_deck(Monitor *mon); static void vertical_deck(Monitor *mon);
static void tgmix(Monitor *m); static void dwindle(Monitor *m);
/* layout(s) */ /* layout(s) */
Layout overviewlayout = {"󰃇", overview, "overview"}; Layout overviewlayout = {"󰃇", overview, "overview"};
@ -28,7 +28,7 @@ enum {
VERTICAL_GRID, VERTICAL_GRID,
VERTICAL_DECK, VERTICAL_DECK,
RIGHT_TILE, RIGHT_TILE,
TGMIX, DWINDLE,
}; };
Layout layouts[] = { Layout layouts[] = {
@ -46,5 +46,5 @@ Layout layouts[] = {
{"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局 {"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局
{"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局 {"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局
{"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局 {"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局
{"TG", tgmix, "tgmix", TGMIX}, // 混合布局 {"DW", dwindle, "dwindle", DWINDLE},
}; };

View file

@ -314,6 +314,7 @@ struct Client {
Monitor *mon; Monitor *mon;
struct wlr_scene_tree *scene; struct wlr_scene_tree *scene;
struct wlr_scene_rect *border; /* top, bottom, left, right */ struct wlr_scene_rect *border; /* top, bottom, left, right */
struct wlr_scene_rect *droparea;
struct wlr_scene_shadow *shadow; struct wlr_scene_shadow *shadow;
struct wlr_scene_tree *scene_surface; struct wlr_scene_tree *scene_surface;
struct wl_list link; struct wl_list link;
@ -422,6 +423,9 @@ struct Client {
bool isfocusing; bool isfocusing;
struct Client *next_in_stack; struct Client *next_in_stack;
struct Client *prev_in_stack; struct Client *prev_in_stack;
bool enable_drop_area_draw;
int32_t drop_direction;
struct wlr_box drag_tile_float_backup_geom;
}; };
typedef struct { typedef struct {
@ -539,6 +543,7 @@ struct Monitor {
struct wlr_scene_optimized_blur *blur; struct wlr_scene_optimized_blur *blur;
char last_surface_ws_name[256]; char last_surface_ws_name[256];
struct wlr_ext_workspace_group_handle_v1 *ext_group; struct wlr_ext_workspace_group_handle_v1 *ext_group;
bool iscleanuping;
}; };
typedef struct { typedef struct {
@ -555,6 +560,8 @@ typedef struct {
struct wl_listener destroy; struct wl_listener destroy;
} SessionLock; } SessionLock;
typedef struct DwindleNode DwindleNode;
/* function declarations */ /* function declarations */
static void applybounds( static void applybounds(
Client *c, Client *c,
@ -648,8 +655,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device,
double sy_unaccel); double sy_unaccel);
static void motionrelative(struct wl_listener *listener, void *data); static void motionrelative(struct wl_listener *listener, void *data);
static void reset_foreign_tolevel(Client *c); static void reset_foreign_tolevel(Client *c, Monitor *oldmon, Monitor *newmon);
static void remove_foreign_topleve(Client *c);
static void add_foreign_topleve(Client *c); static void add_foreign_topleve(Client *c);
static void exchange_two_client(Client *c1, Client *c2); static void exchange_two_client(Client *c1, Client *c2);
static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapply(struct wl_listener *listener, void *data);
@ -809,6 +815,13 @@ static void pre_caculate_before_arrange(Monitor *m, bool want_animation,
static void client_pending_fullscreen_state(Client *c, int32_t isfullscreen); static void client_pending_fullscreen_state(Client *c, int32_t isfullscreen);
static void client_pending_maximized_state(Client *c, int32_t ismaximized); static void client_pending_maximized_state(Client *c, int32_t ismaximized);
static void client_pending_minimized_state(Client *c, int32_t isminimized); static void client_pending_minimized_state(Client *c, int32_t isminimized);
static void scroller_insert_stack(Client *c, Client *target_client,
bool insert_before);
static void dwindle_move_client(DwindleNode **root, Client *c, Client *target,
float ratio, int32_t dir);
static void dwindle_resize_client_step(Monitor *m, Client *c, int32_t dx,
int32_t dy);
static void dwindle_resize_client(Monitor *m, Client *c);
#include "data/static_keymap.h" #include "data/static_keymap.h"
#include "dispatch/bind_declare.h" #include "dispatch/bind_declare.h"
@ -870,7 +883,7 @@ static KeyboardGroup *kb_group;
static struct wl_list inputdevices; static struct wl_list inputdevices;
static struct wl_list keyboard_shortcut_inhibitors; static struct wl_list keyboard_shortcut_inhibitors;
static uint32_t cursor_mode; static uint32_t cursor_mode;
static Client *grabc; static Client *grabc, *dropc;
static int32_t rzcorner; static int32_t rzcorner;
static int32_t grabcx, grabcy; /* client-relative */ static int32_t grabcx, grabcy; /* client-relative */
static int32_t drag_begin_cursorx, drag_begin_cursory; /* client-relative */ static int32_t drag_begin_cursorx, drag_begin_cursory; /* client-relative */
@ -940,6 +953,7 @@ struct Pertag {
int32_t no_hide[LENGTH(tags) + 1]; /* no_hide per tag */ int32_t no_hide[LENGTH(tags) + 1]; /* no_hide per tag */
int32_t no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */ int32_t no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */
int32_t open_as_floating[LENGTH(tags) + 1]; /* open_as_floating per tag */ int32_t open_as_floating[LENGTH(tags) + 1]; /* open_as_floating per tag */
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
const Layout const Layout
*ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */ *ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */
}; };
@ -1012,6 +1026,7 @@ static struct wl_event_source *sync_keymap;
#include "ext-protocol/all.h" #include "ext-protocol/all.h"
#include "fetch/fetch.h" #include "fetch/fetch.h"
#include "layout/arrange.h" #include "layout/arrange.h"
#include "layout/dwindle.h"
#include "layout/horizontal.h" #include "layout/horizontal.h"
#include "layout/vertical.h" #include "layout/vertical.h"
@ -1143,6 +1158,7 @@ void swallow(Client *c, Client *w) {
c->scroller_proportion = w->scroller_proportion; c->scroller_proportion = w->scroller_proportion;
c->next_in_stack = w->next_in_stack; c->next_in_stack = w->next_in_stack;
c->prev_in_stack = w->prev_in_stack; c->prev_in_stack = w->prev_in_stack;
c->isglobal = w->isglobal;
if (w->next_in_stack) if (w->next_in_stack)
w->next_in_stack->prev_in_stack = c; w->next_in_stack->prev_in_stack = c;
@ -1152,8 +1168,12 @@ void swallow(Client *c, Client *w) {
wl_list_insert(&w->link, &c->link); wl_list_insert(&w->link, &c->link);
wl_list_insert(&w->flink, &c->flink); wl_list_insert(&w->flink, &c->flink);
if (w->foreign_toplevel) if (w->foreign_toplevel) {
remove_foreign_topleve(w); wlr_foreign_toplevel_handle_v1_output_leave(w->foreign_toplevel,
w->mon->wlr_output);
wlr_foreign_toplevel_handle_v1_destroy(w->foreign_toplevel);
w->foreign_toplevel = NULL;
}
wlr_scene_node_set_enabled(&w->scene->node, false); wlr_scene_node_set_enabled(&w->scene->node, false);
wlr_scene_node_set_enabled(&c->scene->node, true); wlr_scene_node_set_enabled(&c->scene->node, true);
@ -1161,10 +1181,25 @@ void swallow(Client *c, Client *w) {
if (!c->foreign_toplevel && c->mon) if (!c->foreign_toplevel && c->mon)
add_foreign_toplevel(c); add_foreign_toplevel(c);
else if (c->foreign_toplevel && c->mon) {
wlr_foreign_toplevel_handle_v1_output_enter(c->foreign_toplevel,
c->mon->wlr_output);
}
client_pending_fullscreen_state(c, w->isfullscreen); client_pending_fullscreen_state(c, w->isfullscreen);
client_pending_maximized_state(c, w->ismaximizescreen); client_pending_maximized_state(c, w->ismaximizescreen);
client_pending_minimized_state(c, w->isminimized); client_pending_minimized_state(c, w->isminimized);
Monitor *m;
wl_list_for_each(m, &mons, link) {
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) {
DwindleNode **root = &m->pertag->dwindle_root[t];
dwindle_remove(root, c);
DwindleNode *wn = dwindle_find_leaf(*root, w);
if (wn)
wn->client = c;
}
}
} }
bool switch_scratchpad_client_state(Client *c) { bool switch_scratchpad_client_state(Client *c) {
@ -1175,7 +1210,7 @@ bool switch_scratchpad_client_state(Client *c) {
Monitor *oldmon = c->mon; Monitor *oldmon = c->mon;
c->scratchpad_switching_mon = true; c->scratchpad_switching_mon = true;
c->mon = selmon; c->mon = selmon;
reset_foreign_tolevel(c); reset_foreign_tolevel(c, oldmon, c->mon);
client_update_oldmonname_record(c, selmon); client_update_oldmonname_record(c, selmon);
// 根据新monitor调整窗口尺寸 // 根据新monitor调整窗口尺寸
@ -1569,12 +1604,17 @@ void applyrules(Client *c) {
int32_t fullscreen_state_backup = int32_t fullscreen_state_backup =
c->isfullscreen || client_wants_fullscreen(c); c->isfullscreen || client_wants_fullscreen(c);
setmon(c, mon, newtags, bool should_init_get_focus =
!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 &&
mon && (!c->istagsilent || !newtags || newtags & mon->tagset[mon->seltags]);
(!c->istagsilent || !newtags ||
newtags & mon->tagset[mon->seltags])); if (!should_init_get_focus) {
wl_list_remove(&c->flink);
wl_list_insert(fstack.prev, &c->flink);
}
setmon(c, mon, newtags, should_init_get_focus);
if (!c->isfloating) { if (!c->isfloating) {
c->old_stack_inner_per = c->stack_inner_per; c->old_stack_inner_per = c->stack_inner_per;
@ -1819,6 +1859,9 @@ void arrangelayers(Monitor *m) {
if (!m->wlr_output->enabled) if (!m->wlr_output->enabled)
return; return;
if (m->iscleanuping)
return;
/* Arrange exclusive surfaces from top->bottom */ /* Arrange exclusive surfaces from top->bottom */
for (i = 3; i >= 0; i--) for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 1); arrangelayer(m, &m->layers[i], &usable_area, 1);
@ -1833,9 +1876,21 @@ void arrangelayers(Monitor *m) {
arrangelayer(m, &m->layers[i], &usable_area, 0); arrangelayer(m, &m->layers[i], &usable_area, 0);
} }
bool pointer_is_trackpad(struct wlr_pointer *pointer) {
struct libinput_device *device;
if (wlr_input_device_is_libinput(&pointer->base) &&
(device = wlr_libinput_get_device_handle(&pointer->base))) {
if (libinput_device_config_tap_get_finger_count(device) > 0) {
return true;
}
}
return false;
}
void // 鼠标滚轮事件 void // 鼠标滚轮事件
axisnotify(struct wl_listener *listener, void *data) { axisnotify(struct wl_listener *listener, void *data) {
/* This event is forwarded by the cursor when a pointer emits an axis event, /* This event is forwarded by the cursor when a pointer emits an axis event,
* for example when you move the scroll wheel. */ * for example when you move the scroll wheel. */
struct wlr_pointer_axis_event *event = data; struct wlr_pointer_axis_event *event = data;
@ -1844,6 +1899,7 @@ axisnotify(struct wl_listener *listener, void *data) {
AxisBinding *a; AxisBinding *a;
int32_t ji; int32_t ji;
uint32_t adir; uint32_t adir;
double target_scroll_factor;
// IDLE_NOTIFY_ACTIVITY; // IDLE_NOTIFY_ACTIVITY;
handlecursoractivity(); handlecursoractivity();
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@ -1890,12 +1946,17 @@ axisnotify(struct wl_listener *listener, void *data) {
* implemented checking the event's orientation and the delta of the event * implemented checking the event's orientation and the delta of the event
*/ */
/* Notify the client with pointer focus of the axis event. */ /* Notify the client with pointer focus of the axis event. */
target_scroll_factor = pointer_is_trackpad(event->pointer)
? config.trackpad_scroll_factor
: config.axis_scroll_factor;
wlr_seat_pointer_notify_axis( wlr_seat_pointer_notify_axis(
seat, // 滚轮事件发送给客户端也就是窗口 seat, // 滚轮事件发送给客户端也就是窗口
event->time_msec, event->orientation, event->time_msec, event->orientation,
event->delta * config.axis_scroll_factor, event->delta * target_scroll_factor,
roundf(event->delta_discrete * config.axis_scroll_factor), roundf(event->delta_discrete * target_scroll_factor), event->source,
event->source, event->relative_direction); event->relative_direction);
} }
int32_t ongesture(struct wlr_pointer_swipe_end_event *event) { int32_t ongesture(struct wlr_pointer_swipe_end_event *event) {
@ -2018,52 +2079,180 @@ void hold_end(struct wl_listener *listener, void *data) {
event->time_msec, event->cancelled); event->time_msec, event->cancelled);
} }
void place_drag_tile_client(Client *c) { Client *find_closest_tiled_client(Client *c) {
Client *tc = NULL; Client *tc, *closest = NULL;
Client *closest_client = NULL; long min_dist = LONG_MAX;
long min_distant = LONG_MAX;
long temp_distant;
int32_t x, y;
wl_list_for_each(tc, &clients, link) { wl_list_for_each(tc, &clients, link) {
if (tc != c && ISTILED(tc) && VISIBLEON(tc, c->mon)) { if (tc == c || !ISTILED(tc) || !VISIBLEON(tc, c->mon))
x = tc->geom.x + (int32_t)(tc->geom.width / 2) - cursor->x; continue;
y = tc->geom.y + (int32_t)(tc->geom.height / 2) - cursor->y;
temp_distant = x * x + y * y; if (cursor->x >= tc->geom.x &&
if (temp_distant < min_distant) { cursor->x < tc->geom.x + tc->geom.width &&
min_distant = temp_distant; cursor->y >= tc->geom.y &&
closest_client = tc; cursor->y < tc->geom.y + tc->geom.height) {
} return tc;
}
int32_t dx = tc->geom.x + (int32_t)(tc->geom.width / 2) - cursor->x;
int32_t dy = tc->geom.y + (int32_t)(tc->geom.height / 2) - cursor->y;
long dist = (long)dx * dx + (long)dy * dy;
if (dist < min_dist) {
min_dist = dist;
closest = tc;
} }
} }
if (closest_client && closest_client->link.prev != &c->link) {
wl_list_remove(&c->link); return closest;
c->link.next = &closest_client->link; }
c->link.prev = closest_client->link.prev;
closest_client->link.prev->next = &c->link; void scroller_insert_stack(Client *c, Client *target_client,
closest_client->link.prev = &c->link; bool insert_before) {
} else if (closest_client) { Client *stack_head = NULL;
exchange_two_client(c, closest_client);
if (!target_client || target_client->mon != c->mon) {
return;
} else {
c->isglobal = target_client->isglobal = 0;
c->isunglobal = target_client->isunglobal = 0;
c->tags = target_client->tags = get_tags_first_tag(target_client->tags);
} }
if (c->isfullscreen) {
setfullscreen(c, 0);
}
if (c->ismaximizescreen) {
setmaximizescreen(c, 0);
}
exit_scroller_stack(c);
stack_head = get_scroll_stack_head(target_client);
if (insert_before) {
if (target_client->prev_in_stack) {
target_client->prev_in_stack->next_in_stack = c;
}
c->prev_in_stack = target_client->prev_in_stack;
c->next_in_stack = target_client;
target_client->prev_in_stack = c;
} else {
if (target_client->next_in_stack) {
target_client->next_in_stack->prev_in_stack = c;
}
c->next_in_stack = target_client->next_in_stack;
c->prev_in_stack = target_client;
target_client->next_in_stack = c;
}
if (stack_head->ismaximizescreen) {
setmaximizescreen(stack_head, 0);
}
if (stack_head->isfullscreen) {
setfullscreen(stack_head, 0);
}
arrange(c->mon, false, false);
return;
}
void try_scroller_drop(Client *c, Client *closest, int vertical) {
Client *stack_head = get_scroll_stack_head(closest);
if (vertical) {
if (closest->drop_direction == LEFT) {
setfloating(c, 0);
scroller_insert_stack(c, closest, true);
return;
} else if (closest->drop_direction == RIGHT) {
setfloating(c, 0);
scroller_insert_stack(c, closest, false);
return;
} else if (closest->drop_direction == UP) {
wl_list_remove(&c->link);
wl_list_insert(stack_head->link.prev, &c->link);
} else if (closest->drop_direction == DOWN) {
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
}
} else {
if (closest->drop_direction == UP) {
setfloating(c, 0);
scroller_insert_stack(c, closest, true);
return;
} else if (closest->drop_direction == DOWN) {
setfloating(c, 0);
scroller_insert_stack(c, closest, false);
return;
} else if (closest->drop_direction == LEFT) {
wl_list_remove(&c->link);
wl_list_insert(stack_head->link.prev, &c->link);
} else if (closest->drop_direction == RIGHT) {
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
}
}
setfloating(c, 0);
}
void place_drag_tile_client(Client *c) {
Client *closest = find_closest_tiled_client(c);
if (closest && closest->mon) {
const Layout *layout =
closest->mon->pertag->ltidxs[closest->mon->pertag->curtag];
if (closest->drop_direction == UNDIR) {
setfloating(c, 0);
exchange_two_client(c, closest);
return;
}
if (layout->id == SCROLLER) {
try_scroller_drop(c, closest, 0);
return;
}
if (layout->id == VERTICAL_SCROLLER) {
try_scroller_drop(c, closest, 1);
return;
}
if (layout->id == DWINDLE) {
uint32_t tag = c->mon->pertag->curtag;
bool insert_before = (closest->drop_direction == LEFT ||
closest->drop_direction == UP);
bool split_h = (closest->drop_direction == LEFT ||
closest->drop_direction == RIGHT);
dwindle_insert(&c->mon->pertag->dwindle_root[tag], c, closest,
config.dwindle_split_ratio, insert_before, split_h,
!config.dwindle_drop_simple_split);
setfloating(c, 0);
return;
}
if (closest->drop_direction == LEFT || closest->drop_direction == UP) {
wl_list_remove(&c->link);
wl_list_insert(closest->link.prev, &c->link);
} else {
wl_list_remove(&c->link);
wl_list_insert(&closest->link, &c->link);
}
}
setfloating(c, 0); setfloating(c, 0);
} }
bool check_trackpad_disabled(struct wlr_pointer *pointer) { bool check_trackpad_disabled(struct wlr_pointer *pointer) {
struct libinput_device *device; if (!config.disable_trackpad) {
if (!config.disable_trackpad)
return false; return false;
if (wlr_input_device_is_libinput(&pointer->base) &&
(device = wlr_libinput_get_device_handle(&pointer->base))) {
// 如果是触摸板且被禁用,忽略事件
if (libinput_device_config_tap_get_finger_count(device) > 0) {
return true; // 不处理事件
}
} }
return false; return pointer_is_trackpad(pointer);
} }
void // 鼠标按键事件 void // 鼠标按键事件
@ -2172,10 +2361,16 @@ buttonpress(struct wl_listener *listener, void *data) {
last_apply_drap_time = 0; last_apply_drap_time = 0;
if (tmpc->drag_to_tile && config.drag_tile_to_tile) { if (tmpc->drag_to_tile && config.drag_tile_to_tile) {
place_drag_tile_client(tmpc); place_drag_tile_client(tmpc);
tmpc->float_geom = tmpc->drag_tile_float_backup_geom;
} else { } else {
apply_window_snap(tmpc); apply_window_snap(tmpc);
} }
tmpc->drag_to_tile = false; tmpc->drag_to_tile = false;
if (dropc) {
dropc->enable_drop_area_draw = false;
client_set_drop_area(dropc);
dropc = NULL;
}
return; return;
} else { } else {
cursor_mode = CurNormal; cursor_mode = CurNormal;
@ -2318,6 +2513,8 @@ void cleanupmon(struct wl_listener *listener, void *data) {
LayerSurface *l = NULL, *tmp = NULL; LayerSurface *l = NULL, *tmp = NULL;
uint32_t i; uint32_t i;
m->iscleanuping = true;
/* m->layers[i] are intentionally not unlinked */ /* m->layers[i] are intentionally not unlinked */
for (i = 0; i < LENGTH(m->layers); i++) { for (i = 0; i < LENGTH(m->layers); i++) {
wl_list_for_each_safe(l, tmp, &m->layers[i], link) wl_list_for_each_safe(l, tmp, &m->layers[i], link)
@ -2350,6 +2547,9 @@ void cleanupmon(struct wl_listener *listener, void *data) {
m->skip_frame_timeout = NULL; m->skip_frame_timeout = NULL;
} }
m->wlr_output->data = NULL; m->wlr_output->data = NULL;
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++)
dwindle_free_tree(m->pertag->dwindle_root[t]);
free(m->pertag); free(m->pertag);
free(m); free(m);
} }
@ -2374,7 +2574,13 @@ void closemon(Monitor *m) {
if (c->mon == m) { if (c->mon == m) {
if (selmon == NULL) { if (selmon == NULL) {
remove_foreign_topleve(c); if (c->foreign_toplevel) {
wlr_foreign_toplevel_handle_v1_output_leave(
c->foreign_toplevel, c->mon->wlr_output);
wlr_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel);
c->foreign_toplevel = NULL;
}
c->mon = NULL; c->mon = NULL;
} else { } else {
client_change_mon(c, selmon); client_change_mon(c, selmon);
@ -3019,6 +3225,7 @@ void createmon(struct wl_listener *listener, void *data) {
struct wl_event_loop *loop = wl_display_get_event_loop(dpy); 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->iscleanuping = false;
m->skip_frame_timeout = m->skip_frame_timeout =
wl_event_loop_add_timer(loop, monitor_skip_frame_timeout_callback, m); wl_event_loop_add_timer(loop, monitor_skip_frame_timeout_callback, m);
m->skiping_frame = false; m->skiping_frame = false;
@ -3201,6 +3408,22 @@ void destroyinputdevice(struct wl_listener *listener, void *data) {
free(input_dev); free(input_dev);
} }
void pointer_set_accel(struct libinput_device *device, bool natural_scrolling,
uint32_t mouse_accel_profile, double mouse_accel_speed) {
libinput_device_config_scroll_set_natural_scroll_enabled(device,
natural_scrolling);
if (mouse_accel_profile &&
libinput_device_config_accel_is_available(device)) {
libinput_device_config_accel_set_profile(device, mouse_accel_profile);
libinput_device_config_accel_set_speed(device, mouse_accel_speed);
} else {
// profile cannot be directly applied to 0, need to set to 1 first
libinput_device_config_accel_set_profile(device, 1);
libinput_device_config_accel_set_profile(device, 0);
libinput_device_config_accel_set_speed(device, 0);
}
}
void configure_pointer(struct libinput_device *device) { void configure_pointer(struct libinput_device *device) {
if (libinput_device_config_tap_get_finger_count(device)) { if (libinput_device_config_tap_get_finger_count(device)) {
libinput_device_config_tap_set_enabled(device, config.tap_to_click); libinput_device_config_tap_set_enabled(device, config.tap_to_click);
@ -3209,11 +3432,12 @@ void configure_pointer(struct libinput_device *device) {
libinput_device_config_tap_set_drag_lock_enabled(device, libinput_device_config_tap_set_drag_lock_enabled(device,
config.drag_lock); config.drag_lock);
libinput_device_config_tap_set_button_map(device, config.button_map); libinput_device_config_tap_set_button_map(device, config.button_map);
libinput_device_config_scroll_set_natural_scroll_enabled( pointer_set_accel(device, config.trackpad_natural_scrolling,
device, config.trackpad_natural_scrolling); config.trackpad_accel_profile,
config.trackpad_accel_speed);
} else { } else {
libinput_device_config_scroll_set_natural_scroll_enabled( pointer_set_accel(device, config.mouse_natural_scrolling,
device, config.mouse_natural_scrolling); config.mouse_accel_profile, config.mouse_accel_speed);
} }
if (libinput_device_config_dwt_is_available(device)) if (libinput_device_config_dwt_is_available(device))
@ -3241,17 +3465,6 @@ void configure_pointer(struct libinput_device *device) {
if (libinput_device_config_send_events_get_modes(device)) if (libinput_device_config_send_events_get_modes(device))
libinput_device_config_send_events_set_mode(device, libinput_device_config_send_events_set_mode(device,
config.send_events_mode); config.send_events_mode);
if (config.accel_profile &&
libinput_device_config_accel_is_available(device)) {
libinput_device_config_accel_set_profile(device, config.accel_profile);
libinput_device_config_accel_set_speed(device, config.accel_speed);
} else {
// profile cannot be directly applied to 0, need to set to 1 first
libinput_device_config_accel_set_profile(device, 1);
libinput_device_config_accel_set_profile(device, 0);
libinput_device_config_accel_set_speed(device, 0);
}
} }
void createpointer(struct wlr_pointer *pointer) { void createpointer(struct wlr_pointer *pointer) {
@ -3577,8 +3790,14 @@ void focusclient(Client *c, int32_t lift) {
wl_list_for_each(um, &mons, link) { wl_list_for_each(um, &mons, link) {
if (um->wlr_output->enabled && um != selmon && um->sel && if (um->wlr_output->enabled && um != selmon && um->sel &&
!um->sel->iskilling && um->sel->isfocusing) { !um->sel->iskilling && um->sel->isfocusing) {
um->sel->isfocusing = false; um->sel->isfocusing = false;
client_set_unfocused_opacity_animation(um->sel); client_set_unfocused_opacity_animation(um->sel);
if (um->sel->foreign_toplevel) {
wlr_foreign_toplevel_handle_v1_set_activated(
um->sel->foreign_toplevel, false);
}
} }
} }
@ -4032,6 +4251,8 @@ 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->drop_direction = UNDIR;
c->enable_drop_area_draw = false;
c->isfocusing = false; c->isfocusing = false;
c->isfloating = 0; c->isfloating = 0;
c->isfakefullscreen = 0; c->isfakefullscreen = 0;
@ -4173,6 +4394,12 @@ mapnotify(struct wl_listener *listener, void *data) {
} }
// extra node // extra node
c->droparea = wlr_scene_rect_create(c->scene, 0, 0, config.dropcolor);
wlr_scene_node_lower_to_bottom(&c->droparea->node);
wlr_scene_node_set_position(&c->droparea->node, 0, 0);
wlr_scene_node_set_enabled(&c->droparea->node, false);
c->border = wlr_scene_rect_create( c->border = wlr_scene_rect_create(
c->scene, 0, 0, c->isurgent ? config.urgentcolor : config.bordercolor); c->scene, 0, 0, c->isurgent ? config.urgentcolor : config.bordercolor);
wlr_scene_node_lower_to_bottom(&c->border->node); wlr_scene_node_lower_to_bottom(&c->border->node);
@ -4211,6 +4438,7 @@ mapnotify(struct wl_listener *listener, void *data) {
} }
} else } else
wl_list_insert(clients.prev, &c->link); // 尾部入栈 wl_list_insert(clients.prev, &c->link); // 尾部入栈
wl_list_insert(&fstack, &c->flink); wl_list_insert(&fstack, &c->flink);
applyrules(c); applyrules(c);
@ -4286,7 +4514,11 @@ void set_minimized(Client *c) {
c->is_scratchpad_show = 0; c->is_scratchpad_show = 0;
focusclient(focustop(selmon), 1); focusclient(focustop(selmon), 1);
arrange(c->mon, false, false); arrange(c->mon, false, false);
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, false);
if (c->foreign_toplevel)
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel,
false);
wl_list_remove(&c->link); // 从原来位置移除 wl_list_remove(&c->link); // 从原来位置移除
wl_list_insert(clients.prev, &c->link); // 插入尾部 wl_list_insert(clients.prev, &c->link); // 插入尾部
} }
@ -4362,6 +4594,7 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
double dy, double dx_unaccel, double dy_unaccel) { double dy, double dx_unaccel, double dy_unaccel) {
double sx = 0, sy = 0, sx_confined, sy_confined; double sx = 0, sy = 0, sx_confined, sy_confined;
Client *c = NULL, *w = NULL; Client *c = NULL, *w = NULL;
Client *closet_drop_client = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
struct wlr_pointer_constraint_v1 *constraint; struct wlr_pointer_constraint_v1 *constraint;
@ -4378,20 +4611,23 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
if (active_constraint && cursor_mode != CurResize && if (active_constraint && cursor_mode != CurResize &&
cursor_mode != CurMove) { cursor_mode != CurMove) {
toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); if (active_constraint->surface ==
if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
seat->pointer_state.focused_surface) {
sx = cursor->x - c->geom.x - c->bw;
sy = cursor->y - c->geom.y - c->bw;
if (wlr_region_confine(&active_constraint->region, sx, sy,
sx + dx, sy + dy, &sx_confined,
&sy_confined)) {
dx = sx_confined - sx;
dy = sy_confined - sy;
}
if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
return; return;
toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
if (c) {
sx = cursor->x - c->geom.x - c->bw;
sy = cursor->y - c->geom.y - c->bw;
if (wlr_region_confine(&active_constraint->region, sx, sy,
sx + dx, sy + dy, &sx_confined,
&sy_confined)) {
dx = sx_confined - sx;
dy = sy_confined - sy;
}
}
} }
} }
@ -4430,6 +4666,24 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
.y = (int32_t)round(cursor->y) - grabcy, .y = (int32_t)round(cursor->y) - grabcy,
.width = grabc->geom.width, .width = grabc->geom.width,
.height = grabc->geom.height}; .height = grabc->geom.height};
if (config.drag_tile_to_tile && grabc->drag_to_tile) {
closet_drop_client = find_closest_tiled_client(grabc);
if (closet_drop_client && dropc && closet_drop_client != dropc) {
dropc->enable_drop_area_draw = false;
client_set_drop_area(dropc);
dropc = closet_drop_client;
dropc->enable_drop_area_draw = true;
client_set_drop_area(dropc);
} else if (closet_drop_client) {
dropc = closet_drop_client;
dropc->enable_drop_area_draw = true;
client_set_drop_area(dropc);
} else if (dropc) {
dropc->enable_drop_area_draw = false;
client_set_drop_area(dropc);
dropc = NULL;
}
}
resize(grabc, grabc->float_geom, 1); resize(grabc, grabc->float_geom, 1);
return; return;
} else if (cursor_mode == CurResize) { } else if (cursor_mode == CurResize) {
@ -4890,10 +5144,20 @@ void exchange_two_client(Client *c1, Client *c2) {
tmp_tags = c2->tags; tmp_tags = c2->tags;
setmon(c2, c1->mon, c1->tags, false); setmon(c2, c1->mon, c1->tags, false);
setmon(c1, tmp_mon, tmp_tags, false); setmon(c1, tmp_mon, tmp_tags, false);
if (c1->mon &&
c1->mon->pertag->ltidxs[c1->mon->pertag->curtag]->id == DWINDLE)
dwindle_swap_clients(
&c1->mon->pertag->dwindle_root[c1->mon->pertag->curtag], c1,
c2);
arrange(c1->mon, false, false); arrange(c1->mon, false, false);
arrange(c2->mon, false, false); arrange(c2->mon, false, false);
focusclient(c1, 0); focusclient(c1, 0);
} else { } else {
if (c1->mon &&
c1->mon->pertag->ltidxs[c1->mon->pertag->curtag]->id == DWINDLE)
dwindle_swap_clients(
&c1->mon->pertag->dwindle_root[c1->mon->pertag->curtag], c1,
c2);
arrange(c1->mon, false, false); arrange(c1->mon, false, false);
} }
@ -5424,7 +5688,7 @@ void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) {
arrange(oldmon, false, false); arrange(oldmon, false, false);
if (m) { if (m) {
/* Make sure window actually overlaps with the monitor */ /* Make sure window actually overlaps with the monitor */
reset_foreign_tolevel(c); reset_foreign_tolevel(c, oldmon, m);
resize(c, c->geom, 0); resize(c, c->geom, 0);
client_reset_mon_tags(c, m, newtags); client_reset_mon_tags(c, m, newtags);
check_match_tag_floating_rule(c, m); check_match_tag_floating_rule(c, m);
@ -5471,7 +5735,9 @@ void show_hide_client(Client *c) {
} }
client_pending_minimized_state(c, 0); client_pending_minimized_state(c, 0);
focusclient(c, 1); focusclient(c, 1);
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true);
if (c->foreign_toplevel)
wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, true);
} }
void create_output(struct wlr_backend *backend, void *data) { void create_output(struct wlr_backend *backend, void *data) {
@ -6056,6 +6322,10 @@ void unmapnotify(struct wl_listener *listener, void *data) {
grabc = NULL; grabc = NULL;
} }
if (c == dropc) {
dropc = NULL;
}
wl_list_for_each(m, &mons, link) { wl_list_for_each(m, &mons, link) {
if (!m->wlr_output->enabled) { if (!m->wlr_output->enabled) {
continue; continue;
@ -6126,6 +6396,7 @@ void unmapnotify(struct wl_listener *listener, void *data) {
c->next_in_stack = NULL; c->next_in_stack = NULL;
c->prev_in_stack = NULL; c->prev_in_stack = NULL;
dwindle_remove_client(c);
wlr_scene_node_destroy(&c->scene->node); wlr_scene_node_destroy(&c->scene->node);
printstatus(); printstatus();
motionnotify(0, NULL, 0, 0, 0, 0); motionnotify(0, NULL, 0, 0, 0, 0);
@ -6244,7 +6515,7 @@ void updatemons(struct wl_listener *listener, void *data) {
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!c->mon && client_surface(c)->mapped) { if (!c->mon && client_surface(c)->mapped) {
c->mon = selmon; c->mon = selmon;
reset_foreign_tolevel(c); reset_foreign_tolevel(c, NULL, c->mon);
} }
if (c->tags == 0 && !c->is_in_scratchpad) { if (c->tags == 0 && !c->is_in_scratchpad) {
c->tags = selmon->tagset[selmon->seltags]; c->tags = selmon->tagset[selmon->seltags];
@ -6317,10 +6588,14 @@ void view_in_mon(const Arg *arg, bool want_animation, Monitor *m,
} }
if (arg->ui == UINT32_MAX) { if (arg->ui == UINT32_MAX) {
m->pertag->prevtag = get_tags_first_tag_num(m->tagset[m->seltags]); if (m->tagset[0] != m->tagset[1]) {
m->seltags ^= 1; /* toggle sel tagset */ m->pertag->prevtag = get_tags_first_tag_num(m->tagset[m->seltags]);
m->pertag->curtag = get_tags_first_tag_num(m->tagset[m->seltags]); m->seltags ^= 1; /* toggle sel tagset */
goto toggleseltags; m->pertag->curtag = get_tags_first_tag_num(m->tagset[m->seltags]);
goto toggleseltags;
} else {
return;
}
} }
if ((m->tagset[m->seltags] & arg->ui & TAGMASK) != 0) { if ((m->tagset[m->seltags] & arg->ui & TAGMASK) != 0) {