diff --git a/README.md b/README.md index 843780ba..d3f6e7cf 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ https://github.com/user-attachments/assets/bb83004a-0563-4b48-ad89-6461a9b78b1f - vertical_tile - vertical_grid - vertical_scroller -- tgmix +- dwindle # Installation @@ -95,6 +95,12 @@ Finally, install the package: emerge --ask --verbose gui-wm/mangowm ``` +## openSUSE +The package is in the community-maintained repository called obs. + +```bash +sudo opi in mangowm +``` ## Fedora Linux The package is in the third-party Terra repository. First, add the [Terra Repository](https://terra.fyralabs.com/). @@ -148,7 +154,7 @@ meson build -Dprefix=/usr sudo ninja -C build install git clone https://github.com/mangowm/mango.git -cd mangowm +cd mango meson build -Dprefix=/usr sudo ninja -C build install ``` diff --git a/assets/config.conf b/assets/config.conf index 527638af..b1804574 100644 --- a/assets/config.conf +++ b/assets/config.conf @@ -86,6 +86,7 @@ enable_floating_snap=0 snap_distance=30 cursor_size=24 drag_tile_to_tile=1 +drag_tile_small=1 # keyboard repeat_rate=25 @@ -119,6 +120,7 @@ scratchpad_height_ratio=0.9 borderpx=4 rootcolor=0x201b14ff bordercolor=0x444444ff +dropcolor=0x8FBA7C55 focuscolor=0xc9b890ff maximizescreencolor=0x89aa61ff urgentcolor=0xad401fff diff --git a/docs/configuration/input.md b/docs/configuration/input.md index ac30f179..ee12906a 100644 --- a/docs/configuration/input.md +++ b/docs/configuration/input.md @@ -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.1–10.0). | +--- + ### Trackpad Settings 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_and_drag` | `1` | Tap and hold to drag items. | | `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_method` | `1` | `1` (Two-finger), `2` (Edge), `4` (Button). | | `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. | | `disable_while_typing` | `1` | Disable trackpad while typing. | | `left_handed` | `0` | Swap left/right buttons. | | `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.1–10.0). | --- **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. - `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. - - `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. - `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. --- - -### 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 diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md index 2e5a1e92..de95f358 100644 --- a/docs/configuration/miscellaneous.md +++ b/docs/configuration/miscellaneous.md @@ -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. | | `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_small` | `1` | Allow dragging a tiled window temporarily to small size.| | `drag_corner` | `3` | Corner for drag-to-tile detection (0: none, 1–3: corners, 4: auto-detect). | | `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_scroll_factor` | `1.0` | Scroll factor for axis scroll speed (0.1–10.0). | ## Multi-Monitor & Tags diff --git a/docs/faq.md b/docs/faq.md index 13c6391b..9c9288de 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -41,11 +41,10 @@ blur_optimized=0 ### 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 syncobj_enable=1 -adaptive_sync=1 ``` --- diff --git a/docs/ipc.md b/docs/ipc.md index 72beefb5..8bb0f5c1 100644 --- a/docs/ipc.md +++ b/docs/ipc.md @@ -64,7 +64,7 @@ mmsg -s -t 2^ ### 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 # Switch to Scroller diff --git a/docs/quick-start.md b/docs/quick-start.md index 85f67cc2..bc192474 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -64,7 +64,7 @@ To get a fully functional desktop experience, we recommend installing the follow | Terminal Emulator | foot, wezterm, alacritty, kitty, ghostty | | Status Bar | waybar, eww, quickshell, ags | | Desktop Shell | Noctalia, DankMaterialShell | -| Wallpaper Setup | swww, swaybg | +| Wallpaper Setup | awww(swww), swaybg | | Notification Daemon | swaync, dunst, mako | | Desktop Portal | xdg-desktop-portal, xdg-desktop-portal-wlr, xdg-desktop-portal-gtk | | Clipboard | wl-clipboard, wl-clip-persist, cliphist | diff --git a/docs/visuals/theming.md b/docs/visuals/theming.md index 8137eb4e..03750e4e 100644 --- a/docs/visuals/theming.md +++ b/docs/visuals/theming.md @@ -26,6 +26,9 @@ rootcolor=0x323232ff # Inactive window border bordercolor=0x444444ff +# Drop shadow when dragging windows +dropcolor=0x8FBA7C55 + # Active window border focuscolor=0xc66b25ff diff --git a/docs/window-management/layouts.md b/docs/window-management/layouts.md index 26c05fe4..9b0b8537 100644 --- a/docs/window-management/layouts.md +++ b/docs/window-management/layouts.md @@ -18,7 +18,7 @@ mangowm supports a variety of layouts that can be assigned per tag. - `vertical_scroller` - `vertical_grid` - `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 You can switch layouts dynamically or set a default for specific tags using [Tag Rules](/docs/window-management/rules#tag-rules). diff --git a/meson.build b/meson.build index 64394cd9..ca9f2c2b 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('mango', ['c', 'cpp'], - version : '0.12.8', + version : '0.12.9', ) subdir('protocols') diff --git a/src/animation/client.h b/src/animation/client.h index e94f872a..8ce81cd2 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -510,6 +510,163 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) { 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) { if (c->iskilling || !client_surface(c)->mapped) diff --git a/src/client/client.h b/src/client/client.h index 965b4106..27e8ef33 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -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 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 && c->geom.width - 2 * (int32_t)c->bw < size_hints->min_width) width = size_hints->min_width; diff --git a/src/config/parse_config.h b/src/config/parse_config.h index a68dbc7c..7652ea1a 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -226,6 +226,7 @@ typedef struct { int32_t snap_distance; int32_t enable_floating_snap; int32_t drag_tile_to_tile; + int32_t drag_tile_small; uint32_t swipe_min_threshold; float focused_opacity; float unfocused_opacity; @@ -241,6 +242,15 @@ typedef struct { int32_t center_master_overspread; 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_corner; uint32_t enable_hotarea; @@ -262,26 +272,33 @@ typedef struct { int32_t repeat_delay; uint32_t numlockon; - /* Trackpad */ - 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; + /* common pointer */ int32_t disable_while_typing; int32_t left_handed; int32_t middle_button_emulation; - uint32_t accel_profile; - double accel_speed; uint32_t scroll_method; uint32_t scroll_button; uint32_t click_method; 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; + /* 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_layer; int32_t blur_optimized; @@ -297,6 +314,7 @@ typedef struct { int32_t shadows_position_y; float shadowscolor[4]; + /* appearance */ int32_t smartgaps; uint32_t gappih; uint32_t gappiv; @@ -307,6 +325,7 @@ typedef struct { float scratchpad_height_ratio; float rootcolor[4]; float bordercolor[4]; + float dropcolor[4]; float focuscolor[4]; float maximizescreencolor[4]; float urgentcolor[4]; @@ -1440,6 +1459,8 @@ bool parse_option(Config *config, char *key, char *value) { config->enable_floating_snap = atoi(value); } else if (strcmp(key, "drag_tile_to_tile") == 0) { 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) { config->swipe_min_threshold = atoi(value); } 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); } else if (strcmp(key, "center_when_single_stack") == 0) { 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) { config->hotarea_size = atoi(value); } 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); } else if (strcmp(key, "middle_button_emulation") == 0) { config->middle_button_emulation = atoi(value); - } else if (strcmp(key, "accel_profile") == 0) { - config->accel_profile = atoi(value); - } else if (strcmp(key, "accel_speed") == 0) { - config->accel_speed = atof(value); + } else if (strcmp(key, "mouse_accel_profile") == 0) { + config->mouse_accel_profile = atoi(value); + } else if (strcmp(key, "mouse_accel_speed") == 0) { + 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) { config->scroll_method = atoi(value); } 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); } else if (strcmp(key, "axis_scroll_factor") == 0) { 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) { config->gappih = atoi(value); } else if (strcmp(key, "gappiv") == 0) { @@ -1730,6 +1771,17 @@ bool parse_option(Config *config, char *key, char *value) { } else { 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) { int64_t color = parse_color(value); if (color == -1) { @@ -3154,6 +3206,16 @@ void override_config(void) { config.center_when_single_stack = CLAMP_INT(config.center_when_single_stack, 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_corner = CLAMP_INT(config.hotarea_corner, 0, 3); config.enable_hotarea = CLAMP_INT(config.enable_hotarea, 0, 1); @@ -3167,6 +3229,7 @@ void override_config(void) { config.drag_floating_refresh_interval = 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_small = CLAMP_INT(config.drag_tile_small, 0, 1); config.allow_tearing = CLAMP_INT(config.allow_tearing, 0, 2); config.allow_shortcuts_inhibit = 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.mouse_natural_scrolling = CLAMP_INT(config.mouse_natural_scrolling, 0, 1); - config.accel_profile = CLAMP_INT(config.accel_profile, 0, 2); - config.accel_speed = CLAMP_FLOAT(config.accel_speed, -1.0f, 1.0f); + config.mouse_accel_profile = CLAMP_INT(config.mouse_accel_profile, 0, 2); + 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_button = CLAMP_INT(config.scroll_button, 272, 279); 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.axis_scroll_factor = 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.gappiv = CLAMP_INT(config.gappiv, 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_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.numlockon = 0; config.capslock = 0; @@ -3315,6 +3393,7 @@ void set_value_default() { config.scratchpad_cross_monitor = 0; config.focus_cross_tag = 0; config.axis_scroll_factor = 1.0; + config.trackpad_scroll_factor = 1.0; config.view_current_to_back = 0; config.single_scratchpad = 1; config.xwayland_persistence = 1; @@ -3328,6 +3407,7 @@ void set_value_default() { config.no_radius_when_single = 0; config.snap_distance = 30; config.drag_tile_to_tile = 0; + config.drag_tile_small = 1; config.enable_floating_snap = 0; config.swipe_min_threshold = 1; @@ -3355,8 +3435,10 @@ void set_value_default() { config.disable_while_typing = 1; config.left_handed = 0; config.middle_button_emulation = 0; - config.accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; - config.accel_speed = 0.0; + config.mouse_accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; + 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_button = 274; 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[2] = 0x44 / 255.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[1] = 0x6b / 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); } -void reapply_border(void) { +void reapply_property(void) { Client *c = NULL; // reset border width when config change @@ -3684,6 +3770,8 @@ void reapply_border(void) { if (!c->isnoborder && !c->isfullscreen) { c->bw = config.borderpx; } + + wlr_scene_rect_set_color(c->droparea, config.dropcolor); } } } @@ -3829,7 +3917,7 @@ void reset_option(void) { run_exec(); reapply_cursor_style(); - reapply_border(); + reapply_property(); reapply_rootbg(); reapply_keyboard(); reapply_pointer(); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 8e4ffc41..7d5e9b12 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -112,6 +112,10 @@ int32_t exchange_client(const Arg *arg) { Client *tc = direction_select(arg); tc = get_focused_stack_client(tc); + + if (!tc) + return 0; + exchange_two_client(c, tc); return 0; } @@ -378,11 +382,20 @@ int32_t moveresize(const Arg *arg) { grabc->drag_to_tile = true; exit_scroller_stack(grabc); setfloating(grabc, 1); + grabc->drag_tile_float_backup_geom = grabc->float_geom; grabc->old_stack_inner_per = 0.0f; grabc->old_master_inner_per = 0.0f; 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) { case CurMove: @@ -1124,7 +1137,7 @@ int32_t tag(const Arg *arg) { } int32_t tagmon(const Arg *arg) { - Monitor *m = NULL, *cm = NULL; + Monitor *m = NULL, *cm = NULL, *oldmon = NULL; if (!selmon) return 0; Client *c = focustop(selmon); @@ -1132,6 +1145,8 @@ int32_t tagmon(const Arg *arg) { if (!c) return 0; + oldmon = c->mon; + if (arg->i != UNDIR) { m = dirtomon(arg->i); } else if (arg->v) { @@ -1166,12 +1181,12 @@ int32_t tagmon(const Arg *arg) { setmon(c, m, newtags, true); client_update_oldmonname_record(c, m); - reset_foreign_tolevel(c); + reset_foreign_tolevel(c, oldmon, c->mon); 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 = - (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; 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; } -int32_t scroller_stack(const Arg *arg) { - if (!selmon) - 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; +int32_t scroller_apply_stack(Client *c, Client *target_client, + int32_t direction) { + Client *source_stack_head = NULL; + Client *stack_head = NULL; bool is_horizontal_layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true : false; - Client *target_client = find_client_by_direction(c, arg, false, true); - if (target_client) { stack_head = get_scroll_stack_head(target_client); } @@ -1820,32 +1829,32 @@ int32_t scroller_stack(const Arg *arg) { setmaximizescreen(c, 0); } - if (c->prev_in_stack) { - if ((is_horizontal_layout && arg->i == LEFT) || - (!is_horizontal_layout && arg->i == UP)) { + if (c->prev_in_stack && direction != UNDIR) { + if ((is_horizontal_layout && direction == LEFT) || + (!is_horizontal_layout && direction == UP)) { exit_scroller_stack(c); wl_list_remove(&c->link); wl_list_insert(source_stack_head->link.prev, &c->link); arrange(selmon, false, false); - } else if ((is_horizontal_layout && arg->i == RIGHT) || - (!is_horizontal_layout && arg->i == DOWN)) { + } else if ((is_horizontal_layout && direction == RIGHT) || + (!is_horizontal_layout && direction == DOWN)) { exit_scroller_stack(c); wl_list_remove(&c->link); wl_list_insert(&source_stack_head->link, &c->link); arrange(selmon, false, false); } return 0; - } else if (c->next_in_stack) { + } else if (c->next_in_stack && direction != UNDIR) { Client *next_in_stack = c->next_in_stack; - if ((is_horizontal_layout && arg->i == LEFT) || - (!is_horizontal_layout && arg->i == UP)) { + if ((is_horizontal_layout && direction == LEFT) || + (!is_horizontal_layout && direction == UP)) { exit_scroller_stack(c); wl_list_remove(&c->link); wl_list_insert(next_in_stack->link.prev, &c->link); arrange(selmon, false, false); - } else if ((is_horizontal_layout && arg->i == RIGHT) || - (!is_horizontal_layout && arg->i == DOWN)) { + } else if ((is_horizontal_layout && direction == RIGHT) || + (!is_horizontal_layout && direction == DOWN)) { exit_scroller_stack(c); wl_list_remove(&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) { 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 Client *stack_tail = target_client; while (stack_tail->next_in_stack) { stack_tail = stack_tail->next_in_stack; } - // Add c to the stack - stack_tail->next_in_stack = c; - c->prev_in_stack = stack_tail; - c->next_in_stack = NULL; + scroller_insert_stack(c, stack_tail, false); - if (stack_head->ismaximizescreen) { - setmaximizescreen(stack_head, 0); - } - - if (stack_head->isfullscreen) { - setfullscreen(stack_head, 0); - } - - arrange(selmon, false, false); 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) { if (!selmon || !selmon->sel) return 0; diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h index 28aaeeeb..d938ce51 100644 --- a/src/ext-protocol/ext-workspace.h +++ b/src/ext-protocol/ext-workspace.h @@ -7,15 +7,11 @@ typedef struct Monitor Monitor; struct workspace { - struct wl_list link; // Link in global workspaces list - uint32_t tag; // Numeric identifier (1-9, 0=overview) - Monitor *m; // Associated monitor - struct wlr_ext_workspace_handle_v1 *ext_workspace; // Protocol object - /* Event listeners */ - struct wl_listener activate; - struct wl_listener deactivate; - struct wl_listener assign; - struct wl_listener remove; + struct wl_list link; + uint32_t tag; + Monitor *m; + struct wlr_ext_workspace_handle_v1 *ext_workspace; + struct wl_listener commit; }; 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, - void *data) { - struct workspace *workspace = - wl_container_of(listener, workspace, activate); +static void handle_ext_commit(struct wl_listener *listener, void *data) { + struct wlr_ext_workspace_v1_commit_event *event = data; + struct wlr_ext_workspace_v1_request *request; - if (workspace->m->isoverview) { - return; + wl_list_for_each(request, event->requests, link) { + 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) { @@ -76,8 +102,6 @@ static const char *get_name_from_tag(uint32_t tag) { } 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); wl_list_remove(&workspace->link); free(workspace); @@ -112,17 +136,12 @@ static void add_workspace_by_tag(int32_t tag, Monitor *m) { workspace->m = m; workspace->ext_workspace = wlr_ext_workspace_handle_v1_create( ext_manager, name, EXT_WORKSPACE_ENABLE_CAPS); + + workspace->ext_workspace->data = workspace; + wlr_ext_workspace_handle_v1_set_group(workspace->ext_workspace, m->ext_group); 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) { @@ -180,8 +199,11 @@ void refresh_monitors_workspaces_status(Monitor *m) { } void workspaces_init() { - /* Create the global workspace manager with activation capability */ ext_manager = wlr_ext_workspace_manager_v1_create(dpy, 1); - /* Initialize the global workspaces list */ + 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); } \ No newline at end of file diff --git a/src/ext-protocol/foreign-toplevel.h b/src/ext-protocol/foreign-toplevel.h index 89f3839a..0c09e0c2 100644 --- a/src/ext-protocol/foreign-toplevel.h +++ b/src/ext-protocol/foreign-toplevel.h @@ -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); uint32_t target; - if (c->swallowing) + if (c->swallowing || !c->mon) return; 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); struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; - if (c->swallowing) + if (c->swallowing || !c->mon) return; 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); struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; - if (c->swallowing) + if (c->swallowing || !c->mon) return; 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); struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; - if (c->swallowing) + if (c->swallowing || !c->mon) return; 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_close_request.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; } @@ -144,7 +140,23 @@ void add_foreign_toplevel(Client *c) { } } -void reset_foreign_tolevel(Client *c) { - remove_foreign_topleve(c); - add_foreign_toplevel(c); +void reset_foreign_tolevel(Client *c, Monitor *oldmon, Monitor *newmon) { + if (!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); } diff --git a/src/ext-protocol/wlr_ext_workspace_v1.c b/src/ext-protocol/wlr_ext_workspace_v1.c index 2d781b34..dca93e93 100644 --- a/src/ext-protocol/wlr_ext_workspace_v1.c +++ b/src/ext-protocol/wlr_ext_workspace_v1.c @@ -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 "ext-workspace-v1-protocol.h" #include @@ -11,27 +7,6 @@ #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_output *output; struct wlr_ext_workspace_group_handle_v1 *group; @@ -117,7 +92,7 @@ static void workspace_handle_activate(struct wl_client *client, return; } 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); } @@ -136,7 +111,7 @@ workspace_handle_deactivate(struct wl_client *client, return; } 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); } @@ -157,8 +132,8 @@ static void workspace_handle_assign(struct wl_client *client, return; } req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN; - req->group = group_res->group; - req->workspace = workspace_res->workspace; + req->assign.group = group_res->group; + req->assign.workspace = workspace_res->workspace; wl_list_insert(workspace_res->manager->requests.prev, &req->link); } @@ -176,7 +151,7 @@ static void workspace_handle_remove(struct wl_client *client, return; } 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); } @@ -202,14 +177,14 @@ static void group_handle_create_workspace(struct wl_client *client, wl_resource_post_no_memory(group_resource); return; } - req->name = strdup(name); - if (!req->name) { + req->create_workspace.name = strdup(name); + if (!req->create_workspace.name) { free(req); wl_resource_post_no_memory(group_resource); return; } 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); } @@ -313,10 +288,55 @@ static struct wlr_ext_workspace_group_v1_resource *create_group_resource( return group_res; } -static void destroy_request(struct wlr_ext_workspace_v1_request *req) { - wl_list_remove(&req->link); - free(req->name); - free(req); +static void +destroy_requests(struct wlr_ext_workspace_manager_v1_resource *manager_res) { + struct wlr_ext_workspace_v1_request *req, *tmp; + 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, @@ -327,32 +347,11 @@ static void manager_handle_commit(struct wl_client *client, return; } - 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:; - struct wlr_ext_workspace_group_handle_v1_create_workspace_event - 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); - } + struct wlr_ext_workspace_v1_commit_event commit_event = { + .requests = &manager_res->requests, + }; + wl_signal_emit_mutable(&manager_res->manager->events.commit, &commit_event); + destroy_requests(manager_res); } 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( struct wlr_ext_workspace_manager_v1_resource *manager_res) { - struct wlr_ext_workspace_v1_request *req, *tmp; - wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { - destroy_request(req); - } + destroy_requests(manager_res); + struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2; wl_list_for_each_safe(workspace_res, tmp2, &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_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)); 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->workspaces); wl_list_init(&manager->resources); + wl_signal_init(&manager->events.commit); wl_signal_init(&manager->events.destroy); return manager; @@ -601,7 +600,6 @@ wlr_ext_workspace_group_handle_v1_create( wl_list_init(&group->outputs); wl_list_init(&group->resources); - wl_signal_init(&group->events.create_workspace); wl_signal_init(&group->events.destroy); 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); - assert(wl_list_empty(&group->events.create_workspace.listener_list)); assert(wl_list_empty(&group->events.destroy.listener_list)); 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; wl_list_for_each(manager_res, &group->manager->resources, link) { - struct wlr_ext_workspace_v1_request *req, *tmp2; - wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) { - if (req->group == group) { - destroy_request(req); - } - } + clear_requests_by(manager_res, group, NULL); } 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_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_list_insert(&manager->workspaces, &workspace->link); + wl_list_insert(manager->workspaces.prev, &workspace->link); struct wlr_ext_workspace_manager_v1_resource *manager_res; 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); - 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)); if (workspace->group) { @@ -873,12 +857,7 @@ void wlr_ext_workspace_handle_v1_destroy( struct wlr_ext_workspace_manager_v1_resource *manager_res; wl_list_for_each(manager_res, &workspace->manager->resources, link) { - struct wlr_ext_workspace_v1_request *req, *tmp2; - wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) { - if (req->workspace == workspace) { - destroy_request(req); - } - } + clear_requests_by(manager_res, NULL, workspace); } manager_schedule_done(workspace->manager); @@ -929,23 +908,22 @@ void wlr_ext_workspace_handle_v1_set_name( 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( - struct wlr_ext_workspace_handle_v1 *workspace, - struct wl_array *coordinates) { - assert(coordinates); - - if (array_equal(&workspace->coordinates, coordinates)) { + struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords, + size_t coords_len) { + size_t size = coords_len * sizeof(coords[0]); + if (size == workspace->coordinates.size && + (size == 0 || memcmp(workspace->coordinates.data, coords, size) == 0)) { return; } wl_array_release(&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; wl_list_for_each(workspace_res, &workspace->resources, link) { diff --git a/src/ext-protocol/wlr_ext_workspace_v1.h b/src/ext-protocol/wlr_ext_workspace_v1.h index a2a733b3..183d5801 100644 --- a/src/ext-protocol/wlr_ext_workspace_v1.h +++ b/src/ext-protocol/wlr_ext_workspace_v1.h @@ -1,21 +1,69 @@ -// 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 +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * 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 #include 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 wl_global *global; struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link struct { + struct wl_signal commit; // wlr_ext_workspace_v1_commit_event struct wl_signal destroy; } events; + void *data; + struct { struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link 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_manager_v1 *manager; uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities struct { - struct wl_signal - create_workspace; // wlr_ext_workspace_group_handle_v1_create_workspace_event struct wl_signal destroy; } events; struct wl_list link; // wlr_ext_workspace_manager_v1.groups + void *data; + struct { struct wl_list outputs; // wlr_ext_workspace_v1_group_output.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 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; } events; struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces + void *data; + struct { 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( struct wlr_ext_workspace_handle_v1 *workspace, const char *name); void wlr_ext_workspace_handle_v1_set_coordinates( - struct wlr_ext_workspace_handle_v1 *workspace, - struct wl_array *coordinates); + struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords, + size_t coords_len); void wlr_ext_workspace_handle_v1_set_active( struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); void wlr_ext_workspace_handle_v1_set_urgent( struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); void wlr_ext_workspace_handle_v1_set_hidden( struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); + +#endif diff --git a/src/fetch/client.h b/src/fetch/client.h index 8fe831be..be9be420 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -558,7 +558,7 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) { if (id != SCROLLER && id != VERTICAL_SCROLLER && id != TILE && id != VERTICAL_TILE && id != DECK && id != VERTICAL_DECK && - id != CENTER_TILE && id != RIGHT_TILE && id != TGMIX) + id != CENTER_TILE && id != RIGHT_TILE) return false; if (id == SCROLLER || id == VERTICAL_SCROLLER) { @@ -583,15 +583,6 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) { 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 (tc->ismaster ^ sc->ismaster) return false; diff --git a/src/layout/arrange.h b/src/layout/arrange.h index 04f4554b..b822a729 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -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, int32_t offsety, uint32_t time, bool isvertical) { Client *tc = NULL; @@ -692,8 +707,7 @@ void resize_tile_client(Client *grabc, bool isdrag, int32_t offsetx, const Layout *current_layout = grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag]; if (current_layout->id == TILE || current_layout->id == DECK || - current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE || - (current_layout->id == TGMIX && grabc->mon->visible_tiling_clients <= 3) + current_layout->id == CENTER_TILE || current_layout->id == RIGHT_TILE ) { 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); } else if (current_layout->id == VERTICAL_SCROLLER) { resize_tile_scroller(grabc, isdrag, offsetx, offsety, time, true); + } else if (current_layout->id == DWINDLE) { + resize_tile_dwindle(grabc, isdrag, offsetx, offsety, time, true); } } diff --git a/src/layout/dwindle.h b/src/layout/dwindle.h new file mode 100644 index 00000000..94d65be6 --- /dev/null +++ b/src/layout/dwindle.h @@ -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); +} diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index eaa7b5c2..e69b4cdd 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -993,15 +993,4 @@ monocle(Monitor *m) { } if ((c = focustop(m))) 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; - } -} +} \ No newline at end of file diff --git a/src/layout/layout.h b/src/layout/layout.h index f896ac27..16773dc7 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -11,7 +11,7 @@ static void vertical_overview(Monitor *m); static void vertical_grid(Monitor *m); static void vertical_scroller(Monitor *m); static void vertical_deck(Monitor *mon); -static void tgmix(Monitor *m); +static void dwindle(Monitor *m); /* layout(s) */ Layout overviewlayout = {"󰃇", overview, "overview"}; @@ -28,7 +28,7 @@ enum { VERTICAL_GRID, VERTICAL_DECK, RIGHT_TILE, - TGMIX, + DWINDLE, }; Layout layouts[] = { @@ -46,5 +46,5 @@ Layout layouts[] = { {"VT", vertical_tile, "vertical_tile", VERTICAL_TILE}, // 垂直平铺布局 {"VG", vertical_grid, "vertical_grid", VERTICAL_GRID}, // 垂直格子布局 {"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局 - {"TG", tgmix, "tgmix", TGMIX}, // 混合布局 + {"DW", dwindle, "dwindle", DWINDLE}, }; \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index 8fdff709..34d4d764 100644 --- a/src/mango.c +++ b/src/mango.c @@ -314,6 +314,7 @@ struct Client { Monitor *mon; struct wlr_scene_tree *scene; struct wlr_scene_rect *border; /* top, bottom, left, right */ + struct wlr_scene_rect *droparea; struct wlr_scene_shadow *shadow; struct wlr_scene_tree *scene_surface; struct wl_list link; @@ -422,6 +423,9 @@ struct Client { bool isfocusing; struct Client *next_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 { @@ -539,6 +543,7 @@ struct Monitor { struct wlr_scene_optimized_blur *blur; char last_surface_ws_name[256]; struct wlr_ext_workspace_group_handle_v1 *ext_group; + bool iscleanuping; }; typedef struct { @@ -555,6 +560,8 @@ typedef struct { struct wl_listener destroy; } SessionLock; +typedef struct DwindleNode DwindleNode; + /* function declarations */ static void applybounds( Client *c, @@ -648,8 +655,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double sy_unaccel); static void motionrelative(struct wl_listener *listener, void *data); -static void reset_foreign_tolevel(Client *c); -static void remove_foreign_topleve(Client *c); +static void reset_foreign_tolevel(Client *c, Monitor *oldmon, Monitor *newmon); static void add_foreign_topleve(Client *c); static void exchange_two_client(Client *c1, Client *c2); 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_maximized_state(Client *c, int32_t ismaximized); 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 "dispatch/bind_declare.h" @@ -870,7 +883,7 @@ static KeyboardGroup *kb_group; static struct wl_list inputdevices; static struct wl_list keyboard_shortcut_inhibitors; static uint32_t cursor_mode; -static Client *grabc; +static Client *grabc, *dropc; static int32_t rzcorner; static int32_t grabcx, grabcy; /* 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_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */ int32_t open_as_floating[LENGTH(tags) + 1]; /* open_as_floating per tag */ + struct DwindleNode *dwindle_root[LENGTH(tags) + 1]; const Layout *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 "fetch/fetch.h" #include "layout/arrange.h" +#include "layout/dwindle.h" #include "layout/horizontal.h" #include "layout/vertical.h" @@ -1143,6 +1158,7 @@ void swallow(Client *c, Client *w) { c->scroller_proportion = w->scroller_proportion; c->next_in_stack = w->next_in_stack; c->prev_in_stack = w->prev_in_stack; + c->isglobal = w->isglobal; if (w->next_in_stack) 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->flink, &c->flink); - if (w->foreign_toplevel) - remove_foreign_topleve(w); + if (w->foreign_toplevel) { + 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(&c->scene->node, true); @@ -1161,10 +1181,25 @@ void swallow(Client *c, Client *w) { if (!c->foreign_toplevel && c->mon) 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_maximized_state(c, w->ismaximizescreen); 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) { @@ -1175,7 +1210,7 @@ bool switch_scratchpad_client_state(Client *c) { Monitor *oldmon = c->mon; c->scratchpad_switching_mon = true; c->mon = selmon; - reset_foreign_tolevel(c); + reset_foreign_tolevel(c, oldmon, c->mon); client_update_oldmonname_record(c, selmon); // 根据新monitor调整窗口尺寸 @@ -1569,12 +1604,17 @@ void applyrules(Client *c) { int32_t fullscreen_state_backup = c->isfullscreen || client_wants_fullscreen(c); - setmon(c, mon, newtags, - !c->isopensilent && - !(client_is_x11_popup(c) && client_should_ignore_focus(c)) && - mon && - (!c->istagsilent || !newtags || - newtags & mon->tagset[mon->seltags])); + bool should_init_get_focus = + !c->isopensilent && + !(client_is_x11_popup(c) && client_should_ignore_focus(c)) && mon && + (!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) { c->old_stack_inner_per = c->stack_inner_per; @@ -1819,6 +1859,9 @@ void arrangelayers(Monitor *m) { if (!m->wlr_output->enabled) return; + if (m->iscleanuping) + return; + /* Arrange exclusive surfaces from top->bottom */ for (i = 3; i >= 0; i--) arrangelayer(m, &m->layers[i], &usable_area, 1); @@ -1833,9 +1876,21 @@ void arrangelayers(Monitor *m) { 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 // 鼠标滚轮事件 axisnotify(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an axis event, * for example when you move the scroll wheel. */ struct wlr_pointer_axis_event *event = data; @@ -1844,6 +1899,7 @@ axisnotify(struct wl_listener *listener, void *data) { AxisBinding *a; int32_t ji; uint32_t adir; + double target_scroll_factor; // IDLE_NOTIFY_ACTIVITY; handlecursoractivity(); 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 */ /* 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( seat, // 滚轮事件发送给客户端也就是窗口 event->time_msec, event->orientation, - event->delta * config.axis_scroll_factor, - roundf(event->delta_discrete * config.axis_scroll_factor), - event->source, event->relative_direction); + event->delta * target_scroll_factor, + roundf(event->delta_discrete * target_scroll_factor), event->source, + event->relative_direction); } 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); } -void place_drag_tile_client(Client *c) { - Client *tc = NULL; - Client *closest_client = NULL; - long min_distant = LONG_MAX; - long temp_distant; - int32_t x, y; +Client *find_closest_tiled_client(Client *c) { + Client *tc, *closest = NULL; + long min_dist = LONG_MAX; wl_list_for_each(tc, &clients, link) { - if (tc != c && ISTILED(tc) && VISIBLEON(tc, c->mon)) { - x = tc->geom.x + (int32_t)(tc->geom.width / 2) - cursor->x; - y = tc->geom.y + (int32_t)(tc->geom.height / 2) - cursor->y; - temp_distant = x * x + y * y; - if (temp_distant < min_distant) { - min_distant = temp_distant; - closest_client = tc; - } + if (tc == c || !ISTILED(tc) || !VISIBLEON(tc, c->mon)) + continue; + + if (cursor->x >= tc->geom.x && + cursor->x < tc->geom.x + tc->geom.width && + cursor->y >= tc->geom.y && + 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); - c->link.next = &closest_client->link; - c->link.prev = closest_client->link.prev; - closest_client->link.prev->next = &c->link; - closest_client->link.prev = &c->link; - } else if (closest_client) { - exchange_two_client(c, closest_client); + + return closest; +} + +void scroller_insert_stack(Client *c, Client *target_client, + bool insert_before) { + Client *stack_head = NULL; + + 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); } bool check_trackpad_disabled(struct wlr_pointer *pointer) { - struct libinput_device *device; - - if (!config.disable_trackpad) + if (!config.disable_trackpad) { 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 // 鼠标按键事件 @@ -2172,10 +2361,16 @@ buttonpress(struct wl_listener *listener, void *data) { last_apply_drap_time = 0; if (tmpc->drag_to_tile && config.drag_tile_to_tile) { place_drag_tile_client(tmpc); + tmpc->float_geom = tmpc->drag_tile_float_backup_geom; } else { apply_window_snap(tmpc); } tmpc->drag_to_tile = false; + if (dropc) { + dropc->enable_drop_area_draw = false; + client_set_drop_area(dropc); + dropc = NULL; + } return; } else { cursor_mode = CurNormal; @@ -2318,6 +2513,8 @@ void cleanupmon(struct wl_listener *listener, void *data) { LayerSurface *l = NULL, *tmp = NULL; uint32_t i; + m->iscleanuping = true; + /* m->layers[i] are intentionally not unlinked */ for (i = 0; i < LENGTH(m->layers); i++) { 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->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); } @@ -2374,7 +2574,13 @@ void closemon(Monitor *m) { if (c->mon == m) { 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; } else { 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); m = wlr_output->data = ecalloc(1, sizeof(*m)); + m->iscleanuping = false; m->skip_frame_timeout = wl_event_loop_add_timer(loop, monitor_skip_frame_timeout_callback, m); m->skiping_frame = false; @@ -3201,6 +3408,22 @@ void destroyinputdevice(struct wl_listener *listener, void *data) { 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) { if (libinput_device_config_tap_get_finger_count(device)) { 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, config.drag_lock); libinput_device_config_tap_set_button_map(device, config.button_map); - libinput_device_config_scroll_set_natural_scroll_enabled( - device, config.trackpad_natural_scrolling); + pointer_set_accel(device, config.trackpad_natural_scrolling, + config.trackpad_accel_profile, + config.trackpad_accel_speed); } else { - libinput_device_config_scroll_set_natural_scroll_enabled( - device, config.mouse_natural_scrolling); + pointer_set_accel(device, config.mouse_natural_scrolling, + config.mouse_accel_profile, config.mouse_accel_speed); } 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)) libinput_device_config_send_events_set_mode(device, 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) { @@ -3577,8 +3790,14 @@ void focusclient(Client *c, int32_t lift) { wl_list_for_each(um, &mons, link) { if (um->wlr_output->enabled && um != selmon && um->sel && !um->sel->iskilling && um->sel->isfocusing) { + um->sel->isfocusing = false; 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) { + c->drop_direction = UNDIR; + c->enable_drop_area_draw = false; c->isfocusing = false; c->isfloating = 0; c->isfakefullscreen = 0; @@ -4173,6 +4394,12 @@ mapnotify(struct wl_listener *listener, void *data) { } // 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->scene, 0, 0, c->isurgent ? config.urgentcolor : config.bordercolor); wlr_scene_node_lower_to_bottom(&c->border->node); @@ -4211,6 +4438,7 @@ mapnotify(struct wl_listener *listener, void *data) { } } else wl_list_insert(clients.prev, &c->link); // 尾部入栈 + wl_list_insert(&fstack, &c->flink); applyrules(c); @@ -4286,7 +4514,11 @@ void set_minimized(Client *c) { c->is_scratchpad_show = 0; focusclient(focustop(selmon), 1); 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_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 sx = 0, sy = 0, sx_confined, sy_confined; Client *c = NULL, *w = NULL; + Client *closet_drop_client = NULL; LayerSurface *l = NULL; struct wlr_surface *surface = NULL; 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 && cursor_mode != CurMove) { - toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); - if (c && active_constraint->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->surface == + seat->pointer_state.focused_surface) { if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) 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, .width = grabc->geom.width, .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); return; } else if (cursor_mode == CurResize) { @@ -4890,10 +5144,20 @@ void exchange_two_client(Client *c1, Client *c2) { tmp_tags = c2->tags; setmon(c2, c1->mon, c1->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(c2->mon, false, false); focusclient(c1, 0); } 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); } @@ -5424,7 +5688,7 @@ void setmon(Client *c, Monitor *m, uint32_t newtags, bool focus) { arrange(oldmon, false, false); if (m) { /* Make sure window actually overlaps with the monitor */ - reset_foreign_tolevel(c); + reset_foreign_tolevel(c, oldmon, m); resize(c, c->geom, 0); client_reset_mon_tags(c, m, newtags); check_match_tag_floating_rule(c, m); @@ -5471,7 +5735,9 @@ void show_hide_client(Client *c) { } client_pending_minimized_state(c, 0); 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) { @@ -6056,6 +6322,10 @@ void unmapnotify(struct wl_listener *listener, void *data) { grabc = NULL; } + if (c == dropc) { + dropc = NULL; + } + wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) { continue; @@ -6126,6 +6396,7 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->next_in_stack = NULL; c->prev_in_stack = NULL; + dwindle_remove_client(c); wlr_scene_node_destroy(&c->scene->node); printstatus(); 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) { if (!c->mon && client_surface(c)->mapped) { c->mon = selmon; - reset_foreign_tolevel(c); + reset_foreign_tolevel(c, NULL, c->mon); } if (c->tags == 0 && !c->is_in_scratchpad) { 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) { - m->pertag->prevtag = get_tags_first_tag_num(m->tagset[m->seltags]); - m->seltags ^= 1; /* toggle sel tagset */ - m->pertag->curtag = get_tags_first_tag_num(m->tagset[m->seltags]); - goto toggleseltags; + if (m->tagset[0] != m->tagset[1]) { + m->pertag->prevtag = get_tags_first_tag_num(m->tagset[m->seltags]); + m->seltags ^= 1; /* toggle sel tagset */ + 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) {