From e577b6143c140983d31e527b39045cb58451a59e Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Fri, 28 Jul 2023 14:54:30 +0800 Subject: [PATCH 66/95] desktop-shell: Support desktop launchers Tested on RK3588 EVB with: 1/ sed -i "s/\[\(launcher\)/[desktop-\1/" /etc/xdg/weston/weston.ini.d/* 2/ killall weston; weston& Signed-off-by: Jeffy Chen --- clients/desktop-shell.c | 700 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 688 insertions(+), 12 deletions(-) diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index f88f63e..fb32632 100644 --- a/clients/desktop-shell.c +++ b/clients/desktop-shell.c @@ -128,6 +128,8 @@ struct background { char *image; int type; uint32_t color; + + struct wl_list launcher_group_list; }; struct output { @@ -135,6 +137,8 @@ struct output { uint32_t server_output_id; struct wl_list link; + char *name; + int x; int y; struct panel *panel; @@ -164,6 +168,72 @@ struct panel_clock { time_t refresh_timer; }; +enum gravity { + CENTER, + LEFT_TOP, + LEFT_BOTTOM, + RIGHT_TOP, + RIGHT_BOTTOM, +}; + +struct desktop_launcher_group { + struct widget *widget; + struct background *background; + struct wl_list link; + struct wl_list launcher_list; + + char *name; + uint32_t row; + uint32_t column; + + struct desktop_launcher_group *left_of; + struct desktop_launcher_group *right_of; + struct desktop_launcher_group *top_of; + struct desktop_launcher_group *bottom_of; + + double padding_left; + double padding_right; + double padding_top; + double padding_bottom; + + double max_width; + double max_height; + + enum gravity gravity; + + bool fit_size; + + double launcher_ratio; + double launcher_width; + double launcher_height; + + double spacing_x; + double spacing_y; + + uint32_t color; + + bool show_text; + uint32_t text_color; + double text_scale; + double text_alpha; + + double icon_alpha; + uint32_t highlight_color; +}; + +struct desktop_launcher { + struct widget *widget; + struct desktop_launcher_group *group; + cairo_surface_t *icon; + int focused; + char *path; + char *displayname; + struct wl_list link; + struct custom_env env; + char * const *argp; + char * const *envp; +}; + struct unlock_dialog { struct window *window; struct widget *widget; @@ -248,7 +318,7 @@ check_desktop_ready(struct window *window) } static void -panel_launcher_activate(struct panel_launcher *widget) +launcher_activate(char * const *argp, char * const *envp) { pid_t pid; @@ -264,8 +334,8 @@ panel_launcher_activate(struct panel_launcher *widget) if (setsid() == -1) exit(EXIT_FAILURE); - if (execve(widget->argp[0], widget->argp, widget->envp) < 0) { - fprintf(stderr, "execl '%s' failed: %s\n", widget->argp[0], + if (execve(argp[0], argp, envp) < 0) { + fprintf(stderr, "execl '%s' failed: %s\n", argp[0], strerror(errno)); exit(1); } @@ -383,8 +453,7 @@ panel_launcher_button_handler(struct widget *widget, launcher = widget_get_user_data(widget); widget_schedule_redraw(widget); if (state == WL_POINTER_BUTTON_STATE_RELEASED) - panel_launcher_activate(launcher); - + launcher_activate(launcher->argp, launcher->envp); } static void @@ -409,7 +478,7 @@ panel_launcher_touch_up_handler(struct widget *widget, struct input *input, launcher = widget_get_user_data(widget); launcher->focused = 0; widget_schedule_redraw(widget); - panel_launcher_activate(launcher); + launcher_activate(launcher->argp, launcher->envp); } static void @@ -443,7 +512,7 @@ panel_launcher_tablet_tool_up_handler(struct widget *widget, struct panel_launcher *launcher; launcher = widget_get_user_data(widget); - panel_launcher_activate(launcher); + launcher_activate(launcher->argp, launcher->envp); } static void @@ -459,7 +528,7 @@ panel_launcher_tablet_tool_button_handler(struct widget *widget, launcher = widget_get_user_data(widget); if (state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED) - panel_launcher_activate(launcher); + launcher_activate(launcher->argp, launcher->envp); } static int clock_timer_reset(struct panel_clock *clock); @@ -907,6 +976,194 @@ background_draw(struct widget *widget, void *data) check_desktop_ready(background->window); } +#define PERCENT_VALUE(v, size) ((int)((v) >= 1 ? (v) : (v) * (size))) + +static void +desktop_group_resize(struct desktop_launcher_group *group) +{ + struct desktop_launcher *launcher; + struct rectangle allocation; + int row, column, width, height, spacing_x, spacing_y, count; + + widget_get_allocation(group->widget, &allocation); + + width = PERCENT_VALUE(group->launcher_width, allocation.width); + height = PERCENT_VALUE(group->launcher_height, allocation.height); + + row = group->row; + column = group->column; + + if (!width) { + if (group->launcher_ratio && height) + width = height * group->launcher_ratio; + else if (!row) + width = 200; + else if (group->spacing_x >= 1) + width = allocation.width / row - group->spacing_x; + else + width = allocation.width / row / (1 + group->spacing_x); + } + spacing_x = PERCENT_VALUE(group->spacing_x ? group->spacing_x : 0.1, + width); + row = MAX(row ? row : allocation.width / (width + spacing_x), 1); + + if (!height) { + if (group->launcher_ratio) + height = width / group->launcher_ratio; + else if (!column) + height = width / (group->show_text ? 0.8 : 1); + else if (group->spacing_y >= 1) + height = allocation.height / column - group->spacing_y; + else + height = allocation.height / column / (1 + group->spacing_y); + } + spacing_y = PERCENT_VALUE(group->spacing_y ? group->spacing_y : 0.1, + height); + column = MAX(column ? column : allocation.height / (height + spacing_y), 1); + + /* Auto spacing */ + if (!group->spacing_x) + spacing_x = allocation.width / row - width; + if (!group->spacing_y) + spacing_y = allocation.height / column - height; + + /* Fit group size */ + count = wl_list_length(&group->launcher_list); + if (count < row) + row = count; + if ((count + row - 1) / row < column) + column = (count + row - 1) / row; + allocation.width = (width + spacing_x) * row; + allocation.height = (height + spacing_y) * column; + + count = 0; + wl_list_for_each(launcher, &group->launcher_list, link) { + int x, y; + + x = allocation.x + spacing_x / 2 + + allocation.width * (count % row) / row; + y = allocation.y + spacing_y / 2 + + allocation.height * (count / row) / column; + + widget_set_allocation(launcher->widget, x, y, width, height); + + count++; + if (count >= row * column) + break; + } + + /* Apply fit size */ + if (group->fit_size) + widget_set_allocation(group->widget, + allocation.x, allocation.y, + allocation.width, allocation.height); +} + +static void +background_resize(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct desktop_launcher_group *group; + struct background *background = data; + struct panel *panel = background->owner->panel; + int start_x, start_y; + + /* Background is always at (0,0) */ + start_x = start_y = 0; + + /* Remove panel area */ + if (panel) { + struct desktop *desktop = background->owner->desktop; + struct rectangle allocation; + + widget_get_allocation(panel->widget, &allocation); + + switch (desktop->panel_position) { + case WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP: + start_y += allocation.height; + /* fallthrough */ + case WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM: + height -= allocation.height; + break; + case WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT: + start_x += allocation.width; + /* fallthrough */ + case WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT: + width -= allocation.width; + break; + } + } + + wl_list_for_each(group, &background->launcher_group_list, link) { + struct rectangle allocation; + int left, right, top, bottom, x, y, w, h; + int max_width, max_height; + + left = top = 0; + right = width; + bottom = height; + + if (group->left_of) { + widget_get_allocation(group->left_of->widget, + &allocation); + right = allocation.x - start_x; + } + if (group->right_of) { + widget_get_allocation(group->right_of->widget, + &allocation); + left = allocation.x + allocation.width - start_x; + } + if (group->top_of) { + widget_get_allocation(group->top_of->widget, + &allocation); + bottom = allocation.y - start_y; + } + if (group->bottom_of) { + widget_get_allocation(group->bottom_of->widget, + &allocation); + top = allocation.y + allocation.height - start_y; + } + + left += PERCENT_VALUE(group->padding_left, width); + right -= PERCENT_VALUE(group->padding_right, width); + top += PERCENT_VALUE(group->padding_top, height); + bottom -= PERCENT_VALUE(group->padding_bottom, height); + + max_width = PERCENT_VALUE(group->max_width, width); + max_height = PERCENT_VALUE(group->max_height, height); + + w = MAX(MIN(max_width ? max_width : width, right - left),0); + h = MAX(MIN(max_height ? max_height : height, bottom - top),0); + + switch (group->gravity) { + case LEFT_TOP: + x = left; + y = top; + break; + case LEFT_BOTTOM: + x = left; + y = bottom - h; + break; + case RIGHT_TOP: + x = right - w; + y = top; + break; + case RIGHT_BOTTOM: + x = right - w; + y = bottom - h; + break; + default: + x = left + (right - left - w) / 2; + y = top + (bottom - top - h) / 2; + break; + }; + + widget_set_allocation(group->widget, + x + start_x, y + start_y, w, h); + desktop_group_resize(group); + } +} + static void background_destroy(struct background *background); @@ -1195,6 +1452,400 @@ static const struct weston_desktop_shell_listener listener = { desktop_shell_grab_cursor }; +static int +desktop_launcher_enter_handler(struct widget *widget, struct input *input, + float x, float y, void *data) +{ + struct desktop_launcher *launcher = data; + + launcher->focused = 1; + widget_schedule_redraw(widget); + + return CURSOR_LEFT_PTR; +} + +static void +desktop_launcher_leave_handler(struct widget *widget, + struct input *input, void *data) +{ + struct desktop_launcher *launcher = data; + + launcher->focused = 0; + widget_schedule_redraw(widget); +} + +static void +desktop_launcher_button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, + enum wl_pointer_button_state state, void *data) +{ + struct desktop_launcher *launcher; + + launcher = widget_get_user_data(widget); + widget_schedule_redraw(widget); + if (state == WL_POINTER_BUTTON_STATE_RELEASED) + launcher_activate(launcher->argp, launcher->envp); + +} + +static void +desktop_launcher_touch_down_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + float x, float y, void *data) +{ + struct desktop_launcher *launcher; + + launcher = widget_get_user_data(widget); + launcher->focused = 1; + widget_schedule_redraw(widget); +} + +static void +desktop_launcher_touch_up_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + void *data) +{ + struct desktop_launcher *launcher; + + launcher = widget_get_user_data(widget); + launcher->focused = 0; + widget_schedule_redraw(widget); + launcher_activate(launcher->argp, launcher->envp); +} + +static void +desktop_launcher_redraw_handler(struct widget *widget, void *data) +{ + struct desktop_launcher *launcher = data; + struct desktop_launcher_group *group = launcher->group; + struct rectangle allocation; + cairo_t *cr; + int x, y, icon_w, icon_h; + double scale; + + widget_get_allocation(widget, &allocation); + + /* Draw text */ + if (group->show_text) { + cairo_text_extents_t extents; + double text_alpha = (group->text_color >> 24) / 255.0; + int spacing, text_height, text_offset; + + if (group->spacing_y > 1) + spacing = group->spacing_y / 2; + else if (group->spacing_y) + spacing = group->spacing_y * allocation.height / 2; + else + spacing = allocation.height * 0.05; + + cr = widget_cairo_create(group->widget); + cairo_rectangle(cr, allocation.x, allocation.y, + allocation.width, allocation.height); + cairo_clip(cr); + + cairo_set_font_size(cr, 40 * group->text_scale); + + cairo_text_extents(cr, "j", &extents); + text_height = extents.height; + text_offset = extents.height + extents.y_bearing; + + cairo_text_extents(cr, launcher->displayname, &extents); + + x = allocation.x + + MAX((allocation.width - extents.width) / 2, 0); + y = allocation.y + MAX(allocation.height - text_height, 0) + + text_height - text_offset; + + cairo_move_to(cr, x + 1, y + 1); + cairo_set_source_rgba(cr, 0, 0, 0, text_alpha); + cairo_show_text(cr, launcher->displayname); + cairo_move_to(cr, x, y); + set_hex_color(cr, group->text_color); + + cairo_show_text(cr, launcher->displayname); + cairo_destroy(cr); + + allocation.height -= text_height + spacing; + } + + /* Draw icon */ + cr = widget_cairo_create(group->widget); + + icon_w = cairo_image_surface_get_width(launcher->icon); + icon_h = cairo_image_surface_get_height(launcher->icon); + + scale = MIN(1.0 * allocation.width / icon_w, + 1.0 * allocation.height / icon_h); + + x = allocation.x + (allocation.width - icon_w * scale) / 2; + y = allocation.y + (allocation.height - icon_h * scale) / 2; + + x = round(x / scale); + y = round(y / scale); + cairo_scale(cr, scale, scale); + + cairo_set_source_surface(cr, launcher->icon, x, y); + cairo_paint_with_alpha(cr, group->icon_alpha); + + if (launcher->focused) { + set_hex_color(cr, group->highlight_color); + cairo_mask_surface(cr, launcher->icon, x, y); + } + + cairo_destroy(cr); +} + +static void +desktop_group_redraw(struct widget *widget, void *data) +{ + cairo_t *cr; + struct desktop_launcher_group *group = data; + struct rectangle allocation; + + if (wl_list_empty(&group->launcher_list)) + return; + + widget_get_allocation(widget, &allocation); + + cr = widget_cairo_create(group->widget); + cairo_rectangle(cr, allocation.x, allocation.y, + allocation.width, allocation.height); + set_hex_color(cr, group->color); + cairo_fill(cr); + cairo_destroy(cr); +} + +static void +desktop_add_launcher(struct desktop_launcher_group *group, + const char *icon, const char *path, const char *displayname) +{ + struct desktop_launcher *launcher; + + launcher = xzalloc(sizeof *launcher); + launcher->icon = load_icon_or_fallback(icon); + launcher->path = xstrdup(path); + launcher->displayname = xstrdup(displayname); + + custom_env_init_from_environ(&launcher->env); + custom_env_add_from_exec_string(&launcher->env, launcher->path); + launcher->envp = custom_env_get_envp(&launcher->env); + launcher->argp = custom_env_get_argp(&launcher->env); + + launcher->group = group; + wl_list_insert(group->launcher_list.prev, &launcher->link); + + launcher->widget = widget_add_widget(group->widget, launcher); + widget_set_enter_handler(launcher->widget, + desktop_launcher_enter_handler); + widget_set_leave_handler(launcher->widget, + desktop_launcher_leave_handler); + widget_set_button_handler(launcher->widget, + desktop_launcher_button_handler); + widget_set_touch_down_handler(launcher->widget, + desktop_launcher_touch_down_handler); + widget_set_touch_up_handler(launcher->widget, + desktop_launcher_touch_up_handler); + widget_set_redraw_handler(launcher->widget, + desktop_launcher_redraw_handler); +} + +static void +desktop_add_launchers(struct desktop_launcher_group *group, + struct desktop *desktop) +{ + struct weston_config_section *s; + char *name, *icon, *path, *displayname; + const char *section; + + s = NULL; + while (weston_config_next_section(desktop->config, &s, §ion)) { + if (strcmp(section, "desktop-launcher") != 0) + continue; + + weston_config_section_get_string(s, "group", &name, NULL); + if ((!group->name != !name) || + (name && group->name && strcmp(name, group->name))) { + free(name); + continue; + } + free(name); + + weston_config_section_get_string(s, "icon", &icon, NULL); + weston_config_section_get_string(s, "path", &path, NULL); + weston_config_section_get_string(s, "displayname", &displayname, NULL); + if (displayname == NULL) + displayname = xstrdup(basename(path)); + + if (icon != NULL && path != NULL) { + desktop_add_launcher(group, icon, path, displayname); + } else { + fprintf(stderr, "invalid desktop-launcher section\n"); + } + + free(icon); + free(path); + free(displayname); + } +} + +static struct desktop_launcher_group * +background_find_group(struct background *background, const char *name) +{ + struct desktop_launcher_group *group; + + if (!name) + return NULL; + + wl_list_for_each(group, &background->launcher_group_list, link) { + if (group->name && !strcmp(group->name, name)) + return group; + } + return NULL; +} + +static void +background_add_group(struct background *background, + struct weston_config_section *s, struct desktop *desktop) +{ + struct desktop_launcher_group *group; + char *gravity, *name; + + group = xzalloc(sizeof *group); + group->background = background; + + weston_config_section_get_string(s, "name", &group->name, NULL); + + /* Auto calculated by default */ + weston_config_section_get_uint(s, "row", &group->row, 0); + weston_config_section_get_uint(s, "column", &group->column, 0); + + weston_config_section_get_string(s, "left-to", &name, NULL); + group->left_of = background_find_group(background, name); + if (name && !group->left_of) { + fprintf(stderr, "unknown left-of: %s\n", name); + free(group); + return; + } + + weston_config_section_get_string(s, "right-of", &name, NULL); + group->right_of = background_find_group(background, name); + if (name && !group->right_of) { + fprintf(stderr, "unknown right-of: %s\n", name); + free(group); + return; + } + + weston_config_section_get_string(s, "top-of", &name, NULL); + group->top_of = background_find_group(background, name); + if (name && !group->top_of) { + fprintf(stderr, "unknown top-of: %s\n", name); + free(group); + return; + } + + weston_config_section_get_string(s, "bottom-of", &name, NULL); + group->bottom_of = background_find_group(background, name); + if (name && !group->bottom_of) { + fprintf(stderr, "unknown bottom-of: %s\n", name); + free(group); + return; + } + + weston_config_section_get_double(s, "padding-left", + &group->padding_left, 0.05); + weston_config_section_get_double(s, "padding-right", + &group->padding_right, 0.05); + weston_config_section_get_double(s, "padding-top", + &group->padding_top, 0.05); + weston_config_section_get_double(s, "padding-bottom", + &group->padding_bottom, 0.05); + + weston_config_section_get_double(s, "max-width", &group->max_width, 0); + weston_config_section_get_double(s, "max-height", + &group->max_height, 0); + + weston_config_section_get_string(s, "gravity", &gravity, NULL); + if (gravity) { + if (!strcmp(gravity, "left-top")) + group->gravity = LEFT_TOP; + else if (!strcmp(gravity, "left-bottom")) + group->gravity = LEFT_BOTTOM; + else if (!strcmp(gravity, "right-top")) + group->gravity = RIGHT_TOP; + else if (!strcmp(gravity, "right-bottom")) + group->gravity = RIGHT_BOTTOM; + else + group->gravity = CENTER; + } + + weston_config_section_get_bool(s, "fit-size", &group->fit_size, false); + + weston_config_section_get_double(s, "launcher-ratio", + &group->launcher_ratio, 0); + weston_config_section_get_double(s, "launcher-width", + &group->launcher_width, 0); + weston_config_section_get_double(s, "launcher-height", + &group->launcher_height, 0); + + /* Auto spacing by default */ + weston_config_section_get_double(s, "spacing-x", &group->spacing_x, 0); + weston_config_section_get_double(s, "spacing-y", &group->spacing_y, 0); + + weston_config_section_get_color(s, "color", &group->color, 0x11FFFFFF); + + weston_config_section_get_bool(s, "show-text", + &group->show_text, true); + weston_config_section_get_color(s, "text-color", + &group->text_color, 0xDDFFFFFF); + weston_config_section_get_double(s, "text-scale", + &group->text_scale, 1); + + weston_config_section_get_double(s, "icon-alpha", + &group->icon_alpha, 1); + weston_config_section_get_color(s, "highlight-color", + &group->highlight_color, 0x66FFFFFF); + + group->widget = widget_add_widget(background->widget, group); + widget_set_redraw_handler(group->widget, desktop_group_redraw); + + wl_list_init(&group->launcher_list); + desktop_add_launchers(group, desktop); + + wl_list_insert(background->launcher_group_list.prev, &group->link); +} + +static void +background_add_groups(struct background *background, struct desktop *desktop) +{ + struct weston_config_section *s; + const char *section; + char *name; + int count; + + count = 0; + s = NULL; + while (weston_config_next_section(desktop->config, &s, §ion)) { + if (strcmp(section, "desktop-launcher-group") != 0) + continue; + + weston_config_section_get_string(s, "output", &name, NULL); + if (name && background->owner->name && + strcmp(name, background->owner->name)) { + free(name); + continue; + } + free(name); + + background_add_group(background, s, desktop); + count++; + } + + /* add default launcher group */ + if (!count) + background_add_group(background, NULL, desktop); +} + static void background_destroy(struct background *background) { @@ -1219,6 +1870,7 @@ background_create(struct desktop *desktop, struct output *output) background->widget = window_add_widget(background->window, background); window_set_user_data(background->window, background); widget_set_redraw_handler(background->widget, background_draw); + widget_set_resize_handler(background->widget, background_resize); widget_set_transparent(background->widget, 0); s = weston_config_get_section(desktop->config, "shell", NULL, NULL); @@ -1250,6 +1902,9 @@ background_create(struct desktop *desktop, struct output *output) free(type); + wl_list_init(&background->launcher_group_list); + background_add_groups(background, desktop); + return background; } @@ -1300,6 +1955,7 @@ output_destroy(struct output *output) wl_output_destroy(output->output); wl_list_remove(&output->link); + free(output->name); free(output); } @@ -1381,11 +2037,30 @@ output_handle_scale(void *data, window_set_buffer_scale(output->background->window, scale); } +static void +output_handle_name(void *data, + struct wl_output *wl_output, + const char *name) +{ + struct output *output = data; + + output->name = strdup(name); +} + +static void +output_handle_description(void *data, + struct wl_output *wl_output, + const char *description) +{ +} + static const struct wl_output_listener output_listener = { output_handle_geometry, output_handle_mode, output_handle_done, - output_handle_scale + output_handle_scale, + output_handle_name, + output_handle_description, }; static void @@ -1407,7 +2082,7 @@ output_init(struct output *output, struct desktop *desktop) } static void -create_output(struct desktop *desktop, uint32_t id) +create_output(struct desktop *desktop, uint32_t id, uint32_t version) { struct output *output; @@ -1417,7 +2092,8 @@ create_output(struct desktop *desktop, uint32_t id) output->desktop = desktop; output->output = - display_bind(desktop->display, id, &wl_output_interface, 2); + display_bind(desktop->display, id, &wl_output_interface, + MIN(version, 4)); output->server_output_id = id; wl_output_add_listener(output->output, &output_listener, output); @@ -1499,7 +2175,7 @@ global_handler(struct display *display, uint32_t id, &listener, desktop); } else if (!strcmp(interface, "wl_output")) { - create_output(desktop, id); + create_output(desktop, id, version); } } -- 2.20.1