From 8368e8184ff94a9107d16a62fc3726c584e454d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 23 Feb 2020 14:17:48 +0100 Subject: [PATCH 001/125] csd: wip: something to get started... --- render.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- wayland.c | 31 ++++++++++++++++++++++++++++--- wayland.h | 7 +++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/render.c b/render.c index 346b5351..c7d702b8 100644 --- a/render.c +++ b/render.c @@ -38,6 +38,10 @@ static struct { static void fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data); +#define shm_cookie_grid(term) ((unsigned long)((uintptr_t)term + 0)) +#define shm_cookie_search(term) ((unsigned long)((uintptr_t)term + 1)) +#define shm_cookie_csd(term, n) ((unsigned long)((uintptr_t)term + 2 + (n))) + struct renderer * render_init(struct fdm *fdm, struct wayland *wayl) { @@ -655,6 +659,45 @@ render_worker_thread(void *_ctx) return -1; } +static void +render_csd(struct terminal *term) +{ + LOG_INFO("rendering CSD"); + + const int border_width = 2 * term->scale; + const int title_height = 20 * term->scale; + + const int geom[5][4] = { + {-border_width, -title_height, term->width + 2 * border_width, title_height}, + }; + + for (size_t i = 0; i < 1; i++) { + const int x = geom[i][0]; + const int y = geom[i][1]; + const int width = geom[i][2]; + const int height = geom[i][3]; + + unsigned long cookie = shm_cookie_csd(term, 0); + struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); + + pixman_color_t color = color_hex_to_pixman(0xffffff); + pixman_image_t *src = pixman_image_create_solid_fill(&color); + + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &color, 1, + &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); + pixman_image_unref(src); + + wl_subsurface_set_position( + term->window->csd.sub_surface[i], x, y); + + wl_surface_attach(term->window->csd.surface[i], buf->wl_buf, 0, 0); + wl_surface_damage_buffer(term->window->csd.surface[i], 0, 0, buf->width, buf->height); + wl_surface_set_buffer_scale(term->window->csd.surface[i], term->scale); + wl_surface_commit(term->window->csd.surface[i]); + } +} + static void frame_callback( void *data, struct wl_callback *wl_callback, uint32_t callback_data); @@ -675,10 +718,13 @@ grid_render(struct terminal *term) gettimeofday(&start_time, NULL); #endif + if (term->window->use_csd) + render_csd(term); + assert(term->width > 0); assert(term->height > 0); - unsigned long cookie = (uintptr_t)term; + unsigned long cookie = shm_cookie_grid(term); struct buffer *buf = shm_get_buffer( term->wl->shm, term->width, term->height, cookie); @@ -990,7 +1036,7 @@ render_search_box(struct terminal *term) const size_t visible_chars = (width - 2 * margin) / term->cell_width; size_t glyph_offset = term->render.search_glyph_offset; - unsigned long cookie = (uintptr_t)term + 1; + unsigned long cookie = shm_cookie_search(term); struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); /* Background - yellow on empty/match, red on mismatch */ diff --git a/wayland.c b/wayland.c index 67757679..478d87b4 100644 --- a/wayland.c +++ b/wayland.c @@ -20,7 +20,7 @@ #include #define LOG_MODULE "wayland" -#define LOG_ENABLE_DBG 0 +#define LOG_ENABLE_DBG 1 #include "log.h" #include "config.h" @@ -537,13 +537,17 @@ xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode) { + struct wl_window *win = data; + switch (mode) { case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: - LOG_ERR("unimplemented: client-side decorations"); + LOG_DBG("using client-side decorations"); + win->use_csd = true; break; case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: LOG_DBG("using server-side decorations"); + win->use_csd = false; break; default: @@ -918,6 +922,12 @@ wayl_win_init(struct terminal *term) zxdg_toplevel_decoration_v1_add_listener( win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win); + for (size_t i = 0; i < 5; i++) { + win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); + win->csd.sub_surface[i] = wl_subcompositor_get_subsurface( + wayl->sub_compositor, win->csd.surface[i], win->surface); + } + /* Scrollback search box */ win->search_surface = wl_compositor_create_surface(wayl->compositor); win->search_sub_surface = wl_subcompositor_get_subsurface( @@ -951,14 +961,29 @@ wayl_win_destroy(struct wl_window *win) /* Scrollback search */ wl_surface_attach(win->search_surface, NULL, 0, 0); wl_surface_commit(win->search_surface); + + /* CSD */ + for (size_t i = 0; i < 5; i++) { + wl_surface_attach(win->csd.surface[i], NULL, 0, 0); + wl_surface_commit(win->csd.surface[i]); + } + wayl_roundtrip(win->term->wl); - /* Main window */ + /* Main window */ wl_surface_attach(win->surface, NULL, 0, 0); wl_surface_commit(win->surface); wayl_roundtrip(win->term->wl); tll_free(win->on_outputs); + + for (size_t i = 0; i < 5; i++) { + if (win->csd.sub_surface[i] != NULL) + wl_subsurface_destroy(win->csd.sub_surface[i]); + if (win->csd.surface[i] != NULL) + wl_surface_destroy(win->csd.surface[i]); + } + if (win->search_sub_surface != NULL) wl_subsurface_destroy(win->search_sub_surface); if (win->search_surface != NULL) diff --git a/wayland.h b/wayland.h index 83a84273..7de13850 100644 --- a/wayland.h +++ b/wayland.h @@ -91,6 +91,13 @@ struct wl_window { struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; + bool use_csd; + + struct { + struct wl_surface *surface[5]; + struct wl_subsurface *sub_surface[5]; + } csd; + /* Scrollback search */ struct wl_surface *search_surface; struct wl_subsurface *search_sub_surface; From 2798807853d14104e3a72e1d7d682c085fd84724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Feb 2020 22:06:20 +0100 Subject: [PATCH 002/125] render: csd: render all borders and the title bar * Still no content in the title bar * Colors are temporary * No resize/drag yet --- render.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/render.c b/render.c index 0e019431..3e7f57b1 100644 --- a/render.c +++ b/render.c @@ -668,10 +668,14 @@ render_csd(struct terminal *term) const int title_height = 20 * term->scale; const int geom[5][4] = { - {-border_width, -title_height, term->width + 2 * border_width, title_height}, + {0, -title_height, term->width, title_height}, + {-border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, + {-border_width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, + {term->width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, + {-border_width, term->height, term->width + 2 * border_width, border_width}, }; - for (size_t i = 0; i < 1; i++) { + for (size_t i = 0; i < 5; i++) { const int x = geom[i][0]; const int y = geom[i][1]; const int width = geom[i][2]; @@ -680,7 +684,7 @@ render_csd(struct terminal *term) unsigned long cookie = shm_cookie_csd(term, 0); struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); - pixman_color_t color = color_hex_to_pixman(0xffffff); + pixman_color_t color = color_hex_to_pixman(i == 0 ? 0xffffff : 0xff0000); pixman_image_t *src = pixman_image_create_solid_fill(&color); pixman_image_fill_rectangles( From 7f270a9f01f8c2674a29a1fcd1b91c8ea67cf6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Feb 2020 22:38:35 +0100 Subject: [PATCH 003/125] term: add term_surface_kind(), and track currently active surface This is needed to handle pointer motion and button events correctly, since mouse actions in e.g. CSD surfaces are very different from mouse actions in the main window. --- terminal.c | 21 +++++++++++++++++++++ terminal.h | 15 +++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/terminal.c b/terminal.c index 3977caae..d97354cd 100644 --- a/terminal.c +++ b/terminal.c @@ -2133,3 +2133,24 @@ term_print(struct terminal *term, wchar_t wc, int width) else term->cursor.lcf = true; } + +enum term_surface +term_surface_kind(const struct terminal *term, const struct wl_surface *surface) +{ + if (surface == term->window->surface) + return TERM_SURF_GRID; + else if (surface == term->window->search_surface) + return TERM_SURF_SEARCH; + else if (surface == term->window->csd.surface[0]) + return TERM_SURF_TITLE; + else if (surface == term->window->csd.surface[1]) + return TERM_SURF_BORDER_LEFT; + else if (surface == term->window->csd.surface[2]) + return TERM_SURF_BORDER_RIGHT; + else if (surface == term->window->csd.surface[3]) + return TERM_SURF_BORDER_TOP; + else if (surface == term->window->csd.surface[4]) + return TERM_SURF_BORDER_BOTTOM; + else + return TERM_SURF_NONE; +} diff --git a/terminal.h b/terminal.h index 53fecf6b..c25c0245 100644 --- a/terminal.h +++ b/terminal.h @@ -185,6 +185,17 @@ struct sixel { struct coord pos; }; +enum term_surface { + TERM_SURF_NONE, + TERM_SURF_GRID, + TERM_SURF_SEARCH, + TERM_SURF_TITLE, + TERM_SURF_BORDER_LEFT, + TERM_SURF_BORDER_RIGHT, + TERM_SURF_BORDER_TOP, + TERM_SURF_BORDER_BOTTOM, +}; + struct terminal { struct fdm *fdm; const struct config *conf; @@ -312,6 +323,7 @@ struct terminal { struct wayland *wl; struct wl_window *window; bool visual_focus; + enum term_surface active_surface; struct { bool refresh_needed; /* Terminal needs to be re-rendered, as soon-as-possible */ @@ -477,3 +489,6 @@ bool term_spawn_new(const struct terminal *term); void term_enable_app_sync_updates(struct terminal *term); void term_disable_app_sync_updates(struct terminal *term); + +enum term_surface term_surface_kind( + const struct terminal *term, const struct wl_surface *surface); From b725ac4c73526b4b54da6711e46d9ff0a7bfef3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Feb 2020 22:39:37 +0100 Subject: [PATCH 004/125] wayland: terminal_from_surface(): use term_surface_kind() --- wayland.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/wayland.c b/wayland.c index 478d87b4..d67bd268 100644 --- a/wayland.c +++ b/wayland.c @@ -1033,11 +1033,8 @@ struct terminal * wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface) { tll_foreach(wayl->terms, it) { - if (it->item->window->surface == surface || - it->item->window->search_surface == surface) - { + if (term_surface_kind(it->item, surface) != TERM_SURF_NONE) return it->item; - } } assert(false); From 0b265cc9a5f222769e0a4e80bc360380501694bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Feb 2020 22:40:02 +0100 Subject: [PATCH 005/125] render: csd: sort geometry entries --- render.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/render.c b/render.c index 3e7f57b1..bc054046 100644 --- a/render.c +++ b/render.c @@ -664,15 +664,15 @@ render_csd(struct terminal *term) { LOG_INFO("rendering CSD"); - const int border_width = 2 * term->scale; + const int border_width = 1 * term->scale; const int title_height = 20 * term->scale; const int geom[5][4] = { {0, -title_height, term->width, title_height}, - {-border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, - {-border_width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, - {term->width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, - {-border_width, term->height, term->width + 2 * border_width, border_width}, + {-border_width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, /* left */ + {term->width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, /* right */ + {-border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, /* top */ + {-border_width, term->height, term->width + 2 * border_width, border_width}, /* bottom */ }; for (size_t i = 0; i < 5; i++) { From d303084a81f7682c23768267a22f8c49e51b5219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Feb 2020 22:40:24 +0100 Subject: [PATCH 006/125] input: pointer-enter: differentiate between the surfaces --- input.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index d04b228d..a84a80f8 100644 --- a/input.c +++ b/input.c @@ -626,10 +626,40 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; - wayl->mouse.col = x / term->cell_width; - wayl->mouse.row = y / term->cell_height; + switch ((term->active_surface = term_surface_kind(term, surface))) { + case TERM_SURF_NONE: + assert(false); + break; + + case TERM_SURF_GRID: + wayl->mouse.col = x / term->cell_width; + wayl->mouse.row = y / term->cell_height; + term_xcursor_update(term); + break; + + case TERM_SURF_SEARCH: + term_xcursor_update(term); + break; + + case TERM_SURF_TITLE: + term->xcursor = "left_ptr"; + render_xcursor_set(term); + break; + + case TERM_SURF_BORDER_LEFT: + case TERM_SURF_BORDER_RIGHT: + term->xcursor = "size_hor"; + render_xcursor_set(term); + break; + + case TERM_SURF_BORDER_TOP: + case TERM_SURF_BORDER_BOTTOM: + term->xcursor = "size_ver"; + render_xcursor_set(term); + break; + } + - term_xcursor_update(term); } static void From 7b18f8394e45aaf5a930e6ea08b6a4a58f5c7dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Feb 2020 22:41:46 +0100 Subject: [PATCH 007/125] input: pointer-leave: reset 'active-surface' in terminal --- input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index a84a80f8..7d1c6c04 100644 --- a/input.c +++ b/input.c @@ -684,8 +684,10 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, LOG_WARN( "compositor sent pointer_leave event without a pointer_enter " "event: surface=%p", surface); - } else + } else { + old_moused->active_surface = TERM_SURF_NONE; term_xcursor_update(old_moused); + } } static void From 551170d94067614ce42d853f09bee908ed8ff9c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 24 Feb 2020 22:42:04 +0100 Subject: [PATCH 008/125] input: pointer-motion/button: ignore actions in non-main surfaces --- input.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/input.c b/input.c index 7d1c6c04..f0cac085 100644 --- a/input.c +++ b/input.c @@ -716,6 +716,9 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, assert(term != NULL); + if (term->active_surface != TERM_SURF_GRID) + return; + int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; @@ -771,6 +774,10 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } assert(term != NULL); + + if (term->active_surface != TERM_SURF_GRID) + return; + search_cancel(term); switch (state) { From ac32bcda07015dbf00f3b804f5e4595cd7994245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:05:48 +0100 Subject: [PATCH 009/125] main: geometry defaults to 800x600 pixels --- completions/zsh/_foot | 2 +- config.c | 4 ++-- doc/foot.1.scd | 2 +- footrc | 2 +- terminal.c | 7 ------- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/completions/zsh/_foot b/completions/zsh/_foot index ad04a467..0132604a 100644 --- a/completions/zsh/_foot +++ b/completions/zsh/_foot @@ -6,7 +6,7 @@ _arguments \ '(-f --font)'{-f,--font}'[font name and style in fontconfig format (monospace)]:font:->fonts' \ '(-t --term)'{-t,--term}'[value to set the environment variable TERM to (foot)]:term:->terms' \ '--login-shell[start shell as a login shell]' \ - '(-g --geometry)'{-g,--geometry}'[window WIDTHxHEIGHT, in pixels (80x24 cells)]:geometry:()' \ + '(-g --geometry)'{-g,--geometry}'[window WIDTHxHEIGHT, in pixels (800x600)]:geometry:()' \ '(-s --server)'{-s,--server}'[run as server; open terminals by running footclient]:server:_files' \ '--hold[remain open after child process exits]' \ '(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running (server mode only)]:pidfile:_files' \ diff --git a/config.c b/config.c index 39a72648..7f23d7fe 100644 --- a/config.c +++ b/config.c @@ -491,8 +491,8 @@ config_load(struct config *conf, const char *conf_path) *conf = (struct config) { .term = strdup("foot"), .shell = get_shell(), - .width = -1, - .height = -1, + .width = 800, + .height = 600, .pad_x = 2, .pad_y = 2, .fonts = tll_init(), diff --git a/doc/foot.1.scd b/doc/foot.1.scd index 8b5268b7..97815066 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -31,7 +31,7 @@ execute (instead of the shell). Default: _monospace_. *-g*,*--geometry*=_WIDTHxHEIGHT_ - Set initial window width and height. + Set initial window width and height. Default: *800x600*. *-t*,*--term*=_TERM_ Value to set the environment variable *TERM* to. Default: *foot*. diff --git a/footrc b/footrc index 337be31b..e6010eb2 100644 --- a/footrc +++ b/footrc @@ -2,7 +2,7 @@ # font=monospace # scrollback=1000 -# geometry=500x300 +# geometry=800x600 # pad=2x2 # shell= (you may need to override if you need a login shell) # term=foot diff --git a/terminal.c b/terminal.c index d97354cd..000d95b3 100644 --- a/terminal.c +++ b/terminal.c @@ -791,13 +791,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, unsigned width = conf->width; unsigned height = conf->height; - if (width == -1) { - /* No user-configuration - use 80x24 cells */ - assert(height == -1); - width = 80 * term->cell_width; - height = 24 * term->cell_height; - } - /* Don't go below a single cell */ width = max(width, term->cell_width); height = max(height, term->cell_height); From 3f601a31dc24d7acba7b57124e3b319b792b9581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:07:23 +0100 Subject: [PATCH 010/125] shm: handle EINTR in posix_fallocate() --- shm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shm.c b/shm.c index 33095e74..ab717bd0 100644 --- a/shm.c +++ b/shm.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -124,7 +125,9 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie) size = stride * height; LOG_DBG("cookie=%lx: allocating new buffer: %zu KB", cookie, size / 1024); - int err = posix_fallocate(pool_fd, 0, size); + int err = EINTR; + while (err == EINTR) + err = posix_fallocate(pool_fd, 0, size); if (err != 0) { static bool failure_logged = false; From d15eb936ef2ee533cde2596b4c9b9f09cdb03973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:08:14 +0100 Subject: [PATCH 011/125] render: make CSD border and title size globally accessible --- render.c | 3 +++ wayland.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/render.c b/render.c index bc054046..845bc0f1 100644 --- a/render.c +++ b/render.c @@ -24,6 +24,9 @@ #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) +const int csd_border_size = 5; +const int csd_title_size = 20; + struct renderer { struct fdm *fdm; struct wayland *wayl; diff --git a/wayland.h b/wayland.h index 7de13850..243df207 100644 --- a/wayland.h +++ b/wayland.h @@ -82,6 +82,9 @@ struct wl_primary { uint32_t serial; }; +extern const int csd_border_size; +extern const int csd_title_size; + struct wayland; struct wl_window { struct terminal *term; From 70ce7245c6fc4b1e19b03acf7b53400a81523258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:09:29 +0100 Subject: [PATCH 012/125] render: export render_csd() --- render.c | 51 +++++++++++++++++++++++++++++++++------------------ render.h | 1 + 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/render.c b/render.c index 845bc0f1..dc01f45b 100644 --- a/render.c +++ b/render.c @@ -662,32 +662,44 @@ render_worker_thread(void *_ctx) return -1; } -static void +void render_csd(struct terminal *term) { - LOG_INFO("rendering CSD"); + if (!term->window->use_csd) + return; - const int border_width = 1 * term->scale; - const int title_height = 20 * term->scale; + const int border_width = csd_border_size * term->scale; + const int title_height = csd_title_size * term->scale; const int geom[5][4] = { - {0, -title_height, term->width, title_height}, - {-border_width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, /* left */ - {term->width, -title_height - border_width, border_width, term->height + title_height + 2 * border_width}, /* right */ - {-border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, /* top */ - {-border_width, term->height, term->width + 2 * border_width, border_width}, /* bottom */ + /* X, Y, WIDTH, HEIGHT */ + { border_width, border_width, term->width - 2 * border_width, title_height}, /* title */ + { 0, border_width, border_width, term->height - 2 * border_width}, /* left */ + {term->width - border_width, border_width, border_width, term->height - 2 * border_width}, /* right */ + { 0, 0, term->width, border_width}, /* top */ + { 0, term->height - border_width, term->width, border_width}, /* bottom */ }; - for (size_t i = 0; i < 5; i++) { + struct wl_region *region = wl_compositor_create_region(term->wl->compositor); + if (region != NULL) + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + + for (size_t i = 0; i < sizeof(geom) / sizeof(geom[0]); i++) { + struct wl_surface *surf = term->window->csd.surface[i]; + struct wl_subsurface *sub = term->window->csd.sub_surface[i]; + const int x = geom[i][0]; const int y = geom[i][1]; const int width = geom[i][2]; const int height = geom[i][3]; - unsigned long cookie = shm_cookie_csd(term, 0); + unsigned long cookie = shm_cookie_csd(term, i); struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); - pixman_color_t color = color_hex_to_pixman(i == 0 ? 0xffffff : 0xff0000); + pixman_color_t color = color_hex_to_pixman(term->colors.fg); + if (!term->visual_focus) + pixman_color_dim(&color); + pixman_image_t *src = pixman_image_create_solid_fill(&color); pixman_image_fill_rectangles( @@ -695,14 +707,17 @@ render_csd(struct terminal *term) &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); pixman_image_unref(src); - wl_subsurface_set_position( - term->window->csd.sub_surface[i], x, y); + wl_subsurface_set_position(sub, x, y); - wl_surface_attach(term->window->csd.surface[i], buf->wl_buf, 0, 0); - wl_surface_damage_buffer(term->window->csd.surface[i], 0, 0, buf->width, buf->height); - wl_surface_set_buffer_scale(term->window->csd.surface[i], term->scale); - wl_surface_commit(term->window->csd.surface[i]); + wl_surface_attach(surf, buf->wl_buf, 0, 0); + wl_surface_set_opaque_region(surf, region); + wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); + wl_surface_set_buffer_scale(surf, term->scale); + wl_surface_commit(surf); } + + if (region != NULL) + wl_region_destroy(region); } static void frame_callback( diff --git a/render.h b/render.h index 3bd52fa5..9c96aa24 100644 --- a/render.h +++ b/render.h @@ -16,6 +16,7 @@ void render_refresh(struct terminal *term); bool render_xcursor_set(struct terminal *term); void render_search_box(struct terminal *term); +void render_csd(struct terminal *term); struct render_worker_context { int my_id; From d3c7d25dae3124fec14574cb8379411b17e28ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:09:49 +0100 Subject: [PATCH 013/125] render: call render_csd() from render_resize(), not render_grid() The CSDs doesn't need to be re-drawn each time we redraw the grid, only when the window is resized. --- render.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/render.c b/render.c index dc01f45b..95391bb8 100644 --- a/render.c +++ b/render.c @@ -740,9 +740,6 @@ grid_render(struct terminal *term) gettimeofday(&start_time, NULL); #endif - if (term->window->use_csd) - render_csd(term); - assert(term->width > 0); assert(term->height > 0); @@ -1244,6 +1241,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force) damage_view: tll_free(term->normal.scroll_damage); tll_free(term->alt.scroll_damage); + render_csd(term); term->render.last_buf = NULL; term_damage_view(term); render_refresh(term); From 7db9221aa925ba2995e315e7d3fa76247b56c51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:10:48 +0100 Subject: [PATCH 014/125] render: render_resize(): don't allow too small window sizes --- render.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/render.c b/render.c index 95391bb8..29a47cfc 100644 --- a/render.c +++ b/render.c @@ -1139,6 +1139,27 @@ maybe_resize(struct terminal *term, int width, int height, bool force) return; } + /* Scaled CSD border + title bar sizes */ + const int csd_border = term->window->use_csd ? csd_border_size * scale : 0; + const int csd_title = term->window->use_csd ? csd_title_size * scale : 0; + const int csd_x = 2 * csd_border; + const int csd_y = 2 * csd_border + csd_title; + + /* Padding */ + const int pad_x = scale * term->conf->pad_x; + const int pad_y = scale * term->conf->pad_y; + + /* Don't shrink grid too much */ + const int min_cols = 20; + const int min_rows = 4; + + /* Minimum window size */ + const int min_width = csd_x + 2 * pad_x + min_cols * term->cell_width; + const int min_height = csd_y + 2 * pad_y + min_rows * term->cell_height; + + width = max(width, min_width); + height = max(height, min_height); + if (!force && width == term->width && height == term->height && scale == term->scale) return; @@ -1157,13 +1178,9 @@ maybe_resize(struct terminal *term, int width, int height, bool force) const int old_cols = term->cols; const int old_rows = term->rows; - /* Padding */ - const int pad_x = term->width > 2 * scale * term->conf->pad_x ? scale * term->conf->pad_x : 0; - const int pad_y = term->height > 2 * scale * term->conf->pad_y ? scale * term->conf->pad_y : 0; - /* Screen rows/cols after resize */ - const int new_cols = max((term->width - 2 * pad_x) / term->cell_width, 1); - const int new_rows = max((term->height - 2 * pad_y) / term->cell_height, 1); + const int new_cols = (term->width - 2 * pad_x - csd_x) / term->cell_width; + const int new_rows = (term->height - 2 * pad_y - csd_y) / term->cell_height; /* Grid rows/cols after resize */ const int new_normal_grid_rows = 1 << (32 - __builtin_clz(new_rows + scrollback_lines - 1)); @@ -1173,11 +1190,16 @@ maybe_resize(struct terminal *term, int width, int height, bool force) assert(new_rows >= 1); /* Margins */ - term->margins.left = (term->width - new_cols * term->cell_width) / 2; - term->margins.top = (term->height - new_rows * term->cell_height) / 2; + term->margins.left = csd_border + (term->width - csd_x - new_cols * term->cell_width) / 2; + term->margins.top = csd_border + csd_title + (term->height - csd_y - new_rows * term->cell_height) / 2; term->margins.right = term->width - new_cols * term->cell_width - term->margins.left; term->margins.bottom = term->height - new_rows * term->cell_height - term->margins.top; + assert(term->margins.left >= csd_border + pad_x); + assert(term->margins.right >= csd_border + pad_x); + assert(term->margins.top >= csd_border + csd_title + pad_y); + assert(term->margins.bottom >= csd_border + pad_y); + if (new_cols == old_cols && new_rows == old_rows) { LOG_DBG("grid layout unaffected; skipping reflow"); goto damage_view; @@ -1239,6 +1261,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force) term->render.last_cursor.cell = NULL; damage_view: + xdg_toplevel_set_min_size(term->window->xdg_toplevel, min_width / scale, min_height / scale); tll_free(term->normal.scroll_damage); tll_free(term->alt.scroll_damage); render_csd(term); From a4d77608b9a44a06313dd7dcae004ba3818c5bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:16:23 +0100 Subject: [PATCH 015/125] wayland: decoration-configure: call render_resize() to add/remove CSDs Call render_resize_force() when the decoration type changes run-time. This ensures the CSDs are drawn when changing from server -> client side decorations, and removed when going the other way. --- wayland.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wayland.c b/wayland.c index d67bd268..292f711d 100644 --- a/wayland.c +++ b/wayland.c @@ -554,6 +554,13 @@ xdg_toplevel_decoration_configure(void *data, LOG_ERR("unimplemented: unknown XDG toplevel decoration mode: %u", mode); break; } + + if (win->is_configured) { + struct terminal *term = win->term; + int scale = term->scale; + + render_resize_force(term, term->width / scale, term->height / scale); + } } static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { From 595b32ddf99d40fa5c51f7b3e0696a27cc6adb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:51:03 +0100 Subject: [PATCH 016/125] render: render_resize_*() returns a boolean indicating whether size changed. --- render.c | 16 +++++++++------- render.h | 5 +++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/render.c b/render.c index 29a47cfc..460ffd74 100644 --- a/render.c +++ b/render.c @@ -1114,11 +1114,11 @@ render_search_box(struct terminal *term) } /* Move to terminal.c? */ -static void +static bool maybe_resize(struct terminal *term, int width, int height, bool force) { if (!force && (width == 0 || height == 0)) - return; + return false; int scale = -1; tll_foreach(term->window->on_outputs, it) { @@ -1136,7 +1136,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force) if (!force && width == 0 && height == 0) { /* Assume we're not fully up and running yet */ - return; + return false; } /* Scaled CSD border + title bar sizes */ @@ -1161,7 +1161,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force) height = max(height, min_height); if (!force && width == term->width && height == term->height && scale == term->scale) - return; + return false; selection_cancel(term); @@ -1261,22 +1261,24 @@ maybe_resize(struct terminal *term, int width, int height, bool force) term->render.last_cursor.cell = NULL; damage_view: - xdg_toplevel_set_min_size(term->window->xdg_toplevel, min_width / scale, min_height / scale); + xdg_toplevel_set_min_size( + term->window->xdg_toplevel, min_width / scale, min_height / scale); tll_free(term->normal.scroll_damage); tll_free(term->alt.scroll_damage); render_csd(term); term->render.last_buf = NULL; term_damage_view(term); render_refresh(term); + return true; } -void +bool render_resize(struct terminal *term, int width, int height) { return maybe_resize(term, width, height, false); } -void +bool render_resize_force(struct terminal *term, int width, int height) { return maybe_resize(term, width, height, true); diff --git a/render.h b/render.h index 9c96aa24..8602d367 100644 --- a/render.h +++ b/render.h @@ -1,4 +1,5 @@ #pragma once +#include #include "terminal.h" #include "fdm.h" @@ -8,8 +9,8 @@ struct renderer; struct renderer *render_init(struct fdm *fdm, struct wayland *wayl); void render_destroy(struct renderer *renderer); -void render_resize(struct terminal *term, int width, int height); -void render_resize_force(struct terminal *term, int width, int height); +bool render_resize(struct terminal *term, int width, int height); +bool render_resize_force(struct terminal *term, int width, int height); void render_set_title(struct terminal *term, const char *title); void render_refresh(struct terminal *term); From 0126cee55d7a0980c47b76b57d97952c95ed0488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:53:06 +0100 Subject: [PATCH 017/125] wayland: xdg_surface_configure(): call wl_surface_commit() It seems kwin expects a wl_surface_commit() for each xdg_surface_ack_configure(). We don't want to commit before we've rendered a resized surface. So, if we *did* change the window size, let the normal rendering path do the surface commit. Only when we did *not* change the window size do we need to explicitly commit the surface in xdg_surface_configure(). --- wayland.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/wayland.c b/wayland.c index 292f711d..c40d0111 100644 --- a/wayland.c +++ b/wayland.c @@ -513,19 +513,28 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { LOG_DBG("xdg-surface: configure"); - xdg_surface_ack_configure(xdg_surface, serial); struct wl_window *win = data; struct terminal *term = win->term; win->is_configured = true; - render_resize(term, win->configure.width, win->configure.height); + xdg_surface_ack_configure(xdg_surface, serial); + bool resized = render_resize(term, win->configure.width, win->configure.height); if (win->configure.is_activated) term_visual_focus_in(term); else term_visual_focus_out(term); + + if (!resized) { + /* + * kwin seems to need a commit for each configure ack, or it + * will get stuck. Since we'll get a "real" commit soon if we + * resized, only commit here if size did *not* change + */ + wl_surface_commit(win->surface); + } } static const struct xdg_surface_listener xdg_surface_listener = { From 8af5bf49b0a317b8e6d0e8561a42e0ee16f5258c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:55:50 +0100 Subject: [PATCH 018/125] wayland: weston does not implement the xdg decoration manager interface --- wayland.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/wayland.c b/wayland.c index c40d0111..5f7ff049 100644 --- a/wayland.c +++ b/wayland.c @@ -931,12 +931,16 @@ wayl_win_init(struct terminal *term) xdg_toplevel_set_app_id(win->xdg_toplevel, "foot"); /* Request server-side decorations */ - win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - wayl->xdg_decoration_manager, win->xdg_toplevel); - zxdg_toplevel_decoration_v1_set_mode( - win->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - zxdg_toplevel_decoration_v1_add_listener( - win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win); + if (wayl->xdg_decoration_manager != NULL) { + win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + wayl->xdg_decoration_manager, win->xdg_toplevel); +#if 0 /* Let compositor choose */ + zxdg_toplevel_decoration_v1_set_mode( + win->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); +#endif + zxdg_toplevel_decoration_v1_add_listener( + win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win); + } for (size_t i = 0; i < 5; i++) { win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); From 7e26d96d17d32338cdfebd30a4ddfeafae0816f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:56:23 +0100 Subject: [PATCH 019/125] wayland: default to CSDs Only when the compositor implements the XDG decoration manager interface, and it tells us to use server side decorations to we do so. --- wayland.c | 1 + 1 file changed, 1 insertion(+) diff --git a/wayland.c b/wayland.c index 5f7ff049..813229ec 100644 --- a/wayland.c +++ b/wayland.c @@ -902,6 +902,7 @@ wayl_win_init(struct terminal *term) struct wl_window *win = calloc(1, sizeof(*win)); win->term = term; + win->use_csd = true; win->surface = wl_compositor_create_surface(wayl->compositor); if (win->surface == NULL) { From f05e0ad0ae691bb8c476b299a60006c9ba53ede2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:56:56 +0100 Subject: [PATCH 020/125] wayland: synchronize the CSD surfaces to the main surface --- wayland.c | 1 + 1 file changed, 1 insertion(+) diff --git a/wayland.c b/wayland.c index 813229ec..f49e71d2 100644 --- a/wayland.c +++ b/wayland.c @@ -947,6 +947,7 @@ wayl_win_init(struct terminal *term) win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); win->csd.sub_surface[i] = wl_subcompositor_get_subsurface( wayl->sub_compositor, win->csd.surface[i], win->surface); + wl_subsurface_set_sync(win->csd.sub_surface[i]); } /* Scrollback search box */ From 2855d06c6e99bff2ce32e1d94b09afed99b78735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 19:57:19 +0100 Subject: [PATCH 021/125] wayland: mark the whole search surface as opaque --- wayland.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wayland.c b/wayland.c index f49e71d2..1987c72a 100644 --- a/wayland.c +++ b/wayland.c @@ -956,6 +956,11 @@ wayl_win_init(struct terminal *term) wayl->sub_compositor, win->search_surface, win->surface); wl_subsurface_set_desync(win->search_sub_surface); + struct wl_region *region = wl_compositor_create_region(term->wl->compositor); + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_opaque_region(win->search_surface, region); + wl_region_destroy(region); + wl_surface_commit(win->surface); return win; From ef53729242596344f07b1e9329ffaad51252b0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 20:29:44 +0100 Subject: [PATCH 022/125] render: resize with with/height == 0 resizes to user configured dimensions --- render.c | 12 ++++++------ terminal.c | 17 ----------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/render.c b/render.c index 460ffd74..54c9e993 100644 --- a/render.c +++ b/render.c @@ -1117,7 +1117,7 @@ render_search_box(struct terminal *term) static bool maybe_resize(struct terminal *term, int width, int height, bool force) { - if (!force && (width == 0 || height == 0)) + if (term->cell_width == 0 && term->cell_height == 0) return false; int scale = -1; @@ -1131,14 +1131,14 @@ maybe_resize(struct terminal *term, int width, int height, bool force) scale = 1; } + if (width == 0 && height == 0) { + width = term->conf->width; + height = term->conf->height; + } + width *= scale; height *= scale; - if (!force && width == 0 && height == 0) { - /* Assume we're not fully up and running yet */ - return false; - } - /* Scaled CSD border + title bar sizes */ const int csd_border = term->window->use_csd ? csd_border_size * scale : 0; const int csd_title = term->window->use_csd ? csd_title_size * scale : 0; diff --git a/terminal.c b/terminal.c index 000d95b3..9c90e37d 100644 --- a/terminal.c +++ b/terminal.c @@ -773,29 +773,12 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, term_set_window_title(term, "foot"); /* Load fonts */ -#if 0 - struct font *fonts[4]; - if (!load_fonts_from_conf(term, conf, fonts)) - goto err; - term_set_fonts(term, fonts); -#endif term_font_dpi_changed(term); /* Start the slave/client */ if ((term->slave = slave_spawn(term->ptmx, argc, term->cwd, argv, term_env, conf->shell, login_shell)) == -1) goto err; - if (term->width == 0 && term->height == 0) { - - /* Try to use user-configured window dimentions */ - unsigned width = conf->width; - unsigned height = conf->height; - - /* Don't go below a single cell */ - width = max(width, term->cell_width); - height = max(height, term->cell_height); - render_resize(term, width, height); - } return term; From cc3dad9599986a5b4b42cc8e6f99be8f018fc8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 20:30:45 +0100 Subject: [PATCH 023/125] render: search: take CSD into account --- render.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/render.c b/render.c index 54c9e993..91dac6a8 100644 --- a/render.c +++ b/render.c @@ -1040,8 +1040,11 @@ render_search_box(struct terminal *term) const size_t wanted_visible_chars = max(20, term->search.len); - const int scale = term->scale >= 1 ? term->scale : 1; - const size_t margin = scale * 3; + assert(term->scale >= 1); + const int scale = term->scale; + + const int csd = term->window->use_csd ? csd_border_size * scale : 0; + const size_t margin = csd + 3 * scale; const size_t width = min( term->width - 2 * margin, @@ -1102,14 +1105,15 @@ render_search_box(struct terminal *term) if (term->search.cursor >= term->search.len) draw_bar(term, buf->pix, font, &fg, x, y); + wl_surface_attach(term->window->search_surface, buf->wl_buf, 0, 0); + wl_surface_damage_buffer(term->window->search_surface, 0, 0, width, height); + wl_surface_set_buffer_scale(term->window->search_surface, scale); + wl_subsurface_set_position( term->window->search_sub_surface, max(0, (int32_t)term->width - width - margin), max(0, (int32_t)term->height - height - margin)); - wl_surface_damage_buffer(term->window->search_surface, 0, 0, width, height); - wl_surface_attach(term->window->search_surface, buf->wl_buf, 0, 0); - wl_surface_set_buffer_scale(term->window->search_surface, scale); wl_surface_commit(term->window->search_surface); } From c0e3db17120db7efb67e095ff251324518ff5d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 20:31:13 +0100 Subject: [PATCH 024/125] input: wip: trigger move/resize when left-clicking CSD --- input.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/input.c b/input.c index f0cac085..38e20c56 100644 --- a/input.c +++ b/input.c @@ -15,6 +15,8 @@ #include #include +#include + #define LOG_MODULE "input" #define LOG_ENABLE_DBG 0 #include "log.h" @@ -647,14 +649,28 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, break; case TERM_SURF_BORDER_LEFT: + if (y < 10) + term->xcursor = "top_left_corner"; + else + term->xcursor = "left_side"; + render_xcursor_set(term); + break; + case TERM_SURF_BORDER_RIGHT: - term->xcursor = "size_hor"; + term->xcursor = "right_side"; render_xcursor_set(term); break; case TERM_SURF_BORDER_TOP: + if (x < 10) + term->xcursor = "top_left_corner"; + else + term->xcursor = "top_side"; + render_xcursor_set(term); + break; + case TERM_SURF_BORDER_BOTTOM: - term->xcursor = "size_ver"; + term->xcursor = "bottom_side"; render_xcursor_set(term); break; } @@ -716,12 +732,33 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, assert(term != NULL); - if (term->active_surface != TERM_SURF_GRID) - return; - int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; + switch (term->active_surface) { + case TERM_SURF_NONE: + case TERM_SURF_GRID: + case TERM_SURF_SEARCH: + case TERM_SURF_TITLE: + break; + + case TERM_SURF_BORDER_LEFT: + if (y < 10) + term->xcursor = "top_left_corner"; + else + term->xcursor = "left_side"; + render_xcursor_set(term); + break; + + case TERM_SURF_BORDER_RIGHT: + case TERM_SURF_BORDER_TOP: + case TERM_SURF_BORDER_BOTTOM: + break; + } + + if (term->active_surface != TERM_SURF_GRID) + return; + int col = (x - term->margins.left) / term->cell_width; int row = (y - term->margins.top) / term->cell_height; @@ -775,6 +812,37 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, assert(term != NULL); + switch (term->active_surface) { + case TERM_SURF_NONE: + assert(false); + break; + + case TERM_SURF_TITLE: + if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) + xdg_toplevel_move(term->window->xdg_toplevel, term->wl->seat, serial); + return; + + case TERM_SURF_BORDER_LEFT: + case TERM_SURF_BORDER_RIGHT: + case TERM_SURF_BORDER_TOP: + case TERM_SURF_BORDER_BOTTOM: { + static const int map[] = { + [TERM_SURF_BORDER_LEFT] = XDG_TOPLEVEL_RESIZE_EDGE_LEFT, + [TERM_SURF_BORDER_RIGHT] = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT, + [TERM_SURF_BORDER_TOP] = XDG_TOPLEVEL_RESIZE_EDGE_TOP, + [TERM_SURF_BORDER_BOTTOM] = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM, + }; + if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) + xdg_toplevel_resize(term->window->xdg_toplevel, term->wl->seat, serial, map[term->active_surface]); + return; + } + + case TERM_SURF_GRID: + case TERM_SURF_SEARCH: + break; + } + + if (term->active_surface != TERM_SURF_GRID) return; From e9d3e7d87f820d9f7d1f63eef7e8a9a5fa5e5718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 20:31:37 +0100 Subject: [PATCH 025/125] term: visual_focus_in/out: redraw CSDs We draw the CSDs in a darker color when we're inactive. Weston seems to be buggy with synchronized subsurfaces, so temporarily reconfigure them to desynchronized surfaces. --- terminal.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/terminal.c b/terminal.c index 9c90e37d..b568731c 100644 --- a/terminal.c +++ b/terminal.c @@ -1633,6 +1633,15 @@ term_visual_focus_in(struct terminal *term) if (term->cursor_blink.active) cursor_blink_start_timer(term); +#if 1 + for (int i = 0; i < 5; i++) + wl_subsurface_set_desync(term->window->csd.sub_surface[i]); +#endif + render_csd(term); +#if 1 + for (int i = 0; i < 5; i++) + wl_subsurface_set_sync(term->window->csd.sub_surface[i]); +#endif cursor_refresh(term); } @@ -1646,6 +1655,15 @@ term_visual_focus_out(struct terminal *term) if (term->cursor_blink.active) cursor_blink_stop_timer(term); +#if 1 + for (int i = 0; i < 5; i++) + wl_subsurface_set_desync(term->window->csd.sub_surface[i]); +#endif + render_csd(term); +#if 1 + for (int i = 0; i < 5; i++) + wl_subsurface_set_sync(term->window->csd.sub_surface[i]); +#endif cursor_refresh(term); } From 45ba9f9c8f3b38e20092d97ee6a34f7bc27e0198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 Feb 2020 20:33:27 +0100 Subject: [PATCH 026/125] grid: grid_reflow(): may be called with old_rows/cols == new_rows/cols TODO: avoid calling grid_reflow() in this case. --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 463dca7e..d538edc9 100644 --- a/grid.c +++ b/grid.c @@ -60,7 +60,7 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, const int old_rows = grid->num_rows; const int old_cols = grid->num_cols; - assert(old_rows != new_rows || old_cols != new_cols); + //assert(old_rows != new_rows || old_cols != new_cols); int new_col_idx = 0; int new_row_idx = 0; From 2f587f6f3d29c2dbfc6625e94144bf325263d090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:17:58 +0100 Subject: [PATCH 027/125] csd: position CSD sub-surfaces *outside* the main window For now, this behavior is controlled with an ifdef. At least kwin seems very buggy when the decorations are positioned like this (but normally you'd use server-side decorations with kwin anyway). This commit also changes 'use_csd' to be a tri-state variable; when instantiating a window it is set to 'unknown'. If there's no decoration manager available (e.g. weston), we immediately set it to 'yes' (use CSDs). Otherwise, we wait for the decoration manager callback to indicate whether we should use CSDs or not. --- render.c | 113 ++++++++++++++++++++++++++++++++--------------------- terminal.c | 26 +++++++----- wayland.c | 6 +-- wayland.h | 3 +- 4 files changed, 91 insertions(+), 57 deletions(-) diff --git a/render.c b/render.c index 91dac6a8..598141ef 100644 --- a/render.c +++ b/render.c @@ -665,59 +665,74 @@ render_worker_thread(void *_ctx) void render_csd(struct terminal *term) { - if (!term->window->use_csd) - return; + switch (term->window->use_csd) { + case CSD_UNKNOWN: + case CSD_NO: + break; - const int border_width = csd_border_size * term->scale; - const int title_height = csd_title_size * term->scale; + case CSD_YES: { + const int border_width = csd_border_size * term->scale; + const int title_height = csd_title_size * term->scale; - const int geom[5][4] = { - /* X, Y, WIDTH, HEIGHT */ - { border_width, border_width, term->width - 2 * border_width, title_height}, /* title */ - { 0, border_width, border_width, term->height - 2 * border_width}, /* left */ - {term->width - border_width, border_width, border_width, term->height - 2 * border_width}, /* right */ - { 0, 0, term->width, border_width}, /* top */ - { 0, term->height - border_width, term->width, border_width}, /* bottom */ - }; + const int geom[5][4] = { + /* X, Y, WIDTH, HEIGHT */ +#if FOOT_CSD_OUTSIDE + { 0, -title_height, term->width, title_height}, /* title */ + { -border_width, -title_height, border_width, title_height + term->height}, /* left */ + { term->width, -title_height, border_width, title_height + term->height}, /* right */ + { -border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, /* top */ + { -border_width, term->height, term->width + 2 * border_width, border_width}, /* bottom */ +#else + { border_width, border_width, term->width - 2 * border_width, title_height}, /* title */ + { 0, border_width, border_width, term->height - 2 * border_width}, /* left */ + {term->width - border_width, border_width, border_width, term->height - 2 * border_width}, /* right */ + { 0, 0, term->width, border_width}, /* top */ + { 0, term->height - border_width, term->width, border_width}, /* bottom */ +#endif + }; - struct wl_region *region = wl_compositor_create_region(term->wl->compositor); - if (region != NULL) - wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + struct wl_region *region = wl_compositor_create_region(term->wl->compositor); + if (region != NULL) + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); - for (size_t i = 0; i < sizeof(geom) / sizeof(geom[0]); i++) { - struct wl_surface *surf = term->window->csd.surface[i]; - struct wl_subsurface *sub = term->window->csd.sub_surface[i]; + for (size_t i = 0; i < sizeof(geom) / sizeof(geom[0]); i++) { + struct wl_surface *surf = term->window->csd.surface[i]; + struct wl_subsurface *sub = term->window->csd.sub_surface[i]; - const int x = geom[i][0]; - const int y = geom[i][1]; - const int width = geom[i][2]; - const int height = geom[i][3]; + const int x = geom[i][0]; + const int y = geom[i][1]; + const int width = geom[i][2]; + const int height = geom[i][3]; - unsigned long cookie = shm_cookie_csd(term, i); - struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); + unsigned long cookie = shm_cookie_csd(term, i); + struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); - pixman_color_t color = color_hex_to_pixman(term->colors.fg); - if (!term->visual_focus) - pixman_color_dim(&color); + pixman_color_t color = color_hex_to_pixman(term->colors.fg); + if (!term->visual_focus) + pixman_color_dim(&color); - pixman_image_t *src = pixman_image_create_solid_fill(&color); + pixman_image_t *src = pixman_image_create_solid_fill(&color); - pixman_image_fill_rectangles( - PIXMAN_OP_SRC, buf->pix, &color, 1, - &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); - pixman_image_unref(src); + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &color, 1, + &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); + pixman_image_unref(src); - wl_subsurface_set_position(sub, x, y); + wl_subsurface_set_position(sub, x, y); - wl_surface_attach(surf, buf->wl_buf, 0, 0); - wl_surface_set_opaque_region(surf, region); - wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); - wl_surface_set_buffer_scale(surf, term->scale); - wl_surface_commit(surf); + wl_surface_attach(surf, buf->wl_buf, 0, 0); + wl_surface_set_opaque_region(surf, region); + wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); + wl_surface_set_buffer_scale(surf, term->scale); + wl_surface_commit(surf); + } + + if (region != NULL) + wl_region_destroy(region); + + break; + } } - - if (region != NULL) - wl_region_destroy(region); } static void frame_callback( @@ -1043,7 +1058,11 @@ render_search_box(struct terminal *term) assert(term->scale >= 1); const int scale = term->scale; - const int csd = term->window->use_csd ? csd_border_size * scale : 0; +#if FOOT_CSD_OUTSIDE + const int csd = 0; +#else + const int csd = term->window->use_csd == CSD_YES ? csd_border_size * scale : 0; +#endif const size_t margin = csd + 3 * scale; const size_t width = min( @@ -1144,8 +1163,14 @@ maybe_resize(struct terminal *term, int width, int height, bool force) height *= scale; /* Scaled CSD border + title bar sizes */ - const int csd_border = term->window->use_csd ? csd_border_size * scale : 0; - const int csd_title = term->window->use_csd ? csd_title_size * scale : 0; +#if FOOT_CSD_OUTSIDE + const int csd_border = 0; + const int csd_title = 0; +#else + const int csd_border = term->window->use_csd == CSD_YES ? csd_border_size * scale : 0; + const int csd_title = term->window->use_csd == CSD_YES ? ? csd_title_size * scale : 0; +#endif + const int csd_x = 2 * csd_border; const int csd_y = 2 * csd_border + csd_title; diff --git a/terminal.c b/terminal.c index b568731c..fe8860ad 100644 --- a/terminal.c +++ b/terminal.c @@ -1633,14 +1633,18 @@ term_visual_focus_in(struct terminal *term) if (term->cursor_blink.active) cursor_blink_start_timer(term); -#if 1 - for (int i = 0; i < 5; i++) - wl_subsurface_set_desync(term->window->csd.sub_surface[i]); +#if 1 /* Weston seems to be buggy with synchronized CSDs */ + if (term->window->use_csd == CSD_YES) { + for (int i = 0; i < 5; i++) + wl_subsurface_set_desync(term->window->csd.sub_surface[i]); + } #endif render_csd(term); #if 1 - for (int i = 0; i < 5; i++) - wl_subsurface_set_sync(term->window->csd.sub_surface[i]); + if (term->window->use_csd == CSD_YES) { + for (int i = 0; i < 5; i++) + wl_subsurface_set_sync(term->window->csd.sub_surface[i]); + } #endif cursor_refresh(term); } @@ -1656,13 +1660,17 @@ term_visual_focus_out(struct terminal *term) cursor_blink_stop_timer(term); #if 1 - for (int i = 0; i < 5; i++) - wl_subsurface_set_desync(term->window->csd.sub_surface[i]); + if (term->window->use_csd == CSD_YES) { + for (int i = 0; i < 5; i++) + wl_subsurface_set_desync(term->window->csd.sub_surface[i]); + } #endif render_csd(term); #if 1 - for (int i = 0; i < 5; i++) - wl_subsurface_set_sync(term->window->csd.sub_surface[i]); + if (term->window->use_csd == CSD_YES) { + for (int i = 0; i < 5; i++) + wl_subsurface_set_sync(term->window->csd.sub_surface[i]); + } #endif cursor_refresh(term); } diff --git a/wayland.c b/wayland.c index 1987c72a..140c3de9 100644 --- a/wayland.c +++ b/wayland.c @@ -551,12 +551,12 @@ xdg_toplevel_decoration_configure(void *data, switch (mode) { case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: LOG_DBG("using client-side decorations"); - win->use_csd = true; + win->use_csd = CSD_YES; break; case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: LOG_DBG("using server-side decorations"); - win->use_csd = false; + win->use_csd = CSD_NO; break; default: @@ -902,7 +902,7 @@ wayl_win_init(struct terminal *term) struct wl_window *win = calloc(1, sizeof(*win)); win->term = term; - win->use_csd = true; + win->use_csd = CSD_UNKNOWN; win->surface = wl_compositor_create_surface(wayl->compositor); if (win->surface == NULL) { diff --git a/wayland.h b/wayland.h index 243df207..617e8185 100644 --- a/wayland.h +++ b/wayland.h @@ -82,6 +82,7 @@ struct wl_primary { uint32_t serial; }; +#define FOOT_CSD_OUTSIDE 1 extern const int csd_border_size; extern const int csd_title_size; @@ -94,7 +95,7 @@ struct wl_window { struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; - bool use_csd; + enum {CSD_UNKNOWN, CSD_NO, CSD_YES } use_csd; struct { struct wl_surface *surface[5]; From f960e7aff7193505b4dac8a993ebdae241097014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:21:03 +0100 Subject: [PATCH 028/125] render: resize: ignore unconfigured windows --- render.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/render.c b/render.c index 598141ef..7d15a384 100644 --- a/render.c +++ b/render.c @@ -1140,6 +1140,9 @@ render_search_box(struct terminal *term) static bool maybe_resize(struct terminal *term, int width, int height, bool force) { + if (!term->window->is_configured) + return false; + if (term->cell_width == 0 && term->cell_height == 0) return false; From ea97a0dc87f7d8f070de747f0e3adc35780c6cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:22:16 +0100 Subject: [PATCH 029/125] wayland: instantiate sub-surfaces on-demand While most compositors handle instantiated but not-yet-mapped sub-surfaces correctly, e.g. kwin does not. For example, it will incorrectly offset the main surface both horizontally and vertically with a couple of pixels, leaving two transparent areas at the top and left, between the SSDs and the main surface. Note that a workaround is to position the sub-surfaces inside the main surface while they're unmapped. However, since the surfaces may be larger than the main surface (the CSDs, for examples, always are), this doesn't quite work since kwin, at least, resizes the window to include the sub-surfaces, even when unmapped. So, instead we instantiate all sub-surfaces on demand, when we know where to position them, and when they should be mapped. --- search.c | 23 ++++++++++++++-- wayland.c | 80 ++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/search.c b/search.c index a39c75a8..590c7577 100644 --- a/search.c +++ b/search.c @@ -40,8 +40,14 @@ search_ensure_size(struct terminal *term, size_t wanted_size) static void search_cancel_keep_selection(struct terminal *term) { - wl_surface_attach(term->window->search_surface, NULL, 0, 0); - wl_surface_commit(term->window->search_surface); + struct wl_window *win = term->window; + if (win->search_sub_surface != NULL) + wl_subsurface_destroy(win->search_sub_surface); + if (win->search_surface != NULL) + wl_surface_destroy(win->search_surface); + + win->search_surface = NULL; + win->search_sub_surface = NULL; free(term->search.buf); term->search.buf = NULL; @@ -65,6 +71,19 @@ search_begin(struct terminal *term) search_cancel_keep_selection(term); selection_cancel(term); + /* On-demand instantiate wayland surface */ + struct wl_window *win = term->window; + struct wayland *wayl = term->wl; + win->search_surface = wl_compositor_create_surface(wayl->compositor); + win->search_sub_surface = wl_subcompositor_get_subsurface( + wayl->sub_compositor, win->search_surface, win->surface); + wl_subsurface_set_desync(win->search_sub_surface); + + struct wl_region *region = wl_compositor_create_region(term->wl->compositor); + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_opaque_region(win->search_surface, region); + wl_region_destroy(region); + term->search.original_view = term->grid->view; term->search.view_followed_offset = term->grid->view == term->grid->offset; term->is_searching = true; diff --git a/wayland.c b/wayland.c index 140c3de9..a9bfa8c8 100644 --- a/wayland.c +++ b/wayland.c @@ -29,6 +29,7 @@ #include "render.h" #include "selection.h" +#define ALEN(v) (sizeof(v) / sizeof(v[0])) #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -541,6 +542,39 @@ static const struct xdg_surface_listener xdg_surface_listener = { .configure = &xdg_surface_configure, }; +static void +csd_instantiate(struct wl_window *win) +{ + struct wayland *wayl = win->term->wl; + assert(wayl != NULL); + + for (size_t i = 0; i < ALEN(win->csd.surface); i++) { + assert(win->csd.surface[i] == NULL); + assert(win->csd.sub_surface[i] == NULL); + + win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); + win->csd.sub_surface[i] = wl_subcompositor_get_subsurface( + wayl->sub_compositor, win->csd.surface[i], win->surface); + + wl_subsurface_set_sync(win->csd.sub_surface[i]); + wl_surface_commit(win->csd.surface[i]); + } +} + +static void +csd_destroy(struct wl_window *win) +{ + for (size_t i = 0; i < ALEN(win->csd.surface); i++) { + if (win->csd.sub_surface[i] != NULL) + wl_subsurface_destroy(win->csd.sub_surface[i]); + if (win->csd.surface[i] != NULL) + wl_surface_destroy(win->csd.surface[i]); + + win->csd.surface[i] = NULL; + win->csd.sub_surface[i] = NULL; + } +} + static void xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, @@ -552,11 +586,13 @@ xdg_toplevel_decoration_configure(void *data, case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: LOG_DBG("using client-side decorations"); win->use_csd = CSD_YES; + csd_instantiate(win); break; case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: LOG_DBG("using server-side decorations"); win->use_csd = CSD_NO; + csd_destroy(win); break; default: @@ -941,26 +977,12 @@ wayl_win_init(struct terminal *term) #endif zxdg_toplevel_decoration_v1_add_listener( win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win); + } else { + /* No decoration manager - thus we *must* draw our own decorations */ + win->use_csd = CSD_YES; + csd_instantiate(win); } - for (size_t i = 0; i < 5; i++) { - win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); - win->csd.sub_surface[i] = wl_subcompositor_get_subsurface( - wayl->sub_compositor, win->csd.surface[i], win->surface); - wl_subsurface_set_sync(win->csd.sub_surface[i]); - } - - /* Scrollback search box */ - win->search_surface = wl_compositor_create_surface(wayl->compositor); - win->search_sub_surface = wl_subcompositor_get_subsurface( - wayl->sub_compositor, win->search_surface, win->surface); - wl_subsurface_set_desync(win->search_sub_surface); - - struct wl_region *region = wl_compositor_create_region(term->wl->compositor); - wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_set_opaque_region(win->search_surface, region); - wl_region_destroy(region); - wl_surface_commit(win->surface); return win; @@ -986,13 +1008,17 @@ wayl_win_destroy(struct wl_window *win) */ /* Scrollback search */ - wl_surface_attach(win->search_surface, NULL, 0, 0); - wl_surface_commit(win->search_surface); + if (win->search_surface != NULL) { + wl_surface_attach(win->search_surface, NULL, 0, 0); + wl_surface_commit(win->search_surface); + } /* CSD */ - for (size_t i = 0; i < 5; i++) { - wl_surface_attach(win->csd.surface[i], NULL, 0, 0); - wl_surface_commit(win->csd.surface[i]); + for (size_t i = 0; i < ALEN(win->csd.surface); i++) { + if (win->csd.surface[i] != NULL) { + wl_surface_attach(win->csd.surface[i], NULL, 0, 0); + wl_surface_commit(win->csd.surface[i]); + } } wayl_roundtrip(win->term->wl); @@ -1004,13 +1030,7 @@ wayl_win_destroy(struct wl_window *win) tll_free(win->on_outputs); - for (size_t i = 0; i < 5; i++) { - if (win->csd.sub_surface[i] != NULL) - wl_subsurface_destroy(win->csd.sub_surface[i]); - if (win->csd.surface[i] != NULL) - wl_surface_destroy(win->csd.surface[i]); - } - + csd_destroy(win); if (win->search_sub_surface != NULL) wl_subsurface_destroy(win->search_sub_surface); if (win->search_surface != NULL) From 135ca0884d3ae5ee8e0a9f9f72b78b6b52b14ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:26:03 +0100 Subject: [PATCH 030/125] wayland: configure: adjust configured size when using CSDs The size (width, height) arguments provided by the compositor in the XDG toplevel configure event *include* the surrounding CSDs. Since our code assumes the size is for the main surface only (and then positions the sub-surfaces *outside* the main surface), adjust the provided size when using CSDs. This ensures our actual window size ends up being what the compositor wants it to be, and it fixes resize-glitches when resizing using CSDs. --- wayland.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index a9bfa8c8..bc208adf 100644 --- a/wayland.c +++ b/wayland.c @@ -426,6 +426,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { bool is_activated = false; + bool is_resizing = false; #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG char state_str[2048]; @@ -450,9 +451,12 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, is_activated = true; break; + case XDG_TOPLEVEL_STATE_RESIZING: + is_resizing = true; + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: case XDG_TOPLEVEL_STATE_FULLSCREEN: - case XDG_TOPLEVEL_STATE_RESIZING: case XDG_TOPLEVEL_STATE_TILED_LEFT: case XDG_TOPLEVEL_STATE_TILED_RIGHT: case XDG_TOPLEVEL_STATE_TILED_TOP: @@ -490,6 +494,20 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, * xdg_surface_configure() after we've ack:ed the event. */ struct wl_window *win = data; + + if (win->use_csd == CSD_YES && width != 0 && height != 0) { + /* + * The size received here is the *total* window size. + * + * This *includes* the (negatively positioned) CSD + * sub-surfaces. Thus, since our resize code assumes the size + * to resize to is the main window only, adjust the size here, + * to account for the CSDs. + */ + width -= 2 * csd_border_size * win->term->scale; + height -= (2 * csd_border_size + csd_title_size) * win->term->scale; + } + win->configure.is_activated = is_activated; win->configure.width = width; win->configure.height = height; From be5988dd3a96e7db4d4495200cbbcc567c5303b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:28:12 +0100 Subject: [PATCH 031/125] wayland: decoration configure: no need to force a resize - just update the CSDs --- wayland.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/wayland.c b/wayland.c index bc208adf..9959081f 100644 --- a/wayland.c +++ b/wayland.c @@ -618,12 +618,8 @@ xdg_toplevel_decoration_configure(void *data, break; } - if (win->is_configured) { - struct terminal *term = win->term; - int scale = term->scale; - - render_resize_force(term, term->width / scale, term->height / scale); - } + if (win->is_configured) + render_csd(win->term); } static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { From 1e1b2043777511aedcaccf784ebac12bdecd866e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:39:17 +0100 Subject: [PATCH 032/125] csd: don't draw CSDs in fullscreen mode --- render.c | 3 ++ wayland.c | 88 +++++++++++++++++++++++++++++-------------------------- wayland.h | 2 ++ 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/render.c b/render.c index 7d15a384..3920ec90 100644 --- a/render.c +++ b/render.c @@ -671,6 +671,9 @@ render_csd(struct terminal *term) break; case CSD_YES: { + if (term->window->is_fullscreen) + return; + const int border_width = csd_border_size * term->scale; const int title_height = csd_title_size * term->scale; diff --git a/wayland.c b/wayland.c index 9959081f..d875b3d7 100644 --- a/wayland.c +++ b/wayland.c @@ -36,6 +36,39 @@ static bool wayl_reload_cursor_theme( struct wayland *wayl, struct terminal *term); +static void +csd_instantiate(struct wl_window *win) +{ + struct wayland *wayl = win->term->wl; + assert(wayl != NULL); + + for (size_t i = 0; i < ALEN(win->csd.surface); i++) { + assert(win->csd.surface[i] == NULL); + assert(win->csd.sub_surface[i] == NULL); + + win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); + win->csd.sub_surface[i] = wl_subcompositor_get_subsurface( + wayl->sub_compositor, win->csd.surface[i], win->surface); + + wl_subsurface_set_sync(win->csd.sub_surface[i]); + wl_surface_commit(win->csd.surface[i]); + } +} + +static void +csd_destroy(struct wl_window *win) +{ + for (size_t i = 0; i < ALEN(win->csd.surface); i++) { + if (win->csd.sub_surface[i] != NULL) + wl_subsurface_destroy(win->csd.sub_surface[i]); + if (win->csd.surface[i] != NULL) + wl_surface_destroy(win->csd.surface[i]); + + win->csd.surface[i] = NULL; + win->csd.sub_surface[i] = NULL; + } +} + static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { @@ -426,7 +459,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { bool is_activated = false; - bool is_resizing = false; + bool is_fullscreen = false; #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG char state_str[2048]; @@ -447,16 +480,11 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, enum xdg_toplevel_state *state; wl_array_for_each(state, states) { switch (*state) { - case XDG_TOPLEVEL_STATE_ACTIVATED: - is_activated = true; - break; + case XDG_TOPLEVEL_STATE_ACTIVATED: is_activated = true; break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: is_fullscreen = true; break; case XDG_TOPLEVEL_STATE_RESIZING: - is_resizing = true; - break; - case XDG_TOPLEVEL_STATE_MAXIMIZED: - case XDG_TOPLEVEL_STATE_FULLSCREEN: case XDG_TOPLEVEL_STATE_TILED_LEFT: case XDG_TOPLEVEL_STATE_TILED_RIGHT: case XDG_TOPLEVEL_STATE_TILED_TOP: @@ -495,7 +523,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, */ struct wl_window *win = data; - if (win->use_csd == CSD_YES && width != 0 && height != 0) { + if (!is_fullscreen && win->use_csd == CSD_YES && width != 0 && height != 0) { /* * The size received here is the *total* window size. * @@ -509,6 +537,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, } win->configure.is_activated = is_activated; + win->configure.is_fullscreen = is_fullscreen; win->configure.width = width; win->configure.height = height; } @@ -538,6 +567,14 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, win->is_configured = true; + if (win->is_fullscreen != win->configure.is_fullscreen && win->use_csd == CSD_YES) { + if (win->configure.is_fullscreen) + csd_destroy(win); + else + csd_instantiate(win); + win->is_fullscreen = win->configure.is_fullscreen; + } + xdg_surface_ack_configure(xdg_surface, serial); bool resized = render_resize(term, win->configure.width, win->configure.height); @@ -560,39 +597,6 @@ static const struct xdg_surface_listener xdg_surface_listener = { .configure = &xdg_surface_configure, }; -static void -csd_instantiate(struct wl_window *win) -{ - struct wayland *wayl = win->term->wl; - assert(wayl != NULL); - - for (size_t i = 0; i < ALEN(win->csd.surface); i++) { - assert(win->csd.surface[i] == NULL); - assert(win->csd.sub_surface[i] == NULL); - - win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); - win->csd.sub_surface[i] = wl_subcompositor_get_subsurface( - wayl->sub_compositor, win->csd.surface[i], win->surface); - - wl_subsurface_set_sync(win->csd.sub_surface[i]); - wl_surface_commit(win->csd.surface[i]); - } -} - -static void -csd_destroy(struct wl_window *win) -{ - for (size_t i = 0; i < ALEN(win->csd.surface); i++) { - if (win->csd.sub_surface[i] != NULL) - wl_subsurface_destroy(win->csd.sub_surface[i]); - if (win->csd.surface[i] != NULL) - wl_surface_destroy(win->csd.surface[i]); - - win->csd.surface[i] = NULL; - win->csd.sub_surface[i] = NULL; - } -} - static void xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, diff --git a/wayland.h b/wayland.h index 617e8185..f7961552 100644 --- a/wayland.h +++ b/wayland.h @@ -111,8 +111,10 @@ struct wl_window { tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ bool is_configured; + bool is_fullscreen; struct { bool is_activated; + bool is_fullscreen; int width; int height; } configure; From 6ca880bd3e9e81aaac9d9cb933ab784f8f91faed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:47:00 +0100 Subject: [PATCH 033/125] wayland: disable debug logs --- wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index d875b3d7..acd97ef7 100644 --- a/wayland.c +++ b/wayland.c @@ -20,7 +20,7 @@ #include #define LOG_MODULE "wayland" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" #include "config.h" From 90efe6ec025060ea44fc3b888b4745befe410dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 12:51:58 +0100 Subject: [PATCH 034/125] render: don't center grid on surface Up until now, we've centered the grid on the main surface. Meaning, we calculated the unusable area, added the user configured padding and then centered the grid. This may look nice at first, but doesn't anymore when you start resizing the window. Resizing the window will cause the top+left margins to change, which makes the text "jump" or "wobble". So, now we fix the grid in the upper left corner defined by the user configured padding (plus CSDs if they aren't positioned outside the main surface). --- render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index 3920ec90..5706457a 100644 --- a/render.c +++ b/render.c @@ -1225,8 +1225,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force) assert(new_rows >= 1); /* Margins */ - term->margins.left = csd_border + (term->width - csd_x - new_cols * term->cell_width) / 2; - term->margins.top = csd_border + csd_title + (term->height - csd_y - new_rows * term->cell_height) / 2; + term->margins.left = csd_border + pad_x; + term->margins.top = csd_border + csd_title + pad_y; term->margins.right = term->width - new_cols * term->cell_width - term->margins.left; term->margins.bottom = term->height - new_rows * term->cell_height - term->margins.top; From 020ce607788523a5bc251076e0a7502b42d02240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 13:02:15 +0100 Subject: [PATCH 035/125] wayland: don't adjust size when CSDs are positioned inside main surface --- wayland.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wayland.c b/wayland.c index acd97ef7..f8244d53 100644 --- a/wayland.c +++ b/wayland.c @@ -531,9 +531,14 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, * sub-surfaces. Thus, since our resize code assumes the size * to resize to is the main window only, adjust the size here, * to account for the CSDs. + * + * Of course this does *not* apply when we position the CSDs + * *inside* the main surface. */ +#if FOOT_CSD_OUTSIDE width -= 2 * csd_border_size * win->term->scale; height -= (2 * csd_border_size + csd_title_size) * win->term->scale; +#endif } win->configure.is_activated = is_activated; From 6eece79218241acbddfdd8c3473aec7d10e302df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 13:22:20 +0100 Subject: [PATCH 036/125] render: csd: fix typo when CSDs are positioned *inside* main surface --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index 5706457a..6f71ae30 100644 --- a/render.c +++ b/render.c @@ -1174,7 +1174,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force) const int csd_title = 0; #else const int csd_border = term->window->use_csd == CSD_YES ? csd_border_size * scale : 0; - const int csd_title = term->window->use_csd == CSD_YES ? ? csd_title_size * scale : 0; + const int csd_title = term->window->use_csd == CSD_YES ? csd_title_size * scale : 0; #endif const int csd_x = 2 * csd_border; From 5fbbd2f80edb13889267a7af6e75d2d47b92536d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 13:23:00 +0100 Subject: [PATCH 037/125] wayland: track window maximized state --- wayland.c | 7 +++++-- wayland.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/wayland.c b/wayland.c index f8244d53..89c20745 100644 --- a/wayland.c +++ b/wayland.c @@ -460,6 +460,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, { bool is_activated = false; bool is_fullscreen = false; + bool is_maximized = false; #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG char state_str[2048]; @@ -480,11 +481,11 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, enum xdg_toplevel_state *state; wl_array_for_each(state, states) { switch (*state) { - case XDG_TOPLEVEL_STATE_ACTIVATED: is_activated = true; break; + case XDG_TOPLEVEL_STATE_ACTIVATED: is_activated = true; break; case XDG_TOPLEVEL_STATE_FULLSCREEN: is_fullscreen = true; break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: is_maximized = true; break; case XDG_TOPLEVEL_STATE_RESIZING: - case XDG_TOPLEVEL_STATE_MAXIMIZED: case XDG_TOPLEVEL_STATE_TILED_LEFT: case XDG_TOPLEVEL_STATE_TILED_RIGHT: case XDG_TOPLEVEL_STATE_TILED_TOP: @@ -543,6 +544,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, win->configure.is_activated = is_activated; win->configure.is_fullscreen = is_fullscreen; + win->configure.is_maximized = is_maximized; win->configure.width = width; win->configure.height = height; } @@ -571,6 +573,7 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, struct terminal *term = win->term; win->is_configured = true; + win->is_maximized = win->configure.is_maximized; if (win->is_fullscreen != win->configure.is_fullscreen && win->use_csd == CSD_YES) { if (win->configure.is_fullscreen) diff --git a/wayland.h b/wayland.h index f7961552..a634524b 100644 --- a/wayland.h +++ b/wayland.h @@ -112,9 +112,11 @@ struct wl_window { bool is_configured; bool is_fullscreen; + bool is_maximized; struct { bool is_activated; bool is_fullscreen; + bool is_maximized; int width; int height; } configure; From d863ea8a4630069e85e590c2022ff94f3ca43edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 13:23:11 +0100 Subject: [PATCH 038/125] input: temporary code to trigger maximize/minimize --- input.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/input.c b/input.c index 38e20c56..566f6d8d 100644 --- a/input.c +++ b/input.c @@ -820,6 +820,18 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_TITLE: if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) xdg_toplevel_move(term->window->xdg_toplevel, term->wl->seat, serial); + else if (button == BTN_RIGHT && state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (term->window->is_maximized) { + LOG_INFO("UNsetting maximized"); + xdg_toplevel_unset_maximized(term->window->xdg_toplevel); + } else { + LOG_INFO("setting maximized"); + xdg_toplevel_set_maximized(term->window->xdg_toplevel); + } + } else if (button == BTN_MIDDLE && state == WL_POINTER_BUTTON_STATE_PRESSED) { + LOG_INFO("setting minimized"); + xdg_toplevel_set_minimized(term->window->xdg_toplevel); + } return; case TERM_SURF_BORDER_LEFT: From 02fedfb2fc390d848cfe067895ad4617834416e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 13:44:05 +0100 Subject: [PATCH 039/125] wayland: resize main surface when enabling CSDs runtime When we position CSDs inside the main surface, and CSDs are switched on run-time, we need to force a grid resize, since the actual window content will now be smaller. Alternative solution: call resize with width/height increased, to account for the CSDs. This would increase the window size, but would keep the grid size fixed. --- wayland.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index 89c20745..a3b60d72 100644 --- a/wayland.c +++ b/wayland.c @@ -630,8 +630,18 @@ xdg_toplevel_decoration_configure(void *data, break; } - if (win->is_configured) + if (win->is_configured) { +#if FOOT_CSD_OUTSIDE render_csd(win->term); +#else + /* TODO: we could increase the width/height to account for the + * CSDs. This would increase the window size, but keep the + * grid size fixed */ + struct terminal *term = win->term; + int scale = term->scale; + render_resize_force(term, term->width / scale, term->height / scale); +#endif + } } static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { From 1ccfceca05dc5ef12871eb952094e5f9de372be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 13:47:29 +0100 Subject: [PATCH 040/125] wayland: switch back to positioning CSDs inside the main surface --- wayland.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland.h b/wayland.h index a634524b..4e5c62ba 100644 --- a/wayland.h +++ b/wayland.h @@ -82,7 +82,7 @@ struct wl_primary { uint32_t serial; }; -#define FOOT_CSD_OUTSIDE 1 +#define FOOT_CSD_OUTSIDE 0 extern const int csd_border_size; extern const int csd_title_size; From 66decac16b3cbcde70558aa565814b78ce3c9410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 13:49:54 +0100 Subject: [PATCH 041/125] wayland: document what FOOT_CSD_OUTSIDE does --- wayland.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wayland.h b/wayland.h index 4e5c62ba..28145923 100644 --- a/wayland.h +++ b/wayland.h @@ -82,6 +82,11 @@ struct wl_primary { uint32_t serial; }; +/* I'd prefer to position the CSD sub-surfaces outside the main + * surface. Unfortunately, a lot of compositors doesn't handle this + * correctly. When this define is 0, we instead position the CSD + * sub-surfaces inside the main surface, and offset the grid content + * accordingly. */ #define FOOT_CSD_OUTSIDE 0 extern const int csd_border_size; extern const int csd_title_size; From 3228758951ea407886042fa07f586d10084e3a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 15:27:40 +0100 Subject: [PATCH 042/125] render: resize: adjust user configured size for CSDs --- render.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/render.c b/render.c index 6f71ae30..a752dd8e 100644 --- a/render.c +++ b/render.c @@ -1163,6 +1163,11 @@ maybe_resize(struct terminal *term, int width, int height, bool force) if (width == 0 && height == 0) { width = term->conf->width; height = term->conf->height; + +#if FOOT_CSD_OUTSIDE + width -= 2 * csd_border_size; + height -= 2 * csd_border_size + csd_title_size; +#endif } width *= scale; From 77b37fb2888fe41f9cb31152a11f7e646e285e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 20:45:44 +0100 Subject: [PATCH 043/125] wayland: once again switch to positioning CSDs outside the main surface --- wayland.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland.h b/wayland.h index 28145923..95e0e1e9 100644 --- a/wayland.h +++ b/wayland.h @@ -87,7 +87,7 @@ struct wl_primary { * correctly. When this define is 0, we instead position the CSD * sub-surfaces inside the main surface, and offset the grid content * accordingly. */ -#define FOOT_CSD_OUTSIDE 0 +#define FOOT_CSD_OUTSIDE 1 extern const int csd_border_size; extern const int csd_title_size; From ddbfb3676cce56d289271f8287a4389daea562f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 Feb 2020 20:59:11 +0100 Subject: [PATCH 044/125] render: remember, and use, last unmaximized size When the compositor wants us to decide the size (it sends a configure event with width/height == 0), then use the last unmaximized size, if there is one. If there isn't one, use the size from the user configuration. --- render.c | 36 ++++++++++++++++++++++++++---------- terminal.h | 2 ++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/render.c b/render.c index a752dd8e..98ea90f6 100644 --- a/render.c +++ b/render.c @@ -1160,16 +1160,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force) scale = 1; } - if (width == 0 && height == 0) { - width = term->conf->width; - height = term->conf->height; - -#if FOOT_CSD_OUTSIDE - width -= 2 * csd_border_size; - height -= 2 * csd_border_size + csd_title_size; -#endif - } - width *= scale; height *= scale; @@ -1182,6 +1172,27 @@ maybe_resize(struct terminal *term, int width, int height, bool force) const int csd_title = term->window->use_csd == CSD_YES ? csd_title_size * scale : 0; #endif + if (width == 0 && height == 0) { + /* + * The compositor is letting us choose the size + * + * If we have a "last" used size - use that. Otherwise, use + * the size from the user configuration. + */ + if (term->unmaximized_width != 0 && term->unmaximized_height != 0) { + width = term->unmaximized_width; + height = term->unmaximized_height; + } else { + width = term->conf->width * scale; + height = term->conf->height * scale; + +#if FOOT_CSD_OUTSIDE + width -= 2 * csd_border; + height -= 2 * csd_border + csd_title; +#endif + } + } + const int csd_x = 2 * csd_border; const int csd_y = 2 * csd_border + csd_title; @@ -1301,6 +1312,11 @@ maybe_resize(struct terminal *term, int width, int height, bool force) term->render.last_cursor.cell = NULL; damage_view: + if (!term->window->is_maximized) { + term->unmaximized_width = term->width; + term->unmaximized_height = term->height; + } + xdg_toplevel_set_min_size( term->window->xdg_toplevel, min_width / scale, min_height / scale); tll_free(term->normal.scroll_damage); diff --git a/terminal.h b/terminal.h index c25c0245..df3fb06a 100644 --- a/terminal.h +++ b/terminal.h @@ -248,6 +248,8 @@ struct terminal { int scale; int width; /* pixels */ int height; /* pixels */ + int unmaximized_width; /* last unmaximized size, pixels */ + int unmaximized_height; /* last unmaximized size, pixels */ struct { int left; int right; From 3a9a2bb6a4f7797f64bc4a611a739ac922b078b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:33:30 +0100 Subject: [PATCH 045/125] config: add default font when using the default config When there is no configuration file, and we're using the default configuration, we accidentally jumped pasted the code that ensures we have at least "monospace" in the font list. --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index 7f23d7fe..63db806e 100644 --- a/config.c +++ b/config.c @@ -562,10 +562,10 @@ config_load(struct config *conf, const char *conf_path) ret = parse_config_file(f, conf, conf_path); fclose(f); +out: if (ret && tll_length(conf->fonts) == 0) tll_push_back(conf->fonts, strdup("monospace")); -out: free(default_path); return ret; } From 40f3d4c24ccfd116a53120e36df796a959ca6499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:35:05 +0100 Subject: [PATCH 046/125] terminal: error out when we fail to load the primary font --- terminal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index fe8860ad..9388d0d7 100644 --- a/terminal.c +++ b/terminal.c @@ -773,7 +773,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, term_set_window_title(term, "foot"); /* Load fonts */ - term_font_dpi_changed(term); + if (!term_font_dpi_changed(term)) + goto err; /* Start the slave/client */ if ((term->slave = slave_spawn(term->ptmx, argc, term->cwd, argv, term_env, conf->shell, login_shell)) == -1) From 925088a8f21c2b2b136d30ca2d4bc45fd816ce35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:35:21 +0100 Subject: [PATCH 047/125] render: color_hex_to_pixman_with_alpha(): handle alpha == 0 (transparent) --- render.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/render.c b/render.c index 98ea90f6..634e07dc 100644 --- a/render.c +++ b/render.c @@ -218,6 +218,9 @@ color_hex_to_rgb(uint32_t color) static inline pixman_color_t color_hex_to_pixman_with_alpha(uint32_t color, uint16_t alpha) { + if (alpha == 0) + return (pixman_color_t){0, 0, 0, 0}; + int alpha_div = 0xffff / alpha; return (pixman_color_t){ .red = ((color >> 16 & 0xff) | (color >> 8 & 0xff00)) / alpha_div, From 3c7e17e8c1e46a14a18354eca2ef63e8b27bbcc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:35:50 +0100 Subject: [PATCH 048/125] wayland: drop required wl_output interface version from 3 -> 2 We don't need version 3, and mutter 3.34 only implements version 2. --- wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index a3b60d72..b48dfd65 100644 --- a/wayland.c +++ b/wayland.c @@ -356,7 +356,7 @@ handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_output_interface.name) == 0) { - const uint32_t required = 3; + const uint32_t required = 2; if (!verify_iface_version(interface, version, required)) return; From 7d021b5b379f87946b40775d45c893531d9efb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:36:52 +0100 Subject: [PATCH 049/125] wayland: request server side decorations --- wayland.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/wayland.c b/wayland.c index b48dfd65..0a194cad 100644 --- a/wayland.c +++ b/wayland.c @@ -1007,10 +1007,8 @@ wayl_win_init(struct terminal *term) if (wayl->xdg_decoration_manager != NULL) { win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( wayl->xdg_decoration_manager, win->xdg_toplevel); -#if 0 /* Let compositor choose */ zxdg_toplevel_decoration_v1_set_mode( win->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); -#endif zxdg_toplevel_decoration_v1_add_listener( win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win); } else { From 6c20abf279008e556932fdd60ec5f1893dc28143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:37:07 +0100 Subject: [PATCH 050/125] wayland: warn when compositor doesn't implement the decoration manager interface --- wayland.c | 1 + 1 file changed, 1 insertion(+) diff --git a/wayland.c b/wayland.c index 0a194cad..21a69ac3 100644 --- a/wayland.c +++ b/wayland.c @@ -1015,6 +1015,7 @@ wayl_win_init(struct terminal *term) /* No decoration manager - thus we *must* draw our own decorations */ win->use_csd = CSD_YES; csd_instantiate(win); + LOG_WARN("no decoration manager available - using CSDs unconditionally"); } wl_surface_commit(win->surface); From b69c9b5f58ce74831490ae2cd2377ed0e9a55cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:42:10 +0100 Subject: [PATCH 051/125] wayland: fix window size adjustment for CSDs placed outside main surface * Do this both in XDG configure, and when the decoration manager switches between CSDs/SSDs at run-time. * Exclude CSD borders when we're maximized --- wayland.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/wayland.c b/wayland.c index 21a69ac3..979c3bd7 100644 --- a/wayland.c +++ b/wayland.c @@ -537,8 +537,12 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, * *inside* the main surface. */ #if FOOT_CSD_OUTSIDE - width -= 2 * csd_border_size * win->term->scale; - height -= (2 * csd_border_size + csd_title_size) * win->term->scale; + if (!is_maximized) { + width -= 2 * csd_border_size; + height -= 2 * csd_border_size + csd_title_size; + } else { + height -= csd_title_size; + } #endif } @@ -630,17 +634,21 @@ xdg_toplevel_decoration_configure(void *data, break; } - if (win->is_configured) { -#if FOOT_CSD_OUTSIDE - render_csd(win->term); -#else - /* TODO: we could increase the width/height to account for the - * CSDs. This would increase the window size, but keep the - * grid size fixed */ + if (win->is_configured && win->use_csd == CSD_YES) { struct terminal *term = win->term; int scale = term->scale; - render_resize_force(term, term->width / scale, term->height / scale); + + int width = term->width / scale; + int height = term->height / scale; + +#if FOOT_CSD_OUTSIDE + if (!term->window->is_maximized) { + width -= 2 * csd_border_size; + height -= 2 * csd_border_size + csd_title_size; + } else + height -= csd_title_size; #endif + render_resize_force(term, width, height); } } From 6ba476b3bdcd709574750f56fe7c002b9a55bfba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:43:33 +0100 Subject: [PATCH 052/125] wayland: configure: work around GNOME/mutter weirdness When resizing the window under mutter, mutter seems to expect a configure ack *and* a surface commit *right* away, or things get out of sync. Unlike kwin, which is requires a commit for each configure ack, but is fine with having the commit arrive later (after we've rendered it), mutter is not. I even tried delaying the configure ack until just before the commit, but still no go. So for now, detect when we're running under mutter and always do a surface commit right away. This can *not* be done on any other compositor as it breaks the CSD and main surface synchronization; we've resized the CSDs, but not the main surface. I.e. this *should* not work, but for some reason is the *only* way to make things work on mutter. Interestingly, doing it any other way on mutter causes visual glitches; window jumping around when resizing, or de-synchronized CSDs/main surface. --- wayland.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/wayland.c b/wayland.c index 979c3bd7..9b8cb258 100644 --- a/wayland.c +++ b/wayland.c @@ -595,11 +595,41 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, else term_visual_focus_out(term); - if (!resized) { + static bool desktop_is_gnome = false; + static bool desktop_is_initialized = false; + + if (!desktop_is_initialized) { + const char *current_desktop = getenv("XDG_CURRENT_DESKTOP"); + + desktop_is_gnome = current_desktop != NULL && + strcasestr(current_desktop, "gnome") != NULL; + + if (desktop_is_gnome) + LOG_WARN("applying wl_surface_commit() workaround for mutter"); + + desktop_is_initialized = true; + } + + if (desktop_is_gnome) { /* - * kwin seems to need a commit for each configure ack, or it - * will get stuck. Since we'll get a "real" commit soon if we - * resized, only commit here if size did *not* change + * Resizing the window under mutter causes the "other" side to + * jump back and forth, *even* if we re-render and commit a + * properly resized frame immediately. + * + * For that reason, the code path below also does not work, + * since in case we *did* resize, it appears the commit + * happens "too late" after the ack. + * + * Finally, doing this on any other compositor breaks the CSD + * synchronization with the main surface. Hence we only do + * this when running under mutter. + */ + wl_surface_commit(win->surface); + } else if (!resized) { + /* + * If we didn't resize, we won't be commit a new surface + * anytime soon. Some compositors require a commit in + * combination with an ack - make them happy. */ wl_surface_commit(win->surface); } From b44bbb5b2b400b9d69f5e0d1c8d9c57425fb695e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:49:34 +0100 Subject: [PATCH 053/125] render: CSDs: don't render borders (only title bar) in maximized mode --- render.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/render.c b/render.c index 634e07dc..db824f17 100644 --- a/render.c +++ b/render.c @@ -677,7 +677,10 @@ render_csd(struct terminal *term) if (term->window->is_fullscreen) return; - const int border_width = csd_border_size * term->scale; + /* Only title bar is rendered in maximized mode */ + const int border_width = !term->window->is_maximized + ? csd_border_size * term->scale : 0; + const int title_height = csd_title_size * term->scale; const int geom[5][4] = { @@ -710,6 +713,15 @@ render_csd(struct terminal *term) const int width = geom[i][2]; const int height = geom[i][3]; + if (width == 0 || height == 0) { + /* CSD borders aren't rendered in maximized mode */ + assert(term->window->is_maximized); + wl_subsurface_set_position(sub, 0, 0); + wl_surface_attach(surf, NULL, 0, 0); + wl_surface_commit(surf); + continue; + } + unsigned long cookie = shm_cookie_csd(term, i); struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); From e7e553ae5b49624bf38adc70e8e3a209d1ef2e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:50:15 +0100 Subject: [PATCH 054/125] render: resize: exclude CSD borders when loading default geometry in maximized mode --- render.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/render.c b/render.c index db824f17..53bedb75 100644 --- a/render.c +++ b/render.c @@ -1198,13 +1198,24 @@ maybe_resize(struct terminal *term, int width, int height, bool force) width = term->unmaximized_width; height = term->unmaximized_height; } else { - width = term->conf->width * scale; - height = term->conf->height * scale; + width = term->conf->width; + height = term->conf->height; -#if FOOT_CSD_OUTSIDE - width -= 2 * csd_border; - height -= 2 * csd_border + csd_title; -#endif + if (term->window->use_csd == CSD_YES) { + assert(!term->window->is_fullscreen); + + /* Account for CSDs, to make actual window size match + * the configured size */ + if (!term->window->is_maximized) { + width -= 2 * csd_border_size; + height -= 2 * csd_border_size + csd_title_size; + } else { + height -= csd_title_size; + } + } + + width *= scale; + height *= scale; } } From ec2ad7755fb65c809214d8e3615ad46f1a3ddba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:51:09 +0100 Subject: [PATCH 055/125] render: CSDs: transparent borders This gives the illusion that window is border-less, but still gives us surfaces the user can grab to resize the window. --- render.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/render.c b/render.c index 53bedb75..f2b2dc4e 100644 --- a/render.c +++ b/render.c @@ -723,9 +723,12 @@ render_csd(struct terminal *term) } unsigned long cookie = shm_cookie_csd(term, i); - struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie); + struct buffer *buf = shm_get_buffer( + term->wl->shm, width, height, cookie); + + pixman_color_t color = color_hex_to_pixman_with_alpha( + i == 0 ? term->colors.fg : 0x0, i == 0 ? 0xffff : 0x0); - pixman_color_t color = color_hex_to_pixman(term->colors.fg); if (!term->visual_focus) pixman_color_dim(&color); @@ -739,7 +742,7 @@ render_csd(struct terminal *term) wl_subsurface_set_position(sub, x, y); wl_surface_attach(surf, buf->wl_buf, 0, 0); - wl_surface_set_opaque_region(surf, region); + //wl_surface_set_opaque_region(surf, region); wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); wl_surface_set_buffer_scale(surf, term->scale); wl_surface_commit(surf); From 45c1585bfbabaaf11778c55e4c431a975bfd6306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 18:51:51 +0100 Subject: [PATCH 056/125] render: resize: change 'resize' log from info to debug --- render.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/render.c b/render.c index f2b2dc4e..2ceaa4fa 100644 --- a/render.c +++ b/render.c @@ -1299,10 +1299,10 @@ maybe_resize(struct terminal *term, int width, int height, bool force) term->cols = new_cols; term->rows = new_rows; - LOG_INFO("resize: %dx%d, grid: cols=%d, rows=%d " - "(left-margin=%d, right-margin=%d, top-margin=%d, bottom-margin=%d)", - term->width, term->height, term->cols, term->rows, - term->margins.left, term->margins.right, term->margins.top, term->margins.bottom); + LOG_DBG("resize: %dx%d, grid: cols=%d, rows=%d " + "(left-margin=%d, right-margin=%d, top-margin=%d, bottom-margin=%d)", + term->width, term->height, term->cols, term->rows, + term->margins.left, term->margins.right, term->margins.top, term->margins.bottom); /* Signal TIOCSWINSZ */ if (ioctl(term->ptmx, TIOCSWINSZ, From de9fcbc339ecca7b79dbe4c4af0c5114280b5c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Feb 2020 19:00:48 +0100 Subject: [PATCH 057/125] render: csd: use a struct for positioning information --- render.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/render.c b/render.c index 2ceaa4fa..4dbf272a 100644 --- a/render.c +++ b/render.c @@ -683,7 +683,12 @@ render_csd(struct terminal *term) const int title_height = csd_title_size * term->scale; - const int geom[5][4] = { + const struct { + int x; + int y; + int width; + int height; + } geom[] = { /* X, Y, WIDTH, HEIGHT */ #if FOOT_CSD_OUTSIDE { 0, -title_height, term->width, title_height}, /* title */ @@ -708,10 +713,10 @@ render_csd(struct terminal *term) struct wl_surface *surf = term->window->csd.surface[i]; struct wl_subsurface *sub = term->window->csd.sub_surface[i]; - const int x = geom[i][0]; - const int y = geom[i][1]; - const int width = geom[i][2]; - const int height = geom[i][3]; + const int x = geom[i].x; + const int y = geom[i].y; + const int width = geom[i].width; + const int height = geom[i].height; if (width == 0 || height == 0) { /* CSD borders aren't rendered in maximized mode */ From 8c98dfc51a4b313c3f4c019533069a6cc7c08a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 09:26:49 +0100 Subject: [PATCH 058/125] term: loop through all sub-surfaces when switching sync/desync mode --- terminal.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/terminal.c b/terminal.c index 9388d0d7..0b3091f8 100644 --- a/terminal.c +++ b/terminal.c @@ -27,6 +27,7 @@ #include "slave.h" #include "vt.h" +#define ALEN(v) (sizeof(v) / sizeof(v[0])) #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -1636,14 +1637,14 @@ term_visual_focus_in(struct terminal *term) #if 1 /* Weston seems to be buggy with synchronized CSDs */ if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < 5; i++) + for (int i = 0; i < ALEN(term->window->csd.surface); i++) wl_subsurface_set_desync(term->window->csd.sub_surface[i]); } #endif render_csd(term); #if 1 if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < 5; i++) + for (int i = 0; i < ALEN(term->window->csd.surface); i++) wl_subsurface_set_sync(term->window->csd.sub_surface[i]); } #endif @@ -1662,14 +1663,14 @@ term_visual_focus_out(struct terminal *term) #if 1 if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < 5; i++) + for (int i = 0; i < ALEN(term->window->csd.surface); i++) wl_subsurface_set_desync(term->window->csd.sub_surface[i]); } #endif render_csd(term); #if 1 if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < 5; i++) + for (int i = 0; i < ALEN(term->window->csd.surface); i++) wl_subsurface_set_sync(term->window->csd.sub_surface[i]); } #endif From d48a7894120a0040ff240c8dc6df3fc6108363c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 09:32:22 +0100 Subject: [PATCH 059/125] input: handle CSD border corners correctly That is, change to correct cursor, and do resize correctly. --- input.c | 126 +++++++++++++++++++++++++++++++++++++++--------------- wayland.h | 1 + 2 files changed, 92 insertions(+), 35 deletions(-) diff --git a/input.c b/input.c index 566f6d8d..e3095009 100644 --- a/input.c +++ b/input.c @@ -611,6 +611,63 @@ input_repeat(struct wayland *wayl, uint32_t key) keyboard_key(wayl, NULL, wayl->input_serial, 0, key, XKB_KEY_DOWN); } +static bool +is_top_left(const struct terminal *term, int x, int y) +{ + return ( + (term->active_surface == TERM_SURF_BORDER_LEFT && y < 10 * term->scale) || + (term->active_surface == TERM_SURF_BORDER_TOP && x < (10 + csd_border_size) * term->scale)); +} + +static bool +is_top_right(const struct terminal *term, int x, int y) +{ + return ( + (term->active_surface == TERM_SURF_BORDER_RIGHT && y < 10 * term->scale) || + (term->active_surface == TERM_SURF_BORDER_TOP && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)); +} + +static bool +is_bottom_left(const struct terminal *term, int x, int y) +{ + return ( + (term->active_surface == TERM_SURF_BORDER_LEFT && y > csd_title_size * term->scale + term->height) || + (term->active_surface == TERM_SURF_BORDER_BOTTOM && x < (10 + csd_border_size) * term->scale)); +} + +static bool +is_bottom_right(const struct terminal *term, int x, int y) +{ + return ( + (term->active_surface == TERM_SURF_BORDER_RIGHT && y > csd_title_size * term->scale + term->height) || + (term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)); +} + +static const char * +xcursor_for_csd_border(struct terminal *term, int x, int y) +{ + if (is_top_left(term, x, y)) + return "top_left_corner"; + else if (is_top_right(term, x, y)) + return "top_right_corner"; + else if (is_bottom_left(term, x, y)) + return "bottom_left_corner"; + else if (is_bottom_right(term, x, y)) + return "bottom_right_corner"; + else if (term->active_surface == TERM_SURF_BORDER_LEFT) + return "left_side"; + else if (term->active_surface == TERM_SURF_BORDER_RIGHT) + return "right_side"; + else if (term->active_surface == TERM_SURF_BORDER_TOP) + return "top_side"; + else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) + return"bottom_side"; + else { + assert(false); + return NULL; + } +} + static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, @@ -643,36 +700,18 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, term_xcursor_update(term); break; + case TERM_SURF_BORDER_LEFT: + case TERM_SURF_BORDER_RIGHT: + case TERM_SURF_BORDER_TOP: + case TERM_SURF_BORDER_BOTTOM: + term->xcursor = xcursor_for_csd_border(term, x, y); + render_xcursor_set(term); + break; + case TERM_SURF_TITLE: term->xcursor = "left_ptr"; render_xcursor_set(term); break; - - case TERM_SURF_BORDER_LEFT: - if (y < 10) - term->xcursor = "top_left_corner"; - else - term->xcursor = "left_side"; - render_xcursor_set(term); - break; - - case TERM_SURF_BORDER_RIGHT: - term->xcursor = "right_side"; - render_xcursor_set(term); - break; - - case TERM_SURF_BORDER_TOP: - if (x < 10) - term->xcursor = "top_left_corner"; - else - term->xcursor = "top_side"; - render_xcursor_set(term); - break; - - case TERM_SURF_BORDER_BOTTOM: - term->xcursor = "bottom_side"; - render_xcursor_set(term); - break; } @@ -735,6 +774,9 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; + term->window->csd.x = x; + term->window->csd.y = y; + switch (term->active_surface) { case TERM_SURF_NONE: case TERM_SURF_GRID: @@ -743,16 +785,11 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, break; case TERM_SURF_BORDER_LEFT: - if (y < 10) - term->xcursor = "top_left_corner"; - else - term->xcursor = "left_side"; - render_xcursor_set(term); - break; - case TERM_SURF_BORDER_RIGHT: case TERM_SURF_BORDER_TOP: case TERM_SURF_BORDER_BOTTOM: + term->xcursor = xcursor_for_csd_border(term, x, y); + render_xcursor_set(term); break; } @@ -844,8 +881,27 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, [TERM_SURF_BORDER_TOP] = XDG_TOPLEVEL_RESIZE_EDGE_TOP, [TERM_SURF_BORDER_BOTTOM] = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM, }; - if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) - xdg_toplevel_resize(term->window->xdg_toplevel, term->wl->seat, serial, map[term->active_surface]); + + if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) { + int resize_type; + + int x = term->window->csd.x; + int y = term->window->csd.y; + + if (is_top_left(term, x, y)) + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + else if (is_top_right(term, x, y)) + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + else if (is_bottom_left(term, x, y)) + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else if (is_bottom_right(term, x, y)) + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else + resize_type = map[term->active_surface]; + + xdg_toplevel_resize( + term->window->xdg_toplevel, term->wl->seat, serial, resize_type); + } return; } diff --git a/wayland.h b/wayland.h index 95e0e1e9..5ffbab2d 100644 --- a/wayland.h +++ b/wayland.h @@ -105,6 +105,7 @@ struct wl_window { struct { struct wl_surface *surface[5]; struct wl_subsurface *sub_surface[5]; + int x, y; } csd; /* Scrollback search */ From e01030f99fcf4cbc92c0319b6bf083c515099e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 09:39:05 +0100 Subject: [PATCH 060/125] render: csd: bump title bar height from 20 -> 26 --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index 4dbf272a..74c683dd 100644 --- a/render.c +++ b/render.c @@ -25,7 +25,7 @@ #define max(x, y) ((x) > (y) ? (x) : (y)) const int csd_border_size = 5; -const int csd_title_size = 20; +const int csd_title_size = 26; struct renderer { struct fdm *fdm; From 5717a0dfb07d4d302186277a5b6d20ef7d12898c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 11:01:23 +0100 Subject: [PATCH 061/125] config: change default geometry from 800x600 -> 700x500 If the display resolution *is* 800x600, using this size is bad since there will typically be panels and other things on the screen too. Not that 800x600 is something we expect to see in real life, but may happen on virtual displays. --- completions/zsh/_foot | 2 +- config.c | 4 ++-- doc/foot.1.scd | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/completions/zsh/_foot b/completions/zsh/_foot index 0132604a..c573d4cf 100644 --- a/completions/zsh/_foot +++ b/completions/zsh/_foot @@ -6,7 +6,7 @@ _arguments \ '(-f --font)'{-f,--font}'[font name and style in fontconfig format (monospace)]:font:->fonts' \ '(-t --term)'{-t,--term}'[value to set the environment variable TERM to (foot)]:term:->terms' \ '--login-shell[start shell as a login shell]' \ - '(-g --geometry)'{-g,--geometry}'[window WIDTHxHEIGHT, in pixels (800x600)]:geometry:()' \ + '(-g --geometry)'{-g,--geometry}'[window WIDTHxHEIGHT, in pixels (700x50)]:geometry:()' \ '(-s --server)'{-s,--server}'[run as server; open terminals by running footclient]:server:_files' \ '--hold[remain open after child process exits]' \ '(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running (server mode only)]:pidfile:_files' \ diff --git a/config.c b/config.c index 63db806e..37795bb9 100644 --- a/config.c +++ b/config.c @@ -491,8 +491,8 @@ config_load(struct config *conf, const char *conf_path) *conf = (struct config) { .term = strdup("foot"), .shell = get_shell(), - .width = 800, - .height = 600, + .width = 700, + .height = 500, .pad_x = 2, .pad_y = 2, .fonts = tll_init(), diff --git a/doc/foot.1.scd b/doc/foot.1.scd index 97815066..5b3c07cc 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -31,7 +31,7 @@ execute (instead of the shell). Default: _monospace_. *-g*,*--geometry*=_WIDTHxHEIGHT_ - Set initial window width and height. Default: *800x600*. + Set initial window width and height. Default: *700x500*. *-t*,*--term*=_TERM_ Value to set the environment variable *TERM* to. Default: *foot*. From 601dc02ea1758f6690fffa925bffb959ba300739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 11:40:41 +0100 Subject: [PATCH 062/125] render: wl_subsurface_set_position() uses un-scaled coordinates This fixes an issue where the CSDs and the search box was incorrectly positioned when output's scale != 1. --- render.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/render.c b/render.c index 74c683dd..6d3e7180 100644 --- a/render.c +++ b/render.c @@ -744,7 +744,7 @@ render_csd(struct terminal *term) &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); pixman_image_unref(src); - wl_subsurface_set_position(sub, x, y); + wl_subsurface_set_position(sub, x / term->scale, y / term->scale); wl_surface_attach(surf, buf->wl_buf, 0, 0); //wl_surface_set_opaque_region(surf, region); @@ -1156,8 +1156,8 @@ render_search_box(struct terminal *term) wl_subsurface_set_position( term->window->search_sub_surface, - max(0, (int32_t)term->width - width - margin), - max(0, (int32_t)term->height - height - margin)); + max(0, (int32_t)term->width - width - margin) / scale, + max(0, (int32_t)term->height - height - margin) / scale); wl_surface_commit(term->window->search_surface); } From f67572208db6314338fdf57faeda9e999673303a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 11:41:40 +0100 Subject: [PATCH 063/125] render: search: fix off-by-one error in number of visible characters --- render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index 6d3e7180..a1162a39 100644 --- a/render.c +++ b/render.c @@ -1119,7 +1119,7 @@ render_search_box(struct terminal *term) pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]); if (term->search.cursor < glyph_offset || - term->search.cursor >= glyph_offset + visible_chars + 2) + term->search.cursor >= glyph_offset + visible_chars + 1) { /* Make sure cursor is always visible */ term->render.search_glyph_offset = glyph_offset = term->search.cursor; @@ -1127,7 +1127,7 @@ render_search_box(struct terminal *term) /* Text (what the user entered - *not* match(es)) */ for (size_t i = glyph_offset; - i < term->search.len && i - glyph_offset < visible_chars + 1; + i < term->search.len && i - glyph_offset < visible_chars; i++) { if (i == term->search.cursor) From cbf657e2d23bc58041faf7736b87af59f826bf54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 11:42:00 +0100 Subject: [PATCH 064/125] render: resize: redraw search box, if visible --- render.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/render.c b/render.c index a1162a39..a22ee8d1 100644 --- a/render.c +++ b/render.c @@ -1353,12 +1353,18 @@ damage_view: xdg_toplevel_set_min_size( term->window->xdg_toplevel, min_width / scale, min_height / scale); + + render_csd(term); + if (term->is_searching) + render_search_box(term); + tll_free(term->normal.scroll_damage); tll_free(term->alt.scroll_damage); - render_csd(term); + term->render.last_buf = NULL; term_damage_view(term); render_refresh(term); + return true; } From 7b51d6919defbc87d0d11430f6ac9e5bfd137592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 11:55:43 +0100 Subject: [PATCH 065/125] input: handle double/triple click state regardless of surface --- input.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/input.c b/input.c index e3095009..462cbf32 100644 --- a/input.c +++ b/input.c @@ -849,6 +849,28 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, assert(term != NULL); + /* Update double/triple click state */ + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + /* Time since last click */ + struct timeval now, since_last; + gettimeofday(&now, NULL); + timersub(&now, &wayl->mouse.last_time, &since_last); + + /* Double- or triple click? */ + if (button == wayl->mouse.last_button && + since_last.tv_sec == 0 && + since_last.tv_usec <= 300 * 1000) + { + wayl->mouse.count++; + } else + wayl->mouse.count = 1; + + wayl->mouse.button = button; /* For motion events */ + wayl->mouse.last_button = button; + wayl->mouse.last_time = now; + } else + wayl->mouse.button = 0; /* For motion events */ + switch (term->active_surface) { case TERM_SURF_NONE: assert(false); @@ -910,28 +932,12 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, break; } - - if (term->active_surface != TERM_SURF_GRID) - return; + assert(term->active_surface == TERM_SURF_GRID); search_cancel(term); switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: { - /* Time since last click */ - struct timeval now, since_last; - gettimeofday(&now, NULL); - timersub(&now, &wayl->mouse.last_time, &since_last); - - /* Double- or triple click? */ - if (button == wayl->mouse.last_button && - since_last.tv_sec == 0 && - since_last.tv_usec <= 300 * 1000) - { - wayl->mouse.count++; - } else - wayl->mouse.count = 1; - if (button == BTN_LEFT) { switch (wayl->mouse.count) { case 1: @@ -955,9 +961,6 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, selection_cancel(term); } - wayl->mouse.button = button; /* For motion events */ - wayl->mouse.last_button = button; - wayl->mouse.last_time = now; term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col); break; } @@ -968,7 +971,6 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, else selection_finalize(term, serial); - wayl->mouse.button = 0; /* For motion events */ term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col); break; } From 4ee5a81d1a969ba9529c95d93b92f7c83de41fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 11:56:16 +0100 Subject: [PATCH 066/125] input: toggle maximized state when double-clicking the title bar --- input.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/input.c b/input.c index 462cbf32..9f57307b 100644 --- a/input.c +++ b/input.c @@ -877,19 +877,18 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, break; case TERM_SURF_TITLE: - if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) - xdg_toplevel_move(term->window->xdg_toplevel, term->wl->seat, serial); - else if (button == BTN_RIGHT && state == WL_POINTER_BUTTON_STATE_PRESSED) { - if (term->window->is_maximized) { - LOG_INFO("UNsetting maximized"); - xdg_toplevel_unset_maximized(term->window->xdg_toplevel); - } else { - LOG_INFO("setting maximized"); - xdg_toplevel_set_maximized(term->window->xdg_toplevel); + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + + /* Toggle maximized state on double-click */ + if (button == BTN_LEFT && wayl->mouse.count == 2) { + if (term->window->is_maximized) + xdg_toplevel_unset_maximized(term->window->xdg_toplevel); + else + xdg_toplevel_set_maximized(term->window->xdg_toplevel); } - } else if (button == BTN_MIDDLE && state == WL_POINTER_BUTTON_STATE_PRESSED) { - LOG_INFO("setting minimized"); - xdg_toplevel_set_minimized(term->window->xdg_toplevel); + + else if (button == BTN_LEFT) + xdg_toplevel_move(term->window->xdg_toplevel, term->wl->seat, serial); } return; From 1091d1c0785cd07f2cb96fe88b9ccd0d331ede01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 12:08:59 +0100 Subject: [PATCH 067/125] input: reset mouse state on pointer leave --- input.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/input.c b/input.c index 9f57307b..6a2bd0bf 100644 --- a/input.c +++ b/input.c @@ -734,6 +734,9 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, wayl->pointer.xcursor = NULL; } + /* Reset mouse state */ + memset(&wayl->mouse, 0, sizeof(wayl->mouse)); + wayl->mouse_focus = NULL; if (old_moused == NULL) { LOG_WARN( From 43c05518064d6b6dc9a24cf2bded216ab84508f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 12:09:28 +0100 Subject: [PATCH 068/125] wayland: store pointer to window instance in all surfaces --- input.c | 8 +++++++- search.c | 2 ++ wayland.c | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index 6a2bd0bf..d77061eb 100644 --- a/input.c +++ b/input.c @@ -676,7 +676,8 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, assert(surface != NULL); struct wayland *wayl = data; - struct terminal *term = wayl_terminal_from_surface(wayl, surface); + struct wl_window *win = wl_surface_get_user_data(surface); + struct terminal *term = win->term; LOG_DBG("pointer-enter: surface = %p, new-moused = %p", surface, term); @@ -743,6 +744,11 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, "compositor sent pointer_leave event without a pointer_enter " "event: surface=%p", surface); } else { + if (surface != NULL) { + /* Sway 1.4 sends this event with a NULL surface when we destroy the window */ + const struct wl_window *win = wl_surface_get_user_data(surface); + assert(old_moused == win->term); + } old_moused->active_surface = TERM_SURF_NONE; term_xcursor_update(old_moused); } diff --git a/search.c b/search.c index 590c7577..c0c33f3b 100644 --- a/search.c +++ b/search.c @@ -75,6 +75,8 @@ search_begin(struct terminal *term) struct wl_window *win = term->window; struct wayland *wayl = term->wl; win->search_surface = wl_compositor_create_surface(wayl->compositor); + wl_surface_set_user_data(win->search_surface, term->window); + win->search_sub_surface = wl_subcompositor_get_subsurface( wayl->sub_compositor, win->search_surface, win->surface); wl_subsurface_set_desync(win->search_sub_surface); diff --git a/wayland.c b/wayland.c index 9b8cb258..e5249a1d 100644 --- a/wayland.c +++ b/wayland.c @@ -51,6 +51,7 @@ csd_instantiate(struct wl_window *win) wayl->sub_compositor, win->csd.surface[i], win->surface); wl_subsurface_set_sync(win->csd.sub_surface[i]); + wl_surface_set_user_data(win->csd.surface[i], win); wl_surface_commit(win->csd.surface[i]); } } From c917a74f487941fa16548261680484cb78e63189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 12:13:58 +0100 Subject: [PATCH 069/125] wayland: remove all usages of wayl_terminal_from_surface() Use the 'user data' pointer from the wayland surface instead. --- input.c | 9 ++++++--- wayland.c | 13 ------------- wayland.h | 3 --- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/input.c b/input.c index d77061eb..a7a78a5d 100644 --- a/input.c +++ b/input.c @@ -88,9 +88,11 @@ keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, assert(surface != NULL); struct wayland *wayl = data; + struct wl_window *win = wl_surface_get_user_data(surface); + struct terminal *term = win->term; + + wayl->kbd_focus = term; wayl->input_serial = serial; - wayl->kbd_focus = wayl_terminal_from_surface(wayl, surface); - assert(wayl->kbd_focus != NULL); term_kbd_focus_in(wayl->kbd_focus); term_xcursor_update(wayl->kbd_focus); @@ -147,7 +149,8 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, assert( wayl->kbd_focus == NULL || surface == NULL || /* Seen on Sway 1.2 */ - wayl_terminal_from_surface(wayl, surface) == wayl->kbd_focus); + ((const struct wl_window *)wl_surface_get_user_data(surface))->term == wayl->kbd_focus + ); struct terminal *old_focused = wayl->kbd_focus; wayl->kbd_focus = NULL; diff --git a/wayland.c b/wayland.c index e5249a1d..5d3a30df 100644 --- a/wayland.c +++ b/wayland.c @@ -1150,19 +1150,6 @@ wayl_reload_cursor_theme(struct wayland *wayl, struct terminal *term) return render_xcursor_set(term); } -struct terminal * -wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface) -{ - tll_foreach(wayl->terms, it) { - if (term_surface_kind(it->item, surface) != TERM_SURF_NONE) - return it->item; - } - - assert(false); - LOG_WARN("surface %p doesn't map to a terminal", surface); - return NULL; -} - void wayl_flush(struct wayland *wayl) { diff --git a/wayland.h b/wayland.h index 5ffbab2d..a18b40a3 100644 --- a/wayland.h +++ b/wayland.h @@ -205,8 +205,5 @@ void wayl_destroy(struct wayland *wayl); void wayl_flush(struct wayland *wayl); void wayl_roundtrip(struct wayland *wayl); -struct terminal *wayl_terminal_from_surface( - struct wayland *wayl, struct wl_surface *surface); - struct wl_window *wayl_win_init(struct terminal *term); void wayl_win_destroy(struct wl_window *win); From 43b07b122a1fa460bdda3edc529726e3b6a6463d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 12:52:55 +0100 Subject: [PATCH 070/125] input: csd: add a small delay before initiating a move This ensures the user has time to double-click to toggle the maximized state. --- input.c | 41 ++++++++++++++++++++++++++++++++++++----- wayland.c | 4 ++++ wayland.h | 3 +++ 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/input.c b/input.c index a7a78a5d..1db3541f 100644 --- a/input.c +++ b/input.c @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -833,6 +834,19 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col); } +static bool +fdm_csd_move(struct fdm *fdm, int fd, int events, void *data) +{ + struct wl_window *win = data; + struct wayland *wayl = win->term->wl; + + fdm_del(fdm, fd); + win->csd.move_timeout_fd = -1; + + xdg_toplevel_move(win->xdg_toplevel, wayl->seat, win->csd.serial); + return true; +} + static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) @@ -891,16 +905,33 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_TITLE: if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + struct wl_window *win = term->window; + /* Toggle maximized state on double-click */ if (button == BTN_LEFT && wayl->mouse.count == 2) { - if (term->window->is_maximized) - xdg_toplevel_unset_maximized(term->window->xdg_toplevel); + if (win->is_maximized) + xdg_toplevel_unset_maximized(win->xdg_toplevel); else - xdg_toplevel_set_maximized(term->window->xdg_toplevel); + xdg_toplevel_set_maximized(win->xdg_toplevel); } - else if (button == BTN_LEFT) - xdg_toplevel_move(term->window->xdg_toplevel, term->wl->seat, serial); + else if (button == BTN_LEFT && win->csd.move_timeout_fd == -1) { + const struct itimerspec timeout = { + .it_value = {.tv_nsec = 100000000}, + }; + + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + if (fd >= 0 && + timerfd_settime(fd, 0, &timeout, NULL) == 0 && + fdm_add(wayl->fdm, fd, EPOLLIN, &fdm_csd_move, win)) + { + win->csd.move_timeout_fd = fd; + win->csd.serial = serial; + } else { + LOG_ERRNO("failed to configure XDG toplevel move timer FD"); + close(fd); + } + } } return; diff --git a/wayland.c b/wayland.c index 5d3a30df..7322a32f 100644 --- a/wayland.c +++ b/wayland.c @@ -1014,6 +1014,7 @@ wayl_win_init(struct terminal *term) struct wl_window *win = calloc(1, sizeof(*win)); win->term = term; win->use_csd = CSD_UNKNOWN; + win->csd.move_timeout_fd = -1; win->surface = wl_compositor_create_surface(wayl->compositor); if (win->surface == NULL) { @@ -1072,6 +1073,9 @@ wayl_win_destroy(struct wl_window *win) if (win == NULL) return; + if (win->csd.move_timeout_fd != -1) + close(win->csd.move_timeout_fd); + /* * First, unmap all surfaces to trigger things like * keyboard_leave() and wl_pointer_leave(). diff --git a/wayland.h b/wayland.h index a18b40a3..4d73be42 100644 --- a/wayland.h +++ b/wayland.h @@ -106,6 +106,9 @@ struct wl_window { struct wl_surface *surface[5]; struct wl_subsurface *sub_surface[5]; int x, y; + + int move_timeout_fd; + uint32_t serial; } csd; /* Scrollback search */ From e496d81f4d5a0432e0f12fd3494a48724f4d7d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 13:04:18 +0100 Subject: [PATCH 071/125] input: don't update xcursor on keyboard enter/leave I'm not sure why I added this in the first place... our cursor doesn't depend on keyboard focus. Furthermore, with CSDs, we may get keyboard enter events for the CSD surfaces, and these should definitely *not* update the xcursor as if it was inside the main surface. --- input.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/input.c b/input.c index 1db3541f..91b49460 100644 --- a/input.c +++ b/input.c @@ -96,7 +96,6 @@ keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, wayl->input_serial = serial; term_kbd_focus_in(wayl->kbd_focus); - term_xcursor_update(wayl->kbd_focus); } static bool @@ -163,10 +162,9 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, wayl->kbd.meta = false;; xkb_compose_state_reset(wayl->kbd.xkb_compose_state); - if (old_focused != NULL) { + if (old_focused != NULL) term_kbd_focus_out(old_focused); - term_xcursor_update(old_focused); - } else { + else { /* * Sway bug - under certain conditions we get a * keyboard_leave() (and keyboard_key()) without first having From 73133c10eed3ad8415646d42a3f6386d7d702348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 15:29:00 +0100 Subject: [PATCH 072/125] input: cleanup --- input.c | 140 +++++++++++++++++++++++++++--------------------------- wayland.h | 4 +- 2 files changed, 71 insertions(+), 73 deletions(-) diff --git a/input.c b/input.c index 91b49460..705fc7af 100644 --- a/input.c +++ b/input.c @@ -689,10 +689,6 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, int y = wl_fixed_to_int(surface_y) * term->scale; switch ((term->active_surface = term_surface_kind(term, surface))) { - case TERM_SURF_NONE: - assert(false); - break; - case TERM_SURF_GRID: wayl->mouse.col = x / term->cell_width; wayl->mouse.row = y / term->cell_height; @@ -700,7 +696,9 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, break; case TERM_SURF_SEARCH: - term_xcursor_update(term); + case TERM_SURF_TITLE: + term->xcursor = "left_ptr"; + render_xcursor_set(term); break; case TERM_SURF_BORDER_LEFT: @@ -711,13 +709,10 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, render_xcursor_set(term); break; - case TERM_SURF_TITLE: - term->xcursor = "left_ptr"; - render_xcursor_set(term); + case TERM_SURF_NONE: + assert(false); break; } - - } static void @@ -785,12 +780,11 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; - term->window->csd.x = x; - term->window->csd.y = y; + wayl->mouse.x = x; + wayl->mouse.y = y; switch (term->active_surface) { case TERM_SURF_NONE: - case TERM_SURF_GRID: case TERM_SURF_SEARCH: case TERM_SURF_TITLE: break; @@ -802,34 +796,34 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, term->xcursor = xcursor_for_csd_border(term, x, y); render_xcursor_set(term); break; - } - if (term->active_surface != TERM_SURF_GRID) - return; + case TERM_SURF_GRID: { + int col = (x - term->margins.left) / term->cell_width; + int row = (y - term->margins.top) / term->cell_height; - int col = (x - term->margins.left) / term->cell_width; - int row = (y - term->margins.top) / term->cell_height; + if (col < 0 || row < 0 || col >= term->cols || row >= term->rows) + return; - if (col < 0 || row < 0 || col >= term->cols || row >= term->rows) - return; + bool update_selection = wayl->mouse.button == BTN_LEFT; + bool update_selection_early = term->selection.end.row == -1; - bool update_selection = wayl->mouse.button == BTN_LEFT; - bool update_selection_early = term->selection.end.row == -1; + if (update_selection && update_selection_early) + selection_update(term, col, row); - if (update_selection && update_selection_early) - selection_update(term, col, row); + if (col == wayl->mouse.col && row == wayl->mouse.row) + break; - if (col == wayl->mouse.col && row == wayl->mouse.row) - return; + wayl->mouse.col = col; + wayl->mouse.row = row; - wayl->mouse.col = col; - wayl->mouse.row = row; + if (update_selection && !update_selection_early) + selection_update(term, col, row); - if (update_selection && !update_selection_early) - selection_update(term, col, row); - - term_mouse_motion( + term_mouse_motion( term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col); + break; + } + } } static bool @@ -896,10 +890,6 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, wayl->mouse.button = 0; /* For motion events */ switch (term->active_surface) { - case TERM_SURF_NONE: - assert(false); - break; - case TERM_SURF_TITLE: if (state == WL_POINTER_BUTTON_STATE_PRESSED) { @@ -947,8 +937,8 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) { int resize_type; - int x = term->window->csd.x; - int y = term->window->csd.y; + int x = wayl->mouse.x; + int y = wayl->mouse.y; if (is_top_left(term, x, y)) resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; @@ -967,52 +957,60 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, return; } - case TERM_SURF_GRID: case TERM_SURF_SEARCH: break; - } - assert(term->active_surface == TERM_SURF_GRID); + case TERM_SURF_GRID: { + search_cancel(term); - search_cancel(term); + switch (state) { + case WL_POINTER_BUTTON_STATE_PRESSED: { + if (button == BTN_LEFT) { + switch (wayl->mouse.count) { + case 1: + selection_start( + term, wayl->mouse.col, wayl->mouse.row, + wayl->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL); + break; - switch (state) { - case WL_POINTER_BUTTON_STATE_PRESSED: { - if (button == BTN_LEFT) { - switch (wayl->mouse.count) { - case 1: - selection_start( - term, wayl->mouse.col, wayl->mouse.row, - wayl->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL); - break; + case 2: + selection_mark_word(term, wayl->mouse.col, wayl->mouse.row, + wayl->kbd.ctrl, serial); + break; - case 2: - selection_mark_word(term, wayl->mouse.col, wayl->mouse.row, - wayl->kbd.ctrl, serial); - break; - - case 3: - selection_mark_row(term, wayl->mouse.row, serial); - break; + case 3: + selection_mark_row(term, wayl->mouse.row, serial); + break; + } + } else { + if (wayl->mouse.count == 1 && button == BTN_MIDDLE && + selection_enabled(term)) + { + selection_from_primary(term); + } + selection_cancel(term); } - } else { - if (wayl->mouse.count == 1 && button == BTN_MIDDLE && selection_enabled(term)) - selection_from_primary(term); - selection_cancel(term); + + term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col); + break; } - term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col); + case WL_POINTER_BUTTON_STATE_RELEASED: + if (button != BTN_LEFT || term->selection.end.col == -1) + selection_cancel(term); + else + selection_finalize(term, serial); + + term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col); + break; + } break; } - case WL_POINTER_BUTTON_STATE_RELEASED: - if (button != BTN_LEFT || term->selection.end.col == -1) - selection_cancel(term); - else - selection_finalize(term, serial); - - term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col); + case TERM_SURF_NONE: + assert(false); break; + } } diff --git a/wayland.h b/wayland.h index 4d73be42..768fee5f 100644 --- a/wayland.h +++ b/wayland.h @@ -105,8 +105,6 @@ struct wl_window { struct { struct wl_surface *surface[5]; struct wl_subsurface *sub_surface[5]; - int x, y; - int move_timeout_fd; uint32_t serial; } csd; @@ -182,6 +180,8 @@ struct wayland { } pointer; struct { + int x; + int y; int col; int row; int button; From 33744ebe63545540b583448f3d6b8aebc791911f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 15:29:58 +0100 Subject: [PATCH 073/125] input: fix 'unused variable' warning (release builds) --- input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index 705fc7af..3bb9eaba 100644 --- a/input.c +++ b/input.c @@ -743,7 +743,8 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, } else { if (surface != NULL) { /* Sway 1.4 sends this event with a NULL surface when we destroy the window */ - const struct wl_window *win = wl_surface_get_user_data(surface); + const struct wl_window *win __attribute__((unused)) + = wl_surface_get_user_data(surface); assert(old_moused == win->term); } old_moused->active_surface = TERM_SURF_NONE; From ea2d7f8b8c5782d422d02058861fa9a754055b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 15:38:04 +0100 Subject: [PATCH 074/125] input: start window move right away if user starts dragging the window When the user left-clicks the title-bar, we start a timer. When the timer has elapsed, we initiate a 'move' operation. However, if the user clicked, and then started dragging right away, there was a very visible lag since we waited for the timeout before starting the move. Now, on a pointer motion event we detect a running 'move' timer, and abort it and instead start the 'move' operation right away. --- input.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/input.c b/input.c index 3bb9eaba..b5973364 100644 --- a/input.c +++ b/input.c @@ -758,6 +758,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, { struct wayland *wayl = data; struct terminal *term = wayl->mouse_focus; + struct wl_window *win = term->window; /* Workaround buggy Sway 1.2 */ if (term == NULL) { @@ -787,7 +788,17 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, switch (term->active_surface) { case TERM_SURF_NONE: case TERM_SURF_SEARCH: + break; + case TERM_SURF_TITLE: + /* We've started a 'move' timer, but user started dragging + * right away - abort the timer and initiate the actual move + * right away */ + if (wayl->mouse.button == BTN_LEFT && win->csd.move_timeout_fd != -1) { + fdm_del(wayl->fdm, win->csd.move_timeout_fd); + win->csd.move_timeout_fd = -1; + xdg_toplevel_move(win->xdg_toplevel, wayl->seat, win->csd.serial); + } break; case TERM_SURF_BORDER_LEFT: @@ -922,6 +933,14 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } } } + + else if (state == WL_POINTER_BUTTON_STATE_RELEASED) { + struct wl_window *win = term->window; + if (win->csd.move_timeout_fd != -1) { + fdm_del(wayl->fdm, win->csd.move_timeout_fd); + win->csd.move_timeout_fd = -1; + } + } return; case TERM_SURF_BORDER_LEFT: From 32a3f567108236b14d811562c1e1f54ce4e29ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 15:46:40 +0100 Subject: [PATCH 075/125] wayland: don't resize when we're not mapped anywhere Normally, we resize and update the font's DPI whenever our window enters or leaves an output. This is since a change in the outputs we're mapped on means the scale factor to use, or the DPI to use for the fonts may have changed. However, a special case is when we're removed from the last output. This should only happen at shutdown, when we're un-mapping ourselves. In this case, we typically don't have a access to e.g. the PTMX fd (often, the reason we're shutting down is because the client exited). This resulted in (harmless) error messages when emitting the TIOCSWINSZ event. Since we're shutting down anyway, we can simply skip the resize and everything. This gets rid of the error message, and also means we're shutting down faster. --- wayland.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wayland.c b/wayland.c index 7322a32f..b694e23c 100644 --- a/wayland.c +++ b/wayland.c @@ -133,6 +133,9 @@ static const struct wl_seat_listener seat_listener = { static void update_term_for_output_change(struct terminal *term) { + if (tll_length(term->window->on_outputs) == 0) + return; + render_resize(term, term->width / term->scale, term->height / term->scale); term_font_dpi_changed(term); wayl_reload_cursor_theme(term->wl, term); From d6f0a47fb8f4392438e0210d8f9068183e054966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 17:24:45 +0100 Subject: [PATCH 076/125] wayland: always update window->is_fullscreen, not just when using CSDs --- wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index b694e23c..957f736e 100644 --- a/wayland.c +++ b/wayland.c @@ -588,8 +588,8 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, csd_destroy(win); else csd_instantiate(win); - win->is_fullscreen = win->configure.is_fullscreen; } + win->is_fullscreen = win->configure.is_fullscreen; xdg_surface_ack_configure(xdg_surface, serial); bool resized = render_resize(term, win->configure.width, win->configure.height); From 9264e1e5bd00243859e6281d717b6cbe0355e3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 17:25:08 +0100 Subject: [PATCH 077/125] render: don't update last 'un-maximized' size when in fullscreen --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index a22ee8d1..a067ad68 100644 --- a/render.c +++ b/render.c @@ -1346,7 +1346,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force) term->render.last_cursor.cell = NULL; damage_view: - if (!term->window->is_maximized) { + if (!term->window->is_maximized && !term->window->is_fullscreen) { term->unmaximized_width = term->width; term->unmaximized_height = term->height; } From 92d638eb1c34859a08605ffc79be8f0a4a3c63a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Feb 2020 18:02:38 +0100 Subject: [PATCH 078/125] render: csd: split up positioning from rendering --- render.c | 219 ++++++++++++++++++++++++++++++++--------------------- render.h | 1 + terminal.c | 6 +- wayland.h | 9 +++ 4 files changed, 148 insertions(+), 87 deletions(-) diff --git a/render.c b/render.c index a067ad68..340747c0 100644 --- a/render.c +++ b/render.c @@ -43,7 +43,7 @@ static void fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data); #define shm_cookie_grid(term) ((unsigned long)((uintptr_t)term + 0)) #define shm_cookie_search(term) ((unsigned long)((uintptr_t)term + 1)) -#define shm_cookie_csd(term, n) ((unsigned long)((uintptr_t)term + 2 + (n))) +#define shm_cookie_csd(term, n) ((unsigned long)((uintptr_t)term + 2 + (n))) /* Should be placed last */ struct renderer * render_init(struct fdm *fdm, struct wayland *wayl) @@ -665,100 +665,149 @@ render_worker_thread(void *_ctx) return -1; } +struct csd_data { + enum csd_surface idx; + int x; + int y; + int width; + int height; +}; + +static const struct csd_data * +get_csd_data(const struct terminal *term, enum csd_surface surf_idx) +{ + assert(term->window->use_csd == CSD_YES); + + /* Only title bar is rendered in maximized mode */ + const int border_width = !term->window->is_maximized + ? csd_border_size * term->scale : 0; + + const int title_height = !term->window->is_fullscreen + ? csd_title_size * term->scale : 0; + + const struct csd_data csd_data[] = { + /* SURF IDX, X, Y, WIDTH, HEIGHT */ +#if FOOT_CSD_OUTSIDE + {CSD_SURF_TITLE, 0, -title_height, term->width, title_height}, + {CSD_SURF_LEFT, -border_width, -title_height, border_width, title_height + term->height}, + {CSD_SURF_RIGHT , term->width, -title_height, border_width, title_height + term->height}, + {CSD_SURF_TOP, -border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, + {CSD_SURF_BOTTOM, -border_width, term->height, term->width + 2 * border_width, border_width}, +#else + {CSD_SURF_TITLE, border_width, border_width, term->width - 2 * border_width, title_height}, + {CSD_SURF_LEFT, 0, border_width, border_width, term->height - 2 * border_width}, + {CSD_SURF_RIGHT, term->width - border_width, border_width, border_width, term->height - 2 * border_width}, + {CSD_SURF_TOP, 0, 0, term->width, border_width}, + {CSD_SURF_BOTTOM, 0, term->height - border_width, term->width, border_width}, +#endif + }; + + const struct csd_data *ret = &csd_data[surf_idx]; + assert(ret->idx == surf_idx); + return ret; +} + +static void +render_csd_part(struct terminal *term, + struct wl_surface *surf, struct buffer *buf, + int width, int height, pixman_color_t *color) +{ + assert(term->window->use_csd == CSD_YES); + + pixman_image_t *src = pixman_image_create_solid_fill(color); + + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, color, 1, + &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); + pixman_image_unref(src); + + wl_surface_attach(surf, buf->wl_buf, 0, 0); + wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); + wl_surface_set_buffer_scale(surf, term->scale); + wl_surface_commit(surf); +} + +void +render_csd_title(struct terminal *term) +{ + if (term->window->use_csd != CSD_YES) + return; + + const struct csd_data *info = get_csd_data(term, CSD_SURF_TITLE); + struct wl_surface *surf = term->window->csd.surface[CSD_SURF_TITLE]; + + unsigned long cookie = shm_cookie_csd(term, CSD_SURF_TITLE); + struct buffer *buf = shm_get_buffer( + term->wl->shm, info->width, info->height, cookie); + + pixman_color_t color = color_hex_to_pixman(term->colors.fg); + + if (!term->visual_focus) + pixman_color_dim(&color); + + struct wl_region *region = wl_compositor_create_region(term->wl->compositor); + if (region != NULL) { + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_opaque_region(surf, region); + wl_region_destroy(region); + } + + render_csd_part(term, surf, buf, info->width, info->height, &color); +} + +static void +render_csd_border(struct terminal *term, enum csd_surface surf_idx) +{ + assert(surf_idx >= CSD_SURF_LEFT && surf_idx <= CSD_SURF_BOTTOM); + + if (term->window->use_csd != CSD_YES) + return; + + const struct csd_data *info = get_csd_data(term, surf_idx); + struct wl_surface *surf = term->window->csd.surface[surf_idx]; + + unsigned long cookie = shm_cookie_csd(term, surf_idx); + struct buffer *buf = shm_get_buffer( + term->wl->shm, info->width, info->height, cookie); + + pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0); + + if (!term->visual_focus) + pixman_color_dim(&color); + render_csd_part(term, surf, buf, info->width, info->height, &color); +} + void render_csd(struct terminal *term) { - switch (term->window->use_csd) { - case CSD_UNKNOWN: - case CSD_NO: - break; + if (term->window->use_csd != CSD_YES) + return; - case CSD_YES: { - if (term->window->is_fullscreen) - return; + for (size_t i = 0; i < CSD_SURF_COUNT; i++) { + const struct csd_data *info = get_csd_data(term, i); + const int x = info->x; + const int y = info->y; + const int width = info->width; + const int height = info->height; - /* Only title bar is rendered in maximized mode */ - const int border_width = !term->window->is_maximized - ? csd_border_size * term->scale : 0; + struct wl_surface *surf = term->window->csd.surface[i]; + struct wl_subsurface *sub = term->window->csd.sub_surface[i]; - const int title_height = csd_title_size * term->scale; - - const struct { - int x; - int y; - int width; - int height; - } geom[] = { - /* X, Y, WIDTH, HEIGHT */ -#if FOOT_CSD_OUTSIDE - { 0, -title_height, term->width, title_height}, /* title */ - { -border_width, -title_height, border_width, title_height + term->height}, /* left */ - { term->width, -title_height, border_width, title_height + term->height}, /* right */ - { -border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, /* top */ - { -border_width, term->height, term->width + 2 * border_width, border_width}, /* bottom */ -#else - { border_width, border_width, term->width - 2 * border_width, title_height}, /* title */ - { 0, border_width, border_width, term->height - 2 * border_width}, /* left */ - {term->width - border_width, border_width, border_width, term->height - 2 * border_width}, /* right */ - { 0, 0, term->width, border_width}, /* top */ - { 0, term->height - border_width, term->width, border_width}, /* bottom */ -#endif - }; - - struct wl_region *region = wl_compositor_create_region(term->wl->compositor); - if (region != NULL) - wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); - - for (size_t i = 0; i < sizeof(geom) / sizeof(geom[0]); i++) { - struct wl_surface *surf = term->window->csd.surface[i]; - struct wl_subsurface *sub = term->window->csd.sub_surface[i]; - - const int x = geom[i].x; - const int y = geom[i].y; - const int width = geom[i].width; - const int height = geom[i].height; - - if (width == 0 || height == 0) { - /* CSD borders aren't rendered in maximized mode */ - assert(term->window->is_maximized); - wl_subsurface_set_position(sub, 0, 0); - wl_surface_attach(surf, NULL, 0, 0); - wl_surface_commit(surf); - continue; - } - - unsigned long cookie = shm_cookie_csd(term, i); - struct buffer *buf = shm_get_buffer( - term->wl->shm, width, height, cookie); - - pixman_color_t color = color_hex_to_pixman_with_alpha( - i == 0 ? term->colors.fg : 0x0, i == 0 ? 0xffff : 0x0); - - if (!term->visual_focus) - pixman_color_dim(&color); - - pixman_image_t *src = pixman_image_create_solid_fill(&color); - - pixman_image_fill_rectangles( - PIXMAN_OP_SRC, buf->pix, &color, 1, - &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); - pixman_image_unref(src); - - wl_subsurface_set_position(sub, x / term->scale, y / term->scale); - - wl_surface_attach(surf, buf->wl_buf, 0, 0); - //wl_surface_set_opaque_region(surf, region); - wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); - wl_surface_set_buffer_scale(surf, term->scale); + if (width == 0 || height == 0) { + /* CSD borders aren't rendered in maximized mode */ + assert(term->window->is_maximized); + wl_subsurface_set_position(sub, 0, 0); + wl_surface_attach(surf, NULL, 0, 0); wl_surface_commit(surf); + continue; } - if (region != NULL) - wl_region_destroy(region); + wl_subsurface_set_position(sub, x / term->scale, y / term->scale); + } - break; - } - } + render_csd_title(term); + for (size_t i = CSD_SURF_LEFT; i <= CSD_SURF_BOTTOM; i++) + render_csd_border(term, i); } static void frame_callback( diff --git a/render.h b/render.h index 8602d367..2177e8bd 100644 --- a/render.h +++ b/render.h @@ -18,6 +18,7 @@ bool render_xcursor_set(struct terminal *term); void render_search_box(struct terminal *term); void render_csd(struct terminal *term); +void render_csd_title(struct terminal *term); struct render_worker_context { int my_id; diff --git a/terminal.c b/terminal.c index 0b3091f8..1d17ba5d 100644 --- a/terminal.c +++ b/terminal.c @@ -1641,7 +1641,8 @@ term_visual_focus_in(struct terminal *term) wl_subsurface_set_desync(term->window->csd.sub_surface[i]); } #endif - render_csd(term); + //render_csd(term); + render_csd_title(term); #if 1 if (term->window->use_csd == CSD_YES) { for (int i = 0; i < ALEN(term->window->csd.surface); i++) @@ -1667,7 +1668,8 @@ term_visual_focus_out(struct terminal *term) wl_subsurface_set_desync(term->window->csd.sub_surface[i]); } #endif - render_csd(term); + //render_csd(term); + render_csd_title(term); #if 1 if (term->window->use_csd == CSD_YES) { for (int i = 0; i < ALEN(term->window->csd.surface); i++) diff --git a/wayland.h b/wayland.h index 768fee5f..b879c319 100644 --- a/wayland.h +++ b/wayland.h @@ -91,6 +91,15 @@ struct wl_primary { extern const int csd_border_size; extern const int csd_title_size; +enum csd_surface { + CSD_SURF_TITLE, + CSD_SURF_LEFT, + CSD_SURF_RIGHT, + CSD_SURF_TOP, + CSD_SURF_BOTTOM, + CSD_SURF_COUNT, +}; + struct wayland; struct wl_window { struct terminal *term; From fff480e5849ec5cd2916e4cfe006374806ebb756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 11:43:58 +0100 Subject: [PATCH 079/125] input: use xdg_toplevel_resize_edge enum type --- input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index b5973364..bf3a0a91 100644 --- a/input.c +++ b/input.c @@ -947,7 +947,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_BORDER_RIGHT: case TERM_SURF_BORDER_TOP: case TERM_SURF_BORDER_BOTTOM: { - static const int map[] = { + static const enum xdg_toplevel_resize_edge map[] = { [TERM_SURF_BORDER_LEFT] = XDG_TOPLEVEL_RESIZE_EDGE_LEFT, [TERM_SURF_BORDER_RIGHT] = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT, [TERM_SURF_BORDER_TOP] = XDG_TOPLEVEL_RESIZE_EDGE_TOP, @@ -955,7 +955,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, }; if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) { - int resize_type; + enum xdg_toplevel_resize_edge resize_type; int x = wayl->mouse.x; int y = wayl->mouse.y; From 9d834bb43d349729568cc15de371535b3f78b33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 12:19:28 +0100 Subject: [PATCH 080/125] shm: log 'size' when failing to fallocate() --- shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shm.c b/shm.c index ab717bd0..56395fab 100644 --- a/shm.c +++ b/shm.c @@ -133,7 +133,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie) if (!failure_logged) { failure_logged = true; - LOG_ERRNO_P("failed to fallocate", err); + LOG_ERRNO_P("failed to fallocate %zu bytes", err, size); } if (ftruncate(pool_fd, size) == -1) { From 3ec50369cef48f0eeb9b4044201c076bcbbb91bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 12:24:37 +0100 Subject: [PATCH 081/125] search: update search box before we update the main grid --- search.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search.c b/search.c index c0c33f3b..1c5d8e71 100644 --- a/search.c +++ b/search.c @@ -621,6 +621,6 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask LOG_DBG("search: buffer: %S", term->search.buf); search_find_next(term); - render_refresh(term); render_search_box(term); + render_refresh(term); } From f038a27366b5b704c0ded0a1a094f2d5c4d0d542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 12:28:01 +0100 Subject: [PATCH 082/125] render: csd: switch-based CSD positioning --- render.c | 68 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/render.c b/render.c index 340747c0..cd49255e 100644 --- a/render.c +++ b/render.c @@ -666,14 +666,13 @@ render_worker_thread(void *_ctx) } struct csd_data { - enum csd_surface idx; int x; int y; int width; int height; }; -static const struct csd_data * +static struct csd_data get_csd_data(const struct terminal *term, enum csd_surface surf_idx) { assert(term->window->use_csd == CSD_YES); @@ -685,26 +684,33 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) const int title_height = !term->window->is_fullscreen ? csd_title_size * term->scale : 0; - const struct csd_data csd_data[] = { - /* SURF IDX, X, Y, WIDTH, HEIGHT */ #if FOOT_CSD_OUTSIDE - {CSD_SURF_TITLE, 0, -title_height, term->width, title_height}, - {CSD_SURF_LEFT, -border_width, -title_height, border_width, title_height + term->height}, - {CSD_SURF_RIGHT , term->width, -title_height, border_width, title_height + term->height}, - {CSD_SURF_TOP, -border_width, -title_height - border_width, term->width + 2 * border_width, border_width}, - {CSD_SURF_BOTTOM, -border_width, term->height, term->width + 2 * border_width, border_width}, -#else - {CSD_SURF_TITLE, border_width, border_width, term->width - 2 * border_width, title_height}, - {CSD_SURF_LEFT, 0, border_width, border_width, term->height - 2 * border_width}, - {CSD_SURF_RIGHT, term->width - border_width, border_width, border_width, term->height - 2 * border_width}, - {CSD_SURF_TOP, 0, 0, term->width, border_width}, - {CSD_SURF_BOTTOM, 0, term->height - border_width, term->width, border_width}, -#endif - }; + switch (surf_idx) { + case CSD_SURF_TITLE: return (struct csd_data){ 0, -title_height, term->width, title_height}; + case CSD_SURF_LEFT: return (struct csd_data){-border_width, -title_height, border_width, title_height + term->height}; + case CSD_SURF_RIGHT: return (struct csd_data){ term->width, -title_height, border_width, title_height + term->height}; + case CSD_SURF_TOP: return (struct csd_data){-border_width, -title_height - border_width, term->width + 2 * border_width, border_width}; + case CSD_SURF_BOTTOM: return (struct csd_data){-border_width, term->height, term->width + 2 * border_width, border_width}; - const struct csd_data *ret = &csd_data[surf_idx]; - assert(ret->idx == surf_idx); - return ret; + case CSD_SURF_COUNT: + assert(false); + return (struct csd_data){0}; + } +#else + switch (surf_idx) { + case CSD_SURF_TITLE: return (struct csd_data){ border_width, border_width, term->width - 2 * border_width, title_height}; + case CSD_SURF_LEFT: return (struct csd_data){ 0, border_width, border_width, term->height - 2 * border_width}; + case CSD_SURF_RIGHT: return (struct csd_data){term->width - border_width, border_width, border_width, term->height - 2 * border_width}; + case CSD_SURF_TOP: return (struct csd_data){ 0, 0, term->width, border_width}; + + case CSD_SURF_COUNT: + assert(false); + return (struct csd_data){0}; + } +#endif + + assert(false); + return (struct csd_data){0}; } static void @@ -733,12 +739,12 @@ render_csd_title(struct terminal *term) if (term->window->use_csd != CSD_YES) return; - const struct csd_data *info = get_csd_data(term, CSD_SURF_TITLE); + struct csd_data info = get_csd_data(term, CSD_SURF_TITLE); struct wl_surface *surf = term->window->csd.surface[CSD_SURF_TITLE]; unsigned long cookie = shm_cookie_csd(term, CSD_SURF_TITLE); struct buffer *buf = shm_get_buffer( - term->wl->shm, info->width, info->height, cookie); + term->wl->shm, info.width, info.height, cookie); pixman_color_t color = color_hex_to_pixman(term->colors.fg); @@ -752,7 +758,7 @@ render_csd_title(struct terminal *term) wl_region_destroy(region); } - render_csd_part(term, surf, buf, info->width, info->height, &color); + render_csd_part(term, surf, buf, info.width, info.height, &color); } static void @@ -763,18 +769,18 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx) if (term->window->use_csd != CSD_YES) return; - const struct csd_data *info = get_csd_data(term, surf_idx); + struct csd_data info = get_csd_data(term, surf_idx); struct wl_surface *surf = term->window->csd.surface[surf_idx]; unsigned long cookie = shm_cookie_csd(term, surf_idx); struct buffer *buf = shm_get_buffer( - term->wl->shm, info->width, info->height, cookie); + term->wl->shm, info.width, info.height, cookie); pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0); if (!term->visual_focus) pixman_color_dim(&color); - render_csd_part(term, surf, buf, info->width, info->height, &color); + render_csd_part(term, surf, buf, info.width, info.height, &color); } void @@ -784,11 +790,11 @@ render_csd(struct terminal *term) return; for (size_t i = 0; i < CSD_SURF_COUNT; i++) { - const struct csd_data *info = get_csd_data(term, i); - const int x = info->x; - const int y = info->y; - const int width = info->width; - const int height = info->height; + struct csd_data info = get_csd_data(term, i); + const int x = info.x; + const int y = info.y; + const int width = info.width; + const int height = info.height; struct wl_surface *surf = term->window->csd.surface[i]; struct wl_subsurface *sub = term->window->csd.sub_surface[i]; From 7b1dafae0f84cde10ad8950642535ca7d9a4427c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 12:28:33 +0100 Subject: [PATCH 083/125] render: search: kwin has problems with a resizing/repositioned sub-surface So, make it equal to the window size, and make the non-used area fully transparent. --- render.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/render.c b/render.c index cd49255e..70d93033 100644 --- a/render.c +++ b/render.c @@ -1146,14 +1146,15 @@ render_search_box(struct terminal *term) #endif const size_t margin = csd + 3 * scale; - const size_t width = min( + const size_t width = term->width - 2 * margin; + const size_t visible_width = min( term->width - 2 * margin, 2 * margin + wanted_visible_chars * term->cell_width); const size_t height = min( term->height - 2 * margin, 2 * margin + 1 * term->cell_height); - const size_t visible_chars = (width - 2 * margin) / term->cell_width; + const size_t visible_chars = (visible_width - 2 * margin) / term->cell_width; size_t glyph_offset = term->render.search_glyph_offset; unsigned long cookie = shm_cookie_search(term); @@ -1166,10 +1167,15 @@ render_search_box(struct terminal *term) pixman_image_fill_rectangles( PIXMAN_OP_SRC, buf->pix, &color, - 1, &(pixman_rectangle16_t){0, 0, width, height}); + 1, &(pixman_rectangle16_t){width - visible_width, 0, visible_width, height}); + + pixman_color_t transparent = color_hex_to_pixman_with_alpha(0, 0); + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &transparent, + 1, &(pixman_rectangle16_t){0, 0, width - visible_width, height}); struct font *font = term->fonts[0]; - int x = margin; + int x = width - visible_width + margin; int y = margin; pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]); @@ -1205,15 +1211,15 @@ render_search_box(struct terminal *term) if (term->search.cursor >= term->search.len) draw_bar(term, buf->pix, font, &fg, x, y); + wl_subsurface_set_position( + term->window->search_sub_surface, + margin / scale, + max(0, (int32_t)term->height - height - margin) / scale); + wl_surface_attach(term->window->search_surface, buf->wl_buf, 0, 0); wl_surface_damage_buffer(term->window->search_surface, 0, 0, width, height); wl_surface_set_buffer_scale(term->window->search_surface, scale); - wl_subsurface_set_position( - term->window->search_sub_surface, - max(0, (int32_t)term->width - width - margin) / scale, - max(0, (int32_t)term->height - height - margin) / scale); - wl_surface_commit(term->window->search_surface); } From b8d79c719b77fb1e9f958353c62aea09d592067b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 12:54:27 +0100 Subject: [PATCH 084/125] render: search: mark visible portion of sub-surface opaque --- quirks.c | 35 +++++++++++++++++++++++++++++++++++ quirks.h | 5 +++++ render.c | 7 +++++++ 3 files changed, 47 insertions(+) create mode 100644 quirks.c create mode 100644 quirks.h diff --git a/quirks.c b/quirks.c new file mode 100644 index 00000000..a1b1bafe --- /dev/null +++ b/quirks.c @@ -0,0 +1,35 @@ +#include "quirks.h" + +#include +#include + +void +quirk_weston_subsurface_desync(struct wl_subsurface *sub) +{ + /* + * On weston (8.0), synchronized subsurfaces aren't updated + * correctly. + + * They appear to render once, but after that, updates are + * sporadic. Sometimes they update, most of the time they + * don't. + * + * Adding explicit parent surface commits right after the + * subsurface commit doesn't help (and would be useless anyway, + * since it would defeat the purpose of having the subsurface + * synchronized in the first place). + */ + + static bool is_weston = false; + static bool initialized = false; + + if (!initialized) { + initialized = true; + is_weston = getenv("WESTON_CONFIG_FILE") != NULL; + } + + if (!is_weston) + return; + + wl_subsurface_set_desync(sub); +} diff --git a/quirks.h b/quirks.h new file mode 100644 index 00000000..1e087692 --- /dev/null +++ b/quirks.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void quirk_weston_subsurface_desync(struct wl_subsurface *sub); diff --git a/render.c b/render.c index 70d93033..d15aa756 100644 --- a/render.c +++ b/render.c @@ -1220,6 +1220,13 @@ render_search_box(struct terminal *term) wl_surface_damage_buffer(term->window->search_surface, 0, 0, width, height); wl_surface_set_buffer_scale(term->window->search_surface, scale); + struct wl_region *region = wl_compositor_create_region(term->wl->compositor); + if (region != NULL) { + wl_region_add(region, width - visible_width, 0, visible_width, height); + wl_surface_set_opaque_region(term->window->search_surface, region); + wl_region_destroy(region); + } + wl_surface_commit(term->window->search_surface); } From a29427a185f170d2e33f4530f06a6a8e23f81d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 12:54:50 +0100 Subject: [PATCH 085/125] render: search: add todo to only position sub-surface on a window resize --- render.c | 1 + 1 file changed, 1 insertion(+) diff --git a/render.c b/render.c index d15aa756..6e68f76b 100644 --- a/render.c +++ b/render.c @@ -1211,6 +1211,7 @@ render_search_box(struct terminal *term) if (term->search.cursor >= term->search.len) draw_bar(term, buf->pix, font, &fg, x, y); + /* TODO: this is only necessary on a window resize */ wl_subsurface_set_position( term->window->search_sub_surface, margin / scale, From b6f8a2e422fe05c1c1e63d39f72857d4c480bc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 13:06:00 +0100 Subject: [PATCH 086/125] search: enable/disable weston sub-surface desync quirk when rendering search box --- meson.build | 1 + quirks.c | 22 ++++++++++++++++++---- quirks.h | 3 ++- render.c | 4 ++++ search.c | 2 +- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index 21c23718..fcb85f90 100644 --- a/meson.build +++ b/meson.build @@ -112,6 +112,7 @@ executable( 'main.c', 'misc.c', 'misc.h', 'osc.c', 'osc.h', + 'quirks.c', 'quirks.h', 'render.c', 'render.h', 'search.c', 'search.h', 'selection.c', 'selection.h', diff --git a/quirks.c b/quirks.c index a1b1bafe..e96ad98e 100644 --- a/quirks.c +++ b/quirks.c @@ -3,8 +3,8 @@ #include #include -void -quirk_weston_subsurface_desync(struct wl_subsurface *sub) +static bool +is_weston(void) { /* * On weston (8.0), synchronized subsurfaces aren't updated @@ -19,7 +19,6 @@ quirk_weston_subsurface_desync(struct wl_subsurface *sub) * since it would defeat the purpose of having the subsurface * synchronized in the first place). */ - static bool is_weston = false; static bool initialized = false; @@ -28,8 +27,23 @@ quirk_weston_subsurface_desync(struct wl_subsurface *sub) is_weston = getenv("WESTON_CONFIG_FILE") != NULL; } - if (!is_weston) + return is_weston; +} + +void +quirk_weston_subsurface_desync_on(struct wl_subsurface *sub) +{ + if (!is_weston()) return; wl_subsurface_set_desync(sub); } + +void +quirk_weston_subsurface_desync_off(struct wl_subsurface *sub) +{ + if (!is_weston()) + return; + + wl_subsurface_set_sync(sub); +} diff --git a/quirks.h b/quirks.h index 1e087692..ce4b8b01 100644 --- a/quirks.h +++ b/quirks.h @@ -2,4 +2,5 @@ #include -void quirk_weston_subsurface_desync(struct wl_subsurface *sub); +void quirk_weston_subsurface_desync_on(struct wl_subsurface *sub); +void quirk_weston_subsurface_desync_off(struct wl_subsurface *sub); diff --git a/render.c b/render.c index 6e68f76b..df8a98de 100644 --- a/render.c +++ b/render.c @@ -18,6 +18,7 @@ #include "log.h" #include "config.h" #include "grid.h" +#include "quirks.h" #include "selection.h" #include "shm.h" @@ -1211,6 +1212,8 @@ render_search_box(struct terminal *term) if (term->search.cursor >= term->search.len) draw_bar(term, buf->pix, font, &fg, x, y); + quirk_weston_subsurface_desync_on(term->window->search_sub_surface); + /* TODO: this is only necessary on a window resize */ wl_subsurface_set_position( term->window->search_sub_surface, @@ -1229,6 +1232,7 @@ render_search_box(struct terminal *term) } wl_surface_commit(term->window->search_surface); + quirk_weston_subsurface_desync_off(term->window->search_sub_surface); } /* Move to terminal.c? */ diff --git a/search.c b/search.c index 1c5d8e71..f81ce6c6 100644 --- a/search.c +++ b/search.c @@ -79,12 +79,12 @@ search_begin(struct terminal *term) win->search_sub_surface = wl_subcompositor_get_subsurface( wayl->sub_compositor, win->search_surface, win->surface); - wl_subsurface_set_desync(win->search_sub_surface); struct wl_region *region = wl_compositor_create_region(term->wl->compositor); wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); wl_surface_set_opaque_region(win->search_surface, region); wl_region_destroy(region); + wl_subsurface_set_sync(win->search_sub_surface); term->search.original_view = term->grid->view; term->search.view_followed_offset = term->grid->view == term->grid->offset; From 7b3fffc6eceac1aeaa452e4c0a26c3c94ba7e8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 13:06:30 +0100 Subject: [PATCH 087/125] search: don't mark the entire sub-surface as opaque --- search.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/search.c b/search.c index f81ce6c6..3d5159ae 100644 --- a/search.c +++ b/search.c @@ -79,11 +79,6 @@ search_begin(struct terminal *term) win->search_sub_surface = wl_subcompositor_get_subsurface( wayl->sub_compositor, win->search_surface, win->surface); - - struct wl_region *region = wl_compositor_create_region(term->wl->compositor); - wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_set_opaque_region(win->search_surface, region); - wl_region_destroy(region); wl_subsurface_set_sync(win->search_sub_surface); term->search.original_view = term->grid->view; From 70cdb7af08fd4237f063ec241383b7c27e9685b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 13:09:25 +0100 Subject: [PATCH 088/125] term: visual_focus_{in,out}: use quirk_weston_subsurface_desync_{on,off} --- terminal.c | 57 +++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/terminal.c b/terminal.c index 1d17ba5d..b0f0679c 100644 --- a/terminal.c +++ b/terminal.c @@ -21,6 +21,7 @@ #include "async.h" #include "config.h" #include "grid.h" +#include "quirks.h" #include "render.h" #include "selection.h" #include "sixel.h" @@ -1625,6 +1626,26 @@ term_restore_cursor(struct terminal *term) term->cursor.lcf = term->saved_cursor.lcf; } +static void +quirk_weston_csd_on(struct terminal *term) +{ + if (term->window->use_csd != CSD_YES) + return; + + for (int i = 0; i < ALEN(term->window->csd.surface); i++) + quirk_weston_subsurface_desync_on(term->window->csd.sub_surface[i]); +} + +static void +quirk_weston_csd_off(struct terminal *term) +{ + if (term->window->use_csd != CSD_YES) + return; + + for (int i = 0; i < ALEN(term->window->csd.surface); i++) + quirk_weston_subsurface_desync_off(term->window->csd.sub_surface[i]); +} + void term_visual_focus_in(struct terminal *term) { @@ -1635,20 +1656,10 @@ term_visual_focus_in(struct terminal *term) if (term->cursor_blink.active) cursor_blink_start_timer(term); -#if 1 /* Weston seems to be buggy with synchronized CSDs */ - if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < ALEN(term->window->csd.surface); i++) - wl_subsurface_set_desync(term->window->csd.sub_surface[i]); - } -#endif - //render_csd(term); - render_csd_title(term); -#if 1 - if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < ALEN(term->window->csd.surface); i++) - wl_subsurface_set_sync(term->window->csd.sub_surface[i]); - } -#endif + quirk_weston_csd_on(term); + render_csd(term); + quirk_weston_csd_off(term); + cursor_refresh(term); } @@ -1662,20 +1673,10 @@ term_visual_focus_out(struct terminal *term) if (term->cursor_blink.active) cursor_blink_stop_timer(term); -#if 1 - if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < ALEN(term->window->csd.surface); i++) - wl_subsurface_set_desync(term->window->csd.sub_surface[i]); - } -#endif - //render_csd(term); - render_csd_title(term); -#if 1 - if (term->window->use_csd == CSD_YES) { - for (int i = 0; i < ALEN(term->window->csd.surface); i++) - wl_subsurface_set_sync(term->window->csd.sub_surface[i]); - } -#endif + quirk_weston_csd_on(term); + render_csd(term); + quirk_weston_csd_off(term); + cursor_refresh(term); } From 875b067f136f05c04db56be9d9de69b3391832c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 13:17:54 +0100 Subject: [PATCH 089/125] render: csd: don't try to render a zero-width/height border --- render.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/render.c b/render.c index df8a98de..740d3e55 100644 --- a/render.c +++ b/render.c @@ -743,6 +743,8 @@ render_csd_title(struct terminal *term) struct csd_data info = get_csd_data(term, CSD_SURF_TITLE); struct wl_surface *surf = term->window->csd.surface[CSD_SURF_TITLE]; + assert(info.width > 0 && info.height > 0); + unsigned long cookie = shm_cookie_csd(term, CSD_SURF_TITLE); struct buffer *buf = shm_get_buffer( term->wl->shm, info.width, info.height, cookie); @@ -773,6 +775,9 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx) struct csd_data info = get_csd_data(term, surf_idx); struct wl_surface *surf = term->window->csd.surface[surf_idx]; + if (info.width == 0 || info.height == 0) + return; + unsigned long cookie = shm_cookie_csd(term, surf_idx); struct buffer *buf = shm_get_buffer( term->wl->shm, info.width, info.height, cookie); From ae22366f3bc8e9a364c1a25436e182269e050032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Mar 2020 13:20:07 +0100 Subject: [PATCH 090/125] input: raise window 'move' timeout to 200ms 100ms was a bit low and prevented double-tap on touchpads from working. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index bf3a0a91..2fde9ca4 100644 --- a/input.c +++ b/input.c @@ -917,7 +917,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, else if (button == BTN_LEFT && win->csd.move_timeout_fd == -1) { const struct itimerspec timeout = { - .it_value = {.tv_nsec = 100000000}, + .it_value = {.tv_nsec = 200000000}, }; int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); From 22ce09eb44db887d32a5b3a88740939b7285c763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:42:49 +0100 Subject: [PATCH 091/125] config: make CSD user configurable The user can now configure the following: * Whether to prefer CSDs or SSDs. But note that this is only a hint to the compositor - it may deny our request. Furthermore, not all compositors implement the decoration manager protocol, meaning CSDs will be used regardless of the user configuration (GNOME/mutter being the most prominent one). * Title bar size and color, including transparency * Border size and color, including transparency Also drop support for rendering the CSDs inside the main surface. --- config.c | 90 +++++++++++++++++++++++++++++++++++++++++--- config.h | 14 +++++++ doc/foot.5.scd | 31 +++++++++++++++ footrc | 7 ++++ input.c | 31 ++++++++------- render.c | 100 +++++++++++++++++++------------------------------ wayland.c | 35 +++++++++-------- wayland.h | 9 ----- 8 files changed, 210 insertions(+), 107 deletions(-) diff --git a/config.c b/config.c index 37795bb9..246e0d18 100644 --- a/config.c +++ b/config.c @@ -133,7 +133,7 @@ str_to_double(const char *s, double *res) } static bool -str_to_color(const char *s, uint32_t *color, const char *path, int lineno) +str_to_color(const char *s, uint32_t *color, bool allow_alpha, const char *path, int lineno) { unsigned long value; if (!str_to_ulong(s, 16, &value)) { @@ -141,7 +141,12 @@ str_to_color(const char *s, uint32_t *color, const char *path, int lineno) return false; } - *color = value & 0xffffff; + if (!allow_alpha && (value & 0xff000000) != 0) { + LOG_ERR("%s:%d: color value must not have an alpha component", path, lineno); + return false; + } + + *color = value; return true; } @@ -273,7 +278,7 @@ parse_section_colors(const char *key, const char *value, struct config *conf, } uint32_t color_value; - if (!str_to_color(value, &color_value, path, lineno)) + if (!str_to_color(value, &color_value, false, path, lineno)) return false; *color = color_value; @@ -305,8 +310,8 @@ parse_section_cursor(const char *key, const char *value, struct config *conf, uint32_t text_color, cursor_color; if (text == NULL || cursor == NULL || - !str_to_color(text, &text_color, path, lineno) || - !str_to_color(cursor, &cursor_color, path, lineno)) + !str_to_color(text, &text_color, false, path, lineno) || + !str_to_color(cursor, &cursor_color, false, path, lineno)) { LOG_ERR("%s:%d: invalid cursor colors: %s", path, lineno, value); free(value_copy); @@ -326,6 +331,66 @@ parse_section_cursor(const char *key, const char *value, struct config *conf, return true; } +static bool +parse_section_csd(const char *key, const char *value, struct config *conf, + const char *path, unsigned lineno) +{ + if (strcmp(key, "preferred") == 0) { + if (strcmp(value, "server") == 0) + conf->csd.preferred = CONF_CSD_PREFER_SERVER; + else if (strcmp(value, "client") == 0) + conf->csd.preferred = CONF_CSD_PREFER_CLIENT; + else { + LOG_ERR("%s:%d: expected either 'server' or 'client'", path, lineno); + return false; + } + } + + else if (strcmp(key, "titlebar-color") == 0) { + uint32_t color; + if (!str_to_color(value, &color, true, path, lineno)) { + LOG_ERR("%s:%d: invalid titlebar-color: %s", path, lineno, value); + return false; + } + + conf->csd.color.title_set = true; + conf->csd.color.title = color; + } + + else if (strcmp(key, "border-color") == 0) { + uint32_t color; + if (!str_to_color(value, &color, true, path, lineno)) { + LOG_ERR("%s:%d: invalid border-color: %s", path, lineno, value); + return false; + } + + conf->csd.color.border_set = true; + conf->csd.color.border = color; + } + + else if (strcmp(key, "titlebar") == 0) { + unsigned long pixels; + if (!str_to_ulong(value, 10, &pixels)) { + LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); + return false; + } + + conf->csd.title_height = pixels; + } + + else if (strcmp(key, "border") == 0) { + unsigned long pixels; + if (!str_to_ulong(value, 10, &pixels)) { + LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); + return false; + } + + conf->csd.border_width = pixels; + } + + return true; +} + static bool parse_config_file(FILE *f, struct config *conf, const char *path) { @@ -333,6 +398,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path) SECTION_MAIN, SECTION_COLORS, SECTION_CURSOR, + SECTION_CSD, } section = SECTION_MAIN; /* Function pointer, called for each key/value line */ @@ -345,6 +411,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path) [SECTION_MAIN] = &parse_section_main, [SECTION_COLORS] = &parse_section_colors, [SECTION_CURSOR] = &parse_section_cursor, + [SECTION_CSD] = &parse_section_csd, }; #if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG @@ -352,6 +419,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path) [SECTION_MAIN] = "main", [SECTION_COLORS] = "colors", [SECTION_CURSOR] = "cursor", + [SECTION_CSD] = "csd", }; #endif @@ -408,6 +476,8 @@ parse_config_file(FILE *f, struct config *conf, const char *path) section = SECTION_COLORS; else if (strcmp(&line[1], "cursor") == 0) section = SECTION_CURSOR; + else if (strcmp(&line[1], "csd") == 0) + section = SECTION_CSD; else { LOG_ERR("%s:%d: invalid section name: %s", path, lineno, &line[1]); goto err; @@ -532,6 +602,16 @@ config_load(struct config *conf, const char *conf_path) }, }, + .csd = { + .preferred = CONF_CSD_PREFER_SERVER, + .title_height = 26, + .border_width = 5, + .color = { + .title_set = false, + .border_set = false, + }, + }, + .render_worker_count = sysconf(_SC_NPROCESSORS_ONLN), .server_socket_path = get_server_socket_path(), .presentation_timings = false, diff --git a/config.h b/config.h index 3677129f..fa765476 100644 --- a/config.h +++ b/config.h @@ -36,6 +36,20 @@ struct config { } color; } cursor; + struct { + enum { CONF_CSD_PREFER_SERVER, CONF_CSD_PREFER_CLIENT } preferred; + + int title_height; + int border_width; + + struct { + bool title_set; + bool border_set; + uint32_t title; + uint32_t border; + } color; + } csd; + size_t render_worker_count; char *server_socket_path; bool presentation_timings; diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 41785366..30fd735c 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -98,6 +98,37 @@ in this order: Background translucency. A value in the range 0.0-1.0, where 0.0 means completely transparent, and 1.0 is opaque. Default: _1.0_. +# SECTION: csd + +This section controls the look of the _CSDs_ (Client Side +Decorations). Note that the default is to *not* use CSDs, but instead +to use _SSDs_ (Server Side Decorations) when the compositor supports +it. + +*preferred* + Which type of window decorations to prefer: *client* (CSD) or + *server* (SSD). Note that this is only a hint to the + compositor. Depending on the compositor's configuration and + capabilities, it may not have any effect. Default: _server_. + +*titlebar* + Height, in pixels (but subject to output scaling), of the + titlebar, not including the window border. Default: _26_. + +*border* + Width, in pixels (but subject to output scaling), of the + borders. Default: _5_. + +*titlebar-color* + Titlebar AARRGGBB color. Note that unlike the other color values, + the *titlebar-color* value also has an _alpha_ component. Default: + use the default _foreground_ color. + +*border-color* + Border AARRGGBB color. Note that unlike the other color values, + the *border-color* value also has an _alpha_ component. Default: + _transparent_. + # FONT FORMAT The font is specified in FontConfig syntax. That is, a colon-separated diff --git a/footrc b/footrc index e6010eb2..7eea3ada 100644 --- a/footrc +++ b/footrc @@ -33,3 +33,10 @@ # bright6=b3ffff # bright7=ffffff # alpha=1.0 + +[csd] +# preferred=server +# titlebar=26 +# border=5 +# titlebar-color= +# border-color=00000000 diff --git a/input.c b/input.c index 2fde9ca4..dbb8ec33 100644 --- a/input.c +++ b/input.c @@ -21,6 +21,7 @@ #define LOG_MODULE "input" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "config.h" #include "commands.h" #include "keymap.h" #include "render.h" @@ -616,6 +617,7 @@ input_repeat(struct wayland *wayl, uint32_t key) static bool is_top_left(const struct terminal *term, int x, int y) { + int csd_border_size = term->conf->csd.border_width; return ( (term->active_surface == TERM_SURF_BORDER_LEFT && y < 10 * term->scale) || (term->active_surface == TERM_SURF_BORDER_TOP && x < (10 + csd_border_size) * term->scale)); @@ -624,6 +626,7 @@ is_top_left(const struct terminal *term, int x, int y) static bool is_top_right(const struct terminal *term, int x, int y) { + int csd_border_size = term->conf->csd.border_width; return ( (term->active_surface == TERM_SURF_BORDER_RIGHT && y < 10 * term->scale) || (term->active_surface == TERM_SURF_BORDER_TOP && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)); @@ -632,6 +635,8 @@ is_top_right(const struct terminal *term, int x, int y) static bool is_bottom_left(const struct terminal *term, int x, int y) { + int csd_title_size = term->conf->csd.title_height; + int csd_border_size = term->conf->csd.border_width; return ( (term->active_surface == TERM_SURF_BORDER_LEFT && y > csd_title_size * term->scale + term->height) || (term->active_surface == TERM_SURF_BORDER_BOTTOM && x < (10 + csd_border_size) * term->scale)); @@ -640,6 +645,8 @@ is_bottom_left(const struct terminal *term, int x, int y) static bool is_bottom_right(const struct terminal *term, int x, int y) { + int csd_title_size = term->conf->csd.title_height; + int csd_border_size = term->conf->csd.border_width; return ( (term->active_surface == TERM_SURF_BORDER_RIGHT && y > csd_title_size * term->scale + term->height) || (term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)); @@ -648,22 +655,14 @@ is_bottom_right(const struct terminal *term, int x, int y) static const char * xcursor_for_csd_border(struct terminal *term, int x, int y) { - if (is_top_left(term, x, y)) - return "top_left_corner"; - else if (is_top_right(term, x, y)) - return "top_right_corner"; - else if (is_bottom_left(term, x, y)) - return "bottom_left_corner"; - else if (is_bottom_right(term, x, y)) - return "bottom_right_corner"; - else if (term->active_surface == TERM_SURF_BORDER_LEFT) - return "left_side"; - else if (term->active_surface == TERM_SURF_BORDER_RIGHT) - return "right_side"; - else if (term->active_surface == TERM_SURF_BORDER_TOP) - return "top_side"; - else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) - return"bottom_side"; + if (is_top_left(term, x, y)) return "top_left_corner"; + else if (is_top_right(term, x, y)) return "top_right_corner"; + else if (is_bottom_left(term, x, y)) return "bottom_left_corner"; + else if (is_bottom_right(term, x, y)) return "bottom_right_corner"; + else if (term->active_surface == TERM_SURF_BORDER_LEFT) return "left_side"; + else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return "right_side"; + else if (term->active_surface == TERM_SURF_BORDER_TOP) return "top_side"; + else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return"bottom_side"; else { assert(false); return NULL; diff --git a/render.c b/render.c index 740d3e55..f08c4cf4 100644 --- a/render.c +++ b/render.c @@ -25,9 +25,6 @@ #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) -const int csd_border_size = 5; -const int csd_title_size = 26; - struct renderer { struct fdm *fdm; struct wayland *wayl; @@ -680,12 +677,11 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) /* Only title bar is rendered in maximized mode */ const int border_width = !term->window->is_maximized - ? csd_border_size * term->scale : 0; + ? term->conf->csd.border_width * term->scale : 0; const int title_height = !term->window->is_fullscreen - ? csd_title_size * term->scale : 0; + ? term->conf->csd.title_height * term->scale : 0; -#if FOOT_CSD_OUTSIDE switch (surf_idx) { case CSD_SURF_TITLE: return (struct csd_data){ 0, -title_height, term->width, title_height}; case CSD_SURF_LEFT: return (struct csd_data){-border_width, -title_height, border_width, title_height + term->height}; @@ -697,18 +693,6 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) assert(false); return (struct csd_data){0}; } -#else - switch (surf_idx) { - case CSD_SURF_TITLE: return (struct csd_data){ border_width, border_width, term->width - 2 * border_width, title_height}; - case CSD_SURF_LEFT: return (struct csd_data){ 0, border_width, border_width, term->height - 2 * border_width}; - case CSD_SURF_RIGHT: return (struct csd_data){term->width - border_width, border_width, border_width, term->height - 2 * border_width}; - case CSD_SURF_TOP: return (struct csd_data){ 0, 0, term->width, border_width}; - - case CSD_SURF_COUNT: - assert(false); - return (struct csd_data){0}; - } -#endif assert(false); return (struct csd_data){0}; @@ -749,18 +733,18 @@ render_csd_title(struct terminal *term) struct buffer *buf = shm_get_buffer( term->wl->shm, info.width, info.height, cookie); - pixman_color_t color = color_hex_to_pixman(term->colors.fg); + uint32_t _color = term->colors.fg; + uint16_t alpha = 0xffff; + if (term->conf->csd.color.title_set) { + _color = term->conf->csd.color.title; + alpha = _color >> 24 | (_color >> 24 << 8); + } + + pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha); if (!term->visual_focus) pixman_color_dim(&color); - struct wl_region *region = wl_compositor_create_region(term->wl->compositor); - if (region != NULL) { - wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_set_opaque_region(surf, region); - wl_region_destroy(region); - } - render_csd_part(term, surf, buf, info.width, info.height, &color); } @@ -782,8 +766,15 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx) struct buffer *buf = shm_get_buffer( term->wl->shm, info.width, info.height, cookie); - pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0); + uint32_t _color = 0; + uint16_t alpha = 0; + if (term->conf->csd.color.border_set) { + _color = term->conf->csd.color.border; + alpha = _color >> 24 | (_color >> 24 << 8); + } + + pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha); if (!term->visual_focus) pixman_color_dim(&color); render_csd_part(term, surf, buf, info.width, info.height, &color); @@ -1145,12 +1136,7 @@ render_search_box(struct terminal *term) assert(term->scale >= 1); const int scale = term->scale; -#if FOOT_CSD_OUTSIDE - const int csd = 0; -#else - const int csd = term->window->use_csd == CSD_YES ? csd_border_size * scale : 0; -#endif - const size_t margin = csd + 3 * scale; + const size_t margin = 3 * scale; const size_t width = term->width - 2 * margin; const size_t visible_width = min( @@ -1264,15 +1250,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force) width *= scale; height *= scale; - /* Scaled CSD border + title bar sizes */ -#if FOOT_CSD_OUTSIDE - const int csd_border = 0; - const int csd_title = 0; -#else - const int csd_border = term->window->use_csd == CSD_YES ? csd_border_size * scale : 0; - const int csd_title = term->window->use_csd == CSD_YES ? csd_title_size * scale : 0; -#endif - if (width == 0 && height == 0) { /* * The compositor is letting us choose the size @@ -1293,10 +1270,10 @@ maybe_resize(struct terminal *term, int width, int height, bool force) /* Account for CSDs, to make actual window size match * the configured size */ if (!term->window->is_maximized) { - width -= 2 * csd_border_size; - height -= 2 * csd_border_size + csd_title_size; + width -= 2 * term->conf->csd.border_width; + height -= 2 * term->conf->csd.border_width + term->conf->csd.title_height; } else { - height -= csd_title_size; + height -= term->conf->csd.title_height; } } @@ -1305,24 +1282,23 @@ maybe_resize(struct terminal *term, int width, int height, bool force) } } - const int csd_x = 2 * csd_border; - const int csd_y = 2 * csd_border + csd_title; - - /* Padding */ - const int pad_x = scale * term->conf->pad_x; - const int pad_y = scale * term->conf->pad_y; - /* Don't shrink grid too much */ const int min_cols = 20; const int min_rows = 4; /* Minimum window size */ - const int min_width = csd_x + 2 * pad_x + min_cols * term->cell_width; - const int min_height = csd_y + 2 * pad_y + min_rows * term->cell_height; + const int min_width = min_cols * term->cell_width; + const int min_height = min_rows * term->cell_height; width = max(width, min_width); height = max(height, min_height); + /* Padding */ + const int max_pad_x = (width - min_width) / 2; + const int max_pad_y = (height - min_height) / 2; + const int pad_x = min(max_pad_x, scale * term->conf->pad_x); + const int pad_y = min(max_pad_y, scale * term->conf->pad_y); + if (!force && width == term->width && height == term->height && scale == term->scale) return false; @@ -1342,8 +1318,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force) const int old_rows = term->rows; /* Screen rows/cols after resize */ - const int new_cols = (term->width - 2 * pad_x - csd_x) / term->cell_width; - const int new_rows = (term->height - 2 * pad_y - csd_y) / term->cell_height; + const int new_cols = (term->width - 2 * pad_x) / term->cell_width; + const int new_rows = (term->height - 2 * pad_y) / term->cell_height; /* Grid rows/cols after resize */ const int new_normal_grid_rows = 1 << (32 - __builtin_clz(new_rows + scrollback_lines - 1)); @@ -1353,15 +1329,15 @@ maybe_resize(struct terminal *term, int width, int height, bool force) assert(new_rows >= 1); /* Margins */ - term->margins.left = csd_border + pad_x; - term->margins.top = csd_border + csd_title + pad_y; + term->margins.left = pad_x; + term->margins.top = pad_y; term->margins.right = term->width - new_cols * term->cell_width - term->margins.left; term->margins.bottom = term->height - new_rows * term->cell_height - term->margins.top; - assert(term->margins.left >= csd_border + pad_x); - assert(term->margins.right >= csd_border + pad_x); - assert(term->margins.top >= csd_border + csd_title + pad_y); - assert(term->margins.bottom >= csd_border + pad_y); + assert(term->margins.left >= pad_x); + assert(term->margins.right >= pad_x); + assert(term->margins.top >= pad_y); + assert(term->margins.bottom >= pad_y); if (new_cols == old_cols && new_rows == old_rows) { LOG_DBG("grid layout unaffected; skipping reflow"); diff --git a/wayland.c b/wayland.c index 957f736e..c5272994 100644 --- a/wayland.c +++ b/wayland.c @@ -536,18 +536,14 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, * sub-surfaces. Thus, since our resize code assumes the size * to resize to is the main window only, adjust the size here, * to account for the CSDs. - * - * Of course this does *not* apply when we position the CSDs - * *inside* the main surface. */ -#if FOOT_CSD_OUTSIDE + const struct config *conf = win->term->conf; if (!is_maximized) { - width -= 2 * csd_border_size; - height -= 2 * csd_border_size + csd_title_size; + width -= 2 * conf->csd.border_width; + height -= 2 * conf->csd.border_width + conf->csd.title_height; } else { - height -= csd_title_size; + height -= conf->csd.title_height; } -#endif } win->configure.is_activated = is_activated; @@ -670,18 +666,18 @@ xdg_toplevel_decoration_configure(void *data, if (win->is_configured && win->use_csd == CSD_YES) { struct terminal *term = win->term; - int scale = term->scale; + const struct config *conf = term->conf; + int scale = term->scale; int width = term->width / scale; int height = term->height / scale; -#if FOOT_CSD_OUTSIDE if (!term->window->is_maximized) { - width -= 2 * csd_border_size; - height -= 2 * csd_border_size + csd_title_size; + width -= 2 * conf->csd.border_width; + height -= 2 * conf->csd.border_width + conf->csd.title_height; } else - height -= csd_title_size; -#endif + height -= conf->csd.title_height; + render_resize_force(term, width, height); } } @@ -1013,6 +1009,7 @@ struct wl_window * wayl_win_init(struct terminal *term) { struct wayland *wayl = term->wl; + const struct config *conf = wayl->conf; struct wl_window *win = calloc(1, sizeof(*win)); win->term = term; @@ -1050,8 +1047,16 @@ wayl_win_init(struct terminal *term) if (wayl->xdg_decoration_manager != NULL) { win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( wayl->xdg_decoration_manager, win->xdg_toplevel); + + LOG_INFO("preferring %s decorations", + conf->csd.preferred == CONF_CSD_PREFER_SERVER ? "SSD" : "CSD"); + zxdg_toplevel_decoration_v1_set_mode( - win->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + win->xdg_toplevel_decoration, + (conf->csd.preferred == CONF_CSD_PREFER_SERVER + ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE + : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE)); + zxdg_toplevel_decoration_v1_add_listener( win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win); } else { diff --git a/wayland.h b/wayland.h index b879c319..f94ad0e5 100644 --- a/wayland.h +++ b/wayland.h @@ -82,15 +82,6 @@ struct wl_primary { uint32_t serial; }; -/* I'd prefer to position the CSD sub-surfaces outside the main - * surface. Unfortunately, a lot of compositors doesn't handle this - * correctly. When this define is 0, we instead position the CSD - * sub-surfaces inside the main surface, and offset the grid content - * accordingly. */ -#define FOOT_CSD_OUTSIDE 1 -extern const int csd_border_size; -extern const int csd_title_size; - enum csd_surface { CSD_SURF_TITLE, CSD_SURF_LEFT, From def4395bd1ca2441b374ce53208cc5b2b000f015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:43:07 +0100 Subject: [PATCH 092/125] wayland: log whether CSDs or SSDs are used at info level, not debug --- wayland.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wayland.c b/wayland.c index c5272994..95d2880e 100644 --- a/wayland.c +++ b/wayland.c @@ -648,13 +648,13 @@ xdg_toplevel_decoration_configure(void *data, switch (mode) { case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: - LOG_DBG("using client-side decorations"); + LOG_INFO("using CSD decorations"); win->use_csd = CSD_YES; csd_instantiate(win); break; case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: - LOG_DBG("using server-side decorations"); + LOG_INFO("using SSD decorations"); win->use_csd = CSD_NO; csd_destroy(win); break; From f235bfdfdf2a33f9cf0a7074fb3c1722722ffb7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:43:23 +0100 Subject: [PATCH 093/125] terminal: workaround founds with negative line gaps Some fonts, even monospaced ones, have a negative line gap (line height < ascent + descent). Using the font's line height as cell height will result in some glyphs overflowing into the cell above or below. Workaround by using which ever value is the largest: the line height or ascent + descent. --- terminal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index b0f0679c..290a1425 100644 --- a/terminal.c +++ b/terminal.c @@ -521,7 +521,8 @@ term_set_fonts(struct terminal *term, struct font *fonts[static 4]) term->cell_width = term->fonts[0]->space_x_advance > 0 ? term->fonts[0]->space_x_advance : term->fonts[0]->max_x_advance; - term->cell_height = term->fonts[0]->height; + term->cell_height = max(term->fonts[0]->height, + term->fonts[0]->ascent + term->fonts[0]->descent); LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); render_resize_force(term, term->width, term->height); From 74175b5bd12d5d28bcb298f3dfdad63b7e5bba56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:45:38 +0100 Subject: [PATCH 094/125] config: prefer $SHELL over /etc/passwd If the user hasn't configured a shell in footrc, use $SHELL. Only if that variable isn't set do we use the shell from /etc/passwd. --- config.c | 16 ++++++++++------ doc/foot.5.scd | 7 ++++--- footrc | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/config.c b/config.c index 246e0d18..4303c4ab 100644 --- a/config.c +++ b/config.c @@ -44,15 +44,19 @@ static const uint32_t default_bright[] = { static char * get_shell(void) { - struct passwd *passwd = getpwuid(getuid()); - if (passwd == NULL) { - LOG_ERRNO("failed to lookup user"); - return NULL; + const char *shell = getenv("SHELL"); + + if (shell == NULL) { + struct passwd *passwd = getpwuid(getuid()); + if (passwd == NULL) { + LOG_ERRNO("failed to lookup user"); + return NULL; + } + + shell = passwd->pw_shell; } - const char *shell = passwd->pw_shell; LOG_DBG("user's shell: %s", shell); - return strdup(shell); } diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 30fd735c..2eebcf11 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -41,9 +41,10 @@ in this order: _XxY_ (-padding). *shell* - Executable to launch. Typically a shell. Default: the user's - default shell (as specified in _/etc/passwd_). You can also pass - arguments. For example "/bin/bash --norc". + Executable to launch. Typically a shell. Default: _$SHELL_ if set, + otherwise the user's default shell (as specified in + _/etc/passwd_). You can also pass arguments. For example + "/bin/bash --norc". *login-shell* Start a login shell, by prepending a '-' to argv[0]. Default: _no_. diff --git a/footrc b/footrc index 7eea3ada..e63403f8 100644 --- a/footrc +++ b/footrc @@ -4,7 +4,7 @@ # scrollback=1000 # geometry=800x600 # pad=2x2 -# shell= (you may need to override if you need a login shell) +# shell=$SHELL (if set, otherwise user's default shell from /etc/passwd) # term=foot # login-shell=no # workers= From 6912bbd310d1c8cdc4f019dd5887cc9fdf679806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:46:35 +0100 Subject: [PATCH 095/125] slave: set $SHELL when command line is a shell listed in /etc/shells --- slave.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/slave.c b/slave.c index 2703c784..edd8e2cb 100644 --- a/slave.c +++ b/slave.c @@ -1,7 +1,9 @@ #include "slave.h" #include +#include #include +#include #include #include #include @@ -16,6 +18,52 @@ #include "tokenize.h" +static bool +is_valid_shell(const char *shell) +{ + FILE *f = fopen("/etc/shells", "r"); + if (f == NULL) + goto err; + + char *_line = NULL; + size_t count = 0; + + while (true) { + errno = 0; + ssize_t ret = getline(&_line, &count, f); + + if (ret < 0) { + free(_line); + break; + } + + char *line = _line; + { + while (isspace(*line)) + line++; + if (line[0] != '\0') { + char *end = line + strlen(line) - 1; + while (isspace(*end)) + end--; + *(end + 1) = '\0'; + } + } + + if (line[0] == '#') + continue; + + if (strcmp(line, shell) == 0) { + fclose(f); + return true; + } + } + +err: + if (f != NULL) + fclose(f); + return false; +} + static void slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell) { @@ -145,6 +193,9 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv, shell_argv[count] = NULL; } + if (is_valid_shell(shell_argv[0])) + setenv("SHELL", shell_argv[0], 1); + slave_exec(ptmx, shell_argv, fork_pipe[1], login_shell); assert(false); break; From b268b69a7b5633af58107d625b51b61023f0f824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:47:04 +0100 Subject: [PATCH 096/125] quirks: log when applying weston sub-surface desync quirk --- quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/quirks.c b/quirks.c index e96ad98e..0db29a5e 100644 --- a/quirks.c +++ b/quirks.c @@ -3,6 +3,10 @@ #include #include +#define LOG_MODULE "quirks" +#define LOG_ENABLE_DBG 0 +#include "log.h" + static bool is_weston(void) { @@ -25,6 +29,8 @@ is_weston(void) if (!initialized) { initialized = true; is_weston = getenv("WESTON_CONFIG_FILE") != NULL; + if (is_weston) + LOG_WARN("applying wl_subsurface_set_desync() workaround for weston"); } return is_weston; From 6eae5ebd94d2fbc8b0fec3275b8fd3a094a40ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:47:17 +0100 Subject: [PATCH 097/125] input: don't change xcursor if active surface isn't the main grid --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index dbb8ec33..2526807a 100644 --- a/input.c +++ b/input.c @@ -585,7 +585,7 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, wayl->kbd.meta = xkb_state_mod_index_is_active( wayl->kbd.xkb_state, wayl->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); - if (wayl->kbd_focus) + if (wayl->kbd_focus && wayl->kbd_focus->active_surface == TERM_SURF_GRID) term_xcursor_update(wayl->kbd_focus); } From 6c317396e5fee0d55e330795e08068d8bcb52457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:47:35 +0100 Subject: [PATCH 098/125] footrc: default geometry has changed to 700x500 --- footrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/footrc b/footrc index e63403f8..7078ff43 100644 --- a/footrc +++ b/footrc @@ -2,7 +2,7 @@ # font=monospace # scrollback=1000 -# geometry=800x600 +# geometry=700x500 # pad=2x2 # shell=$SHELL (if set, otherwise user's default shell from /etc/passwd) # term=foot @@ -14,6 +14,7 @@ # color=111111 dcdccc [colors] +# alpha=1.0 # foreground=dcdccc # background=111111 # regular0=222222 @@ -32,7 +33,6 @@ # bright5=fcace3 # bright6=b3ffff # bright7=ffffff -# alpha=1.0 [csd] # preferred=server From 1f33b4a292fbfa097dd591692cb8c1ba7a5f7de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:47:48 +0100 Subject: [PATCH 099/125] doc: foot.5: describe what each section in the configuration file does --- doc/foot.5.scd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 2eebcf11..99240b21 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -64,6 +64,9 @@ in this order: # SECTION: cursor +This section controls the cursor style and color. Note that +applications can change these runtime. + *style* Configures the default cursor style, and is one of: _block_, _bar_ or _underline_. Default: _block_. @@ -77,6 +80,9 @@ in this order: # SECTION: colors +This section controls the 16 ANSI colors and the default foreground +and background colors. Note that applications can change these runtime. + *foreground* Default RRGGBB foreground color. This is the color used when no ANSI color is being used. Default: _dcdccc_. From 1b2050de7b533f0907b84bd540e04022ce5b8761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:48:29 +0100 Subject: [PATCH 100/125] config: no need to free 'line' between each call to getline() getline() will re-use the allocated line if it large enough, or resize it otherwise. Thus there's no need to free it and set it to NULL between each call. --- config.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/config.c b/config.c index 4303c4ab..983cda99 100644 --- a/config.c +++ b/config.c @@ -428,21 +428,20 @@ parse_config_file(FILE *f, struct config *conf, const char *path) #endif unsigned lineno = 0; + char *_line = NULL; + size_t count = 0; while (true) { errno = 0; lineno++; - _line = NULL; - size_t count = 0; ssize_t ret = getline(&_line, &count, f); if (ret < 0) { - free(_line); if (errno != 0) { LOG_ERRNO("failed to read from configuration"); - return false; + goto err; } break; } @@ -461,10 +460,8 @@ parse_config_file(FILE *f, struct config *conf, const char *path) } /* Empty line, or comment */ - if (line[0] == '\0' || line[0] == '#') { - free(_line); + if (line[0] == '\0' || line[0] == '#') continue; - } /* Check for new section */ if (line[0] == '[') { @@ -487,7 +484,6 @@ parse_config_file(FILE *f, struct config *conf, const char *path) goto err; } - free(_line); continue; } @@ -510,7 +506,6 @@ parse_config_file(FILE *f, struct config *conf, const char *path) goto err; } - free(_line); continue; } @@ -521,10 +516,8 @@ parse_config_file(FILE *f, struct config *conf, const char *path) assert(!isspace(*(value + strlen(value) - 1))); } - if (key[0] == '#') { - free(_line); + if (key[0] == '#') continue; - } LOG_DBG("section=%s, key='%s', value='%s'", section_names[section], key, value); @@ -534,10 +527,9 @@ parse_config_file(FILE *f, struct config *conf, const char *path) if (!section_parser(key, value, conf, path, lineno)) goto err; - - free(_line); } + free(_line); return true; err: From c845c90835774535d7b554950ea67468a71c1310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 18:50:21 +0100 Subject: [PATCH 101/125] README: CSDs have been implemented --- README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/README.md b/README.md index 46cd07cc..184854de 100644 --- a/README.md +++ b/README.md @@ -61,18 +61,6 @@ This is a list of known, but probably not all, issues: Examples: á (`LATIN SMALL LETTER A` + `COMBINING ACUTE ACCENT`) -* GNOME; might work, but without window decorations. - - Strictly speaking, foot is at fault here; all Wayland applications - _must_ be able to draw their own window decorations (but foot is - not). - - However, most people want a uniform look and feel on their - desktop, including the window decorations. For this reason, a - Wayland application can request _Server Side Decorations_ - (SSD). GNOME will reply with a "_I hear you, but sorry, I wont do - that_". - ## Fonts From 9699c9b8bfcbd7f9ebef5ee4ffe370cb03b1c8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 20:29:28 +0100 Subject: [PATCH 102/125] csd: initial implementation of minimize/maximize/close buttons --- config.c | 47 +++++++++++++++++++++++++++-- config.h | 7 +++++ doc/foot.5.scd | 34 ++++++++++++++++----- footrc | 8 +++-- input.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ render.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++- render.h | 1 + terminal.c | 16 ++++++---- terminal.h | 3 ++ wayland.c | 6 +++- wayland.h | 7 +++-- 11 files changed, 268 insertions(+), 21 deletions(-) diff --git a/config.c b/config.c index 983cda99..ef7f9a25 100644 --- a/config.c +++ b/config.c @@ -372,7 +372,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, conf->csd.color.border = color; } - else if (strcmp(key, "titlebar") == 0) { + else if (strcmp(key, "titlebar-size") == 0) { unsigned long pixels; if (!str_to_ulong(value, 10, &pixels)) { LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); @@ -382,7 +382,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, conf->csd.title_height = pixels; } - else if (strcmp(key, "border") == 0) { + else if (strcmp(key, "border-width") == 0) { unsigned long pixels; if (!str_to_ulong(value, 10, &pixels)) { LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); @@ -392,6 +392,48 @@ parse_section_csd(const char *key, const char *value, struct config *conf, conf->csd.border_width = pixels; } + else if (strcmp(key, "button-width") == 0) { + unsigned long pixels; + if (!str_to_ulong(value, 10, &pixels)) { + LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); + return false; + } + + conf->csd.button_width = pixels; + } + + else if (strcmp(key, "button-minimize-color") == 0) { + uint32_t color; + if (!str_to_color(value, &color, true, path, lineno)) { + LOG_ERR("%s:%d: invalid button-minimize-color: %s", path, lineno, value); + return false; + } + + conf->csd.color.minimize_set = true; + conf->csd.color.minimize = color; + } + + else if (strcmp(key, "button-maximize-color") == 0) { + uint32_t color; + if (!str_to_color(value, &color, true, path, lineno)) { + LOG_ERR("%s:%d: invalid button-maximize-color: %s", path, lineno, value); + return false; + } + + conf->csd.color.maximize_set = true; + conf->csd.color.maximize = color; + } + + else if (strcmp(key, "button-close-color") == 0) { + uint32_t color; + if (!str_to_color(value, &color, true, path, lineno)) { + LOG_ERR("%s:%d: invalid button-close-color: %s", path, lineno, value); + return false; + } + + conf->csd.color.close_set = true; + conf->csd.color.close = color; + } return true; } @@ -602,6 +644,7 @@ config_load(struct config *conf, const char *conf_path) .preferred = CONF_CSD_PREFER_SERVER, .title_height = 26, .border_width = 5, + .button_width = 22, .color = { .title_set = false, .border_set = false, diff --git a/config.h b/config.h index fa765476..dd34c589 100644 --- a/config.h +++ b/config.h @@ -41,12 +41,19 @@ struct config { int title_height; int border_width; + int button_width; struct { bool title_set; bool border_set; + bool minimize_set; + bool maximize_set; + bool close_set; uint32_t title; uint32_t border; + uint32_t minimize; + uint32_t maximize; + uint32_t close; } color; } csd; diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 99240b21..778809a4 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -83,6 +83,10 @@ applications can change these runtime. This section controls the 16 ANSI colors and the default foreground and background colors. Note that applications can change these runtime. +The colors are in RRGGBB format. That is, they do *not* have an alpha +component. You can configure the background transparency with the +_alpha_ option. + *foreground* Default RRGGBB foreground color. This is the color used when no ANSI color is being used. Default: _dcdccc_. @@ -112,29 +116,43 @@ Decorations). Note that the default is to *not* use CSDs, but instead to use _SSDs_ (Server Side Decorations) when the compositor supports it. +Note that unlike the colors defined in the _colors_ section, the color +values here are in AARRGGBB format. I.e. they contain an alpha +component. + *preferred* Which type of window decorations to prefer: *client* (CSD) or *server* (SSD). Note that this is only a hint to the compositor. Depending on the compositor's configuration and capabilities, it may not have any effect. Default: _server_. -*titlebar* +*titlebar-size* Height, in pixels (but subject to output scaling), of the titlebar, not including the window border. Default: _26_. -*border* +*border-width* Width, in pixels (but subject to output scaling), of the borders. Default: _5_. *titlebar-color* - Titlebar AARRGGBB color. Note that unlike the other color values, - the *titlebar-color* value also has an _alpha_ component. Default: - use the default _foreground_ color. + Titlebar AARRGGBB color. Default: use the default _foreground_ + color. *border-color* - Border AARRGGBB color. Note that unlike the other color values, - the *border-color* value also has an _alpha_ component. Default: - _transparent_. + Border AARRGGBB color. Default: _transparent_. + +*button-width* + Width, in pixels (but subject to output scaling), of the + minimize/maximize/close buttons. Default: _22_. + +*button-minimize-color* + Minimize button's AARRGGBB color. Default: _ff0000ff_. + +*button-maximize-color* + Maximize button's AARRGGBB color. Default: _ff00ff00_. + +*button-close-color* + Close button's AARRGGBB color. Default: _ffff0000_. # FONT FORMAT diff --git a/footrc b/footrc index 7078ff43..a9bce529 100644 --- a/footrc +++ b/footrc @@ -36,7 +36,11 @@ [csd] # preferred=server -# titlebar=26 -# border=5 +# titlebar-size=26 +# border-width=5 # titlebar-color= # border-color=00000000 +# button-width=22 +# button-minimize-color=ff0000ff +# button-maximize-color=ff00ff00 +# button-close-color=ffff0000 diff --git a/input.c b/input.c index 2526807a..5ca13785 100644 --- a/input.c +++ b/input.c @@ -24,6 +24,7 @@ #include "config.h" #include "commands.h" #include "keymap.h" +#include "quirks.h" #include "render.h" #include "search.h" #include "selection.h" @@ -708,6 +709,28 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, render_xcursor_set(term); break; + case TERM_SURF_BUTTON_MINIMIZE: + case TERM_SURF_BUTTON_MAXIMIZE: + case TERM_SURF_BUTTON_CLOSE: { + enum csd_surface idx = + term->active_surface == TERM_SURF_BUTTON_MINIMIZE ? CSD_SURF_MINIMIZE : + term->active_surface == TERM_SURF_BUTTON_MAXIMIZE ? CSD_SURF_MAXIMIZE : + CSD_SURF_CLOSE; + + quirk_weston_subsurface_desync_on(term->window->csd.sub_surface[CSD_SURF_TITLE]); + quirk_weston_subsurface_desync_on(term->window->csd.sub_surface[idx]); + + render_csd_button(term, idx); + + quirk_weston_subsurface_desync_off(term->window->csd.sub_surface[idx]); + quirk_weston_subsurface_desync_off(term->window->csd.sub_surface[CSD_SURF_TITLE]); + wl_surface_commit(win->surface); + + term->xcursor = "left_ptr"; + render_xcursor_set(term); + break; + } + case TERM_SURF_NONE: assert(false); break; @@ -746,8 +769,43 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, = wl_surface_get_user_data(surface); assert(old_moused == win->term); } + + enum term_surface active_surface = old_moused->active_surface; + old_moused->active_surface = TERM_SURF_NONE; term_xcursor_update(old_moused); + + switch (active_surface) { + case TERM_SURF_BUTTON_MINIMIZE: + case TERM_SURF_BUTTON_MAXIMIZE: + case TERM_SURF_BUTTON_CLOSE: { + enum csd_surface idx = + active_surface == TERM_SURF_BUTTON_MINIMIZE ? CSD_SURF_MINIMIZE : + active_surface == TERM_SURF_BUTTON_MAXIMIZE ? CSD_SURF_MAXIMIZE : + CSD_SURF_CLOSE; + + quirk_weston_subsurface_desync_on(old_moused->window->csd.sub_surface[CSD_SURF_TITLE]); + quirk_weston_subsurface_desync_on(old_moused->window->csd.sub_surface[idx]); + + render_csd_button(old_moused, idx); + + quirk_weston_subsurface_desync_off(old_moused->window->csd.sub_surface[idx]); + quirk_weston_subsurface_desync_off(old_moused->window->csd.sub_surface[CSD_SURF_TITLE]); + wl_surface_commit(old_moused->window->surface); + break; + } + + case TERM_SURF_NONE: + case TERM_SURF_GRID: + case TERM_SURF_SEARCH: + case TERM_SURF_TITLE: + case TERM_SURF_BORDER_LEFT: + case TERM_SURF_BORDER_RIGHT: + case TERM_SURF_BORDER_TOP: + case TERM_SURF_BORDER_BOTTOM: + break; + } + } } @@ -787,6 +845,9 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, switch (term->active_surface) { case TERM_SURF_NONE: case TERM_SURF_SEARCH: + case TERM_SURF_BUTTON_MINIMIZE: + case TERM_SURF_BUTTON_MAXIMIZE: + case TERM_SURF_BUTTON_CLOSE: break; case TERM_SURF_TITLE: @@ -976,6 +1037,26 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, return; } + case TERM_SURF_BUTTON_MINIMIZE: + if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) + xdg_toplevel_set_minimized(term->window->xdg_toplevel); + break; + + case TERM_SURF_BUTTON_MAXIMIZE: + if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (term->window->is_maximized) + xdg_toplevel_unset_maximized(term->window->xdg_toplevel); + else + xdg_toplevel_set_maximized(term->window->xdg_toplevel); + } + break; + + case TERM_SURF_BUTTON_CLOSE: + if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) + term_shutdown(term); + //LOG_ERR("unimplemented: terminate"); + break; + case TERM_SURF_SEARCH: break; diff --git a/render.c b/render.c index f08c4cf4..c24c41cc 100644 --- a/render.c +++ b/render.c @@ -682,6 +682,9 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) const int title_height = !term->window->is_fullscreen ? term->conf->csd.title_height * term->scale : 0; + const int button_width = !term->window->is_fullscreen + ? term->conf->csd.button_width * term->scale : 0; + switch (surf_idx) { case CSD_SURF_TITLE: return (struct csd_data){ 0, -title_height, term->width, title_height}; case CSD_SURF_LEFT: return (struct csd_data){-border_width, -title_height, border_width, title_height + term->height}; @@ -689,6 +692,11 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) case CSD_SURF_TOP: return (struct csd_data){-border_width, -title_height - border_width, term->width + 2 * border_width, border_width}; case CSD_SURF_BOTTOM: return (struct csd_data){-border_width, term->height, term->width + 2 * border_width, border_width}; + /* Positioned relative to CSD_SURF_TITLE */ + case CSD_SURF_MINIMIZE: return (struct csd_data){term->width - 3 * button_width, 0, button_width, title_height}; + case CSD_SURF_MAXIMIZE: return (struct csd_data){term->width - 2 * button_width, 0, button_width, title_height}; + case CSD_SURF_CLOSE: return (struct csd_data){term->width - 1 * button_width, 0, button_width, title_height}; + case CSD_SURF_COUNT: assert(false); return (struct csd_data){0}; @@ -780,6 +788,73 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx) render_csd_part(term, surf, buf, info.width, info.height, &color); } +void +render_csd_button(struct terminal *term, enum csd_surface surf_idx) +{ + assert(surf_idx >= CSD_SURF_MINIMIZE); + + if (term->window->use_csd != CSD_YES) + return; + + struct csd_data info = get_csd_data(term, surf_idx); + struct wl_surface *surf = term->window->csd.surface[surf_idx]; + + if (info.width == 0 || info.height == 0) + return; + + unsigned long cookie = shm_cookie_csd(term, surf_idx); + struct buffer *buf = shm_get_buffer( + term->wl->shm, info.width, info.height, cookie); + + uint32_t _color; + uint16_t alpha = 0xffff; + bool is_active = false; + const bool *is_set = NULL; + const uint32_t *conf_color = NULL; + + switch (surf_idx) { + case CSD_SURF_MINIMIZE: + _color = 0xff0000ff; + is_set = &term->conf->csd.color.minimize_set; + conf_color = &term->conf->csd.color.minimize; + is_active = term->active_surface == TERM_SURF_BUTTON_MINIMIZE; + break; + + case CSD_SURF_MAXIMIZE: + _color = 0xff00ff00; + is_set = &term->conf->csd.color.maximize_set; + conf_color = &term->conf->csd.color.maximize; + is_active = term->active_surface == TERM_SURF_BUTTON_MAXIMIZE; + break; + + case CSD_SURF_CLOSE: + _color = 0xffff0000; + is_set = &term->conf->csd.color.close_set; + conf_color = &term->conf->csd.color.close; + is_active = term->active_surface == TERM_SURF_BUTTON_CLOSE; + break; + + default: + assert(false); + break; + } + + if (is_active) { + if (*is_set) { + _color = *conf_color; + alpha = _color >> 24 | (_color >> 24 << 8); + } + } else { + _color = 0; + alpha = 0; + } + + pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha); + if (!term->visual_focus) + pixman_color_dim(&color); + render_csd_part(term, surf, buf, info.width, info.height, &color); +} + void render_csd(struct terminal *term) { @@ -798,7 +873,7 @@ render_csd(struct terminal *term) if (width == 0 || height == 0) { /* CSD borders aren't rendered in maximized mode */ - assert(term->window->is_maximized); + assert(term->window->is_maximized || term->window->is_fullscreen); wl_subsurface_set_position(sub, 0, 0); wl_surface_attach(surf, NULL, 0, 0); wl_surface_commit(surf); @@ -811,6 +886,8 @@ render_csd(struct terminal *term) render_csd_title(term); for (size_t i = CSD_SURF_LEFT; i <= CSD_SURF_BOTTOM; i++) render_csd_border(term, i); + for (size_t i = CSD_SURF_MINIMIZE; i < CSD_SURF_COUNT; i++) + render_csd_button(term, i); } static void frame_callback( diff --git a/render.h b/render.h index 2177e8bd..cccf2001 100644 --- a/render.h +++ b/render.h @@ -19,6 +19,7 @@ bool render_xcursor_set(struct terminal *term); void render_search_box(struct terminal *term); void render_csd(struct terminal *term); void render_csd_title(struct terminal *term); +void render_csd_button(struct terminal *term, enum csd_surface surf_idx); struct render_worker_context { int my_id; diff --git a/terminal.c b/terminal.c index 290a1425..c57b3c1c 100644 --- a/terminal.c +++ b/terminal.c @@ -2149,16 +2149,22 @@ term_surface_kind(const struct terminal *term, const struct wl_surface *surface) return TERM_SURF_GRID; else if (surface == term->window->search_surface) return TERM_SURF_SEARCH; - else if (surface == term->window->csd.surface[0]) + else if (surface == term->window->csd.surface[CSD_SURF_TITLE]) return TERM_SURF_TITLE; - else if (surface == term->window->csd.surface[1]) + else if (surface == term->window->csd.surface[CSD_SURF_LEFT]) return TERM_SURF_BORDER_LEFT; - else if (surface == term->window->csd.surface[2]) + else if (surface == term->window->csd.surface[CSD_SURF_RIGHT]) return TERM_SURF_BORDER_RIGHT; - else if (surface == term->window->csd.surface[3]) + else if (surface == term->window->csd.surface[CSD_SURF_TOP]) return TERM_SURF_BORDER_TOP; - else if (surface == term->window->csd.surface[4]) + else if (surface == term->window->csd.surface[CSD_SURF_BOTTOM]) return TERM_SURF_BORDER_BOTTOM; + else if (surface == term->window->csd.surface[CSD_SURF_MINIMIZE]) + return TERM_SURF_BUTTON_MINIMIZE; + else if (surface == term->window->csd.surface[CSD_SURF_MAXIMIZE]) + return TERM_SURF_BUTTON_MAXIMIZE; + else if (surface == term->window->csd.surface[CSD_SURF_CLOSE]) + return TERM_SURF_BUTTON_CLOSE; else return TERM_SURF_NONE; } diff --git a/terminal.h b/terminal.h index df3fb06a..3731ee0d 100644 --- a/terminal.h +++ b/terminal.h @@ -194,6 +194,9 @@ enum term_surface { TERM_SURF_BORDER_RIGHT, TERM_SURF_BORDER_TOP, TERM_SURF_BORDER_BOTTOM, + TERM_SURF_BUTTON_MINIMIZE, + TERM_SURF_BUTTON_MAXIMIZE, + TERM_SURF_BUTTON_CLOSE, }; struct terminal { diff --git a/wayland.c b/wayland.c index 95d2880e..26982815 100644 --- a/wayland.c +++ b/wayland.c @@ -47,8 +47,12 @@ csd_instantiate(struct wl_window *win) assert(win->csd.sub_surface[i] == NULL); win->csd.surface[i] = wl_compositor_create_surface(wayl->compositor); + + struct wl_surface *parent = i < CSD_SURF_MINIMIZE + ? win->surface : win->csd.surface[CSD_SURF_TITLE]; + win->csd.sub_surface[i] = wl_subcompositor_get_subsurface( - wayl->sub_compositor, win->csd.surface[i], win->surface); + wayl->sub_compositor, win->csd.surface[i], parent); wl_subsurface_set_sync(win->csd.sub_surface[i]); wl_surface_set_user_data(win->csd.surface[i], win); diff --git a/wayland.h b/wayland.h index f94ad0e5..ed53591d 100644 --- a/wayland.h +++ b/wayland.h @@ -88,6 +88,9 @@ enum csd_surface { CSD_SURF_RIGHT, CSD_SURF_TOP, CSD_SURF_BOTTOM, + CSD_SURF_MINIMIZE, + CSD_SURF_MAXIMIZE, + CSD_SURF_CLOSE, CSD_SURF_COUNT, }; @@ -103,8 +106,8 @@ struct wl_window { enum {CSD_UNKNOWN, CSD_NO, CSD_YES } use_csd; struct { - struct wl_surface *surface[5]; - struct wl_subsurface *sub_surface[5]; + struct wl_surface *surface[CSD_SURF_COUNT]; + struct wl_subsurface *sub_surface[CSD_SURF_COUNT]; int move_timeout_fd; uint32_t serial; } csd; From 20eaa152719156d06cf141db710d54283034cc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 21:06:15 +0100 Subject: [PATCH 103/125] render: initial minimize/maximize/close glyphs These are really ugly, but is meant to get something up there, that can be polished afterwards. --- render.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/render.c b/render.c index c24c41cc..c9aa8f78 100644 --- a/render.c +++ b/render.c @@ -706,6 +706,15 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) return (struct csd_data){0}; } +static void +csd_commit(struct terminal *term, struct wl_surface *surf, struct buffer *buf) +{ + wl_surface_attach(surf, buf->wl_buf, 0, 0); + wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); + wl_surface_set_buffer_scale(surf, term->scale); + wl_surface_commit(surf); +} + static void render_csd_part(struct terminal *term, struct wl_surface *surf, struct buffer *buf, @@ -719,11 +728,6 @@ render_csd_part(struct terminal *term, PIXMAN_OP_SRC, buf->pix, color, 1, &(pixman_rectangle16_t){0, 0, buf->width, buf->height}); pixman_image_unref(src); - - wl_surface_attach(surf, buf->wl_buf, 0, 0); - wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); - wl_surface_set_buffer_scale(surf, term->scale); - wl_surface_commit(surf); } void @@ -754,6 +758,7 @@ render_csd_title(struct terminal *term) pixman_color_dim(&color); render_csd_part(term, surf, buf, info.width, info.height, &color); + csd_commit(term, surf, buf); } static void @@ -786,6 +791,58 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx) if (!term->visual_focus) pixman_color_dim(&color); render_csd_part(term, surf, buf, info.width, info.height, &color); + csd_commit(term, surf, buf); +} + +static void +render_csd_button_minimize(struct terminal *term, struct buffer *buf) +{ + pixman_color_t color = color_hex_to_pixman(0); + pixman_image_t *src = pixman_image_create_solid_fill(&color); + + int x_margin = (term->conf->csd.button_width * 1 / 4) * term->scale; + int y_margin = (term->conf->csd.title_height * 4 / 6) * term->scale; + + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &color, 1, + &(pixman_rectangle16_t){x_margin, y_margin, buf->width - 2 * x_margin, 1 * term->scale}); + + pixman_image_unref(src); +} + +static void +render_csd_button_maximize(struct terminal *term, struct buffer *buf) +{ + pixman_color_t color = color_hex_to_pixman(0); + pixman_image_t *src = pixman_image_create_solid_fill(&color); + + int x_margin = (term->conf->csd.button_width * 1 / 4) * term->scale; + int y_margin = (term->conf->csd.title_height * 2 / 6) * term->scale; + + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &color, 1, + &(pixman_rectangle16_t){x_margin, y_margin, buf->width - 2 * x_margin, 2 * term->scale}); + + pixman_image_unref(src); +} + +static void +render_csd_button_close(struct terminal *term, struct buffer *buf) +{ + pixman_color_t color = color_hex_to_pixman(0); + pixman_image_t *src = pixman_image_create_solid_fill(&color); + + int min_length = min(buf->width, buf->height); + int length = min_length / 2; + + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &color, 2, + (pixman_rectangle16_t[]){ + {(buf->width - length) / 2, buf->height / 2, length, 1 * term->scale}, + {buf->width / 2, (buf->height - length) / 2, 1 * term->scale, length}, + }); + + pixman_image_unref(src); } void @@ -853,6 +910,19 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx) if (!term->visual_focus) pixman_color_dim(&color); render_csd_part(term, surf, buf, info.width, info.height, &color); + + switch (surf_idx) { + case CSD_SURF_MINIMIZE: render_csd_button_minimize(term, buf); break; + case CSD_SURF_MAXIMIZE: render_csd_button_maximize(term, buf); break; + case CSD_SURF_CLOSE: render_csd_button_close(term, buf); break; + break; + + default: + assert(false); + break; + } + + csd_commit(term, surf, buf); } void From b14c217fb62bbdf4350c2877b889f42fe2ba69b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 21:09:21 +0100 Subject: [PATCH 104/125] render: csd: minimize: change default color to a lighter blue --- doc/foot.5.scd | 2 +- render.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 778809a4..f4a263d8 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -146,7 +146,7 @@ component. minimize/maximize/close buttons. Default: _22_. *button-minimize-color* - Minimize button's AARRGGBB color. Default: _ff0000ff_. + Minimize button's AARRGGBB color. Default: _ff1e90ff_. *button-maximize-color* Maximize button's AARRGGBB color. Default: _ff00ff00_. diff --git a/render.c b/render.c index c9aa8f78..c3ea0744 100644 --- a/render.c +++ b/render.c @@ -871,7 +871,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx) switch (surf_idx) { case CSD_SURF_MINIMIZE: - _color = 0xff0000ff; + _color = 0xff1e90ff; is_set = &term->conf->csd.color.minimize_set; conf_color = &term->conf->csd.color.minimize; is_active = term->active_surface == TERM_SURF_BUTTON_MINIMIZE; From 63a3d6ce03f0de8bf5f1d7d8ccc789970c8c11c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 21:10:25 +0100 Subject: [PATCH 105/125] render: csd: close: change default color to a lighter blue --- doc/foot.5.scd | 2 +- render.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/foot.5.scd b/doc/foot.5.scd index f4a263d8..8d5f9bab 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -152,7 +152,7 @@ component. Maximize button's AARRGGBB color. Default: _ff00ff00_. *button-close-color* - Close button's AARRGGBB color. Default: _ffff0000_. + Close button's AARRGGBB color. Default: _ffff3030_. # FONT FORMAT diff --git a/render.c b/render.c index c3ea0744..468b2d31 100644 --- a/render.c +++ b/render.c @@ -885,7 +885,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx) break; case CSD_SURF_CLOSE: - _color = 0xffff0000; + _color = 0xffff3030; is_set = &term->conf->csd.color.close_set; conf_color = &term->conf->csd.color.close; is_active = term->active_surface == TERM_SURF_BUTTON_CLOSE; From cb6616ef8ac9f778a4e86b713033d0355dc27279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2020 21:11:17 +0100 Subject: [PATCH 106/125] render: csd: maximize: change default color to a lighter green --- doc/foot.5.scd | 2 +- render.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 8d5f9bab..3d775cb5 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -149,7 +149,7 @@ component. Minimize button's AARRGGBB color. Default: _ff1e90ff_. *button-maximize-color* - Maximize button's AARRGGBB color. Default: _ff00ff00_. + Maximize button's AARRGGBB color. Default: _ff30ff30_. *button-close-color* Close button's AARRGGBB color. Default: _ffff3030_. diff --git a/render.c b/render.c index 468b2d31..52c80e08 100644 --- a/render.c +++ b/render.c @@ -878,7 +878,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx) break; case CSD_SURF_MAXIMIZE: - _color = 0xff00ff00; + _color = 0xff30ff30; is_set = &term->conf->csd.color.maximize_set; conf_color = &term->conf->csd.color.maximize; is_active = term->active_surface == TERM_SURF_BUTTON_MAXIMIZE; From c90d70b2bfc93fa7bf58f9e8805a55b191828886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:18:59 +0100 Subject: [PATCH 107/125] config: CSD borders are always invisible That is, remove all configuration options and always draw them fully transparent. --- config.c | 29 ++--------------------------- config.h | 2 -- doc/foot.5.scd | 17 +++++------------ footrc | 6 ++---- render.c | 12 +----------- 5 files changed, 10 insertions(+), 56 deletions(-) diff --git a/config.c b/config.c index ef7f9a25..158eabec 100644 --- a/config.c +++ b/config.c @@ -350,7 +350,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, } } - else if (strcmp(key, "titlebar-color") == 0) { + else if (strcmp(key, "color") == 0) { uint32_t color; if (!str_to_color(value, &color, true, path, lineno)) { LOG_ERR("%s:%d: invalid titlebar-color: %s", path, lineno, value); @@ -361,18 +361,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, conf->csd.color.title = color; } - else if (strcmp(key, "border-color") == 0) { - uint32_t color; - if (!str_to_color(value, &color, true, path, lineno)) { - LOG_ERR("%s:%d: invalid border-color: %s", path, lineno, value); - return false; - } - - conf->csd.color.border_set = true; - conf->csd.color.border = color; - } - - else if (strcmp(key, "titlebar-size") == 0) { + else if (strcmp(key, "size") == 0) { unsigned long pixels; if (!str_to_ulong(value, 10, &pixels)) { LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); @@ -382,16 +371,6 @@ parse_section_csd(const char *key, const char *value, struct config *conf, conf->csd.title_height = pixels; } - else if (strcmp(key, "border-width") == 0) { - unsigned long pixels; - if (!str_to_ulong(value, 10, &pixels)) { - LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); - return false; - } - - conf->csd.border_width = pixels; - } - else if (strcmp(key, "button-width") == 0) { unsigned long pixels; if (!str_to_ulong(value, 10, &pixels)) { @@ -645,10 +624,6 @@ config_load(struct config *conf, const char *conf_path) .title_height = 26, .border_width = 5, .button_width = 22, - .color = { - .title_set = false, - .border_set = false, - }, }, .render_worker_count = sysconf(_SC_NPROCESSORS_ONLN), diff --git a/config.h b/config.h index dd34c589..a89637e1 100644 --- a/config.h +++ b/config.h @@ -45,12 +45,10 @@ struct config { struct { bool title_set; - bool border_set; bool minimize_set; bool maximize_set; bool close_set; uint32_t title; - uint32_t border; uint32_t minimize; uint32_t maximize; uint32_t close; diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 3d775cb5..69c6f385 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -126,23 +126,16 @@ component. compositor. Depending on the compositor's configuration and capabilities, it may not have any effect. Default: _server_. -*titlebar-size* - Height, in pixels (but subject to output scaling), of the - titlebar, not including the window border. Default: _26_. +*size* + Height, in pixels (subject to output scaling), of the + titlebar. Default: _26_. -*border-width* - Width, in pixels (but subject to output scaling), of the - borders. Default: _5_. - -*titlebar-color* +*color* Titlebar AARRGGBB color. Default: use the default _foreground_ color. -*border-color* - Border AARRGGBB color. Default: _transparent_. - *button-width* - Width, in pixels (but subject to output scaling), of the + Width, in pixels (subject to output scaling), of the minimize/maximize/close buttons. Default: _22_. *button-minimize-color* diff --git a/footrc b/footrc index a9bce529..e2799fdd 100644 --- a/footrc +++ b/footrc @@ -36,10 +36,8 @@ [csd] # preferred=server -# titlebar-size=26 -# border-width=5 -# titlebar-color= -# border-color=00000000 +# size=26 +# color= # button-width=22 # button-minimize-color=ff0000ff # button-maximize-color=ff00ff00 diff --git a/render.c b/render.c index 52c80e08..b422ef93 100644 --- a/render.c +++ b/render.c @@ -779,17 +779,7 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx) struct buffer *buf = shm_get_buffer( term->wl->shm, info.width, info.height, cookie); - uint32_t _color = 0; - uint16_t alpha = 0; - - if (term->conf->csd.color.border_set) { - _color = term->conf->csd.color.border; - alpha = _color >> 24 | (_color >> 24 << 8); - } - - pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha); - if (!term->visual_focus) - pixman_color_dim(&color); + pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0); render_csd_part(term, surf, buf, info.width, info.height, &color); csd_commit(term, surf, buf); } From e077290c56022255a986c0560f4d496a29bab55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:19:47 +0100 Subject: [PATCH 108/125] quirks: add shortcut for flipping all CSD surfaces sync/desync state --- quirks.c | 22 ++++++++++++++++++++++ quirks.h | 6 ++++++ terminal.c | 20 -------------------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/quirks.c b/quirks.c index 0db29a5e..6db96e46 100644 --- a/quirks.c +++ b/quirks.c @@ -7,6 +7,8 @@ #define LOG_ENABLE_DBG 0 #include "log.h" +#define ALEN(v) (sizeof(v) / sizeof(v[0])) + static bool is_weston(void) { @@ -53,3 +55,23 @@ quirk_weston_subsurface_desync_off(struct wl_subsurface *sub) wl_subsurface_set_sync(sub); } + +void +quirk_weston_csd_on(struct terminal *term) +{ + if (term->window->use_csd != CSD_YES) + return; + + for (int i = 0; i < ALEN(term->window->csd.surface); i++) + quirk_weston_subsurface_desync_on(term->window->csd.sub_surface[i]); +} + +void +quirk_weston_csd_off(struct terminal *term) +{ + if (term->window->use_csd != CSD_YES) + return; + + for (int i = 0; i < ALEN(term->window->csd.surface); i++) + quirk_weston_subsurface_desync_off(term->window->csd.sub_surface[i]); +} diff --git a/quirks.h b/quirks.h index ce4b8b01..7b6ab4f8 100644 --- a/quirks.h +++ b/quirks.h @@ -2,5 +2,11 @@ #include +#include "terminal.h" + void quirk_weston_subsurface_desync_on(struct wl_subsurface *sub); void quirk_weston_subsurface_desync_off(struct wl_subsurface *sub); + +/* Shortcuts to call desync_{on,off} on all CSD subsurfaces */ +void quirk_weston_csd_on(struct terminal *term); +void quirk_weston_csd_off(struct terminal *term); diff --git a/terminal.c b/terminal.c index c57b3c1c..ebc27b3c 100644 --- a/terminal.c +++ b/terminal.c @@ -1627,26 +1627,6 @@ term_restore_cursor(struct terminal *term) term->cursor.lcf = term->saved_cursor.lcf; } -static void -quirk_weston_csd_on(struct terminal *term) -{ - if (term->window->use_csd != CSD_YES) - return; - - for (int i = 0; i < ALEN(term->window->csd.surface); i++) - quirk_weston_subsurface_desync_on(term->window->csd.sub_surface[i]); -} - -static void -quirk_weston_csd_off(struct terminal *term) -{ - if (term->window->use_csd != CSD_YES) - return; - - for (int i = 0; i < ALEN(term->window->csd.surface); i++) - quirk_weston_subsurface_desync_off(term->window->csd.sub_surface[i]); -} - void term_visual_focus_in(struct terminal *term) { From f0892988c0bc590d885701c42c0a93b4cf6c6ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:20:53 +0100 Subject: [PATCH 109/125] wayland: don't destroy keyboard/pointer if they haven't been removed When the seat capabilities change, we used to destroy all pointers and keyboards, and then re-creating them as necessary. This caused a crash on mutter - probably because we removed a keyboard device the compositor had already sent an event for (or was about to). Now, we only destroy and create devices when it's needed. --- wayland.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/wayland.c b/wayland.c index 26982815..38fafc49 100644 --- a/wayland.c +++ b/wayland.c @@ -103,24 +103,32 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat, { struct wayland *wayl = data; - if (wayl->keyboard != NULL) { - wl_keyboard_release(wayl->keyboard); - wayl->keyboard = NULL; - } - - if (wayl->pointer.pointer != NULL) { - wl_pointer_release(wayl->pointer.pointer); - wayl->pointer.pointer = NULL; - } - if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { - wayl->keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); + if (wayl->keyboard == NULL) { + wayl->keyboard = wl_seat_get_keyboard(wl_seat); + LOG_INFO("got KBD %p", wayl->keyboard); + wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); + } + } else { + if (wayl->keyboard != NULL) { + LOG_INFO("releasing KBD %p", wayl->keyboard); + wl_keyboard_release(wayl->keyboard); + wayl->keyboard = NULL; + } } if (caps & WL_SEAT_CAPABILITY_POINTER) { - wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); + if (wayl->pointer.pointer == NULL) { + wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); + LOG_INFO("got pointer %p", wayl->pointer.pointer); + wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); + } + } else { + if (wayl->pointer.pointer != NULL) { + LOG_INFO("releasing pointer %p", wayl->pointer.pointer); + wl_pointer_release(wayl->pointer.pointer); + wayl->pointer.pointer = NULL; + } } } From 264acd6463794a659d52f833702a8e864034cecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:22:32 +0100 Subject: [PATCH 110/125] input: do a full CSD repaint on pointer motion over CSD buttons This fixes repainting issues on mutter. --- input.c | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/input.c b/input.c index 5ca13785..a9e7ac83 100644 --- a/input.c +++ b/input.c @@ -39,7 +39,6 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - /* TODO: free old context + keymap */ if (wayl->kbd.xkb_compose_state != NULL) { xkb_compose_state_unref(wayl->kbd.xkb_compose_state); wayl->kbd.xkb_compose_state = NULL; @@ -711,25 +710,15 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_BUTTON_MINIMIZE: case TERM_SURF_BUTTON_MAXIMIZE: - case TERM_SURF_BUTTON_CLOSE: { - enum csd_surface idx = - term->active_surface == TERM_SURF_BUTTON_MINIMIZE ? CSD_SURF_MINIMIZE : - term->active_surface == TERM_SURF_BUTTON_MAXIMIZE ? CSD_SURF_MAXIMIZE : - CSD_SURF_CLOSE; - - quirk_weston_subsurface_desync_on(term->window->csd.sub_surface[CSD_SURF_TITLE]); - quirk_weston_subsurface_desync_on(term->window->csd.sub_surface[idx]); - - render_csd_button(term, idx); - - quirk_weston_subsurface_desync_off(term->window->csd.sub_surface[idx]); - quirk_weston_subsurface_desync_off(term->window->csd.sub_surface[CSD_SURF_TITLE]); - wl_surface_commit(win->surface); + case TERM_SURF_BUTTON_CLOSE: + quirk_weston_csd_on(term); + render_csd(term); + quirk_weston_csd_off(term); + render_refresh(term); term->xcursor = "left_ptr"; render_xcursor_set(term); break; - } case TERM_SURF_NONE: assert(false); @@ -778,22 +767,15 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, switch (active_surface) { case TERM_SURF_BUTTON_MINIMIZE: case TERM_SURF_BUTTON_MAXIMIZE: - case TERM_SURF_BUTTON_CLOSE: { - enum csd_surface idx = - active_surface == TERM_SURF_BUTTON_MINIMIZE ? CSD_SURF_MINIMIZE : - active_surface == TERM_SURF_BUTTON_MAXIMIZE ? CSD_SURF_MAXIMIZE : - CSD_SURF_CLOSE; + case TERM_SURF_BUTTON_CLOSE: + if (old_moused->is_shutting_down) + break; - quirk_weston_subsurface_desync_on(old_moused->window->csd.sub_surface[CSD_SURF_TITLE]); - quirk_weston_subsurface_desync_on(old_moused->window->csd.sub_surface[idx]); - - render_csd_button(old_moused, idx); - - quirk_weston_subsurface_desync_off(old_moused->window->csd.sub_surface[idx]); - quirk_weston_subsurface_desync_off(old_moused->window->csd.sub_surface[CSD_SURF_TITLE]); - wl_surface_commit(old_moused->window->surface); + quirk_weston_csd_on(old_moused); + render_csd(old_moused); + quirk_weston_csd_off(old_moused); + render_refresh(old_moused); break; - } case TERM_SURF_NONE: case TERM_SURF_GRID: @@ -1054,7 +1036,6 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_BUTTON_CLOSE: if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) term_shutdown(term); - //LOG_ERR("unimplemented: terminate"); break; case TERM_SURF_SEARCH: From c10f1d5459568d4883e55d3d7531594d6c93066b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:23:37 +0100 Subject: [PATCH 111/125] render: don't try to render CSDs when the terminal is shutting down --- render.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/render.c b/render.c index b422ef93..771cfe64 100644 --- a/render.c +++ b/render.c @@ -736,6 +736,9 @@ render_csd_title(struct terminal *term) if (term->window->use_csd != CSD_YES) return; + if (term->is_shutting_down) + return; + struct csd_data info = get_csd_data(term, CSD_SURF_TITLE); struct wl_surface *surf = term->window->csd.surface[CSD_SURF_TITLE]; @@ -756,7 +759,6 @@ render_csd_title(struct terminal *term) pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha); if (!term->visual_focus) pixman_color_dim(&color); - render_csd_part(term, surf, buf, info.width, info.height, &color); csd_commit(term, surf, buf); } @@ -843,6 +845,9 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx) if (term->window->use_csd != CSD_YES) return; + if (term->is_shutting_down) + return; + struct csd_data info = get_csd_data(term, surf_idx); struct wl_surface *surf = term->window->csd.surface[surf_idx]; @@ -921,6 +926,9 @@ render_csd(struct terminal *term) if (term->window->use_csd != CSD_YES) return; + if (term->is_shutting_down) + return; + for (size_t i = 0; i < CSD_SURF_COUNT; i++) { struct csd_data info = get_csd_data(term, i); const int x = info.x; From c9659ecd14d7d94d1c0b26cf1864606f994bd22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:23:52 +0100 Subject: [PATCH 112/125] render: csd: don't even try to render CSDs when we're in fullscreen mode --- render.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/render.c b/render.c index 771cfe64..7d7da79e 100644 --- a/render.c +++ b/render.c @@ -929,6 +929,9 @@ render_csd(struct terminal *term) if (term->is_shutting_down) return; + if (term->window->is_fullscreen) + return; + for (size_t i = 0; i < CSD_SURF_COUNT; i++) { struct csd_data info = get_csd_data(term, i); const int x = info.x; From 09bb9bef33b6e9a2bbac830bd14dca1d14986e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:24:09 +0100 Subject: [PATCH 113/125] render: csd: center 'close' button's cross --- render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index 7d7da79e..dc884d17 100644 --- a/render.c +++ b/render.c @@ -830,8 +830,8 @@ render_csd_button_close(struct terminal *term, struct buffer *buf) pixman_image_fill_rectangles( PIXMAN_OP_SRC, buf->pix, &color, 2, (pixman_rectangle16_t[]){ - {(buf->width - length) / 2, buf->height / 2, length, 1 * term->scale}, - {buf->width / 2, (buf->height - length) / 2, 1 * term->scale, length}, + {(buf->width - length - term->scale) / 2, (buf->height - term->scale) / 2, length, 1 * term->scale}, + {(buf->width - term->scale) / 2, (buf->height - length - term->scale) / 2, 1 * term->scale, length}, }); pixman_image_unref(src); From da91a9de4b6b3d4501a053e1528abe4e44b3a726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:24:31 +0100 Subject: [PATCH 114/125] render: csd: assert surfaces exist before trying to use them --- render.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/render.c b/render.c index dc884d17..919cf8bd 100644 --- a/render.c +++ b/render.c @@ -942,6 +942,9 @@ render_csd(struct terminal *term) struct wl_surface *surf = term->window->csd.surface[i]; struct wl_subsurface *sub = term->window->csd.sub_surface[i]; + assert(surf != NULL); + assert(sub != NULL); + if (width == 0 || height == 0) { /* CSD borders aren't rendered in maximized mode */ assert(term->window->is_maximized || term->window->is_fullscreen); From 044556ef3e1a04173e4b1c12baa2947b08222fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:24:51 +0100 Subject: [PATCH 115/125] render: csd: render surfaces in reverse order This ensures the inner most child surfaces are rendered and comitted before the parent surfaces. --- render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index 919cf8bd..a492fb28 100644 --- a/render.c +++ b/render.c @@ -957,11 +957,11 @@ render_csd(struct terminal *term) wl_subsurface_set_position(sub, x / term->scale, y / term->scale); } - render_csd_title(term); for (size_t i = CSD_SURF_LEFT; i <= CSD_SURF_BOTTOM; i++) render_csd_border(term, i); - for (size_t i = CSD_SURF_MINIMIZE; i < CSD_SURF_COUNT; i++) + for (size_t i = CSD_SURF_MINIMIZE; i <= CSD_SURF_CLOSE; i++) render_csd_button(term, i); + render_csd_title(term); } static void frame_callback( From d76484ae50efb8ede95e6b0921bdb1fad26dccd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:26:15 +0100 Subject: [PATCH 116/125] wayland: set window geometry to exclude the invisible CSD borders But it *does* include the title bar. This simplifies the 'adjustment' needed to be done to the configured window size. It also fixes a number of issues: * the compositor will now properly snap the window to screen edges (before, there was an empty space between the edge and the window - the CSD border). * This also removes the need for the mutter 'commit' workaround. We must be doing something right now. --- render.c | 30 ++++++++++++++++++--------- wayland.c | 61 ++++++++----------------------------------------------- 2 files changed, 28 insertions(+), 63 deletions(-) diff --git a/render.c b/render.c index a492fb28..82abac4d 100644 --- a/render.c +++ b/render.c @@ -1416,16 +1416,9 @@ maybe_resize(struct terminal *term, int width, int height, bool force) height = term->conf->height; if (term->window->use_csd == CSD_YES) { + /* Take CSD title bar into account */ assert(!term->window->is_fullscreen); - - /* Account for CSDs, to make actual window size match - * the configured size */ - if (!term->window->is_maximized) { - width -= 2 * term->conf->csd.border_width; - height -= 2 * term->conf->csd.border_width + term->conf->csd.title_height; - } else { - height -= term->conf->csd.title_height; - } + height -= term->conf->csd.title_height; } width *= scale; @@ -1556,10 +1549,27 @@ damage_view: term->unmaximized_height = term->height; } + render_csd(term); + +#if 0 + /* TODO: doesn't include CSD title bar */ xdg_toplevel_set_min_size( term->window->xdg_toplevel, min_width / scale, min_height / scale); +#endif + + { + bool title_shown = !term->window->is_fullscreen && + term->window->use_csd == CSD_YES; + + int title_height = title_shown ? term->conf->csd.title_height : 0; + xdg_surface_set_window_geometry( + term->window->xdg_surface, + 0, + -title_height, + term->width / term->scale, + term->height / term->scale + title_height); + - render_csd(term); if (term->is_searching) render_search_box(term); diff --git a/wayland.c b/wayland.c index 38fafc49..f965767a 100644 --- a/wayland.c +++ b/wayland.c @@ -540,24 +540,14 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, */ struct wl_window *win = data; - if (!is_fullscreen && win->use_csd == CSD_YES && width != 0 && height != 0) { + if (!is_fullscreen && win->use_csd == CSD_YES && width > 0 && height > 0) { /* - * The size received here is the *total* window size. - * - * This *includes* the (negatively positioned) CSD - * sub-surfaces. Thus, since our resize code assumes the size - * to resize to is the main window only, adjust the size here, - * to account for the CSDs. + * We include the CSD title bar in our window geometry. Thus, + * the height we call render_resize() with must be adjusted, + * since it expects the size to refer to the main grid only. */ - const struct config *conf = win->term->conf; - if (!is_maximized) { - width -= 2 * conf->csd.border_width; - height -= 2 * conf->csd.border_width + conf->csd.title_height; - } else { - height -= conf->csd.title_height; - } + height -= win->term->conf->csd.title_height; } - win->configure.is_activated = is_activated; win->configure.is_fullscreen = is_fullscreen; win->configure.is_maximized = is_maximized; @@ -607,37 +597,7 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, else term_visual_focus_out(term); - static bool desktop_is_gnome = false; - static bool desktop_is_initialized = false; - - if (!desktop_is_initialized) { - const char *current_desktop = getenv("XDG_CURRENT_DESKTOP"); - - desktop_is_gnome = current_desktop != NULL && - strcasestr(current_desktop, "gnome") != NULL; - - if (desktop_is_gnome) - LOG_WARN("applying wl_surface_commit() workaround for mutter"); - - desktop_is_initialized = true; - } - - if (desktop_is_gnome) { - /* - * Resizing the window under mutter causes the "other" side to - * jump back and forth, *even* if we re-render and commit a - * properly resized frame immediately. - * - * For that reason, the code path below also does not work, - * since in case we *did* resize, it appears the commit - * happens "too late" after the ack. - * - * Finally, doing this on any other compositor breaks the CSD - * synchronization with the main surface. Hence we only do - * this when running under mutter. - */ - wl_surface_commit(win->surface); - } else if (!resized) { + if (!resized) { /* * If we didn't resize, we won't be commit a new surface * anytime soon. Some compositors require a commit in @@ -678,18 +638,13 @@ xdg_toplevel_decoration_configure(void *data, if (win->is_configured && win->use_csd == CSD_YES) { struct terminal *term = win->term; - const struct config *conf = term->conf; int scale = term->scale; int width = term->width / scale; int height = term->height / scale; - if (!term->window->is_maximized) { - width -= 2 * conf->csd.border_width; - height -= 2 * conf->csd.border_width + conf->csd.title_height; - } else - height -= conf->csd.title_height; - + /* Take CSD title bar into account */ + height -= term->conf->csd.title_height; render_resize_force(term, width, height); } } From b81b1b6ff7e0a1c4bbe8eeb1216927f1dc3506b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:27:59 +0100 Subject: [PATCH 117/125] render: configure a clip region to exclude the grid margins This ensures content in the last column doesn't flow over into the margins (where they are typically never erased, unless the window is resized). --- render.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/render.c b/render.c index 82abac4d..6f8aa6b5 100644 --- a/render.c +++ b/render.c @@ -994,6 +994,9 @@ grid_render(struct terminal *term) wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0); pixman_image_t *pix = buf->pix; + pixman_region16_t clip; + pixman_region_init_rect(&clip, term->margins.left, term->margins.top, term->cols * term->cell_width, term->rows * term->cell_height); + pixman_image_set_clip_region(pix, &clip); /* If we resized the window, or is flashing, or just stopped flashing */ if (term->render.last_buf != buf || @@ -1027,6 +1030,7 @@ grid_render(struct terminal *term) if (term->is_searching) pixman_color_dim(&bg); + pixman_image_set_clip_region(pix, NULL); pixman_image_fill_rectangles( PIXMAN_OP_SRC, pix, &bg, 4, (pixman_rectangle16_t[]){ @@ -1034,6 +1038,7 @@ grid_render(struct terminal *term) {0, 0, term->margins.left, term->height}, /* Left */ {rmargin, 0, term->margins.right, term->height}, /* Right */ {0, bmargin, term->width, term->margins.bottom}}); /* Bottom */ + pixman_image_set_clip_region(pix, &clip); wl_surface_damage_buffer( term->window->surface, 0, 0, term->width, term->margins.top); From a78cca74a030782e33646d0719f44e20c585e8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 3 Mar 2020 18:29:46 +0100 Subject: [PATCH 118/125] render: oops, add missing '}' --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index 6f8aa6b5..bccd870a 100644 --- a/render.c +++ b/render.c @@ -1573,7 +1573,7 @@ damage_view: -title_height, term->width / term->scale, term->height / term->scale + title_height); - + } if (term->is_searching) render_search_box(term); From 27ef5b472a88706decf6c993eec6e0e0d6775312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 6 Mar 2020 19:11:31 +0100 Subject: [PATCH 119/125] config: change default CSD button width from 22 -> 26 --- config.c | 2 +- doc/foot.5.scd | 2 +- footrc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.c b/config.c index 158eabec..46056567 100644 --- a/config.c +++ b/config.c @@ -623,7 +623,7 @@ config_load(struct config *conf, const char *conf_path) .preferred = CONF_CSD_PREFER_SERVER, .title_height = 26, .border_width = 5, - .button_width = 22, + .button_width = 26, }, .render_worker_count = sysconf(_SC_NPROCESSORS_ONLN), diff --git a/doc/foot.5.scd b/doc/foot.5.scd index 69c6f385..edfecc27 100644 --- a/doc/foot.5.scd +++ b/doc/foot.5.scd @@ -136,7 +136,7 @@ component. *button-width* Width, in pixels (subject to output scaling), of the - minimize/maximize/close buttons. Default: _22_. + minimize/maximize/close buttons. Default: _26_. *button-minimize-color* Minimize button's AARRGGBB color. Default: _ff1e90ff_. diff --git a/footrc b/footrc index e2799fdd..7cac547d 100644 --- a/footrc +++ b/footrc @@ -38,7 +38,7 @@ # preferred=server # size=26 # color= -# button-width=22 +# button-width=26 # button-minimize-color=ff0000ff # button-maximize-color=ff00ff00 # button-close-color=ffff0000 From 0dddb5d119ee26b40405527d0f436e089b9ba08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 6 Mar 2020 19:11:57 +0100 Subject: [PATCH 120/125] sixel: don't try to dirty an un-allocated row This may happen e.g. when resizing a grid --- sixel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sixel.c b/sixel.c index b538fddb..90d886fe 100644 --- a/sixel.c +++ b/sixel.c @@ -59,6 +59,11 @@ sixel_erase(struct terminal *term, struct sixel *sixel) int r = (sixel->pos.row + i) & (term->grid->num_rows - 1); struct row *row = term->grid->rows[r]; + if (row == NULL) { + /* A resize/reflow may cause row to now be unallocated */ + continue; + } + row->dirty = true; for (int c = 0; c < term->grid->num_cols; c++) From f434933824da23ced65e4134d6cb76c99b49897e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 6 Mar 2020 19:13:10 +0100 Subject: [PATCH 121/125] render: CSD: use *default* foreground, not current foreground Applications may temporarily change the foreground color. Don't use this when rendering the CSD title bar - use the default foreground (i.e the default default one, or the one configured by the user in footrc). --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index bccd870a..fa929b37 100644 --- a/render.c +++ b/render.c @@ -748,7 +748,7 @@ render_csd_title(struct terminal *term) struct buffer *buf = shm_get_buffer( term->wl->shm, info.width, info.height, cookie); - uint32_t _color = term->colors.fg; + uint32_t _color = term->colors.default_fg; uint16_t alpha = 0xffff; if (term->conf->csd.color.title_set) { From e5540a0d2ecfe8156946e444d265e4e41818793c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 6 Mar 2020 19:15:09 +0100 Subject: [PATCH 122/125] render: csd: improved look of minimize/maximize/close buttons * minimize: a downward triangle * maximize (window): an upward triangle * maximize (already maximized): a hollow square * close: a filled square The glyphs are now rendered using the default background color instead of hardcoded to black. --- render.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 121 insertions(+), 24 deletions(-) diff --git a/render.c b/render.c index fa929b37..c90a2be5 100644 --- a/render.c +++ b/render.c @@ -789,15 +789,117 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx) static void render_csd_button_minimize(struct terminal *term, struct buffer *buf) { - pixman_color_t color = color_hex_to_pixman(0); + pixman_color_t color = color_hex_to_pixman(term->colors.default_bg); pixman_image_t *src = pixman_image_create_solid_fill(&color); - int x_margin = (term->conf->csd.button_width * 1 / 4) * term->scale; - int y_margin = (term->conf->csd.title_height * 4 / 6) * term->scale; + const int max_height = buf->height / 2; + const int max_width = buf->width / 2; + + int width = max_width; + int height = max_width / 2; + + if (height > max_height) { + height = max_height; + width = height * 2; + } + + assert(width <= max_width); + assert(height <= max_height); + + int x_margin = (buf->width - width) / 2.; + int y_margin = (buf->height - height) / 2.; + + pixman_triangle_t tri = { + .p1 = { + .x = pixman_int_to_fixed(x_margin), + .y = pixman_int_to_fixed(y_margin), + }, + .p2 = { + .x = pixman_int_to_fixed(x_margin + width), + .y = pixman_int_to_fixed(y_margin), + }, + .p3 = { + .x = pixman_int_to_fixed(buf->width / 2), + .y = pixman_int_to_fixed(y_margin + height), + }, + }; + + pixman_composite_triangles( + PIXMAN_OP_OVER, src, buf->pix, PIXMAN_a1, + 0, 0, 0, 0, 1, &tri); + pixman_image_unref(src); +} + +static void +render_csd_button_maximize_maximized( + struct terminal *term, struct buffer *buf) +{ + pixman_color_t color = color_hex_to_pixman(term->colors.default_bg); + pixman_image_t *src = pixman_image_create_solid_fill(&color); + + const int max_height = buf->height / 3; + const int max_width = buf->width / 3; + + int width = min(max_height, max_width); + int thick = 1 * term->scale; + + const int x_margin = (buf->width - width) / 2; + const int y_margin = (buf->height - width) / 2; pixman_image_fill_rectangles( - PIXMAN_OP_SRC, buf->pix, &color, 1, - &(pixman_rectangle16_t){x_margin, y_margin, buf->width - 2 * x_margin, 1 * term->scale}); + PIXMAN_OP_SRC, buf->pix, &color, 4, + (pixman_rectangle16_t[]){ + {x_margin, y_margin, width, thick}, + {x_margin, y_margin + thick, thick, width - 2 * thick}, + {x_margin + width - thick, y_margin + thick, thick, width - 2 * thick}, + {x_margin, y_margin + width - thick, width, thick}}); + + pixman_image_unref(src); + +} + +static void +render_csd_button_maximize_window( + struct terminal *term, struct buffer *buf) +{ + pixman_color_t color = color_hex_to_pixman(term->colors.default_bg); + pixman_image_t *src = pixman_image_create_solid_fill(&color); + + const int max_height = buf->height / 2; + const int max_width = buf->width / 2; + + int width = max_width; + int height = max_width / 2; + + if (height > max_height) { + height = max_height; + width = height * 2; + } + + assert(width <= max_width); + assert(height <= max_height); + + int x_margin = (buf->width - width) / 2.; + int y_margin = (buf->height - height) / 2.; + + pixman_triangle_t tri = { + .p1 = { + .x = pixman_int_to_fixed(buf->width / 2), + .y = pixman_int_to_fixed(y_margin), + }, + .p2 = { + .x = pixman_int_to_fixed(x_margin), + .y = pixman_int_to_fixed(y_margin + height), + }, + .p3 = { + .x = pixman_int_to_fixed(x_margin + width), + .y = pixman_int_to_fixed(y_margin + height), + }, + }; + + pixman_composite_triangles( + PIXMAN_OP_OVER, src, buf->pix, PIXMAN_a1, + 0, 0, 0, 0, 1, &tri); pixman_image_unref(src); } @@ -805,34 +907,29 @@ render_csd_button_minimize(struct terminal *term, struct buffer *buf) static void render_csd_button_maximize(struct terminal *term, struct buffer *buf) { - pixman_color_t color = color_hex_to_pixman(0); - pixman_image_t *src = pixman_image_create_solid_fill(&color); - - int x_margin = (term->conf->csd.button_width * 1 / 4) * term->scale; - int y_margin = (term->conf->csd.title_height * 2 / 6) * term->scale; - - pixman_image_fill_rectangles( - PIXMAN_OP_SRC, buf->pix, &color, 1, - &(pixman_rectangle16_t){x_margin, y_margin, buf->width - 2 * x_margin, 2 * term->scale}); - - pixman_image_unref(src); + if (term->window->is_maximized) + render_csd_button_maximize_maximized(term, buf); + else + render_csd_button_maximize_window(term, buf); } static void render_csd_button_close(struct terminal *term, struct buffer *buf) { - pixman_color_t color = color_hex_to_pixman(0); + pixman_color_t color = color_hex_to_pixman(term->colors.default_bg); pixman_image_t *src = pixman_image_create_solid_fill(&color); - int min_length = min(buf->width, buf->height); - int length = min_length / 2; + const int max_height = buf->height / 3; + const int max_width = buf->width / 3; + + int width = min(max_height, max_width); + + const int x_margin = (buf->width - width) / 2; + const int y_margin = (buf->height - width) / 2; pixman_image_fill_rectangles( - PIXMAN_OP_SRC, buf->pix, &color, 2, - (pixman_rectangle16_t[]){ - {(buf->width - length - term->scale) / 2, (buf->height - term->scale) / 2, length, 1 * term->scale}, - {(buf->width - term->scale) / 2, (buf->height - length - term->scale) / 2, 1 * term->scale, length}, - }); + PIXMAN_OP_SRC, buf->pix, &color, 1, + &(pixman_rectangle16_t){x_margin, y_margin, width, width}); pixman_image_unref(src); } From c5a1af4e534f0f290ea885b221d7d37d020ac994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 6 Mar 2020 19:16:54 +0100 Subject: [PATCH 123/125] render: never render CSD and/or search box "immediately" Handle the CSDs and the search box the same way we handle the main grid; when we need to redraw them, call render_refresh_{csd,search}(). This sets a flag that is checked after each FDM iteration. All actual rendering is done here. This also ties the commits of the Wayland sub-surfaces to the commit of the main surface. --- input.c | 11 +--- render.c | 145 ++++++++++++++++++++++++++++++++++------------------- render.h | 7 +-- search.c | 6 +-- terminal.c | 14 ++---- terminal.h | 16 +++++- 6 files changed, 118 insertions(+), 81 deletions(-) diff --git a/input.c b/input.c index a9e7ac83..efa58c9d 100644 --- a/input.c +++ b/input.c @@ -711,13 +711,9 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_BUTTON_MINIMIZE: case TERM_SURF_BUTTON_MAXIMIZE: case TERM_SURF_BUTTON_CLOSE: - quirk_weston_csd_on(term); - render_csd(term); - quirk_weston_csd_off(term); - render_refresh(term); - term->xcursor = "left_ptr"; render_xcursor_set(term); + render_refresh_csd(term); break; case TERM_SURF_NONE: @@ -771,10 +767,7 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, if (old_moused->is_shutting_down) break; - quirk_weston_csd_on(old_moused); - render_csd(old_moused); - quirk_weston_csd_off(old_moused); - render_refresh(old_moused); + render_refresh_csd(old_moused); break; case TERM_SURF_NONE: diff --git a/render.c b/render.c index c90a2be5..fdb156ea 100644 --- a/render.c +++ b/render.c @@ -730,14 +730,10 @@ render_csd_part(struct terminal *term, pixman_image_unref(src); } -void +static void render_csd_title(struct terminal *term) { - if (term->window->use_csd != CSD_YES) - return; - - if (term->is_shutting_down) - return; + assert(term->window->use_csd == CSD_YES); struct csd_data info = get_csd_data(term, CSD_SURF_TITLE); struct wl_surface *surf = term->window->csd.surface[CSD_SURF_TITLE]; @@ -766,11 +762,9 @@ render_csd_title(struct terminal *term) static void render_csd_border(struct terminal *term, enum csd_surface surf_idx) { + assert(term->window->use_csd == CSD_YES); assert(surf_idx >= CSD_SURF_LEFT && surf_idx <= CSD_SURF_BOTTOM); - if (term->window->use_csd != CSD_YES) - return; - struct csd_data info = get_csd_data(term, surf_idx); struct wl_surface *surf = term->window->csd.surface[surf_idx]; @@ -934,16 +928,11 @@ render_csd_button_close(struct terminal *term, struct buffer *buf) pixman_image_unref(src); } -void +static void render_csd_button(struct terminal *term, enum csd_surface surf_idx) { - assert(surf_idx >= CSD_SURF_MINIMIZE); - - if (term->window->use_csd != CSD_YES) - return; - - if (term->is_shutting_down) - return; + assert(term->window->use_csd == CSD_YES); + assert(surf_idx >= CSD_SURF_MINIMIZE && surf_idx <= CSD_SURF_CLOSE); struct csd_data info = get_csd_data(term, surf_idx); struct wl_surface *surf = term->window->csd.surface[surf_idx]; @@ -1017,14 +1006,10 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx) csd_commit(term, surf, buf); } -void +static void render_csd(struct terminal *term) { - if (term->window->use_csd != CSD_YES) - return; - - if (term->is_shutting_down) - return; + assert(term->window->use_csd == CSD_YES); if (term->window->is_fullscreen) return; @@ -1365,21 +1350,6 @@ grid_render(struct terminal *term) } static void -frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data) -{ - struct terminal *term = data; - - assert(term->window->frame_callback == wl_callback); - wl_callback_destroy(wl_callback); - term->window->frame_callback = NULL; - - if (term->render.pending) { - term->render.pending = false; - grid_render(term); - } -} - -void render_search_box(struct terminal *term) { assert(term->window->search_sub_surface != NULL); @@ -1479,6 +1449,38 @@ render_search_box(struct terminal *term) quirk_weston_subsurface_desync_off(term->window->search_sub_surface); } +static void +frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data) +{ + struct terminal *term = data; + + assert(term->window->frame_callback == wl_callback); + wl_callback_destroy(wl_callback); + term->window->frame_callback = NULL; + + if (term->render.pending.csd) { + term->render.pending.csd = false; + + if (term->window->use_csd == CSD_YES) { + quirk_weston_csd_on(term); + render_csd(term); + quirk_weston_csd_off(term); + } + } + + if (term->render.pending.search) { + term->render.pending.search = false; + + if (term->is_searching) + render_search_box(term); + } + + if (term->render.pending.grid) { + term->render.pending.grid = false; + grid_render(term); + } +} + /* Move to terminal.c? */ static bool maybe_resize(struct terminal *term, int width, int height, bool force) @@ -1651,8 +1653,6 @@ damage_view: term->unmaximized_height = term->height; } - render_csd(term); - #if 0 /* TODO: doesn't include CSD title bar */ xdg_toplevel_set_min_size( @@ -1672,14 +1672,13 @@ damage_view: term->height / term->scale + title_height); } - if (term->is_searching) - render_search_box(term); - tll_free(term->normal.scroll_damage); tll_free(term->alt.scroll_damage); term->render.last_buf = NULL; term_damage_view(term); + render_refresh_csd(term); + render_refresh_search(term); render_refresh(term); return true; @@ -1767,20 +1766,50 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data) tll_foreach(renderer->wayl->terms, it) { struct terminal *term = it->item; - if (!term->render.refresh_needed) + if (!term->render.refresh.grid && + !term->render.refresh.csd && + !term->render.refresh.search) + { continue; + } - if (term->render.app_sync_updates.enabled) + if (term->render.app_sync_updates.enabled && + !term->render.refresh.csd && + !term->render.refresh.search) + { continue; + } + + if (term->render.refresh.csd || term->render.refresh.search) { + /* Force update of parent surface */ + term->render.refresh.grid = true; + } assert(term->window->is_configured); - term->render.refresh_needed = false; - if (term->window->frame_callback == NULL) - grid_render(term); - else { + bool grid = term->render.refresh.grid; + bool csd = term->render.refresh.csd; + bool search = term->render.refresh.search; + + term->render.refresh.grid = false; + term->render.refresh.csd = false; + term->render.refresh.search = false; + + if (term->window->frame_callback == NULL) { + if (csd && term->window->use_csd == CSD_YES) { + quirk_weston_csd_on(term); + render_csd(term); + quirk_weston_csd_off(term); + } + if (search) + render_search_box(term); + if (grid) + grid_render(term); + } else { /* Tells the frame callback to render again */ - term->render.pending = true; + term->render.pending.grid = grid; + term->render.pending.csd = csd; + term->render.pending.search = search; } } @@ -1815,7 +1844,21 @@ render_set_title(struct terminal *term, const char *_title) void render_refresh(struct terminal *term) { - term->render.refresh_needed = true; + term->render.refresh.grid = true; +} + +void +render_refresh_csd(struct terminal *term) +{ + if (term->window->use_csd == CSD_YES) + term->render.refresh.csd = true; +} + +void +render_refresh_search(struct terminal *term) +{ + if (term->is_searching) + term->render.refresh.search = true; } bool diff --git a/render.h b/render.h index cccf2001..99246f17 100644 --- a/render.h +++ b/render.h @@ -14,13 +14,10 @@ bool render_resize_force(struct terminal *term, int width, int height); void render_set_title(struct terminal *term, const char *title); void render_refresh(struct terminal *term); +void render_refresh_csd(struct terminal *term); +void render_refresh_search(struct terminal *term); bool render_xcursor_set(struct terminal *term); -void render_search_box(struct terminal *term); -void render_csd(struct terminal *term); -void render_csd_title(struct terminal *term); -void render_csd_button(struct terminal *term, enum csd_surface surf_idx); - struct render_worker_context { int my_id; struct terminal *term; diff --git a/search.c b/search.c index 3d5159ae..2c58f3f2 100644 --- a/search.c +++ b/search.c @@ -86,8 +86,7 @@ search_begin(struct terminal *term) term->is_searching = true; term_xcursor_update(term); - render_search_box(term); - render_refresh(term); + render_refresh_search(term); } void @@ -616,6 +615,5 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask LOG_DBG("search: buffer: %S", term->search.buf); search_find_next(term); - render_search_box(term); - render_refresh(term); + render_refresh_search(term); } diff --git a/terminal.c b/terminal.c index ebc27b3c..7fc177f0 100644 --- a/terminal.c +++ b/terminal.c @@ -199,7 +199,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) */ if (term->window->frame_callback == NULL) { if (term->render.app_sync_updates.enabled) - term->render.refresh_needed = true; + term->render.refresh.grid = true; else { /* First timeout - reset each time we receive input. */ @@ -235,7 +235,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) } } } else - term->render.pending = true; + term->render.pending.grid = true; if (hup) { if (term->hold_at_exit) { @@ -1637,10 +1637,7 @@ term_visual_focus_in(struct terminal *term) if (term->cursor_blink.active) cursor_blink_start_timer(term); - quirk_weston_csd_on(term); - render_csd(term); - quirk_weston_csd_off(term); - + render_refresh_csd(term); cursor_refresh(term); } @@ -1654,10 +1651,7 @@ term_visual_focus_out(struct terminal *term) if (term->cursor_blink.active) cursor_blink_stop_timer(term); - quirk_weston_csd_on(term); - render_csd(term); - quirk_weston_csd_off(term); - + render_refresh_csd(term); cursor_refresh(term); } diff --git a/terminal.h b/terminal.h index 3731ee0d..92082d8c 100644 --- a/terminal.h +++ b/terminal.h @@ -331,7 +331,20 @@ struct terminal { enum term_surface active_surface; struct { - bool refresh_needed; /* Terminal needs to be re-rendered, as soon-as-possible */ + /* Scheduled for rendering, as soon-as-possible */ + struct { + bool grid; + bool csd; + bool search; + } refresh; + + /* Scheduled for rendering, in the next frame callback */ + struct { + bool grid; + bool csd; + bool search; + } pending; + int scrollback_lines; /* Number of scrollback lines, from conf (TODO: move out from render struct?) */ struct { @@ -358,7 +371,6 @@ struct terminal { struct cell *cell; /* For easy access to content */ } last_cursor; - bool pending; /* Need to re-render again, after next frame-callback */ struct buffer *last_buf; /* Buffer we rendered to last time */ bool was_flashing; /* Flash was active last time we rendered */ bool was_searching; From 4f90cbda5968b5404434077ad94dfcc0e129a191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 6 Mar 2020 19:18:59 +0100 Subject: [PATCH 124/125] wayland: log: say 'requesting' instead of 'referring' client/server decorations --- wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index f965767a..83d3f745 100644 --- a/wayland.c +++ b/wayland.c @@ -1015,7 +1015,7 @@ wayl_win_init(struct terminal *term) win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( wayl->xdg_decoration_manager, win->xdg_toplevel); - LOG_INFO("preferring %s decorations", + LOG_INFO("requesting %s decorations", conf->csd.preferred == CONF_CSD_PREFER_SERVER ? "SSD" : "CSD"); zxdg_toplevel_decoration_v1_set_mode( From e6feed5311bc061963d9f5f19d415d02dec26236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 6 Mar 2020 19:19:18 +0100 Subject: [PATCH 125/125] wayland: remove debug logs --- wayland.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wayland.c b/wayland.c index 83d3f745..4bf77562 100644 --- a/wayland.c +++ b/wayland.c @@ -106,12 +106,10 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat, if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { if (wayl->keyboard == NULL) { wayl->keyboard = wl_seat_get_keyboard(wl_seat); - LOG_INFO("got KBD %p", wayl->keyboard); wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); } } else { if (wayl->keyboard != NULL) { - LOG_INFO("releasing KBD %p", wayl->keyboard); wl_keyboard_release(wayl->keyboard); wayl->keyboard = NULL; } @@ -120,12 +118,10 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat, if (caps & WL_SEAT_CAPABILITY_POINTER) { if (wayl->pointer.pointer == NULL) { wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); - LOG_INFO("got pointer %p", wayl->pointer.pointer); wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); } } else { if (wayl->pointer.pointer != NULL) { - LOG_INFO("releasing pointer %p", wayl->pointer.pointer); wl_pointer_release(wayl->pointer.pointer); wayl->pointer.pointer = NULL; }