feat: add overview jump mode
Some checks are pending
Sync website / sync-website (push) Waiting to run
Sync wiki / sync-wiki (push) Waiting to run

This commit is contained in:
DreamMaoMao 2026-06-16 18:59:25 +08:00
parent 24fb167ae6
commit a515ad9b91
13 changed files with 576 additions and 1 deletions

View file

@ -161,6 +161,7 @@ bindr=Super,Super_L,spawn,rofi -show run
| `reload_config` | - | Hot-reload configuration. |
| `quit` | - | Exit mangowm. |
| `toggleoverview` | - | Toggle overview mode. |
| `togglejump` | - | Toggle overview with jump mode. |
| `create_virtual_output` | - | Create a headless monitor (for VNC/Sunshine). |
| `destroy_all_virtual_output` | - | Destroy all virtual monitors. |
| `toggleoverlay` | - | Toggle overlay state for the focused window. |

View file

@ -274,6 +274,7 @@ If your distribution isn't listed above, or you want the latest unreleased chang
> - `hwdata`
> - `seatd`
> - `pcre2`
> - `pango`
> - `cjson`
> - `pixman`
> - `xorg-xwayland`

View file

@ -52,6 +52,18 @@ You can also color-code windows based on their state:
> **Tip:** For scratchpad window sizing, see [Scratchpad](/docs/window-management/scratchpad) configuration.
### Overview Jump Mode
| Setting | Default | Description |
| :--- | :--- | :--- |
| `jump_hit_fg_color` | `0xc4939dff` | label text color. |
| `jump_hit_bg_color` | `0x201b14ff` | label background color.
| `jump_hit_border_color` | `0x8BAA9Bff` | label border color.
| `jump_hit_border_width` | `4` | label border width.
| `jump_hit_corner_radius` | `5` | label corner radius.
| `jump_hit_padding_x` | `10` | label horizontal padding.
| `jump_hit_padding_y` | `10` | label vertical padding.
| `jump_hit_font_desc` | `monospace Bold 24` | label font set.|
## Cursor Theme
Set the size and theme of your mouse cursor.

View file

@ -54,6 +54,7 @@
hwdata
seatd
pcre2
pango
cjson
libxcb
pixman

View file

@ -37,6 +37,7 @@ pcre2_dep = dependency('libpcre2-8')
libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1')
pixman_dep = dependency('pixman-1')
cjson_dep = dependency('libcjson')
pangocairo_dep = dependency('pangocairo')
# version info
git = find_program('git', required : false)
@ -94,6 +95,7 @@ endif
executable('mango',
'src/mango.c',
'src/common/util.c',
'src/draw/text-node.c',
'src/ext-protocol/wlr_ext_workspace_v1.c',
wayland_sources,
dependencies : [
@ -109,6 +111,7 @@ executable('mango',
pcre2_dep,
pixman_dep,
cjson_dep,
pangocairo_dep,
],
install : true,
c_args : c_args,

View file

@ -5,6 +5,7 @@
libxcb,
libxkbcommon,
pcre2,
pango,
cjson,
pixman,
pkg-config,
@ -49,6 +50,7 @@ stdenv.mkDerivation {
libxcb
libxkbcommon
pcre2
pango
cjson
pixman
wayland

View file

@ -261,6 +261,7 @@ typedef struct {
int32_t enable_hotarea;
int32_t ov_tab_mode;
int32_t ov_no_resize;
int32_t overviewgappi;
int32_t overviewgappo;
uint32_t cursor_hide_timeout;
@ -407,6 +408,7 @@ typedef struct {
struct xkb_context *ctx;
struct xkb_keymap *keymap;
JumphitData jumhitdata;
} Config;
typedef int32_t (*FuncType)(const Arg *);
@ -1030,6 +1032,9 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
} else if (strcmp(func_name, "toggleoverview") == 0) {
func = toggleoverview;
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "togglejump") == 0) {
func = togglejump;
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "set_proportion") == 0) {
func = set_proportion;
(*arg).f = atof(arg_value);
@ -1758,6 +1763,50 @@ bool parse_option(Config *config, char *key, char *value) {
config->cursor_size = atoi(value);
} else if (strcmp(key, "cursor_theme") == 0) {
config->cursor_theme = strdup(value);
} else if (strcmp(key, "jump_hit_fg_color") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_fg_color "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->jumhitdata.fg_color, color);
}
} else if (strcmp(key, "jump_hit_font_desc") == 0) {
config->jumhitdata.font_desc = strdup(value);
} else if (strcmp(key, "jump_hit_bg_color") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_bg_color "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->jumhitdata.bg_color, color);
}
} else if (strcmp(key, "jump_hit_border_color") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(
stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid jump_hit_border_color "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->jumhitdata.border_color, color);
}
} else if (strcmp(key, "jump_hit_border_width") == 0) {
config->jumhitdata.border_width = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "jump_hit_corner_radius") == 0) {
config->jumhitdata.corner_radius = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "jump_hit_padding_x") == 0) {
config->jumhitdata.padding_x = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "jump_hit_padding_y") == 0) {
config->jumhitdata.padding_y = CLAMP_INT(atoi(value), 0, 100);
} else if (strcmp(key, "disable_while_typing") == 0) {
config->disable_while_typing = atoi(value);
} else if (strcmp(key, "left_handed") == 0) {
@ -3247,6 +3296,11 @@ void free_config(void) {
config.cursor_theme = NULL;
}
if (config.jumhitdata.font_desc) {
free((void *)config.jumhitdata.font_desc);
config.jumhitdata.font_desc = NULL;
}
if (config.tablet_map_to_mon) {
free(config.tablet_map_to_mon);
config.tablet_map_to_mon = NULL;
@ -3437,6 +3491,15 @@ void override_config(void) {
config.focused_opacity = CLAMP_FLOAT(config.focused_opacity, 0.0f, 1.0f);
config.unfocused_opacity =
CLAMP_FLOAT(config.unfocused_opacity, 0.0f, 1.0f);
config.jumhitdata.border_width =
CLAMP_INT(config.jumhitdata.border_width, 0, 100);
config.jumhitdata.corner_radius =
CLAMP_INT(config.jumhitdata.corner_radius, 0, 100);
config.jumhitdata.padding_x =
CLAMP_INT(config.jumhitdata.padding_x, 0, 100);
config.jumhitdata.padding_y =
CLAMP_INT(config.jumhitdata.padding_y, 0, 100);
}
void set_value_default() {
@ -3475,7 +3538,6 @@ void set_value_default() {
config.log_level = WLR_ERROR;
config.numlockon = 0;
config.capslock = 0;
config.ov_tab_mode = 1;
config.ov_no_resize = 1;
config.hotarea_size = 10;
@ -3613,6 +3675,23 @@ void set_value_default() {
config.animation_curve_opafadeout[2] = 0.5;
config.animation_curve_opafadeout[3] = 0.5;
config.jumhitdata.fg_color[0] = 0xc4 / 255.0f;
config.jumhitdata.fg_color[1] = 0x93 / 255.0f;
config.jumhitdata.fg_color[2] = 0x9d / 255.0f;
config.jumhitdata.fg_color[3] = 1.0f;
config.jumhitdata.bg_color[0] = 0x32 / 255.0f;
config.jumhitdata.bg_color[1] = 0x32 / 255.0f;
config.jumhitdata.bg_color[2] = 0x32 / 255.0f;
config.jumhitdata.bg_color[3] = 1.0f;
config.jumhitdata.border_color[0] = 0x8b / 255.0f;
config.jumhitdata.border_color[1] = 0xaa / 255.0f;
config.jumhitdata.border_color[2] = 0x9b / 255.0f;
config.jumhitdata.border_color[3] = 1.0f;
config.jumhitdata.border_width = 4;
config.jumhitdata.corner_radius = 5;
config.jumhitdata.padding_x = 10;
config.jumhitdata.padding_y = 10;
config.rootcolor[0] = 0x32 / 255.0f;
config.rootcolor[1] = 0x32 / 255.0f;
config.rootcolor[2] = 0x32 / 255.0f;
@ -3725,6 +3804,7 @@ bool parse_config(void) {
config.tag_rules = NULL;
config.tag_rules_count = 0;
config.cursor_theme = NULL;
config.jumhitdata.font_desc = NULL;
config.tablet_map_to_mon = NULL;
strcpy(config.keymode, "default");

View file

@ -3,6 +3,7 @@ int32_t restore_minimized(const Arg *arg);
int32_t toggle_scratchpad(const Arg *arg);
int32_t focusdir(const Arg *arg);
int32_t toggleoverview(const Arg *arg);
int32_t togglejump(const Arg *arg);
int32_t set_proportion(const Arg *arg);
int32_t switch_proportion_preset(const Arg *arg);
int32_t zoom(const Arg *arg);

View file

@ -1737,6 +1737,10 @@ int32_t toggleoverview(const Arg *arg) {
uint32_t target;
uint32_t visible_client_number = 0;
if (!selmon->isoverview && selmon->is_jump_mode) {
finish_jump_mode(selmon);
}
if (selmon->isoverview) {
wl_list_for_each(c, &clients, link) if (c && c->mon == selmon &&
!client_is_unmanaged(c) &&
@ -1781,6 +1785,7 @@ int32_t toggleoverview(const Arg *arg) {
}
}
} else {
selmon->tagset[selmon->seltags] = target;
wl_list_for_each(c, &clients, link) {
if (c && c->mon == selmon && !c->iskilling &&
@ -1794,6 +1799,24 @@ int32_t toggleoverview(const Arg *arg) {
view(&(Arg){.ui = target}, false);
fix_mon_tagset_from_overview(selmon);
refresh_monitors_workspaces_status(selmon);
return 0;
}
int32_t togglejump(const Arg *arg) {
if (!selmon)
return 0;
if (!selmon->isoverview) {
begin_jump_mode(selmon);
toggleoverview(arg);
return 0;
}
if (selmon->isoverview) {
toggleoverview(arg);
}
return 0;
}

308
src/draw/text-node.c Normal file
View file

@ -0,0 +1,308 @@
#include "text-node.h"
#include <cairo.h>
#include <drm_fourcc.h>
#include <glib.h>
#include <pango/pangocairo.h>
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/interfaces/wlr_buffer.h>
// 自定义 wlr_buffer用于将 Cairo Surface 包装并注入 wlr_scene
struct mango_text_buffer {
struct wlr_buffer base;
cairo_surface_t *surface;
};
// 全局字体描述缓存
static GHashTable *font_desc_cache = NULL;
static PangoFontDescription *get_cached_font_desc(const char *font_desc) {
if (!font_desc_cache) {
font_desc_cache =
g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify)pango_font_description_free);
}
PangoFontDescription *desc =
g_hash_table_lookup(font_desc_cache, font_desc);
if (!desc) {
desc = pango_font_description_from_string(font_desc);
g_hash_table_insert(font_desc_cache, g_strdup(font_desc), desc);
}
return desc;
}
void mango_text_global_finish(void) {
if (font_desc_cache) {
g_hash_table_destroy(font_desc_cache);
font_desc_cache = NULL;
}
}
// wlr_buffer 实现
static void text_buffer_destroy(struct wlr_buffer *wlr_buffer) {
struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base);
cairo_surface_destroy(buf->surface);
free(buf);
}
static bool text_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer,
uint32_t flags, void **data,
uint32_t *format,
size_t *stride) {
(void)flags;
struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base);
*data = cairo_image_surface_get_data(buf->surface);
*format = DRM_FORMAT_ARGB8888;
*stride = cairo_image_surface_get_stride(buf->surface);
return true;
}
static void text_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {}
static const struct wlr_buffer_impl text_buffer_impl = {
.destroy = text_buffer_destroy,
.begin_data_ptr_access = text_buffer_begin_data_ptr_access,
.end_data_ptr_access = text_buffer_end_data_ptr_access,
};
struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent,
JumphitData data) {
struct mango_text_node *node = calloc(1, sizeof(*node));
if (!node)
return NULL;
node->scene_buffer = wlr_scene_buffer_create(parent, NULL);
if (!node->scene_buffer) {
free(node);
return NULL;
}
node->fg_color[0] = data.fg_color[0];
node->fg_color[1] = data.fg_color[1];
node->fg_color[2] = data.fg_color[2];
node->fg_color[3] = data.fg_color[3];
node->bg_color[0] = data.bg_color[0];
node->bg_color[1] = data.bg_color[1];
node->bg_color[2] = data.bg_color[2];
node->bg_color[3] = data.bg_color[3];
node->border_color[0] = data.border_color[0];
node->border_color[1] = data.border_color[1];
node->border_color[2] = data.border_color[2];
node->border_color[3] = data.border_color[3];
node->border_width = data.border_width;
node->corner_radius = data.corner_radius;
node->padding_x = data.padding_x;
node->padding_y = data.padding_y;
node->font_desc = data.font_desc ? data.font_desc : "monospace Bold 24";
return node;
}
void mango_text_node_destroy(struct mango_text_node *node) {
if (!node)
return;
wlr_scene_node_destroy(&node->scene_buffer->node);
free(node);
}
void mango_text_node_set_background(struct mango_text_node *node, float r,
float g, float b, float a) {
if (!node)
return;
node->bg_color[0] = r;
node->bg_color[1] = g;
node->bg_color[2] = b;
node->bg_color[3] = a;
}
void mango_text_node_set_border(struct mango_text_node *node, float r, float g,
float b, float a, float width, float radius) {
if (!node)
return;
node->border_color[0] = r;
node->border_color[1] = g;
node->border_color[2] = b;
node->border_color[3] = a;
node->border_width = width > 0.0f ? width : 0.0f;
node->corner_radius = radius >= 0.0f ? radius : 0.0f;
}
void mango_text_node_set_padding(struct mango_text_node *node, float pad_x,
float pad_y) {
if (!node)
return;
node->padding_x = pad_x >= 0.0f ? pad_x : 0.0f;
node->padding_y = pad_y >= 0.0f ? pad_y : 0.0f;
}
static void draw_rounded_rect(cairo_t *cr, double x, double y, double w,
double h, double r) {
// 使用 Cairo 的标准圆角矩形路径
double degrees = G_PI / 180.0;
cairo_new_sub_path(cr);
cairo_arc(cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees);
cairo_arc(cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees);
cairo_arc(cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees);
cairo_arc(cr, x + r, y + r, r, 180 * degrees, 270 * degrees);
cairo_close_path(cr);
}
void mango_text_node_update(struct mango_text_node *node, const char *text,
float scale) {
if (!node || !text)
return;
if (scale <= 0.0f)
scale = 1.0f;
const char *font_desc = node->font_desc;
PangoFontDescription *desc = get_cached_font_desc(font_desc);
// 测量文字像素尺寸(无缩放的实际像素)
cairo_surface_t *dummy_surface =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
cairo_t *dummy_cr = cairo_create(dummy_surface);
PangoContext *dummy_ctx = pango_cairo_create_context(dummy_cr);
pango_cairo_context_set_resolution(dummy_ctx, 96.0 * scale);
PangoLayout *dummy_layout = pango_layout_new(dummy_ctx);
pango_layout_set_font_description(dummy_layout, desc);
pango_layout_set_text(dummy_layout, text, -1);
int text_pixel_w, text_pixel_h;
pango_layout_get_pixel_size(dummy_layout, &text_pixel_w, &text_pixel_h);
g_object_unref(dummy_layout);
g_object_unref(dummy_ctx);
cairo_destroy(dummy_cr);
cairo_surface_destroy(dummy_surface);
if (text_pixel_w <= 0 || text_pixel_h <= 0) {
wlr_scene_buffer_set_buffer(node->scene_buffer, NULL);
node->logical_width = 0;
node->logical_height = 0;
return;
}
float logical_text_w = text_pixel_w / scale;
float logical_text_h = text_pixel_h / scale;
float pad_x = node->padding_x;
float pad_y = node->padding_y;
float box_logical_w = logical_text_w + 2.0f * pad_x;
float box_logical_h = logical_text_h + 2.0f * pad_y;
float border = node->border_width;
bool draw_border = (border > 0.0f) && (node->border_color[3] > 0.0f);
bool draw_bg = (node->bg_color[3] > 0.0f);
int surface_pixel_w = (int)((box_logical_w + 2.0f * border) * scale + 0.5f);
int surface_pixel_h = (int)((box_logical_h + 2.0f * border) * scale + 0.5f);
if (surface_pixel_w < 1)
surface_pixel_w = 1;
if (surface_pixel_h < 1)
surface_pixel_h = 1;
cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, surface_pixel_w, surface_pixel_h);
cairo_t *cr = cairo_create(surface);
// 清空为全透明
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
double bg_x = border * scale;
double bg_y = border * scale;
double bg_w = box_logical_w * scale;
double bg_h = box_logical_h * scale;
double radius = node->corner_radius * scale;
if (radius < 0) {
radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0;
}
if (radius > bg_w / 2.0)
radius = bg_w / 2.0;
if (radius > bg_h / 2.0)
radius = bg_h / 2.0;
if (draw_bg) {
cairo_set_source_rgba(cr, node->bg_color[0], node->bg_color[1],
node->bg_color[2], node->bg_color[3]);
if (radius > 0.0) {
draw_rounded_rect(cr, bg_x, bg_y, bg_w, bg_h, radius);
cairo_fill(cr);
} else {
cairo_rectangle(cr, bg_x, bg_y, bg_w, bg_h);
cairo_fill(cr);
}
}
cairo_save(cr);
cairo_translate(cr, (border + pad_x) * scale, (border + pad_y) * scale);
PangoContext *ctx = pango_cairo_create_context(cr);
pango_cairo_context_set_resolution(ctx, 96.0 * scale);
PangoLayout *layout = pango_layout_new(ctx);
pango_layout_set_font_description(layout, desc);
pango_layout_set_text(layout, text, -1);
cairo_set_source_rgba(cr, node->fg_color[0], node->fg_color[1],
node->fg_color[2], node->fg_color[3]);
pango_cairo_show_layout(cr, layout);
g_object_unref(layout);
g_object_unref(ctx);
cairo_restore(cr);
if (draw_border) {
cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1],
node->border_color[2], node->border_color[3]);
cairo_set_line_width(cr, border * scale);
double half_lw = border * scale * 0.5;
double bx = bg_x - half_lw;
double by = bg_y - half_lw;
double bw = bg_w + border * scale;
double bh = bg_h + border * scale;
if (radius > 0.0) {
double outer_radius = radius + half_lw;
if (outer_radius < 0.0)
outer_radius = 0.0;
draw_rounded_rect(cr, bx, by, bw, bh, outer_radius);
cairo_stroke(cr);
} else {
cairo_rectangle(cr, bx, by, bw, bh);
cairo_stroke(cr);
}
}
cairo_surface_flush(surface);
// 包装成 wlr_buffer
struct mango_text_buffer *buf = calloc(1, sizeof(*buf));
if (!buf) {
cairo_surface_destroy(surface);
cairo_destroy(cr);
return;
}
wlr_buffer_init(&buf->base, &text_buffer_impl, surface_pixel_w,
surface_pixel_h);
buf->surface = surface;
wlr_scene_buffer_set_buffer(node->scene_buffer, &buf->base);
wlr_buffer_drop(&buf->base);
// 最终逻辑大小 = 背景框逻辑尺寸 + 边框逻辑尺寸
node->logical_width = (int)(box_logical_w + 2.0f * border);
node->logical_height = (int)(box_logical_h + 2.0f * border);
wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width,
node->logical_height);
cairo_destroy(cr);
}

52
src/draw/text-node.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef MANGO_TEXT_NODE_H
#define MANGO_TEXT_NODE_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_scene.h>
struct mango_text_node {
struct wlr_scene_buffer *scene_buffer;
int logical_width;
int logical_height;
const char *font_desc;
// 外观属性
float fg_color[4]; // 文本颜色 RGBA默认白色
float bg_color[4]; // 背景色 RGBA默认透明
float border_color[4]; // 边框色 RGBA默认透明
int32_t border_width; // 边框宽度(逻辑像素),<=0 则不绘制
int32_t corner_radius; // 圆角半径(逻辑像素),<0 时自动取
// min(width,height)/2 形成胶囊
int32_t padding_x; // 文本左右内边距(逻辑像素)
int32_t padding_y; // 文本上下内边距(逻辑像素)
};
typedef struct {
float fg_color[4];
float bg_color[4];
float border_color[4];
int32_t border_width;
int32_t corner_radius;
int32_t padding_x;
int32_t padding_y;
const char *font_desc;
} JumphitData;
struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent,
JumphitData data);
void mango_text_node_destroy(struct mango_text_node *node);
void mango_text_node_update(struct mango_text_node *node, const char *text,
float scale);
// 外观设置接口
void mango_text_node_set_background(struct mango_text_node *node, float r,
float g, float b, float a);
void mango_text_node_set_border(struct mango_text_node *node, float r, float g,
float b, float a, float width, float radius);
void mango_text_node_set_padding(struct mango_text_node *node, float pad_x,
float pad_y);
void mango_text_node_destroy(struct mango_text_node *node);
void mango_text_global_finish(void);
#endif

View file

@ -353,10 +353,65 @@ void overview_resize(Monitor *m) {
free(c_arr);
}
void create_jump_hints(Monitor *m) {
const char jump_labels[] = "HJKLASDFGQWERTYUIOPZXCVBNM";
int label_idx = 0;
Client *c;
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) &&
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
if (label_idx >= 26)
break;
char c_char = jump_labels[label_idx];
c->jump_char = c_char;
// 把字符变成字符串
char label_text[2] = {c_char, '\0'};
mango_text_node_update(c->text_node, label_text, 1.0f);
wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, true);
wlr_scene_node_raise_to_top(&c->text_node->scene_buffer->node);
wlr_scene_node_set_position(
&c->text_node->scene_buffer->node,
c->geom.width / 2 - c->text_node->logical_width / 2,
c->geom.height / 2 - c->text_node->logical_height / 2);
label_idx++;
}
}
}
void begin_jump_mode(Monitor *m) { m->is_jump_mode = 1; }
void finish_jump_mode(Monitor *m) {
Client *c = NULL;
if (!m->is_jump_mode) {
return;
}
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m)) {
if (c->text_node->scene_buffer->node.enabled) {
c->jump_char = '\0';
wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node,
false);
}
}
}
m->is_jump_mode = 0;
}
void overview(Monitor *m) {
if (config.ov_no_resize) {
overview_scale(m);
} else {
overview_resize(m);
}
if (m->is_jump_mode) {
create_jump_hints(m);
}
}

View file

@ -96,6 +96,7 @@
#include <xcb/xcb_icccm.h>
#endif
#include "common/util.h"
#include "draw/text-node.h"
/* macros */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
@ -327,6 +328,7 @@ struct Client {
struct wlr_scene_shadow *shadow;
struct wlr_scene_tree *scene_surface;
struct wlr_scene_tree *overview_scene_surface;
struct mango_text_node *text_node;
struct wl_list link;
struct wl_list flink;
struct wl_list fadeout_link;
@ -433,6 +435,7 @@ struct Client {
int32_t allow_shortcuts_inhibit;
float scroller_proportion_single;
bool isfocusing;
char jump_char;
bool enable_drop_area_draw;
int32_t drop_direction;
struct wlr_box drag_tile_float_backup_geom;
@ -554,6 +557,7 @@ struct Monitor {
uint32_t ovbk_prev_tagset;
Client *sel, *prevsel;
int32_t isoverview;
int32_t is_jump_mode;
int32_t is_in_hotarea;
int32_t asleep;
uint32_t visible_clients;
@ -899,6 +903,10 @@ Client *scroll_get_stack_tail_client(Client *c);
static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c);
static void overview_backup_surface(Client *c);
static void create_jump_hints(Monitor *m);
static void finish_jump_mode(Monitor *m);
static void begin_jump_mode(Monitor *m);
#include "data/static_keymap.h"
#include "dispatch/bind_declare.h"
#include "layout/layout.h"
@ -2561,6 +2569,8 @@ void cleanup(void) {
/* Destroy after the wayland display (when the monitors are already
destroyed) to avoid destroying them with an invalid scene output. */
wlr_scene_node_destroy(&scene->tree.node);
mango_text_global_finish();
}
void cleanupmon(struct wl_listener *listener, void *data) {
@ -4254,6 +4264,27 @@ void keypress(struct wl_listener *listener, void *data) {
if (handled)
return;
if (selmon && selmon->is_jump_mode &&
event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
for (i = 0; i < nsyms; i++) {
xkb_keysym_t sym = xkb_keysym_to_lower(syms[i]);
if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) {
char c_char = 'A' + (sym - XKB_KEY_a);
Client *c;
wl_list_for_each(c, &clients, link) {
if (c->mon == selmon && c->jump_char == c_char) {
focusclient(c, 1);
toggleoverview(&(Arg){.i = 1});
return;
}
}
} else if (sym == XKB_KEY_Escape) {
togglejump(&(Arg){.i = 0});
return;
}
}
}
/* don't pass when popup is focused
* this is better than having popups (like fuzzel or wmenu) closing
* while typing in a passed keybind */
@ -4513,6 +4544,10 @@ mapnotify(struct wl_listener *listener, void *data) {
#endif
// extra node
c->text_node = mango_text_node_create(c->scene, config.jumhitdata);
wlr_scene_node_lower_to_bottom(&c->text_node->scene_buffer->node);
wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, false);
for (i = 0; i < 2; i++) {
c->splitindicator[i] = wlr_scene_rect_create(
c->scene, 0, 0,
@ -6510,6 +6545,7 @@ void unmapnotify(struct wl_listener *listener, void *data) {
c->stack_proportion = 0.0f;
mango_text_node_destroy(c->text_node);
wlr_scene_node_destroy(&c->scene->node);
printstatus(IPC_WATCH_ARRANGGE);
motionnotify(0, NULL, 0, 0, 0, 0);