Merge branch 'single-pixel-client' into 'main'

clients: Add a new single-pixel-buffer test client

See merge request wayland/weston!2013
This commit is contained in:
Derek Foreman 2026-03-20 04:24:54 -05:00
commit 24e07d9f1f
2 changed files with 274 additions and 0 deletions

View file

@ -432,6 +432,13 @@ demo_clients = [
viewporter_protocol_c,
]
},
{
'basename': 'single-pixel',
'add_sources': [
single_pixel_buffer_v1_client_protocol_h,
single_pixel_buffer_v1_protocol_c,
],
},
{ 'basename': 'smoke' },
{ 'basename': 'stacking' },
{

267
clients/single-pixel.c Normal file
View file

@ -0,0 +1,267 @@
/*
* Copyright (C) 2024 SUSE Software Solutions Germany GmbH
* Copyright © 2026 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "single-pixel-buffer-v1-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "window.h"
struct pixel_color {
uint32_t r;
uint32_t g;
uint32_t b;
uint32_t a;
};
struct client {
struct display *display;
struct window *window;
struct widget *parent_widget;
struct widget *widget;
struct wp_single_pixel_buffer_manager_v1 *single_pixel_manager;
struct wp_viewporter *viewporter;
struct wp_viewport *viewport;
struct pixel_color pixel_color;
};
static bool opt_help = false;
static uint32_t opt_width = 250;
static uint32_t opt_height = 250;
static const char *opt_r = NULL;
static const char *opt_g = NULL;
static const char *opt_b = NULL;
static const char *opt_a = NULL;
static const struct weston_option cli_options[] = {
{ WESTON_OPTION_BOOLEAN, "help", 0, &opt_help },
{ WESTON_OPTION_UNSIGNED_INTEGER, "width", 'w', &opt_width },
{ WESTON_OPTION_UNSIGNED_INTEGER, "height", 'h', &opt_height },
{ WESTON_OPTION_STRING, 0, 'R', &opt_r },
{ WESTON_OPTION_STRING, 0, 'G', &opt_g },
{ WESTON_OPTION_STRING, 0, 'B', &opt_b },
{ WESTON_OPTION_STRING, 0, 'A', &opt_a },
};
static bool
validate_color(const char *c, uint32_t *dest, uint32_t fallback)
{
char *end;
double value;
if (!c) {
*dest = fallback;
return true;
}
value = strtod(c, &end);
if (value < 0.0 || value > 1.0 || *end != '\0') {
fprintf(stderr, "Validating color failed, it should be between 0.0 and 1.0\n");
return false;
}
*dest = value * UINT32_MAX;
return true;
}
static bool validate_options(struct client *client)
{
return validate_color(opt_r, &client->pixel_color.r, 0) &&
validate_color(opt_g, &client->pixel_color.g, 0) &&
validate_color(opt_b, &client->pixel_color.b, 0) &&
validate_color(opt_a, &client->pixel_color.a, UINT32_MAX);
}
static void
usage(const char *program_name, int exit_code)
{
fprintf(stderr, "Usage: %s [OPTIONS]\n", program_name);
fprintf(stderr, " --help\n");
fprintf(stderr, " --width or -w\n");
fprintf(stderr, " --height or -h\n");
fprintf(stderr, " -R (0.0 to 1.0)\n");
fprintf(stderr, " -G (0.0 to 1.0)\n");
fprintf(stderr, " -B (0.0 to 1.0)\n");
fprintf(stderr, " -A (0.0 to 1.0)\n");
exit(exit_code);
}
static void
global_handler(struct display *display, uint32_t name,
const char *interface, uint32_t version, void *data)
{
struct client *client = data;
struct wl_surface *surface = widget_get_wl_surface(client->widget);
if (strcmp(interface, wp_single_pixel_buffer_manager_v1_interface.name) == 0) {
client->single_pixel_manager =
display_bind(display, name,
&wp_single_pixel_buffer_manager_v1_interface, 1);
} else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
client->viewporter = display_bind(display, name,
&wp_viewporter_interface, 1);
client->viewport = wp_viewporter_get_viewport(client->viewporter, surface);
}
}
static void
client_destroy(struct client *client)
{
if (client->single_pixel_manager)
wp_single_pixel_buffer_manager_v1_destroy(client->single_pixel_manager);
if (client->viewport)
wp_viewport_destroy(client->viewport);
if (client->viewporter)
wp_viewporter_destroy(client->viewporter);
if (client->widget)
widget_destroy(client->widget);
if (client->parent_widget)
widget_destroy(client->parent_widget);
if (client->window)
window_destroy(client->window);
if (client->display)
display_destroy(client->display);
free(client);
}
static void
resize_handler(struct widget *parent_widget, int32_t width, int32_t height, void *data)
{
struct client *client = data;
struct rectangle allocation;
struct wl_surface *surface = widget_get_wl_surface(client->widget);
struct wl_subsurface *subsurface = widget_get_wl_subsurface(client->widget);
widget_get_allocation(parent_widget, &allocation);
wl_subsurface_set_position(subsurface, allocation.x, allocation.y);
wp_viewport_set_destination(client->viewport, width, height);
wl_surface_commit(surface);
}
static void
set_empty_input_region(struct client *client, struct widget *widget)
{
struct wl_region *region;
struct wl_compositor *compositor;
struct wl_surface *surface = widget_get_wl_surface(widget);
compositor = display_get_compositor(client->display);
region = wl_compositor_create_region(compositor);
wl_surface_set_input_region(surface, region);
wl_region_destroy(region);
}
static void
buffer_release(void *data, struct wl_buffer *buffer)
{
wl_buffer_destroy(buffer);
}
static const struct wl_buffer_listener buffer_listener = {
buffer_release
};
static void
set_single_pixel(struct client *client, struct widget *widget)
{
struct wl_surface *surface = widget_get_wl_surface(widget);
struct wl_buffer *buffer =
wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(client->single_pixel_manager,
client->pixel_color.r,
client->pixel_color.g,
client->pixel_color.b,
client->pixel_color.a);
wl_buffer_add_listener(buffer, &buffer_listener, NULL);
wl_surface_attach(surface, buffer, 0, 0);
}
int
main(int argc, char *argv[])
{
struct client *client;
if (parse_options(cli_options, ARRAY_LENGTH(cli_options), &argc, argv) > 1)
usage(argv[0], EXIT_FAILURE);
if (opt_help)
usage(argv[0], EXIT_SUCCESS);
client = xzalloc(sizeof *client);
if (!validate_options(client)) {
client_destroy(client);
exit(EXIT_FAILURE);
}
client->display = display_create(&argc, argv);
if (!client->display) {
client_destroy(client);
exit(EXIT_FAILURE);
}
client->window = window_create(client->display);
client->parent_widget = window_frame_create(client->window, client);
client->widget = window_add_subsurface(client->window, client, SUBSURFACE_SYNCHRONIZED);
display_set_user_data(client->display, client);
display_set_global_handler(client->display, global_handler);
wl_display_roundtrip(display_get_display(client->display));
window_unset_shadow(client->window);
window_set_title(client->window, "Single Pixel Buffer");
window_set_appid(client->window, "org.freedesktop.weston.single-pixel-buffer");
/* The first resize call sets the min size,
* setting 0, 0 sets a default size */
window_schedule_resize(client->window, 0, 0);
window_schedule_resize(client->window, opt_width, opt_height);
widget_set_resize_handler(client->parent_widget, resize_handler);
widget_set_use_cairo(client->widget, 0);
set_empty_input_region(client, client->widget);
set_single_pixel(client, client->widget);
display_run(client->display);
client_destroy(client);
return 0;
}