mirror of
				https://github.com/labwc/labwc.git
				synced 2025-11-03 09:01:51 -05:00 
			
		
		
		
	osd: support 'alt-tab' on screen display
The osd window shows title, app_id/class and shell of all views that can be cycled between.
This commit is contained in:
		
							parent
							
								
									86c384b227
								
							
						
					
					
						commit
						77ade08158
					
				
					 8 changed files with 200 additions and 109 deletions
				
			
		| 
						 | 
				
			
			@ -109,6 +109,7 @@ struct server {
 | 
			
		|||
	double grab_x, grab_y;
 | 
			
		||||
	struct wlr_box grab_box;
 | 
			
		||||
	uint32_t resize_edges;
 | 
			
		||||
	struct wlr_texture *osd;
 | 
			
		||||
 | 
			
		||||
	struct wl_list outputs;
 | 
			
		||||
	struct wl_listener new_output;
 | 
			
		||||
| 
						 | 
				
			
			@ -234,7 +235,7 @@ struct view {
 | 
			
		|||
	struct wl_listener commit;
 | 
			
		||||
	struct wl_listener request_move;
 | 
			
		||||
	struct wl_listener request_resize;
 | 
			
		||||
	struct wl_listener request_configure;
 | 
			
		||||
	struct wl_listener request_configure;	/* xwayland only */
 | 
			
		||||
	struct wl_listener request_maximize;
 | 
			
		||||
	struct wl_listener set_title;
 | 
			
		||||
	struct wl_listener new_popup;		/* xdg-shell only */
 | 
			
		||||
| 
						 | 
				
			
			@ -324,6 +325,7 @@ void desktop_focus_view(struct seat *seat, struct view *view);
 | 
			
		|||
struct view *desktop_cycle_view(struct server *server, struct view *current);
 | 
			
		||||
struct view *topmost_mapped_view(struct server *server);
 | 
			
		||||
void desktop_focus_topmost_mapped_view(struct server *server);
 | 
			
		||||
bool isfocusable(struct view *view);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * desktop_view_at - find view or layer-surface at co-ordinate (lx, ly)
 | 
			
		||||
| 
						 | 
				
			
			@ -363,8 +365,7 @@ void server_finish(struct server *server);
 | 
			
		|||
 | 
			
		||||
void action(struct server *server, const char *action, const char *command);
 | 
			
		||||
 | 
			
		||||
void dbg_show_one_view(struct view *view);
 | 
			
		||||
void dbg_show_views(struct server *server);
 | 
			
		||||
void dbg_show_keybinds();
 | 
			
		||||
/* update onscreen display 'alt-tab' texture */
 | 
			
		||||
void osd_update(struct server *server);
 | 
			
		||||
 | 
			
		||||
#endif /* __LABWC_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ action(struct server *server, const char *action, const char *command)
 | 
			
		|||
	} else if (!strcasecmp(action, "NextWindow")) {
 | 
			
		||||
		server->cycle_view =
 | 
			
		||||
			desktop_cycle_view(server, server->cycle_view);
 | 
			
		||||
		osd_update(server);
 | 
			
		||||
	} else if (!strcasecmp(action, "Reconfigure")) {
 | 
			
		||||
		spawn_async_no_shell("killall -SIGHUP labwc");
 | 
			
		||||
	} else if (!strcasecmp(action, "ShowMenu")) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ desktop_focus_view(struct seat *seat, struct view *view)
 | 
			
		|||
 * careful when cycling between views. The only views we should focus are
 | 
			
		||||
 * those that are already mapped and those that have been minimized.
 | 
			
		||||
 */
 | 
			
		||||
static bool
 | 
			
		||||
bool
 | 
			
		||||
isfocusable(struct view *view)
 | 
			
		||||
{
 | 
			
		||||
	/* filter out those xwayland surfaces that have never been mapped */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,6 +72,8 @@ keyboard_key_notify(struct wl_listener *listener, void *data)
 | 
			
		|||
			/* cycle to next */
 | 
			
		||||
			server->cycle_view =
 | 
			
		||||
				desktop_cycle_view(server, server->cycle_view);
 | 
			
		||||
			osd_update(server);
 | 
			
		||||
			damage_all_outputs(server);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										262
									
								
								src/osd.c
									
										
									
									
									
								
							
							
						
						
									
										262
									
								
								src/osd.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,118 +1,176 @@
 | 
			
		|||
#include "config.h"
 | 
			
		||||
#include <cairo.h>
 | 
			
		||||
#include <drm_fourcc.h>
 | 
			
		||||
#include <pango/pangocairo.h>
 | 
			
		||||
#include <wlr/util/log.h>
 | 
			
		||||
#include "config/keybind.h"
 | 
			
		||||
#include "common/buf.h"
 | 
			
		||||
#include "common/font.h"
 | 
			
		||||
#include "config/rcxml.h"
 | 
			
		||||
#include "labwc.h"
 | 
			
		||||
 | 
			
		||||
#if HAVE_XWAYLAND
 | 
			
		||||
#define OSD_ITEM_HEIGHT (20)
 | 
			
		||||
#define OSD_ITEM_WIDTH (600)
 | 
			
		||||
#define OSD_ITEM_PADDING (10)
 | 
			
		||||
#define OSD_BORDER_WIDTH (6)
 | 
			
		||||
#define OSD_TAB1 (120)
 | 
			
		||||
#define OSD_TAB2 (300)
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
set_source(cairo_t *cairo, float *c)
 | 
			
		||||
{
 | 
			
		||||
	cairo_set_source_rgba(cairo, c[0], c[1], c[2], c[3]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* is title different from app_id/class? */
 | 
			
		||||
static int
 | 
			
		||||
xwl_nr_parents(struct view *view)
 | 
			
		||||
is_title_different(struct view *view)
 | 
			
		||||
{
 | 
			
		||||
	struct wlr_xwayland_surface *s = view->xwayland_surface;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
 | 
			
		||||
	if (!s) {
 | 
			
		||||
		wlr_log(WLR_ERROR, "no xwayland surface");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	while (s->parent) {
 | 
			
		||||
		s = s->parent;
 | 
			
		||||
		++i;
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
show_one_xdg_view(struct view *view)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "XDG  ");
 | 
			
		||||
	switch (view->xdg_surface->role) {
 | 
			
		||||
	case WLR_XDG_SURFACE_ROLE_NONE:
 | 
			
		||||
		fprintf(stderr, "- ");
 | 
			
		||||
		break;
 | 
			
		||||
	case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
 | 
			
		||||
		fprintf(stderr, "0 ");
 | 
			
		||||
		break;
 | 
			
		||||
	case WLR_XDG_SURFACE_ROLE_POPUP:
 | 
			
		||||
		fprintf(stderr, "? ");
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	fprintf(stderr, "                   %p %s", (void *)view,
 | 
			
		||||
		view->xdg_surface->toplevel->app_id);
 | 
			
		||||
	fprintf(stderr, "  {%d, %d, %d, %d}\n", view->x, view->y, view->w,
 | 
			
		||||
		view->h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	switch (view->type) {
 | 
			
		||||
	case LAB_XDG_SHELL_VIEW:
 | 
			
		||||
		return strcmp(view->impl->get_string_prop(view, "title"),
 | 
			
		||||
			view->impl->get_string_prop(view, "app_id"));
 | 
			
		||||
#if HAVE_XWAYLAND
 | 
			
		||||
static void
 | 
			
		||||
show_one_xwl_view(struct view *view)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "XWL  ");
 | 
			
		||||
	fprintf(stderr, "%d ", xwl_nr_parents(view));
 | 
			
		||||
	fprintf(stderr, "     %d      ",
 | 
			
		||||
		wl_list_length(&view->xwayland_surface->children));
 | 
			
		||||
	if (view->mapped) {
 | 
			
		||||
		fprintf(stderr, "Y");
 | 
			
		||||
	} else {
 | 
			
		||||
		fprintf(stderr, "-");
 | 
			
		||||
	}
 | 
			
		||||
	fprintf(stderr, "      %p %s {%d,%d,%d,%d}\n", (void *)view,
 | 
			
		||||
		view->xwayland_surface->class, view->xwayland_surface->x,
 | 
			
		||||
		view->xwayland_surface->y, view->xwayland_surface->width,
 | 
			
		||||
		view->xwayland_surface->height);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Other variables to consider printing:
 | 
			
		||||
	 *
 | 
			
		||||
	 * view->mapped,
 | 
			
		||||
	 * view->xwayland_surface->override_redirect,
 | 
			
		||||
	 * wlr_xwayland_or_surface_wants_focus(view->xwayland_surface));
 | 
			
		||||
	 * view->xwayland_surface->saved_width,
 | 
			
		||||
	 * view->xwayland_surface->saved_height);
 | 
			
		||||
	 * view->xwayland_surface->surface->sx,
 | 
			
		||||
	 * view->xwayland_surface->surface->sy);
 | 
			
		||||
	 */
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dbg_show_one_view(struct view *view)
 | 
			
		||||
{
 | 
			
		||||
	if (!view->surface) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!view->mapped && !view->minimized) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (view->type == LAB_XDG_SHELL_VIEW) {
 | 
			
		||||
		show_one_xdg_view(view);
 | 
			
		||||
#if HAVE_XWAYLAND
 | 
			
		||||
	} else if (view->type == LAB_XWAYLAND_VIEW) {
 | 
			
		||||
		show_one_xwl_view(view);
 | 
			
		||||
	case LAB_XWAYLAND_VIEW:
 | 
			
		||||
		return strcmp(view->impl->get_string_prop(view, "title"),
 | 
			
		||||
			view->xwayland_surface->class);
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dbg_show_views(struct server *server)
 | 
			
		||||
static const char *
 | 
			
		||||
get_formatted_app_id(struct view *view)
 | 
			
		||||
{
 | 
			
		||||
	struct view *view;
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "---\n");
 | 
			
		||||
	fprintf(stderr, "TYPE NR_PNT NR_CLD MAPPED VIEW-POINTER   NAME\n");
 | 
			
		||||
	wl_list_for_each_reverse (view, &server->views, link) {
 | 
			
		||||
		dbg_show_one_view(view);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
dbg_show_keybinds()
 | 
			
		||||
{
 | 
			
		||||
	struct keybind *keybind;
 | 
			
		||||
	wl_list_for_each_reverse (keybind, &rc.keybinds, link) {
 | 
			
		||||
		printf("KEY=%s-", keybind->action);
 | 
			
		||||
		for (size_t i = 0; i < keybind->keysyms_len; i++) {
 | 
			
		||||
			printf("    %d\n", keybind->keysyms[i]);
 | 
			
		||||
	char *s = (char *)view->impl->get_string_prop(view, "app_id");
 | 
			
		||||
	/* remove the first two nodes of 'org.' strings */
 | 
			
		||||
	if (!strncmp(s, "org.", 4)) {
 | 
			
		||||
		char *p = s + 4;
 | 
			
		||||
		p = strchr(p, '.');
 | 
			
		||||
		if (p) {
 | 
			
		||||
			return ++p;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
get_osd_height(struct wl_list *views)
 | 
			
		||||
{
 | 
			
		||||
	int height = 0;
 | 
			
		||||
	struct view *view;
 | 
			
		||||
	wl_list_for_each(view, views, link) {
 | 
			
		||||
		if (!isfocusable(view)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		height += OSD_ITEM_HEIGHT;
 | 
			
		||||
	}
 | 
			
		||||
	height += 2 * OSD_BORDER_WIDTH;
 | 
			
		||||
	return height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
osd_update(struct server *server)
 | 
			
		||||
{
 | 
			
		||||
	struct wlr_renderer *renderer = server->renderer;
 | 
			
		||||
	int w = OSD_ITEM_WIDTH + 2 * OSD_BORDER_WIDTH;
 | 
			
		||||
	int h = get_osd_height(&server->views);
 | 
			
		||||
 | 
			
		||||
	cairo_surface_t *surf =
 | 
			
		||||
		cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
 | 
			
		||||
	cairo_t *cairo = cairo_create(surf);
 | 
			
		||||
 | 
			
		||||
	/* background */
 | 
			
		||||
	set_source(cairo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
 | 
			
		||||
	cairo_rectangle(cairo, 0, 0, w, h);
 | 
			
		||||
	cairo_fill(cairo);
 | 
			
		||||
 | 
			
		||||
	/* highlight current application */
 | 
			
		||||
	int y = OSD_BORDER_WIDTH;
 | 
			
		||||
	struct view *view;
 | 
			
		||||
	wl_list_for_each(view, &server->views, link) {
 | 
			
		||||
		if (!isfocusable(view)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (view == server->cycle_view) {
 | 
			
		||||
			set_source(cairo, (float[4]){0.3f, 0.3f, 0.3f, 0.5f});
 | 
			
		||||
			cairo_rectangle(cairo, OSD_BORDER_WIDTH, y,
 | 
			
		||||
				OSD_ITEM_WIDTH, OSD_ITEM_HEIGHT);
 | 
			
		||||
			cairo_fill(cairo);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		y += OSD_ITEM_HEIGHT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* text */
 | 
			
		||||
	set_source(cairo, (float[4]){0.0f, 0.0f, 0.0f, 1.0f});
 | 
			
		||||
	PangoLayout *layout = pango_cairo_create_layout(cairo);
 | 
			
		||||
	pango_layout_set_width(layout, w * PANGO_SCALE);
 | 
			
		||||
	pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
 | 
			
		||||
 | 
			
		||||
	/* TODO: use font description from config */
 | 
			
		||||
	PangoFontDescription *desc =
 | 
			
		||||
		pango_font_description_from_string("sans 10");
 | 
			
		||||
	pango_layout_set_font_description(layout, desc);
 | 
			
		||||
	pango_font_description_free(desc);
 | 
			
		||||
 | 
			
		||||
	PangoTabArray *tabs = pango_tab_array_new_with_positions(2, TRUE,
 | 
			
		||||
		PANGO_TAB_LEFT, OSD_TAB1, PANGO_TAB_LEFT, OSD_TAB2);
 | 
			
		||||
	pango_layout_set_tabs(layout, tabs);
 | 
			
		||||
	pango_tab_array_free(tabs);
 | 
			
		||||
 | 
			
		||||
	pango_cairo_update_layout(cairo, layout);
 | 
			
		||||
 | 
			
		||||
	struct buf buf;
 | 
			
		||||
	buf_init(&buf);
 | 
			
		||||
	y = OSD_BORDER_WIDTH;
 | 
			
		||||
 | 
			
		||||
	/* vertically center align */
 | 
			
		||||
	y += (OSD_ITEM_HEIGHT - font_height("sans 10")) / 2;
 | 
			
		||||
 | 
			
		||||
	wl_list_for_each(view, &server->views, link) {
 | 
			
		||||
		if (!isfocusable(view)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		buf.len = 0;
 | 
			
		||||
		cairo_move_to(cairo, OSD_BORDER_WIDTH + OSD_ITEM_PADDING, y);
 | 
			
		||||
 | 
			
		||||
		switch (view->type) {
 | 
			
		||||
		case LAB_XDG_SHELL_VIEW:
 | 
			
		||||
			buf_add(&buf, "[xdg-shell]\t");
 | 
			
		||||
			buf_add(&buf, get_formatted_app_id(view));
 | 
			
		||||
			buf_add(&buf, "\t");
 | 
			
		||||
			break;
 | 
			
		||||
#if HAVE_XWAYLAND
 | 
			
		||||
		case LAB_XWAYLAND_VIEW:
 | 
			
		||||
			buf_add(&buf, "[xwayland]\t");
 | 
			
		||||
			buf_add(&buf, view->impl->get_string_prop(view, "class"));
 | 
			
		||||
			buf_add(&buf, "\t");
 | 
			
		||||
			break;
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (is_title_different(view)) {
 | 
			
		||||
			buf_add(&buf, view->impl->get_string_prop(view, "title"));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pango_layout_set_text(layout, buf.buf, -1);
 | 
			
		||||
		pango_cairo_show_layout(cairo, layout);
 | 
			
		||||
		y += OSD_ITEM_HEIGHT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_object_unref(layout);
 | 
			
		||||
 | 
			
		||||
	/* convert to wlr_texture */
 | 
			
		||||
	cairo_surface_flush(surf);
 | 
			
		||||
	unsigned char *data = cairo_image_surface_get_data(surf);
 | 
			
		||||
	struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
 | 
			
		||||
		DRM_FORMAT_ARGB8888, cairo_image_surface_get_stride(surf),
 | 
			
		||||
		w, h, data);
 | 
			
		||||
 | 
			
		||||
	cairo_destroy(cairo);
 | 
			
		||||
	cairo_surface_destroy(surf);
 | 
			
		||||
	if (server->osd) {
 | 
			
		||||
		wlr_texture_destroy(server->osd);
 | 
			
		||||
	}
 | 
			
		||||
	server->osd = texture;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										23
									
								
								src/output.c
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								src/output.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -460,6 +460,28 @@ render_texture_helper(struct output *output, pixman_region32_t *output_damage,
 | 
			
		|||
		matrix);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
render_osd(struct output *output, pixman_region32_t *damage,
 | 
			
		||||
		struct server *server)
 | 
			
		||||
{
 | 
			
		||||
	if (!server->osd) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* show on screen display (osd) on all outputs */
 | 
			
		||||
	struct output *o;
 | 
			
		||||
	wl_list_for_each(o, &server->outputs, link) {
 | 
			
		||||
		struct wlr_box usable = output_usable_area_in_layout_coords(o);
 | 
			
		||||
		struct wlr_box box = {
 | 
			
		||||
			.x = usable.x + (usable.width - server->osd->width) / 2,
 | 
			
		||||
			.y = usable.y + (usable.height - server->osd->height) / 2,
 | 
			
		||||
			.width = server->osd->width,
 | 
			
		||||
			.height = server->osd->height,
 | 
			
		||||
		};
 | 
			
		||||
		render_texture_helper(output, damage, &box, server->osd);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
isbutton(enum ssd_part_type type)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -673,6 +695,7 @@ output_render(struct output *output, pixman_region32_t *damage)
 | 
			
		|||
	/* 'alt-tab' border */
 | 
			
		||||
	if (output->server->cycle_view) {
 | 
			
		||||
		render_cycle_box(output, damage, output->server->cycle_view);
 | 
			
		||||
		render_osd(output, damage, output->server);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	render_layer_toplevel(output, damage,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -265,7 +265,10 @@ xdg_toplevel_view_get_string_prop(struct view *view, const char *prop)
 | 
			
		|||
	if (!strcmp(prop, "title")) {
 | 
			
		||||
		return view->xdg_surface->toplevel->title;
 | 
			
		||||
	}
 | 
			
		||||
	return "none";
 | 
			
		||||
	if (!strcmp(prop, "app_id")) {
 | 
			
		||||
		return view->xdg_surface->toplevel->app_id;
 | 
			
		||||
	}
 | 
			
		||||
	return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,7 +134,10 @@ get_string_prop(struct view *view, const char *prop)
 | 
			
		|||
	if (!strcmp(prop, "title")) {
 | 
			
		||||
		return view->xwayland_surface->title;
 | 
			
		||||
	}
 | 
			
		||||
	return "none";
 | 
			
		||||
	if (!strcmp(prop, "class")) {
 | 
			
		||||
		return view->xwayland_surface->class;
 | 
			
		||||
	}
 | 
			
		||||
	return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue