lua-shell: Add lua-shell

lua-shell is a new meta-shell for Weston. It does nothing in and of
itself, but is defined to be user-scriptable and configurable. A
supplied Lua script will be interpreted and executed by Weston, allowing
full control over window management in response to events.

This includes a shell.lua example script which behaves as a tiling WM.

Co-authored-by: Michael Tretter <m.tretter@pengutronix.de>
Co-authored-by: Marius Vlad <marius.vlad@collabora.com>
Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
This commit is contained in:
Derek Foreman 2021-11-01 19:35:05 +00:00 committed by Marius Vlad
parent cf44b691c6
commit e91eccd709
16 changed files with 3072 additions and 1 deletions

View file

@ -43,7 +43,7 @@
variables: variables:
FDO_UPSTREAM_REPO: wayland/weston FDO_UPSTREAM_REPO: wayland/weston
FDO_REPO_SUFFIX: "$BUILD_OS-$FDO_DISTRIBUTION_VERSION/$BUILD_ARCH" FDO_REPO_SUFFIX: "$BUILD_OS-$FDO_DISTRIBUTION_VERSION/$BUILD_ARCH"
FDO_DISTRIBUTION_TAG: '2025-05-15-vulkan' FDO_DISTRIBUTION_TAG: '2025-05-21-liblua5.4-dev'
include: include:

View file

@ -69,6 +69,7 @@ apt-get -y --no-install-recommends install \
libjack-jackd2-dev \ libjack-jackd2-dev \
libjpeg-dev \ libjpeg-dev \
libjpeg-dev \ libjpeg-dev \
liblua5.4-dev \
libmtdev-dev \ libmtdev-dev \
libpam0g-dev \ libpam0g-dev \
libpango1.0-dev \ libpango1.0-dev \

View file

@ -825,6 +825,12 @@ usage(int error_code)
" --no-input\t\tDont create input devices\n\n"); " --no-input\t\tDont create input devices\n\n");
#endif #endif
#if defined(BUILD_LUA_SHELL)
fprintf(out,
"Options for lua-shell:\n\n"
" --lua-script=script\t\tLua script\n\n");
#endif
exit(error_code); exit(error_code);
} }

2220
lua-shell/lua-shell.c Normal file

File diff suppressed because it is too large Load diff

259
lua-shell/lua-shell.h Normal file
View file

@ -0,0 +1,259 @@
/*
* Copyright 2020-2025 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.
*/
#ifndef WESTON_LUA_SHELL_H
#define WESTON_LUA_SHELL_H
#include <libweston/desktop.h>
#include <libweston/libweston.h>
#include <libweston/config-parser.h>
#include "libweston/shell-utils.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#define LUA_ENUM(x) do {\
lua_pushnumber(shell->lua, x); \
lua_setglobal(shell->lua, #x); \
} while (0)
enum lua_shell_cb_id {
LUA_SHELL_CB_INIT = 0,
LUA_SHELL_CB_KEYBOARD_FOCUS,
LUA_SHELL_CB_OUTPUT_CREATE,
LUA_SHELL_CB_OUTPUT_RESIZED,
LUA_SHELL_CB_OUTPUT_MOVED,
LUA_SHELL_CB_SEAT_CREATE,
LUA_SHELL_CB_SET_XWAYLAND_POSITION,
LUA_SHELL_CB_SURFACE_ADDED,
LUA_SHELL_CB_SURFACE_COMMITTED,
LUA_SHELL_CB_SURFACE_MOVE,
LUA_SHELL_CB_SURFACE_REMOVED,
LUA_SHELL_CB_SURFACE_RESIZE,
LUA_SHELL_CB_SURFACE_FULLSCREEN,
LUA_SHELL_CB_SURFACE_MAXIMIZE,
LUA_SHELL_NUM_CB,
};
struct lua_shell_callback {
const char *name;
uint32_t regid;
};
struct lua_shell {
struct weston_compositor *compositor;
struct weston_desktop *desktop;
struct lua_shell_callback callbacks[LUA_SHELL_NUM_CB];
struct wl_listener destroy_listener;
struct wl_listener output_created_listener;
struct wl_listener output_resized_listener;
struct wl_listener output_moved_listener;
struct wl_listener seat_created_listener;
struct wl_listener transform_listener;
struct wl_list output_list;
struct wl_list seat_list;
struct wl_list layer_list;
struct wl_list surface_list;
struct wl_list view_list;
struct wl_list timer_list;
struct wl_list curtain_list;
struct wl_list binding_list;
const struct weston_xwayland_surface_api *xwayland_surface_api;
struct weston_config *config;
struct lua_State *lua;
};
struct lua_object {
uint32_t lua_regid;
uint32_t lua_private_regid;
};
struct lua_shell_output {
uint32_t lua_regid;
uint32_t lua_private_regid;
struct lua_shell *shell;
struct weston_output *output;
struct wl_listener output_destroy_listener;
struct wl_list link; /** lua_shell::output_list */
};
struct lua_shell_curtain {
uint32_t lua_regid;
uint32_t lua_private_regid;
struct lua_shell *shell;
struct lua_shell_view *view;
struct weston_curtain_params params;
struct weston_curtain *curtain;
char *name;
struct wl_list link; /** lua_shell:curtain_list */
};
struct lua_shell_surface {
uint32_t lua_regid;
uint32_t lua_private_regid;
struct lua_shell *shell;
struct weston_desktop_surface *desktop_surface;
struct lua_shell_output *shoutput;
struct wl_listener output_destroy_listener;
struct wl_signal destroy_signal;
struct wl_listener parent_destroy_listener;
struct lua_shell_surface *parent;
struct wl_list view_list;
struct wl_list link; /** lua_shell::surface_list */
};
struct lua_shell_view {
uint32_t lua_regid;
uint32_t lua_private_regid;
struct lua_shell *shell;
bool is_desktop_surface;
struct lua_shell_surface *surface;
struct weston_view *view;
struct lua_shell_layer *layer;
struct wl_listener view_destroy_listener;
struct wl_list surface_link; /** lua_shell_surface::view_list */
struct wl_list link; /** lua_shell::view_list */
};
struct lua_shell_layer {
uint32_t lua_regid;
uint32_t lua_private_regid;
struct lua_shell *shell;
struct weston_layer layer;
struct wl_list link; /** lua_shell::layer_list */
};
struct lua_shell_seat {
uint32_t lua_regid;
uint32_t lua_private_regid;
struct lua_shell *shell;
struct weston_seat *seat;
struct wl_listener seat_destroy_listener;
struct weston_surface *focused_surface;
struct wl_listener caps_changed_listener;
struct wl_listener keyboard_focus_listener;
struct wl_list link; /** lua_shell::seat_list */
};
struct lua_shell_timer {
uint32_t lua_regid;
uint32_t lua_private_regid;
struct lua_shell *shell;
struct wl_event_source *event_source;
uint32_t cb_regid;
struct wl_list link; /** lua_shell::timer_list */
};
struct lua_shell_binding {
struct weston_binding *binding;
struct lua_shell *shell;
uint32_t callback_regid;
struct wl_list link; /** lua_shell::binding_list */
};
static inline struct lua_shell *
get_shell_from_arg(struct lua_State *lua, int arg)
{
struct lua_shell **shell = luaL_checkudata(lua, arg, "weston.global");
luaL_argcheck(lua, shell && *shell, arg, "`weston' expected");
return *shell;
}
static inline struct lua_shell_output *
get_output_from_arg(struct lua_State *lua, int arg)
{
struct lua_shell_output **shoutput =
luaL_checkudata(lua, arg, "weston.output");
luaL_argcheck(lua, shoutput && *shoutput, arg,
"`weston.output' expected");
return *shoutput;
}
static inline struct lua_shell_seat *
get_seat_from_arg(struct lua_State *lua, int arg)
{
struct lua_shell_seat **shseat = luaL_checkudata(lua, arg, "weston.seat");
luaL_argcheck(lua, shseat && *shseat, arg, "`weston.seat' expected");
return *shseat;
}
static inline struct lua_shell_surface *
get_surface_from_arg(struct lua_State *lua, int arg)
{
struct lua_shell_surface **shsurf =
luaL_checkudata(lua, arg, "weston.surface");
luaL_argcheck(lua, shsurf && *shsurf, arg, "`weston.surface' expected");
return *shsurf;
}
static inline struct lua_shell_view *
get_view_from_arg(struct lua_State *lua, int arg)
{
struct lua_shell_view **shview = luaL_checkudata(lua, arg, "weston.view");
luaL_argcheck(lua, shview && *shview, arg, "`weston.view' expected");
return *shview;
}
static inline struct lua_shell_layer *
get_layer_from_arg(struct lua_State *lua, int arg)
{
struct lua_shell_layer **shlayer = luaL_checkudata(lua, arg, "weston.layer");
luaL_argcheck(lua, shlayer && *shlayer, arg, "`weston.layer' expected");
return *shlayer;
}
static inline struct lua_shell_curtain *
get_curtain_from_arg(struct lua_State *lua, int arg)
{
struct lua_shell_curtain **shcurtain = luaL_checkudata(lua, arg, "weston.curtain");
luaL_argcheck(lua, shcurtain && *shcurtain, arg, "`weston.curtain` expected");
return *shcurtain;
}
#endif /* WESTON_LUA_SHELL_H */

33
lua-shell/meson.build Normal file
View file

@ -0,0 +1,33 @@
if not get_option('shell-lua')
subdir_done()
endif
config_h.set('BUILD_LUA_SHELL', '1')
dep_lua = dependency('lua', version: '>= 5.4', required: false)
if not dep_lua.found()
error('lua-shell requires Lua >= 5.4 which was not found. Or, you can use \'-Dshell-lua=false\'.')
endif
srcs_shell_lua = [
'lua-shell.c',
]
deps_shell_lua = [
dep_libm,
dep_libexec_weston,
dep_libshared,
dep_libweston_public,
dep_lua,
]
plugin_shell_lua = shared_library(
'lua-shell',
srcs_shell_lua,
include_directories: common_inc,
dependencies: deps_shell_lua,
name_prefix: '',
install: true,
install_dir: dir_module_weston,
install_rpath: '$ORIGIN'
)
env_modmap += 'lua-shell.so=@0@;'.format(plugin_shell_lua.full_path())
install_data('shell.lua', install_dir: get_option('libexecdir'))

338
lua-shell/shell.lua Normal file
View file

@ -0,0 +1,338 @@
background_layer = {}
normal_layer = {}
hidden_layer = {}
fullscreen_layer = {}
current_width = 0
current_height = 0
function get_tile_row_col(count, width, height)
if (count == 0) then
return 1, 1
end
local cols = math.ceil(math.sqrt(count))
local rows = math.ceil(count / cols)
return cols, rows
end
function sort_views(views)
local count = 0
local sorted_views = {}
for k, v in pairs(views) do
count = count + 1
sorted_views[count] = k
end
table.sort(sorted_views)
return count, sorted_views
end
function relayout()
local views = normal_layer:get_views()
local count = 0
local col = 0
local x = 0
local row = 0
local y = 0
local count, sorted_views = sort_views(views)
local output_width, output_height = primary_output:get_dimensions()
local cols, rows = get_tile_row_col(count)
local width = math.floor(output_width / cols)
local height = math.floor(output_height / rows)
for k, v in ipairs(sorted_views) do
col = col + 1
if (col > cols) then
row = row + 1
col = 1
x = 0
y = y + height
end
local view = views[v]
local surface = view:get_surface()
local gx, gy = surface:get_geometry()
view:set_position(x - gx, y - gy)
surface:set_state_normal(width, height)
x = x + width
end
end
function my_output_create(output)
local pd = { has_fullscreen_view = false }
if (primary_output == nil) then
primary_output = output
end
x, y = output:get_position()
w, h = output:get_dimensions()
background_curtain = weston:create_curtain("output curtain")
background_curtain:set_color(0xFF000000)
background_curtain:set_position(x, y)
background_curtain:set_dimensions(w, h)
background_curtain:set_capture_input(true)
bv = background_curtain:get_view()
bv:set_output(output)
bv:set_layer(background_layer)
pd.background_view = bv
output:set_private(pd)
end
function output_moved(output, move_x, move_y)
local views = background_layer:get_views()
for k, v in pairs(views) do
if v:get_output() == output then
x, y = v:get_position()
v:set_position(x + move_x, y + move_y)
end
end
views = normal_layer:get_views()
for k, v in pairs(views) do
if v:get_output() == output then
x, y = v:get_position()
v:set_position(x + move_x, y + move_y)
end
end
end
function surface_added(surface)
local pd = {last_width = 0, last_height = 0, maximized = false,
map_fullscreen = false, fullscreen_output = nil}
pd.view = surface:create_view()
pd.width = 0
pd.height = 0
local outputs = weston:get_outputs()
for n, o in pairs(outputs) do
surface:set_output(o)
pd.view:set_output(o)
if (current_width == 0 and current_height == 0) then
output_width, output_height = o:get_dimensions()
current_width = output_width
current_height = output_height
else
current_width = math.floor(current_width / 2)
current_height = math.floor(current_height / 2)
end
pd.view:set_dimensions(current_width, current_height)
end
surface:set_private(pd)
end
function surface_removed(surface)
local pd = surface:get_private()
if (active_view == pd.view) then
pd.view:deactivate()
active_view = nil
end
if (pd.fullscreen_output) then
unset_fullscreen(surface)
end
pd.view:dispose()
current_width = current_width * 2
current_height = current_height * 2
relayout()
end
function surface_maximize(surface, maximized)
if (maximized) then
local pd = surface:get_private()
pd.view:set_position(0, 0)
local output = pd.view:get_output()
if (output == nil) then
output = primary_output
end
surface:set_state_maximized(output)
pd.maximized = true
else
pd.maximized = false
relayout()
end
end
function set_fullscreen(surface, output)
local surf_pd = surface:get_private()
local output_pd = output:get_private()
-- We only allow one fullscreen client
if (surf_pd.fullscreen_output ~= nil or output_pd.has_fullscreen_view) then
return
end
surface:set_state_fullscreen(output)
output_pd.has_fullscreen_view = true
output_pd.background_view:set_layer(fullscreen_layer)
surf_pd.view:move_in_front_of_other_view(output_pd.background_view)
surf_pd.fullscreen_output = output
surf_pd.view:set_position(0, 0)
end
function unset_fullscreen(surface)
local surf_pd = surface:get_private()
local output = surface:get_output()
local output_pd = output:get_private()
if (surf_pd.fullscreen_output == nil) then
return
end
output_pd.background_view:set_layer(background_layer)
surf_pd.view:set_layer(normal_layer)
output_pd.has_fullscreen_view = false
surf_pd.fullscreen_output = nil
relayout()
end
function surface_fullscreen(surface, output, fullscreen)
if (fullscreen) then
local pd = surface:get_private()
if (output == nil) then
output = pd.view:get_output()
end
if (output == nil) then
output = primary_output
end
if (surface:is_mapped()) then
set_fullscreen(surface, output)
else
pd.map_fullscreen = true
end
else
unset_fullscreen(surface)
relayout()
end
end
function lower_fullscreen_layer(surface, output)
local views_in_fs_layer = fullscreen_layer:get_views()
local count, sorted_views = sort_views(views_in_fs_layer)
for k, v in ipairs(sorted_views) do
local view = views_in_fs_layer[v]
local pd = view:get_private_surface()
-- no continue in lua, we have go the other way around
if (pd ~= nil) then
if (output ~= nil and pd.fullscreen_output == output) then
local output_pd = output:get_private()
if (output_pd.background_view) then
output_pd.background_view:set_layer(background_layer)
end
view:set_layer(normal_layer)
pd.map_fullscreen = false
pd.fullscreen_output = nil
output_pd.has_fullscreen_view = false
relayout()
end
end
end
end
function surface_committed(surface)
local pd = surface:get_private()
local w, h = surface:get_dimensions()
local good_seat = nil
local output = nil
if (w == 0) then
return
end
local is_resized = w ~= pd.width or h ~= pd.height
pd.width = w
pd.height = h
if (is_resized) then
relayout()
end
if surface:is_mapped() then
return
end
surface:map()
local seats = weston:get_seats()
for n, o in pairs(seats) do
good_seat = o
end
output = pd.view:get_output()
lower_fullscreen_layer(surface, output)
if (active_view ~= nil) then
active_view:deactivate()
end
pd.view:activate(good_seat)
active_view = pd.view
pd.view:set_layer(normal_layer)
if (pd.maximized) then
surface:set_state_maximized(output)
return
elseif (pd.map_fullscreen) then
set_fullscreen(surface, output)
return
end
relayout()
end
function click_to_activate(focus_view, seat, button)
if (active_view == focus_view) then
return
end
if (active_view ~= nil) then
active_view:deactivate()
end
focus_view:activate(seat)
active_view = focus_view
end
function my_init()
background_layer = weston:create_layer()
background_layer:set_position(WESTON_LAYER_POSITION_BACKGROUND)
normal_layer = weston:create_layer()
normal_layer:set_position(WESTON_LAYER_POSITION_NORMAL)
hidden_layer = weston:create_layer()
hidden_layer:set_position(WESTON_LAYER_POSITION_HIDDEN)
fullscreen_layer = weston:create_layer()
fullscreen_layer:set_position(WESTON_LAYER_POSITION_FULLSCREEN)
weston:add_button_binding(BTN_LEFT, 0, click_to_activate)
weston:add_button_binding(BTN_RIGHT, 0, click_to_activate)
end
lua_shell_callbacks = {
init = my_init,
surface_added = surface_added,
surface_committed = surface_committed,
surface_fullscreen = surface_fullscreen,
surface_maximize = surface_maximize,
surface_removed = surface_removed,
output_create = my_output_create,
output_moved = output_moved,
}

View file

@ -109,6 +109,7 @@ directory are:
.BR fullscreen (deprecated) .BR fullscreen (deprecated)
.BR ivi .BR ivi
.BR kiosk .BR kiosk
.BR lua
.fi .fi
.RE .RE
.TP 7 .TP 7
@ -483,6 +484,9 @@ sets the cursor theme (string).
.TP 7 .TP 7
.BI "cursor-size=" 24 .BI "cursor-size=" 24
sets the cursor size (unsigned integer). sets the cursor size (unsigned integer).
.TP 7
.BI "lua-script=" script
sets the script for the lua shell.
.\"--------------------------------------------------------------------- .\"---------------------------------------------------------------------
.SH "LAUNCHER SECTION" .SH "LAUNCHER SECTION"
There can be multiple launcher sections, one for each launcher. There can be multiple launcher sections, one for each launcher.

View file

@ -90,6 +90,12 @@ The IVI shell is a special-purpose shell which exposes an API compatible
with the GENIVI Layer Manager to user-provided HMI controller modules. with the GENIVI Layer Manager to user-provided HMI controller modules.
It is intended for use in automotive environments. It is intended for use in automotive environments.
. .
.TP
.I lua
The lua shell is a meta-shell that is user-scriptable and configurable.
A supplied Lua script will be interpreted and executed by Weston,
allowing full control over window management in response to events.
.
.\" *************************************************************** .\" ***************************************************************
.SH XWAYLAND .SH XWAYLAND
Weston can support X11 clients running within a Weston session via an Weston can support X11 clients running within a Weston session via an
@ -319,6 +325,12 @@ Give all outputs a refresh rate of
Supported values range from 0 mHz to 1,000,000 mHz. 0 is a special value Supported values range from 0 mHz to 1,000,000 mHz. 0 is a special value
that repaints as soon as possible on capture requests only, not on damages. that repaints as soon as possible on capture requests only, not on damages.
. .
.SS lua shell options:
.TP
\fB\-\-lua\-script\fR=\fIscript\fR
Override weston.ini lua\-script= with
.IR script "."
.
. .
.\" *************************************************************** .\" ***************************************************************
.SH FILES .SH FILES

View file

@ -231,6 +231,7 @@ subdir('desktop-shell')
subdir('fullscreen-shell') subdir('fullscreen-shell')
subdir('ivi-shell') subdir('ivi-shell')
subdir('kiosk-shell') subdir('kiosk-shell')
subdir('lua-shell')
subdir('remoting') subdir('remoting')
subdir('pipewire') subdir('pipewire')
subdir('clients') subdir('clients')

View file

@ -136,6 +136,12 @@ option(
value: true, value: true,
description: 'Weston shell UI: kiosk (desktop apps)' description: 'Weston shell UI: kiosk (desktop apps)'
) )
option(
'shell-lua',
type: 'boolean',
value: true,
description: 'Weston shell UI: Lua (programmable)'
)
option( option(
'desktop-shell-client-default', 'desktop-shell-client-default',

173
tests/lua-shell-test.c Normal file
View file

@ -0,0 +1,173 @@
/*
* Copyright © 2016-2023 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 <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include "libweston-internal.h"
#include "libweston/desktop.h"
#include "shared/xalloc.h"
#include "weston-test-client-helper.h"
#include "weston-test-fixture-compositor.h"
#include "weston-test-assert.h"
#include "tests/test-config.h"
#include "xdg-client-helper.h"
#define NR_XDG_SURFACES 4
static enum test_result_code
fixture_setup(struct weston_test_harness *harness)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
setup.renderer = WESTON_RENDERER_PIXMAN;
setup.width = 320;
setup.height = 240;
setup.shell = SHELL_LUA;
setup.logging_scopes = "log,test-harness-plugin";
setup.refresh = HIGHEST_OUTPUT_REFRESH;
weston_ini_setup(&setup,
cfgln("[shell]"),
cfgln("lua-script=%s", WESTON_LUA_SHELL_DIR "/shell.lua"));
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP(fixture_setup);
#define DECLARE_LIST_ITERATOR(name, parent, list, child, link) \
static child * \
next_##name(parent *from, child *pos) \
{ \
struct wl_list *entry = pos ? &pos->link : &from->list; \
child *ret = wl_container_of(entry->next, ret, link); \
return (&ret->link == &from->list) ? NULL : ret; \
}
DECLARE_LIST_ITERATOR(pnode_from_z, struct weston_output, paint_node_z_order_list,
struct weston_paint_node, z_order_link);
TEST(four_apps_in_a_square)
{
struct wet_testsuite_data *suite_data = TEST_GET_SUITE_DATA();
struct app {
const char *title_id;
int width, height;
float x, y;
} apps[NR_XDG_SURFACES] = {
{ "one", 320, 240, 0.0, 0.0 },
{ "two", 160, 120, 160.0, 0.0 },
{ "three", 80, 60, 0.0, 120.0 },
{ "four", 40, 30, 160.0, 120.0 },
};
int i = NR_XDG_SURFACES - 1;
struct xdg_client *xdg_client = create_xdg_client();
struct xdg_surface_data *xdg_surface1 = create_xdg_surface(xdg_client);
struct xdg_surface_data *xdg_surface2 = create_xdg_surface(xdg_client);
struct xdg_surface_data *xdg_surface3 = create_xdg_surface(xdg_client);
struct xdg_surface_data *xdg_surface4 = create_xdg_surface(xdg_client);
test_assert_ptr_not_null(xdg_client);
test_assert_ptr_not_null(xdg_surface1);
test_assert_ptr_not_null(xdg_surface2);
test_assert_ptr_not_null(xdg_surface3);
test_assert_ptr_not_null(xdg_surface4);
xdg_surface_make_toplevel(xdg_surface1, "weston.test.lua.one", "one");
xdg_surface_wait_configure(xdg_surface1);
xdg_surface_make_toplevel(xdg_surface2, "weston.test.lua.two", "two");
xdg_surface_wait_configure(xdg_surface2);
xdg_surface_make_toplevel(xdg_surface3, "weston.test.lua.three", "three");
xdg_surface_wait_configure(xdg_surface3);
xdg_surface_make_toplevel(xdg_surface4, "weston.test.lua.four", "four");
xdg_surface_wait_configure(xdg_surface4);
client_push_breakpoint(xdg_client->client, suite_data,
WESTON_TEST_BREAKPOINT_POST_REPAINT,
(struct wl_proxy *) xdg_client->client->output->wl_output);
xdg_surface_commit_solid(xdg_surface1, 255, 0, 0);
xdg_surface_commit_solid(xdg_surface2, 255, 0, 0);
xdg_surface_commit_solid(xdg_surface3, 255, 0, 0);
xdg_surface_commit_solid(xdg_surface4, 255, 0, 0);
RUN_INSIDE_BREAKPOINT(xdg_client->client, suite_data) {
struct weston_head *head = breakpoint->resource;
struct weston_output *output = head->output;
struct weston_paint_node *pnode = NULL;
test_assert_enum(breakpoint->template_->breakpoint,
WESTON_TEST_BREAKPOINT_POST_REPAINT);
while ((pnode = next_pnode_from_z(output, pnode)) != NULL && i > -1) {
struct weston_view *view = pnode->view;
struct weston_surface *surface = view->surface;
struct weston_buffer *buffer = surface->buffer_ref.buffer;
struct weston_desktop_surface *wds =
weston_surface_get_desktop_surface(surface);
const char *wds_title =
weston_desktop_surface_get_title(wds);
struct weston_geometry geom =
weston_desktop_surface_get_geometry(wds);
struct weston_coord_global pos =
weston_view_get_pos_offset_global(view);
struct app app = apps[i--];
test_assert_ptr_not_null(pnode);
test_assert_ptr_not_null(surface);
test_assert_ptr_not_null(view);
test_assert_ptr_not_null(buffer);
test_assert_true(weston_view_is_mapped(view));
test_assert_true(weston_surface_is_mapped(surface));
test_assert_str_eq(wds_title, app.title_id);
test_assert_int_eq(geom.width, app.width);
test_assert_int_eq(geom.height, app.height);
test_assert_f32_eq(pos.c.x, app.x);
test_assert_f32_eq(pos.c.y, app.y);
}
}
destroy_xdg_surface(xdg_surface1);
destroy_xdg_surface(xdg_surface2);
destroy_xdg_surface(xdg_surface3);
destroy_xdg_surface(xdg_surface4);
xdg_client_destroy(xdg_client);
return RESULT_OK;
}

View file

@ -402,10 +402,24 @@ if get_option('shell-kiosk')
] ]
endif endif
if get_option('shell-lua')
tests += [
{
'name': 'lua-shell',
'sources': [
'lua-shell-test.c',
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
],
},
]
endif
test_config_h = configuration_data() test_config_h = configuration_data()
test_config_h.set_quoted('WESTON_TEST_REFERENCE_PATH', meson.current_source_dir() + '/reference') test_config_h.set_quoted('WESTON_TEST_REFERENCE_PATH', meson.current_source_dir() + '/reference')
test_config_h.set_quoted('WESTON_MODULE_MAP', env_modmap) test_config_h.set_quoted('WESTON_MODULE_MAP', env_modmap)
test_config_h.set_quoted('WESTON_DATA_DIR', join_paths(meson.current_source_dir(), '..', 'data')) test_config_h.set_quoted('WESTON_DATA_DIR', join_paths(meson.current_source_dir(), '..', 'data'))
test_config_h.set_quoted('WESTON_LUA_SHELL_DIR', join_paths(meson.current_source_dir(), '..', 'lua-shell'))
test_config_h.set_quoted('TESTSUITE_PLUGIN_PATH', exe_plugin_test.full_path()) test_config_h.set_quoted('TESTSUITE_PLUGIN_PATH', exe_plugin_test.full_path())
test_config_h.set10('WESTON_TEST_SKIP_IS_FAILURE', get_option('test-skip-is-failure')) test_config_h.set10('WESTON_TEST_SKIP_IS_FAILURE', get_option('test-skip-is-failure'))
configure_file(output: 'test-config.h', configuration: test_config_h) configure_file(output: 'test-config.h', configuration: test_config_h)

View file

@ -242,6 +242,7 @@ shell_to_str(enum shell_type t)
[SHELL_FULLSCREEN] = "fullscreen", [SHELL_FULLSCREEN] = "fullscreen",
[SHELL_IVI] = "ivi", [SHELL_IVI] = "ivi",
[SHELL_KIOSK] = "kiosk", [SHELL_KIOSK] = "kiosk",
[SHELL_LUA] = "lua",
}; };
test_assert_true(t >= 0 && t < ARRAY_LENGTH(names)); test_assert_true(t >= 0 && t < ARRAY_LENGTH(names));
return names[t]; return names[t];

View file

@ -54,6 +54,8 @@ enum shell_type {
SHELL_FULLSCREEN, SHELL_FULLSCREEN,
/** The kiosk shell */ /** The kiosk shell */
SHELL_KIOSK, SHELL_KIOSK,
/** The lua shell */
SHELL_LUA,
}; };
/** Weston compositor configuration /** Weston compositor configuration

View file

@ -14,6 +14,7 @@ panel-color=0x90ff0000
locking=true locking=true
animation=zoom animation=zoom
startup-animation=fade startup-animation=fade
lua-script=@libexecdir@/shell.lua
#binding-modifier=ctrl #binding-modifier=ctrl
#num-workspaces=6 #num-workspaces=6
#cursor-theme=whiteglass #cursor-theme=whiteglass