mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2025-12-20 03:30:19 +01:00
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:
parent
cf44b691c6
commit
e91eccd709
16 changed files with 3072 additions and 1 deletions
|
|
@ -43,7 +43,7 @@
|
|||
variables:
|
||||
FDO_UPSTREAM_REPO: wayland/weston
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ apt-get -y --no-install-recommends install \
|
|||
libjack-jackd2-dev \
|
||||
libjpeg-dev \
|
||||
libjpeg-dev \
|
||||
liblua5.4-dev \
|
||||
libmtdev-dev \
|
||||
libpam0g-dev \
|
||||
libpango1.0-dev \
|
||||
|
|
|
|||
|
|
@ -825,6 +825,12 @@ usage(int error_code)
|
|||
" --no-input\t\tDont create input devices\n\n");
|
||||
#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);
|
||||
}
|
||||
|
||||
|
|
|
|||
2220
lua-shell/lua-shell.c
Normal file
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
259
lua-shell/lua-shell.h
Normal 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
33
lua-shell/meson.build
Normal 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
338
lua-shell/shell.lua
Normal 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,
|
||||
}
|
||||
|
|
@ -109,6 +109,7 @@ directory are:
|
|||
.BR fullscreen (deprecated)
|
||||
.BR ivi
|
||||
.BR kiosk
|
||||
.BR lua
|
||||
.fi
|
||||
.RE
|
||||
.TP 7
|
||||
|
|
@ -483,6 +484,9 @@ sets the cursor theme (string).
|
|||
.TP 7
|
||||
.BI "cursor-size=" 24
|
||||
sets the cursor size (unsigned integer).
|
||||
.TP 7
|
||||
.BI "lua-script=" script
|
||||
sets the script for the lua shell.
|
||||
.\"---------------------------------------------------------------------
|
||||
.SH "LAUNCHER SECTION"
|
||||
There can be multiple launcher sections, one for each launcher.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ subdir('desktop-shell')
|
|||
subdir('fullscreen-shell')
|
||||
subdir('ivi-shell')
|
||||
subdir('kiosk-shell')
|
||||
subdir('lua-shell')
|
||||
subdir('remoting')
|
||||
subdir('pipewire')
|
||||
subdir('clients')
|
||||
|
|
|
|||
|
|
@ -136,6 +136,12 @@ option(
|
|||
value: true,
|
||||
description: 'Weston shell UI: kiosk (desktop apps)'
|
||||
)
|
||||
option(
|
||||
'shell-lua',
|
||||
type: 'boolean',
|
||||
value: true,
|
||||
description: 'Weston shell UI: Lua (programmable)'
|
||||
)
|
||||
|
||||
option(
|
||||
'desktop-shell-client-default',
|
||||
|
|
|
|||
173
tests/lua-shell-test.c
Normal file
173
tests/lua-shell-test.c
Normal 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;
|
||||
}
|
||||
|
|
@ -402,10 +402,24 @@ if get_option('shell-kiosk')
|
|||
]
|
||||
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.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_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.set10('WESTON_TEST_SKIP_IS_FAILURE', get_option('test-skip-is-failure'))
|
||||
configure_file(output: 'test-config.h', configuration: test_config_h)
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ shell_to_str(enum shell_type t)
|
|||
[SHELL_FULLSCREEN] = "fullscreen",
|
||||
[SHELL_IVI] = "ivi",
|
||||
[SHELL_KIOSK] = "kiosk",
|
||||
[SHELL_LUA] = "lua",
|
||||
};
|
||||
test_assert_true(t >= 0 && t < ARRAY_LENGTH(names));
|
||||
return names[t];
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ enum shell_type {
|
|||
SHELL_FULLSCREEN,
|
||||
/** The kiosk shell */
|
||||
SHELL_KIOSK,
|
||||
/** The lua shell */
|
||||
SHELL_LUA,
|
||||
};
|
||||
|
||||
/** Weston compositor configuration
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ panel-color=0x90ff0000
|
|||
locking=true
|
||||
animation=zoom
|
||||
startup-animation=fade
|
||||
lua-script=@libexecdir@/shell.lua
|
||||
#binding-modifier=ctrl
|
||||
#num-workspaces=6
|
||||
#cursor-theme=whiteglass
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue