weston/lua-shell/lua-shell.c
Derek Foreman e91eccd709 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>
2025-06-04 15:47:06 +03:00

2220 lines
60 KiB
C

/*
* Copyright 2010-2012 Intel Corporation
* Copyright 2013 Raspberry Pi Foundation
* Copyright 2011-2012,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.
*/
#include <config.h>
#include <assert.h>
#include <linux/input.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "lua-shell.h"
#include "frontend/weston.h"
#include "shared/helpers.h"
#include "shared/weston-assert.h"
#include "shared/xalloc.h"
#include "libweston/shell-utils.h"
#include <libweston/xwayland-api.h>
static void
lua_dump_stack(struct lua_State *L)
{
int top=lua_gettop(L);
weston_log("=== Lua Shell stack dump ===\n");
for (int i=1; i <= top; i++) {
weston_log_continue(STAMP_SPACE "%d\t%s\t", i, luaL_typename(L,i));
switch (lua_type(L, i)) {
case LUA_TNIL:
weston_log_continue(STAMP_SPACE "%s\n", "nil");
break;
case LUA_TNUMBER:
weston_log_continue(STAMP_SPACE "%g\n",lua_tonumber(L,i));
break;
case LUA_TBOOLEAN:
weston_log_continue(STAMP_SPACE "%s\n", (lua_toboolean(L, i) ? "true" : "false"));
break;
case LUA_TSTRING:
weston_log_continue(STAMP_SPACE "%s\n",lua_tostring(L,i));
break;
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TUSERDATA:
case LUA_TLIGHTUSERDATA:
case LUA_TTHREAD:
default:
weston_log_continue(STAMP_SPACE "%p\n",lua_topointer(L,i));
break;
}
}
weston_log_continue(STAMP_SPACE "============================\n");
}
static void *
lxzalloc(struct lua_State *lua, size_t size, const char *meta)
{
struct lua_object *allocation;
void **obj;
uint32_t regid;
allocation = xzalloc(size);
obj = lua_newuserdata(lua, sizeof(*obj));
*obj = allocation;
luaL_getmetatable(lua, meta);
lua_setmetatable(lua, -2);
regid = luaL_ref(lua, LUA_REGISTRYINDEX);
allocation->lua_regid = regid;
return allocation;
}
static void
lfree(struct lua_State *lua, void *data)
{
struct lua_object *obj = data;
int lua_regid = obj->lua_regid;
if (obj->lua_private_regid)
luaL_unref(lua, LUA_REGISTRYINDEX,
obj->lua_private_regid);
obj->lua_private_regid = 0;
obj->lua_regid = 0;
luaL_unref(lua, LUA_REGISTRYINDEX, lua_regid);
/* DO NOT FREE! LUA objects are freed from the garbage
* collector later.
*/
}
static int
lgc(struct lua_State *lua)
{
struct lua_object **udata = lua_touserdata(lua, 1);
struct lua_object *obj = *udata;
assert(obj->lua_regid == 0);
assert(obj->lua_private_regid == 0);
free(obj);
return 0;
}
static bool
lua_shell_push_function(struct lua_shell *shell, enum lua_shell_cb_id id)
{
struct lua_State *lua = shell->lua;
if (!shell->callbacks[id].regid)
return false;
lua_rawgeti(lua, LUA_REGISTRYINDEX, shell->callbacks[id].regid);
assert(lua_isfunction(lua, -1));
return true;
}
static bool
lua_shell_call_function(struct lua_shell *shell, const char *func,
int args, int rets)
{
weston_assert_true(shell->compositor,
lua_isfunction(shell->lua, -(args + 1)));
if (lua_pcall(shell->lua, args, rets, 0) != 0) {
weston_log("error from function '%s': %s\n",
func, lua_tostring(shell->lua, -1));
lua_dump_stack(shell->lua);
return false;
}
return true;
}
static struct lua_shell_surface *
get_lua_shell_surface(struct weston_surface *surface)
{
struct weston_desktop_surface *desktop_surface =
weston_surface_get_desktop_surface(surface);
if (desktop_surface)
return weston_desktop_surface_get_user_data(desktop_surface);
return NULL;
}
static void
lua_shell_seat_handle_destroy(struct wl_listener *listener, void *data);
static struct lua_shell_seat *
get_lua_shell_seat(struct weston_seat *seat)
{
struct wl_listener *listener;
listener = wl_signal_get(&seat->destroy_signal,
lua_shell_seat_handle_destroy);
assert(listener != NULL);
return container_of(listener,
struct lua_shell_seat, seat_destroy_listener);
}
static void
lua_shell_view_handle_destroy(struct wl_listener *listener, void *data)
{
/* We don't really care. We use this to help get lua_shell_view
* from a weston_view
*/
}
static struct lua_shell_view *
get_lua_shell_view(struct weston_view *view)
{
struct wl_listener *listener;
listener = wl_signal_get(&view->destroy_signal,
lua_shell_view_handle_destroy);
if (!listener)
return NULL;
return container_of(listener,
struct lua_shell_view, view_destroy_listener);
}
/*
* lua_shell_surface
*/
static void
lua_shell_surface_set_output(struct lua_shell_surface *shsurf,
struct lua_shell_output *shoutput);
static void
lua_shell_surface_set_parent(struct lua_shell_surface *shsurf,
struct lua_shell_surface *parent);
static void
lua_shell_surface_notify_parent_destroy(struct wl_listener *listener, void *data)
{
struct lua_shell_surface *shsurf =
container_of(listener,
struct lua_shell_surface, parent_destroy_listener);
lua_shell_surface_set_parent(shsurf, shsurf->parent->parent);
}
static void
lua_shell_surface_notify_output_destroy(struct wl_listener *listener, void *data)
{
struct lua_shell_surface *shsurf =
container_of(listener,
struct lua_shell_surface, output_destroy_listener);
lua_shell_surface_set_output(shsurf, NULL);
}
static void
lua_shell_surface_set_output(struct lua_shell_surface *shsurf,
struct lua_shell_output *shoutput)
{
shsurf->shoutput = shoutput;
if (shsurf->output_destroy_listener.notify) {
wl_list_remove(&shsurf->output_destroy_listener.link);
shsurf->output_destroy_listener.notify = NULL;
}
if (!shsurf->shoutput)
return;
shsurf->output_destroy_listener.notify =
lua_shell_surface_notify_output_destroy;
wl_signal_add(&shsurf->shoutput->output->destroy_signal,
&shsurf->output_destroy_listener);
}
static void
lua_shell_surface_set_parent(struct lua_shell_surface *shsurf,
struct lua_shell_surface *parent)
{
if (shsurf->parent_destroy_listener.notify) {
wl_list_remove(&shsurf->parent_destroy_listener.link);
shsurf->parent_destroy_listener.notify = NULL;
}
shsurf->parent = parent;
if (!shsurf->parent)
return;
shsurf->parent_destroy_listener.notify =
lua_shell_surface_notify_parent_destroy;
wl_signal_add(&shsurf->parent->destroy_signal,
&shsurf->parent_destroy_listener);
}
static void
lua_shell_view_dispose(struct lua_shell_view *shview)
{
wl_list_remove(&shview->view_destroy_listener.link);
wl_list_remove(&shview->surface_link);
wl_list_remove(&shview->link);
if (shview->is_desktop_surface) {
weston_desktop_surface_unlink_view(shview->view);
weston_view_destroy(shview->view);
}
lfree(shview->shell->lua, shview);
}
static void
lua_shell_layer_dispose(struct lua_shell_layer *shlayer)
{
wl_list_remove(&shlayer->link);
weston_layer_fini(&shlayer->layer);
lfree(shlayer->shell->lua, shlayer);
}
static void
lua_shell_curtain_dispose(struct lua_shell_curtain *shcurtain)
{
wl_list_remove(&shcurtain->link);
free(shcurtain->name);
weston_shell_utils_curtain_destroy(shcurtain->curtain);
lfree(shcurtain->shell->lua, shcurtain);
}
static void
lua_shell_surface_dispose(struct lua_shell_surface *shsurf)
{
struct lua_shell_view *shview, *tmp;
wl_list_remove(&shsurf->link);
wl_signal_emit(&shsurf->destroy_signal, shsurf);
weston_desktop_surface_set_user_data(shsurf->desktop_surface, NULL);
shsurf->desktop_surface = NULL;
if (shsurf->output_destroy_listener.notify) {
wl_list_remove(&shsurf->output_destroy_listener.link);
shsurf->output_destroy_listener.notify = NULL;
}
if (shsurf->parent_destroy_listener.notify) {
wl_list_remove(&shsurf->parent_destroy_listener.link);
shsurf->parent_destroy_listener.notify = NULL;
shsurf->parent = NULL;
}
wl_list_for_each_safe(shview, tmp, &shsurf->view_list, surface_link)
lua_shell_view_dispose(shview);
lfree(shsurf->shell->lua, shsurf);
}
static struct lua_shell_surface *
lua_shell_surface_added(struct lua_shell *shell,
struct weston_desktop_surface *desktop_surface)
{
struct lua_shell_surface *shsurf;
shsurf = lxzalloc(shell->lua, sizeof *shsurf, "weston.surface");
shsurf->shell = shell;
shsurf->desktop_surface = desktop_surface;
wl_list_init(&shsurf->view_list);
weston_desktop_surface_set_user_data(desktop_surface, shsurf);
wl_signal_init(&shsurf->destroy_signal);
wl_list_insert(shell->surface_list.prev, &shsurf->link);
if (!lua_shell_push_function(shell, LUA_SHELL_CB_SURFACE_ADDED))
return shsurf;
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
lua_shell_call_function(shell, "surface_added", 1, 0);
return shsurf;
}
/*
* lua_shell_seat
*/
static void
lua_shell_seat_handle_keyboard_focus(struct wl_listener *listener, void *data)
{
struct weston_keyboard *keyboard = data;
struct lua_shell_seat *shseat = get_lua_shell_seat(keyboard->seat);
struct lua_shell *shell = shseat->shell;
if (!lua_shell_push_function(shell, LUA_SHELL_CB_KEYBOARD_FOCUS))
return;
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shseat->lua_regid);
lua_shell_call_function(shell, "keyboard_focus", 1, 0);
}
static void
lua_shell_seat_destroy(struct lua_shell_seat *shseat)
{
wl_list_remove(&shseat->keyboard_focus_listener.link);
wl_list_remove(&shseat->caps_changed_listener.link);
wl_list_remove(&shseat->seat_destroy_listener.link);
wl_list_remove(&shseat->link);
lfree(shseat->shell->lua, shseat);
}
static void
lua_shell_seat_handle_destroy(struct wl_listener *listener, void *data)
{
struct lua_shell_seat *shseat =
container_of(listener,
struct lua_shell_seat, seat_destroy_listener);
lua_shell_seat_destroy(shseat);
}
static void
lua_shell_seat_handle_caps_changed(struct wl_listener *listener, void *data)
{
struct weston_keyboard *keyboard;
struct lua_shell_seat *shseat;
shseat = container_of(listener, struct lua_shell_seat,
caps_changed_listener);
keyboard = weston_seat_get_keyboard(shseat->seat);
if (keyboard &&
wl_list_empty(&shseat->keyboard_focus_listener.link)) {
wl_signal_add(&keyboard->focus_signal,
&shseat->keyboard_focus_listener);
} else if (!keyboard) {
wl_list_remove(&shseat->keyboard_focus_listener.link);
wl_list_init(&shseat->keyboard_focus_listener.link);
}
}
static struct lua_shell_seat *
lua_shell_seat_create(struct lua_shell *shell, struct weston_seat *seat)
{
struct lua_shell_seat *shseat;
shseat = lxzalloc(shell->lua, sizeof *shseat, "weston.seat");
shseat->seat = seat;
shseat->shell = shell;
shseat->seat_destroy_listener.notify = lua_shell_seat_handle_destroy;
wl_signal_add(&seat->destroy_signal, &shseat->seat_destroy_listener);
shseat->keyboard_focus_listener.notify = lua_shell_seat_handle_keyboard_focus;
wl_list_init(&shseat->keyboard_focus_listener.link);
shseat->caps_changed_listener.notify = lua_shell_seat_handle_caps_changed;
wl_signal_add(&seat->updated_caps_signal,
&shseat->caps_changed_listener);
lua_shell_seat_handle_caps_changed(&shseat->caps_changed_listener, NULL);
wl_list_insert(&shell->seat_list, &shseat->link);
if (!lua_shell_push_function(shell, LUA_SHELL_CB_SEAT_CREATE))
return shseat;
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shseat->lua_regid);
lua_shell_call_function(shell, "seat_create", 1, 0);
return shseat;
}
/*
* lua_shell_output
*/
static void
lua_shell_output_destroy(struct lua_shell_output *shoutput)
{
shoutput->output = NULL;
shoutput->output_destroy_listener.notify = NULL;
wl_list_remove(&shoutput->output_destroy_listener.link);
wl_list_remove(&shoutput->link);
lfree(shoutput->shell->lua, shoutput);
}
static void
lua_shell_output_notify_output_destroy(struct wl_listener *listener, void *data)
{
struct lua_shell_output *shoutput =
container_of(listener,
struct lua_shell_output, output_destroy_listener);
lua_shell_output_destroy(shoutput);
}
static struct lua_shell_output *
lua_shell_output_create(struct lua_shell *shell, struct weston_output *output)
{
struct lua_shell_output *shoutput;
shoutput = lxzalloc(shell->lua, sizeof *shoutput, "weston.output");
shoutput->output = output;
shoutput->shell = shell;
shoutput->output_destroy_listener.notify =
lua_shell_output_notify_output_destroy;
wl_signal_add(&shoutput->output->destroy_signal,
&shoutput->output_destroy_listener);
wl_list_insert(shell->output_list.prev, &shoutput->link);
weston_output_set_shell_private(output, shoutput);
if (!lua_shell_push_function(shell, LUA_SHELL_CB_OUTPUT_CREATE))
return shoutput;
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shoutput->lua_regid);
lua_shell_call_function(shell, "output_create", 1, 0);
return shoutput;
}
/*
* libweston-desktop
*/
static void
desktop_surface_added(struct weston_desktop_surface *desktop_surface,
void *data)
{
struct weston_surface *surface =
weston_desktop_surface_get_surface(desktop_surface);
struct lua_shell *shell = data;
struct lua_shell_surface *shsurf;
shsurf = lua_shell_surface_added(shell, desktop_surface);
if (!shsurf)
return;
weston_surface_set_label_func(surface,
weston_shell_utils_surface_get_label);
}
static void
desktop_surface_removed(struct weston_desktop_surface *desktop_surface,
void *data)
{
struct lua_shell *shell = data;
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
if (!shsurf)
return;
if (!lua_shell_push_function(shell, LUA_SHELL_CB_SURFACE_REMOVED))
return;
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
lua_shell_call_function(shell, "surface_removed", 1, 0);
lua_shell_surface_dispose(shsurf);
}
static void
desktop_surface_committed(struct weston_desktop_surface *desktop_surface,
struct weston_coord_surface buf_offset, void *data)
{
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct lua_State *lua = shsurf->shell->lua;
if (!lua_shell_push_function(shsurf->shell, LUA_SHELL_CB_SURFACE_COMMITTED))
return;
lua_rawgeti(lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
lua_shell_call_function(shsurf->shell, "surface_committed", 1, 0);
}
static void
desktop_surface_move(struct weston_desktop_surface *desktop_surface,
struct weston_seat *seat, uint32_t serial, void *shell)
{
struct lua_shell *ls = shell;
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct lua_shell_seat *shseat =
get_lua_shell_seat(seat);
if (!lua_shell_push_function(ls, LUA_SHELL_CB_SURFACE_MOVE))
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shseat->lua_regid);
lua_pushnumber(ls->lua, serial);
lua_shell_call_function(ls, "surface_move", 3, 0);
}
static void
desktop_surface_resize(struct weston_desktop_surface *desktop_surface,
struct weston_seat *seat, uint32_t serial,
enum weston_desktop_surface_edge edges, void *shell)
{
struct lua_shell *ls = shell;
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct lua_shell_seat *shseat =
get_lua_shell_seat(seat);
if (!lua_shell_push_function(ls, LUA_SHELL_CB_SURFACE_RESIZE))
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shseat->lua_regid);
lua_pushnumber(ls->lua, edges);
lua_shell_call_function(ls, "surface_resize", 3, 0);
}
static void
desktop_surface_set_parent(struct weston_desktop_surface *desktop_surface,
struct weston_desktop_surface *parent,
void *shell)
{
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
struct lua_shell_surface *shsurf_parent =
parent ? weston_desktop_surface_get_user_data(parent) : NULL;
lua_shell_surface_set_parent(shsurf, shsurf_parent);
}
static void
desktop_surface_fullscreen_requested(struct weston_desktop_surface *desktop_surface,
bool fullscreen,
struct weston_output *output, void *shell)
{
struct lua_shell *ls = shell;
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
if (!lua_shell_push_function(ls, LUA_SHELL_CB_SURFACE_FULLSCREEN))
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
if (output) {
struct lua_shell_output *shoutput =
weston_output_get_shell_private(output);
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shoutput->lua_regid);
} else
lua_pushnil(ls->lua);
lua_pushboolean(ls->lua, fullscreen);
lua_shell_call_function(ls, "surface_fullscreen", 3, 0);
}
static void
desktop_surface_maximized_requested(struct weston_desktop_surface *desktop_surface,
bool maximized, void *shell)
{
struct lua_shell *ls = shell;
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
if (!lua_shell_push_function(ls, LUA_SHELL_CB_SURFACE_MAXIMIZE))
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
lua_pushboolean(ls->lua, maximized);
lua_shell_call_function(ls, "surface_maximize", 2, 0);
}
static void
desktop_surface_minimized_requested(struct weston_desktop_surface *desktop_surface,
void *shell)
{
}
static void
desktop_surface_ping_timeout(struct weston_desktop_client *desktop_client,
void *shell_)
{
}
static void
desktop_surface_pong(struct weston_desktop_client *desktop_client,
void *shell_)
{
}
static void
desktop_surface_set_xwayland_position(struct weston_desktop_surface *desktop_surface,
struct weston_coord_global pos, void *shell)
{
struct lua_shell *ls = shell;
struct lua_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
if (!lua_shell_push_function(ls, LUA_SHELL_CB_SET_XWAYLAND_POSITION))
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
lua_pushnumber(ls->lua, pos.c.x);
lua_pushnumber(ls->lua, pos.c.y);
lua_shell_call_function(ls, "set_xwayland_position", 3, 0);
}
static const struct weston_desktop_api lua_shell_desktop_api = {
.struct_size = sizeof(struct weston_desktop_api),
.surface_added = desktop_surface_added,
.surface_removed = desktop_surface_removed,
.committed = desktop_surface_committed,
.move = desktop_surface_move,
.resize = desktop_surface_resize,
.set_parent = desktop_surface_set_parent,
.fullscreen_requested = desktop_surface_fullscreen_requested,
.maximized_requested = desktop_surface_maximized_requested,
.minimized_requested = desktop_surface_minimized_requested,
.ping_timeout = desktop_surface_ping_timeout,
.pong = desktop_surface_pong,
.set_xwayland_position = desktop_surface_set_xwayland_position,
};
/*
* lua_shell
*/
static void
lua_shell_binding_destroy(struct lua_shell_binding *shbinding)
{
luaL_unref(shbinding->shell->lua, LUA_REGISTRYINDEX,
shbinding->callback_regid);
free(shbinding);
}
static void
button_binding_cb(struct weston_pointer *pointer,
const struct timespec *time,
uint32_t button, void *data)
{
struct lua_shell_binding *shbinding = data;
struct lua_shell *ls = shbinding->shell;
struct lua_shell_view *shview = NULL;
struct lua_shell_seat *shseat = get_lua_shell_seat(pointer->seat);
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shbinding->callback_regid);
if (pointer->focus) {
shview = get_lua_shell_view(pointer->focus);
if (!shview)
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shview->lua_regid);
} else {
lua_pushnil(ls->lua);
}
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shseat->lua_regid);
lua_pushnumber(ls->lua, button);
lua_shell_call_function(ls, "[button callback]", 3, 0);
}
static int
lua_shell_env_add_button_binding(struct lua_State *lua)
{
struct lua_shell_binding *shbinding;
struct lua_shell *shell = get_shell_from_arg(lua, 1);
int button = luaL_checkinteger(lua, 2);
int mods = luaL_checkinteger(lua, 3);
luaL_checktype(lua, 4, LUA_TFUNCTION);
shbinding = xzalloc(sizeof *shbinding);
shbinding->callback_regid = luaL_ref(lua, LUA_REGISTRYINDEX);
shbinding->shell = shell;
shbinding->binding =
weston_compositor_add_button_binding(shell->compositor,
button, mods,
button_binding_cb,
shbinding);
wl_list_insert(shell->binding_list.prev, &shbinding->link);
return 0;
}
static void
touch_binding_cb(struct weston_touch *touch,
const struct timespec *time,
void *data)
{
struct lua_shell_binding *shbinding = data;
struct lua_shell *ls = shbinding->shell;
struct lua_shell_view *shview = NULL;
struct lua_shell_seat *shseat = get_lua_shell_seat(touch->seat);
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shbinding->callback_regid);
if (touch->focus) {
shview = get_lua_shell_view(touch->focus);
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shview->lua_regid);
} else {
lua_pushnil(ls->lua);
}
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shseat->lua_regid);
lua_shell_call_function(ls, "[touch callback]", 2, 0);
}
static int
lua_shell_env_add_touch_binding(struct lua_State *lua)
{
struct lua_shell_binding *shbinding;
struct lua_shell *shell = get_shell_from_arg(lua, 1);
int mods = luaL_checkinteger(lua, 2);
luaL_checktype(lua, 3, LUA_TFUNCTION);
shbinding = xzalloc(sizeof *shbinding);
shbinding->callback_regid = luaL_ref(lua, LUA_REGISTRYINDEX);
shbinding->shell = shell;
shbinding->binding =
weston_compositor_add_touch_binding(shell->compositor,
mods,
touch_binding_cb,
shbinding);
wl_list_insert(shell->binding_list.prev, &shbinding->link);
return 0;
}
static void
lua_shell_handle_output_created(struct wl_listener *listener, void *data)
{
struct lua_shell *shell =
container_of(listener, struct lua_shell, output_created_listener);
struct weston_output *output = data;
lua_shell_output_create(shell, output);
}
static void
lua_shell_handle_output_resized(struct wl_listener *listener, void *data)
{
struct lua_shell *ls =
container_of(listener, struct lua_shell, output_resized_listener);
struct weston_output *output = data;
struct lua_shell_output *shoutput =
weston_output_get_shell_private(output);
if (!lua_shell_push_function(ls, LUA_SHELL_CB_OUTPUT_RESIZED))
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shoutput->lua_regid);
lua_shell_call_function(ls, "output_resized", 1, 0);
}
static void
lua_shell_handle_output_moved(struct wl_listener *listener, void *data)
{
struct lua_shell *ls =
container_of(listener, struct lua_shell, output_moved_listener);
struct weston_output *output = data;
struct lua_shell_output *shoutput = weston_output_get_shell_private(output);
if (!lua_shell_push_function(ls, LUA_SHELL_CB_OUTPUT_MOVED))
return;
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shoutput->lua_regid);
lua_pushnumber(ls->lua, output->move.c.x);
lua_pushnumber(ls->lua, output->move.c.y);
lua_shell_call_function(ls, "output_moved", 3, 0);
}
static void
lua_shell_handle_seat_created(struct wl_listener *listener, void *data)
{
struct weston_seat *seat = data;
struct lua_shell *shell =
container_of(listener, struct lua_shell, seat_created_listener);
lua_shell_seat_create(shell, seat);
}
static void
transform_handler(struct wl_listener *listener, void *data)
{
}
static int
lua_shell_env_output_get_dimensions(struct lua_State *lua)
{
struct lua_shell_output *shoutput = get_output_from_arg(lua, 1);
lua_pushinteger(lua, shoutput->output->width);
lua_pushinteger(lua, shoutput->output->height);
return 2;
}
static int
lua_shell_env_output_get_position(struct lua_State *lua)
{
struct lua_shell_output *shoutput = get_output_from_arg(lua, 1);
lua_pushinteger(lua, shoutput->output->pos.c.x);
lua_pushinteger(lua, shoutput->output->pos.c.y);
return 2;
}
static int
lua_shell_env_output_get_name(struct lua_State *lua)
{
struct lua_shell_output *shoutput = get_output_from_arg(lua, 1);
lua_pushstring(lua, shoutput->output->name);
return 1;
}
static int
lua_shell_env_output_get_scale(struct lua_State *lua)
{
struct lua_shell_output *shoutput = get_output_from_arg(lua, 1);
lua_pushinteger(lua, shoutput->output->current_scale);
return 1;
}
static int
lua_shell_env_output_is_enabled(struct lua_State *lua)
{
struct lua_shell_output *shoutput = get_output_from_arg(lua, 1);
lua_pushinteger(lua, shoutput->output->enabled);
return 1;
}
static int
lua_shell_env_output_set_private(struct lua_State *lua)
{
struct lua_shell_output *shoutput = get_output_from_arg(lua, 1);
assert(shoutput->lua_private_regid == 0);
shoutput->lua_private_regid = luaL_ref(lua, LUA_REGISTRYINDEX);
return 0;
}
static int
lua_shell_env_output_get_private(struct lua_State *lua)
{
struct lua_shell_output *shoutput = get_output_from_arg(lua, 1);
lua_rawgeti(lua, LUA_REGISTRYINDEX, shoutput->lua_private_regid);
return 1;
}
static void
lua_shell_env_init_output(struct lua_shell *shell)
{
static const luaL_Reg lua_output_obj_funcs[] = {
{ "__gc", lgc },
{ "get_dimensions", lua_shell_env_output_get_dimensions },
{ "get_position", lua_shell_env_output_get_position },
{ "get_name", lua_shell_env_output_get_name },
{ "get_scale", lua_shell_env_output_get_scale },
{ "is_enabled", lua_shell_env_output_is_enabled },
{ "set_private", lua_shell_env_output_set_private },
{ "get_private", lua_shell_env_output_get_private },
{ NULL, NULL }
};
luaL_newmetatable(shell->lua, "weston.output");
lua_pushvalue(shell->lua, -1);
lua_setfield(shell->lua, -2, "__index");
luaL_setfuncs(shell->lua, lua_output_obj_funcs, 0);
}
static int
lua_shell_env_seat_get_capabilities(struct lua_State *lua)
{
struct lua_shell_seat *shseat = get_seat_from_arg(lua, 1);
struct weston_seat *seat = shseat->seat;
lua_pushinteger(lua, seat->pointer_device_count);
lua_pushinteger(lua, seat->keyboard_device_count);
lua_pushinteger(lua, seat->touch_device_count);
return 3;
}
static int
lua_shell_env_seat_get_name(struct lua_State *lua)
{
struct lua_shell_seat *shseat = get_seat_from_arg(lua, 1);
lua_pushstring(lua, shseat->seat->seat_name);
return 1;
}
static void
lua_shell_env_init_seat(struct lua_shell *shell)
{
static const luaL_Reg lua_seat_obj_funcs[] = {
{ "__gc", lgc },
{ "get_capabilities", lua_shell_env_seat_get_capabilities },
{ "get_name", lua_shell_env_seat_get_name },
{ NULL, NULL }
};
luaL_newmetatable(shell->lua, "weston.seat");
lua_pushvalue(shell->lua, -1);
lua_setfield(shell->lua, -2, "__index");
luaL_setfuncs(shell->lua, lua_seat_obj_funcs, 0);
}
static int
lua_shell_env_surface_get_role(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct weston_surface *surface;
surface = weston_desktop_surface_get_surface(shsurf->desktop_surface);
lua_pushstring(lua, weston_surface_get_role(surface));
return 1;
}
static int
lua_shell_env_surface_get_app_id(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
const char *app_id =
weston_desktop_surface_get_app_id(shsurf->desktop_surface);
lua_pushstring(lua, app_id);
return 1;
}
static int
lua_shell_env_surface_get_title(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
const char *title =
weston_desktop_surface_get_title(shsurf->desktop_surface);
lua_pushstring(lua, title);
return 1;
}
static int
lua_shell_env_surface_get_dimensions(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct weston_surface *surface;
surface = weston_desktop_surface_get_surface(shsurf->desktop_surface);
lua_pushinteger(lua, surface->width);
lua_pushinteger(lua, surface->height);
return 2;
}
static int
lua_shell_env_surface_get_geometry(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct weston_geometry geometry;
geometry = weston_desktop_surface_get_geometry(shsurf->desktop_surface);
lua_pushinteger(lua, geometry.x);
lua_pushinteger(lua, geometry.y);
return 2;
}
static int
lua_shell_env_surface_get_output(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
lua_rawgeti(lua, LUA_REGISTRYINDEX, shsurf->shoutput->lua_regid);
return 1;
}
static int
lua_shell_env_surface_get_private(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
lua_rawgeti(lua, LUA_REGISTRYINDEX, shsurf->lua_private_regid);
return 1;
}
static int
lua_shell_env_surface_set_output(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct lua_shell_output *shoutput = get_output_from_arg(lua, 2);
lua_shell_surface_set_output(shsurf, shoutput);
return 0;
}
static int
lua_shell_env_surface_set_private(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
assert(shsurf->lua_private_regid == 0);
shsurf->lua_private_regid = luaL_ref(lua, LUA_REGISTRYINDEX);
return 0;
}
static int
lua_shell_env_surface_set_state_fullscreen(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct lua_shell_output *shoutput = get_output_from_arg(lua, 2);
lua_shell_surface_set_output(shsurf, shoutput);
weston_desktop_surface_set_fullscreen(shsurf->desktop_surface, true);
weston_desktop_surface_set_size(shsurf->desktop_surface,
shsurf->shoutput->output->width,
shsurf->shoutput->output->height);
return 0;
}
static int
lua_shell_env_surface_get_state_fullscreen(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct weston_desktop_surface *dsurface = shsurf->desktop_surface;
lua_pushinteger(lua, weston_desktop_surface_get_fullscreen(dsurface));
return 1;
}
static int
lua_shell_env_surface_set_state_maximized(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct lua_shell_output *shoutput = get_output_from_arg(lua, 2);
lua_shell_surface_set_output(shsurf, shoutput);
weston_desktop_surface_set_maximized(shsurf->desktop_surface, true);
weston_desktop_surface_set_size(shsurf->desktop_surface,
shsurf->shoutput->output->width,
shsurf->shoutput->output->height);
return 0;
}
static int
lua_shell_env_surface_get_state_maximized(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct weston_desktop_surface *dsurface = shsurf->desktop_surface;
lua_pushinteger(lua, weston_desktop_surface_get_maximized(dsurface));
return 1;
}
static int
lua_shell_env_surface_set_state_normal(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
int width = luaL_checkinteger(lua, 2);
int height = luaL_checkinteger(lua, 3);
weston_desktop_surface_set_fullscreen(shsurf->desktop_surface, false);
weston_desktop_surface_set_maximized(shsurf->desktop_surface, false);
weston_desktop_surface_set_size(shsurf->desktop_surface, width, height);
return 0;
}
static int
lua_shell_env_surface_get_parent(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct lua_shell_surface *parent = shsurf->parent;
if (!parent)
lua_pushnil(lua);
else
lua_rawgeti(lua, LUA_REGISTRYINDEX, parent->lua_regid);
return 1;
}
static int
lua_shell_env_surface_get_views(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct lua_shell *ls = shsurf->shell;
struct lua_shell_view *shview;
lua_newtable(ls->lua);
wl_list_for_each(shview, &shsurf->view_list, surface_link) {
char buf[32];
lua_rawgeti(ls->lua, LUA_REGISTRYINDEX, shview->lua_regid);
snprintf(buf, sizeof(buf), "view-%"PRIu32, shview->lua_regid);
lua_setfield(ls->lua, -2, buf);
}
return 1;
}
static int
lua_shell_env_surface_create_view(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct lua_shell_view *shview;
struct weston_view *view;
shview = lxzalloc(lua, sizeof(*shview), "weston.view");
view = weston_desktop_surface_create_view(shsurf->desktop_surface);
shview->view = view;
shview->shell = shsurf->shell;
shview->surface = shsurf;
shview->is_desktop_surface = true;
wl_list_insert(shview->shell->view_list.prev, &shview->link);
wl_list_insert(shsurf->view_list.prev, &shview->surface_link);
shview->view_destroy_listener.notify = lua_shell_view_handle_destroy;
wl_signal_add(&view->destroy_signal, &shview->view_destroy_listener);
lua_rawgeti(lua, LUA_REGISTRYINDEX, shview->lua_regid);
return 1;
}
static int
lua_shell_env_surface_map(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct weston_surface *surface = weston_desktop_surface_get_surface(shsurf->desktop_surface);
weston_surface_map(surface);
return 0;
}
static int
lua_shell_env_surface_is_mapped(struct lua_State *lua)
{
struct lua_shell_surface *shsurf = get_surface_from_arg(lua, 1);
struct weston_surface *wsurf;
bool mapped;
wsurf = weston_desktop_surface_get_surface(shsurf->desktop_surface);
mapped = weston_surface_is_mapped(wsurf);
lua_pushboolean(lua, mapped);
return 1;
}
static int
lua_shell_env_view_get_surface(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
if (!shview->surface)
lua_pushnil(lua);
else
lua_rawgeti(lua, LUA_REGISTRYINDEX, shview->surface->lua_regid);
return 1;
}
static int
lua_shell_env_view_get_private_surface(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
if (!shview->surface)
lua_pushnil(lua);
else
lua_rawgeti(lua, LUA_REGISTRYINDEX, shview->surface->lua_private_regid);
return 1;
}
static int
lua_shell_env_view_get_layer(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
if (!shview->layer)
lua_pushnil(lua);
else
lua_rawgeti(lua, LUA_REGISTRYINDEX, shview->layer->lua_regid);
return 1;
}
static int
lua_shell_env_view_set_layer(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct lua_shell_layer *shlayer = get_layer_from_arg(lua, 2);
shview->layer = shlayer;
weston_view_move_to_layer(shview->view, &shlayer->layer.view_list);
return 0;
}
static int
lua_shell_env_view_unset_layer(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
shview->layer = NULL;
weston_view_move_to_layer(shview->view, NULL);
return 0;
}
static int
lua_shell_env_view_get_position(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
lua_pushinteger(lua, shview->view->geometry.pos_offset.x);
lua_pushinteger(lua, shview->view->geometry.pos_offset.y);
return 2;
}
static int
lua_shell_env_view_set_position(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
int x = luaL_checkinteger(lua, 2);
int y = luaL_checkinteger(lua, 3);
struct weston_coord_global pos;
pos.c = weston_coord(x, y);
weston_view_set_position(shview->view, pos);
weston_view_update_transform(shview->view);
return 0;
}
static int
lua_shell_env_view_get_dimensions(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
int width, height;
if (shview->view->geometry.scissor_enabled) {
pixman_box32_t *extents =
pixman_region32_extents(&shview->view->geometry.scissor);
width = extents->x2 - extents->x1;
height = extents->y2 - extents->y1;
} else {
struct weston_output *output;
struct weston_compositor *ec =
shview->view->surface->compositor;
width = 0;
height = 0;
wl_list_for_each(output, &ec->output_list, link) {
width = MAX(width, output->pos.c.x + output->width);
height = MAX(height, output->pos.c.y + output->height);
}
}
lua_pushinteger(lua, width);
lua_pushinteger(lua, height);
return 2;
}
static int
lua_shell_env_view_set_dimensions(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct lua_shell_surface *shsurf = shview->surface;
int width = luaL_checkinteger(lua, 2);
int height = luaL_checkinteger(lua, 3);
if (!shsurf)
return 0;
weston_desktop_surface_set_size(shsurf->desktop_surface, width, height);
return 0;
}
static int
lua_shell_env_view_set_output(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct lua_shell_output *shoutput = get_output_from_arg(lua, 2);
weston_view_set_output(shview->view, shoutput->output);
return 0;
}
static int
lua_shell_env_view_get_output(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct weston_output *output = shview->view->output;
uint32_t regid = LUA_REFNIL;
if (output) {
struct lua_shell_output *shoutput;
shoutput = weston_output_get_shell_private(output);
regid = shoutput->lua_regid;
}
lua_rawgeti(lua, LUA_REGISTRYINDEX, regid);
return 1;
}
static int
lua_shell_env_view_get_alpha(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
lua_pushnumber(lua, shview->view->alpha);
return 1;
}
static int
lua_shell_env_view_set_alpha(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
float alpha = luaL_checknumber(lua, 2);
weston_view_set_alpha(shview->view, alpha);
return 0;
}
static int
lua_shell_env_view_activate(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct lua_shell_seat *shseat = get_seat_from_arg(lua, 2);
struct weston_surface *main_surface =
weston_surface_get_main_surface(shview->view->surface);
struct lua_shell_surface *shsurf = get_lua_shell_surface(main_surface);
if (!shsurf)
return 0;
weston_view_activate_input(shview->view, shseat->seat, WESTON_ACTIVATE_FLAG_NONE);
weston_desktop_surface_set_activated(shsurf->desktop_surface, true);
return 0;
}
static int
lua_shell_env_view_deactivate(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct weston_surface *main_surface =
weston_surface_get_main_surface(shview->view->surface);
struct lua_shell_surface *shsurf = get_lua_shell_surface(main_surface);
if (!shsurf)
return 0;
weston_desktop_surface_set_activated(shsurf->desktop_surface, false);
return 0;
}
static int
lua_shell_env_view_move_behind_other_view(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct lua_shell_view *other_shview = get_view_from_arg(lua, 2);
struct lua_shell *shell = shview->shell;
struct weston_compositor *wc = shell->compositor;
weston_assert_true(wc, &other_shview->view->layer_link.layer);
weston_view_move_to_layer(shview->view,
&other_shview->view->layer_link);
return 0;
}
static int
lua_shell_env_view_move_in_front_of_other_view(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
struct lua_shell_view *other_shview = get_view_from_arg(lua, 2);
struct lua_shell *shell = shview->shell;
struct weston_compositor *wc = shell->compositor;
weston_assert_true(wc, &other_shview->view->layer_link.layer);
weston_view_move_before_layer_entry(shview->view,
&other_shview->view->layer_link);
return 0;
}
static int
lua_shell_env_view_dispose(struct lua_State *lua)
{
struct lua_shell_view *shview = get_view_from_arg(lua, 1);
lua_shell_view_dispose(shview);
return 0;
}
static void
lua_shell_env_init_surface_view(struct lua_shell *shell)
{
static const luaL_Reg lua_surface_obj_funcs[] = {
{ "__gc", lgc },
{ "get_role", lua_shell_env_surface_get_role },
{ "get_app_id", lua_shell_env_surface_get_app_id },
{ "get_title", lua_shell_env_surface_get_title },
{ "get_geometry", lua_shell_env_surface_get_geometry},
{ "get_dimensions", lua_shell_env_surface_get_dimensions },
{ "get_output", lua_shell_env_surface_get_output },
{ "get_private", lua_shell_env_surface_get_private },
{ "set_output", lua_shell_env_surface_set_output },
{ "set_private", lua_shell_env_surface_set_private },
{ "set_state_fullscreen", lua_shell_env_surface_set_state_fullscreen },
{ "get_state_fullscreen", lua_shell_env_surface_get_state_fullscreen },
{ "set_state_maximized", lua_shell_env_surface_set_state_maximized },
{ "get_state_maximized", lua_shell_env_surface_get_state_maximized },
{ "set_state_normal", lua_shell_env_surface_set_state_normal },
{ "get_parent", lua_shell_env_surface_get_parent },
{ "get_views", lua_shell_env_surface_get_views },
{ "create_view", lua_shell_env_surface_create_view },
{ "map", lua_shell_env_surface_map },
{ "is_mapped", lua_shell_env_surface_is_mapped },
{ NULL, NULL }
};
static const luaL_Reg lua_view_obj_funcs[] = {
{ "__gc", lgc },
{ "get_surface", lua_shell_env_view_get_surface },
{ "get_private_surface", lua_shell_env_view_get_private_surface },
{ "get_layer", lua_shell_env_view_get_layer },
{ "set_layer", lua_shell_env_view_set_layer },
{ "unset_layer", lua_shell_env_view_unset_layer },
{ "get_position", lua_shell_env_view_get_position },
{ "set_position", lua_shell_env_view_set_position },
{ "get_dimensions", lua_shell_env_view_get_dimensions },
{ "set_dimensions", lua_shell_env_view_set_dimensions },
{ "get_output", lua_shell_env_view_get_output },
{ "set_output", lua_shell_env_view_set_output },
{ "get_alpha", lua_shell_env_view_get_alpha },
{ "set_alpha", lua_shell_env_view_set_alpha },
{ "activate", lua_shell_env_view_activate },
{ "deactivate", lua_shell_env_view_deactivate },
{ "move_behind_other_view", lua_shell_env_view_move_behind_other_view },
{ "move_in_front_of_other_view", lua_shell_env_view_move_in_front_of_other_view },
{ "dispose", lua_shell_env_view_dispose },
{ NULL, NULL }
};
luaL_newmetatable(shell->lua, "weston.surface");
lua_pushvalue(shell->lua, -1);
lua_setfield(shell->lua, -2, "__index");
luaL_setfuncs(shell->lua, lua_surface_obj_funcs, 0);
luaL_newmetatable(shell->lua, "weston.view");
lua_pushvalue(shell->lua, -1);
lua_setfield(shell->lua, -2, "__index");
luaL_setfuncs(shell->lua, lua_view_obj_funcs, 0);
}
static int
lua_shell_env_layer_get_position(struct lua_State *lua)
{
struct lua_shell_layer *shlayer = get_layer_from_arg(lua, 1);
lua_pushinteger(lua, shlayer->layer.position);
return 1;
}
static int
lua_shell_env_layer_set_position(struct lua_State *lua)
{
struct lua_shell_layer *shlayer = get_layer_from_arg(lua, 1);
int64_t position = luaL_checkinteger(lua, 2);
weston_layer_set_position(&shlayer->layer, position);
return 0;
}
static int
lua_shell_env_layer_get_views(struct lua_State *lua)
{
struct lua_shell_layer *shlayer = get_layer_from_arg(lua, 1);
struct lua_shell_view *shview;
struct lua_shell *shell = shlayer->shell;
struct weston_view *view;
lua_newtable(shell->lua);
wl_list_for_each(view, &shlayer->layer.view_list.link, layer_link.link) {
char buf[32];
shview = get_lua_shell_view(view);
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shview->lua_regid);
snprintf(buf, sizeof(buf), "view-%"PRIu32, shview->lua_regid);
lua_setfield(shell->lua, -2, buf);
}
return 1;
}
static void
lua_shell_env_init_enums(struct lua_shell *shell)
{
/* input-event-codes */
LUA_ENUM(BTN_LEFT);
LUA_ENUM(BTN_RIGHT);
/* enum weston_layer_position */
LUA_ENUM(WESTON_LAYER_POSITION_NONE);
LUA_ENUM(WESTON_LAYER_POSITION_HIDDEN);
LUA_ENUM(WESTON_LAYER_POSITION_BACKGROUND);
LUA_ENUM(WESTON_LAYER_POSITION_BOTTOM_UI);
LUA_ENUM(WESTON_LAYER_POSITION_NORMAL);
LUA_ENUM(WESTON_LAYER_POSITION_UI);
LUA_ENUM(WESTON_LAYER_POSITION_FULLSCREEN);
LUA_ENUM(WESTON_LAYER_POSITION_TOP_UI);
LUA_ENUM(WESTON_LAYER_POSITION_LOCK);
LUA_ENUM(WESTON_LAYER_POSITION_CURSOR);
LUA_ENUM(WESTON_LAYER_POSITION_FADE);
}
static void
lua_shell_env_init_layer(struct lua_shell *shell)
{
static const luaL_Reg lua_layer_obj_funcs[] = {
{ "__gc", lgc },
{ "get_position", lua_shell_env_layer_get_position },
{ "set_position", lua_shell_env_layer_set_position },
{ "get_views", lua_shell_env_layer_get_views },
{ NULL, NULL }
};
luaL_newmetatable(shell->lua, "weston.layer");
lua_pushvalue(shell->lua, -1);
lua_setfield(shell->lua, -2, "__index");
luaL_setfuncs(shell->lua, lua_layer_obj_funcs, 0);
}
static int
lua_shell_env_curtain_set_color(struct lua_State *lua)
{
struct lua_shell_curtain *shcurtain = get_curtain_from_arg(lua, 1);
unsigned int color = luaL_checkinteger(lua, 2);
assert(!shcurtain->view);
shcurtain->params.r = ((color >> 16) & 0xff) / 255.0;
shcurtain->params.g = ((color >> 8) & 0xff) / 255.0;
shcurtain->params.b = ((color >> 0) & 0xff) / 255.0;
shcurtain->params.a = ((color >> 24) & 0xff) / 255.0;
return 0;
}
static int
lua_shell_env_curtain_set_position(struct lua_State *lua)
{
struct lua_shell_curtain *shcurtain = get_curtain_from_arg(lua, 1);
unsigned int x = luaL_checkinteger(lua, 2);
unsigned int y = luaL_checkinteger(lua, 3);
assert(!shcurtain->view);
shcurtain->params.pos.c = weston_coord(x, y);
return 0;
}
static int
lua_shell_env_curtain_set_dimensions(struct lua_State *lua)
{
struct lua_shell_curtain *shcurtain = get_curtain_from_arg(lua, 1);
unsigned int w = luaL_checkinteger(lua, 2);
unsigned int h = luaL_checkinteger(lua, 3);
assert(!shcurtain->view);
shcurtain->params.width = w;
shcurtain->params.height = h;
return 0;
}
static int
lua_shell_env_curtain_set_capture_input(struct lua_State *lua)
{
struct lua_shell_curtain *shcurtain = get_curtain_from_arg(lua, 1);
bool capture_input = lua_toboolean(lua, 2);
assert(!shcurtain->view);
shcurtain->params.capture_input = capture_input;
return 0;
}
static int
lua_shell_curtain_get_label(struct weston_surface *surface,
char *buf, size_t len)
{
struct lua_shell_curtain *shcurtain = surface->committed_private;
const char *name = "unnamed";
if (shcurtain->name)
name = shcurtain->name;
return snprintf(buf, len, "%s (curtain)", name);
}
static int
lua_shell_env_curtain_get_view(struct lua_State *lua)
{
struct lua_shell_curtain *shcurtain = get_curtain_from_arg(lua, 1);
struct lua_shell *shell = shcurtain->shell;
struct lua_shell_view *shview = shcurtain->view;
struct weston_view *view;
if (shview)
goto done;
shcurtain->params.get_label = lua_shell_curtain_get_label;
shcurtain->params.surface_private = shcurtain;
shcurtain->curtain = weston_shell_utils_curtain_create(shell->compositor,
&shcurtain->params);
shview = lxzalloc(lua, sizeof(*shview), "weston.view");
shview->view = shcurtain->curtain->view;
shcurtain->view = shview;
shview->shell = shell;
view = shview->view;
shview->view_destroy_listener.notify = lua_shell_view_handle_destroy;
wl_signal_add(&view->destroy_signal, &shview->view_destroy_listener);
wl_list_insert(shview->shell->view_list.prev, &shview->link);
wl_list_init(&shview->surface_link);
done:
lua_rawgeti(lua, LUA_REGISTRYINDEX, shview->lua_regid);
return 1;
}
static int
lua_shell_env_curtain_dispose(struct lua_State *lua)
{
struct lua_shell_curtain *shcurtain = get_curtain_from_arg(lua, 1);
lua_shell_curtain_dispose(shcurtain);
return 0;
}
static void
lua_shell_env_init_curtain(struct lua_shell *shell)
{
static const luaL_Reg lua_layer_obj_funcs[] = {
{ "__gc", lgc },
{ "set_color", lua_shell_env_curtain_set_color },
{ "set_position", lua_shell_env_curtain_set_position },
{ "set_dimensions", lua_shell_env_curtain_set_dimensions },
{ "set_capture_input", lua_shell_env_curtain_set_capture_input },
{ "get_view", lua_shell_env_curtain_get_view },
{ "dispose", lua_shell_env_curtain_dispose },
{ NULL, NULL }
};
luaL_newmetatable(shell->lua, "weston.curtain");
lua_pushvalue(shell->lua, -1);
lua_setfield(shell->lua, -2, "__index");
luaL_setfuncs(shell->lua, lua_layer_obj_funcs, 0);
}
static void
lua_shell_env_destroy_callbacks(struct lua_shell *shell)
{
struct lua_shell_callback *cb = shell->callbacks;
unsigned int i;
for (i = 0; i < LUA_SHELL_NUM_CB; i++) {
if (!cb[i].regid)
continue;
luaL_unref(shell->lua, LUA_REGISTRYINDEX, cb[i].regid);
cb[i].regid = 0;
}
}
static bool
lua_shell_env_init_callbacks(struct lua_shell *shell)
{
struct lua_State *lua = shell->lua;
int objtype;
unsigned int i;
struct lua_shell_callback *cb = shell->callbacks;
cb[LUA_SHELL_CB_INIT].name = "init";
cb[LUA_SHELL_CB_SURFACE_ADDED].name = "surface_added";
cb[LUA_SHELL_CB_KEYBOARD_FOCUS].name = "keyboard_focus";
cb[LUA_SHELL_CB_SEAT_CREATE].name = "seat_create";
cb[LUA_SHELL_CB_SURFACE_ADDED].name = "surface_added";
cb[LUA_SHELL_CB_SURFACE_COMMITTED].name = "surface_committed";
cb[LUA_SHELL_CB_SURFACE_MOVE].name = "surface_move";
cb[LUA_SHELL_CB_SURFACE_REMOVED].name = "surface_removed";
cb[LUA_SHELL_CB_SURFACE_RESIZE].name = "surface_resize";
cb[LUA_SHELL_CB_SURFACE_FULLSCREEN].name = "surface_fullscreen";
cb[LUA_SHELL_CB_SURFACE_MAXIMIZE].name = "surface_maximize";
cb[LUA_SHELL_CB_SET_XWAYLAND_POSITION].name = "set_xwayland_position";
cb[LUA_SHELL_CB_OUTPUT_CREATE].name = "output_create";
cb[LUA_SHELL_CB_OUTPUT_RESIZED].name = "output_resized";
cb[LUA_SHELL_CB_OUTPUT_MOVED].name = "output_moved";
lua_getglobal(lua, "lua_shell_callbacks");
if (!lua_istable(lua, -1)) {
weston_log("lua_shell_callbacks table missing\n");
return false;
}
for (i = 0; i < LUA_SHELL_NUM_CB; i++) {
assert(shell->callbacks[i].name);
objtype = lua_getfield(lua, -1, shell->callbacks[i].name);
/* No callback provided for this one. */
if (objtype == LUA_TNIL) {
lua_pop(lua, 1);
continue;
}
if (objtype != LUA_TFUNCTION) {
weston_log("LUA callback for '%s' was not a function!\n",
shell->callbacks[i].name);
return false;
}
shell->callbacks[i].regid = luaL_ref(lua, LUA_REGISTRYINDEX);
}
return true;
}
static int
lua_shell_env_get_outputs(struct lua_State *lua)
{
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_output *shoutput;
lua_newtable(shell->lua); /* outputs[] = { */
wl_list_for_each(shoutput, &shell->output_list, link) {
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shoutput->lua_regid);
lua_setfield(shell->lua, -2, shoutput->output->name);
}
return 1;
}
static int
lua_shell_env_get_seats(struct lua_State *lua)
{
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_seat *shseat;
lua_newtable(shell->lua); /* seats[] = { */
wl_list_for_each(shseat, &shell->seat_list, link) {
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shseat->lua_regid);
lua_setfield(shell->lua, -2, shseat->seat->seat_name);
}
return 1;
}
static int
lua_shell_env_get_surfaces(struct lua_State *lua)
{
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_surface *shsurf;
lua_newtable(shell->lua); /* surfaces[] = { */
wl_list_for_each(shsurf, &shell->surface_list, link) {
char buf[32];
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shsurf->lua_regid);
snprintf(buf, sizeof(buf), "surf-%" PRIu32, shsurf->lua_regid);
lua_setfield(shell->lua, -2, buf);
}
return 1;
}
static int
lua_shell_env_get_views(struct lua_State *lua)
{
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_view *shview;
lua_newtable(shell->lua); /* views[] = { */
wl_list_for_each(shview, &shell->view_list, link) {
char buf[32];
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shview->lua_regid);
snprintf(buf, sizeof(buf), "view-%" PRIu32, shview->lua_regid);
lua_setfield(shell->lua, -2, buf);
}
return 1;
}
static int
lua_shell_env_get_layers(struct lua_State *lua)
{
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_layer *shlayer;
lua_newtable(shell->lua); /* layers[] = { */
wl_list_for_each(shlayer, &shell->layer_list, link) {
char buf[32];
lua_rawgeti(shell->lua, LUA_REGISTRYINDEX, shlayer->lua_regid);
snprintf(buf, sizeof(buf), "layer-0x%" PRIx64, shlayer->layer.position);
lua_setfield(shell->lua, -2, buf);
}
return 1;
}
static int
lua_shell_env_create_layer(struct lua_State *lua)
{
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_layer *shlayer;
shlayer = lxzalloc(shell->lua, sizeof *shlayer, "weston.layer");
shlayer->shell = shell;
weston_layer_init(&shlayer->layer, shell->compositor);
wl_list_insert(&shell->layer_list, &shlayer->link);
lua_rawgeti(lua, LUA_REGISTRYINDEX, shlayer->lua_regid);
return 1;
}
static int
lua_shell_env_create_curtain(struct lua_State *lua)
{
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_curtain *shcurtain;
const char *name = lua_tostring(lua, -1);
shcurtain = lxzalloc(shell->lua, sizeof *shcurtain, "weston.curtain");
shcurtain->shell = shell;
wl_list_insert(&shell->curtain_list, &shcurtain->link);
lua_rawgeti(lua, LUA_REGISTRYINDEX, shcurtain->lua_regid);
if (name)
shcurtain->name = strdup(name);
return 1;
}
static int
timer_wrapper(void *data)
{
struct lua_shell_timer *timer = data;
struct lua_shell *shell = timer->shell;
struct lua_State *lua = shell->lua;
lua_rawgeti(lua, LUA_REGISTRYINDEX, timer->cb_regid);
lua_rawgeti(lua, LUA_REGISTRYINDEX, timer->lua_private_regid);
lua_shell_call_function(shell, "[timer callback]", 1, 0);
luaL_unref(lua, LUA_REGISTRYINDEX, timer->cb_regid);
luaL_unref(lua, LUA_REGISTRYINDEX, timer->lua_private_regid);
wl_event_source_remove(timer->event_source);
free(data);
return 0;
}
static int
lua_shell_env_set_timer(struct lua_State *lua)
{
struct wl_event_loop *loop;
struct lua_shell *shell = get_shell_from_arg(lua, 1);
struct lua_shell_timer *timer;
int timeout = luaL_checkinteger(lua, 4);
luaL_checktype(lua, 2, LUA_TFUNCTION);
lua_pop(lua, 1);
timer = xzalloc(sizeof *timer);
timer->shell = shell;
loop = wl_display_get_event_loop(shell->compositor->wl_display);
timer->event_source = wl_event_loop_add_timer(loop, timer_wrapper, timer);
timer->lua_private_regid = luaL_ref(lua, LUA_REGISTRYINDEX);
timer->cb_regid = luaL_ref(lua, LUA_REGISTRYINDEX);
wl_event_source_timer_update(timer->event_source, timeout);
return 0;
}
static void
lua_shell_env_init_weston(struct lua_shell *shell)
{
static const luaL_Reg lua_global_obj_funcs[] = {
{ "get_outputs", lua_shell_env_get_outputs, },
{ "get_seats", lua_shell_env_get_seats, },
{ "get_surfaces", lua_shell_env_get_surfaces, },
{ "get_layers", lua_shell_env_get_layers, },
{ "get_views", lua_shell_env_get_views, },
{ "create_layer", lua_shell_env_create_layer, },
{ "create_curtain", lua_shell_env_create_curtain, },
{ "set_timer", lua_shell_env_set_timer, },
{ "add_touch_binding", lua_shell_env_add_touch_binding, },
{ "add_button_binding", lua_shell_env_add_button_binding, },
{ NULL, NULL }
};
/* instantiate the type */
luaL_newmetatable(shell->lua, "weston.global");
lua_pushvalue(shell->lua, -1);
lua_setfield(shell->lua, -2, "__index");
luaL_setfuncs(shell->lua, lua_global_obj_funcs, 0);
/* create a new typed variable */
struct lua_shell **out = lua_newuserdata(shell->lua, sizeof(*out));
*out = shell;
luaL_getmetatable(shell->lua, "weston.global");
lua_setmetatable(shell->lua, -2);
lua_setglobal(shell->lua, "weston");
}
static int
lua_shell_init_env(struct lua_shell *shell, const char *script)
{
/* set up the core Lua interpreter */
shell->lua = luaL_newstate();
if (!shell->lua) {
weston_log("Couldn't initialize Lua environment\n");
return -1;
}
/* add Lua standard libraries */
luaL_openlibs(shell->lua);
/* initialise our types and global singleton */
lua_shell_env_init_enums(shell);
lua_shell_env_init_curtain(shell);
lua_shell_env_init_output(shell);
lua_shell_env_init_seat(shell);
lua_shell_env_init_surface_view(shell);
lua_shell_env_init_layer(shell);
lua_shell_env_init_weston(shell);
/* Read the initial lua setup script */
if (luaL_dofile(shell->lua, script) != LUA_OK) {
const char *error;
error = lua_tostring(shell->lua, -1);
weston_log("Lua script '%s' is not ok: %s\n", script, error);
return -1;
}
if (!lua_shell_env_init_callbacks(shell))
return -1;
if (!lua_shell_push_function(shell, LUA_SHELL_CB_INIT)) {
weston_log("Lua init-script missing init function\n");
return -1;
}
if (!lua_shell_call_function(shell, "init", 0, 0))
return -1;
return 0;
}
static void
lua_shell_destroy(struct wl_listener *listener, void *data)
{
struct lua_shell *shell =
container_of(listener, struct lua_shell, destroy_listener);
struct lua_shell_output *shoutput, *shoutput_next;
struct lua_shell_seat *shseat, *shseat_next;
struct lua_shell_view *shview, *shview_next;
struct lua_shell_surface *shsurf, *shsurf_next;
struct lua_shell_layer *shlayer, *shlayer_next;
struct lua_shell_curtain *shcurtain, *shcurtain_next;
struct lua_shell_binding *shbinding, *shbinding_next;
wl_list_remove(&shell->destroy_listener.link);
wl_list_remove(&shell->output_created_listener.link);
wl_list_remove(&shell->output_resized_listener.link);
wl_list_remove(&shell->output_moved_listener.link);
wl_list_remove(&shell->seat_created_listener.link);
wl_list_remove(&shell->transform_listener.link);
wl_list_for_each_safe(shcurtain, shcurtain_next, &shell->curtain_list, link)
lua_shell_curtain_dispose(shcurtain);
wl_list_for_each_safe(shoutput, shoutput_next, &shell->output_list, link)
lua_shell_output_destroy(shoutput);
wl_list_for_each_safe(shseat, shseat_next, &shell->seat_list, link)
lua_shell_seat_destroy(shseat);
wl_list_for_each_safe(shsurf, shsurf_next, &shell->surface_list, link)
lua_shell_surface_dispose(shsurf);
wl_list_for_each_safe(shview, shview_next, &shell->view_list, link)
lua_shell_view_dispose(shview);
wl_list_for_each_safe(shlayer, shlayer_next, &shell->layer_list, link)
lua_shell_layer_dispose(shlayer);
wl_list_for_each_safe(shbinding, shbinding_next, &shell->binding_list, link)
lua_shell_binding_destroy(shbinding);
weston_desktop_destroy(shell->desktop);
if (shell->lua) {
lua_shell_env_destroy_callbacks(shell);
lua_close(shell->lua);
}
if (shell->config)
weston_config_destroy(shell->config);
free(shell);
}
WL_EXPORT int
wet_shell_init(struct weston_compositor *ec,
int *argc, char *argv[])
{
struct weston_config_section *shell_section = NULL;
char *script = NULL;
struct lua_shell *shell;
struct weston_seat *seat;
struct weston_output *output;
const char *config_file;
const struct weston_option options[] = {
{ WESTON_OPTION_STRING, "lua-script", 0, &script },
};
shell = zalloc(sizeof *shell);
if (shell == NULL)
return -1;
shell->compositor = ec;
wl_list_init(&shell->surface_list);
wl_list_init(&shell->layer_list);
wl_list_init(&shell->view_list);
wl_list_init(&shell->timer_list);
wl_list_init(&shell->seat_list);
wl_list_init(&shell->output_list);
wl_list_init(&shell->curtain_list);
wl_list_init(&shell->binding_list);
/* Init these because it makes cleanup nicer if
* lua_shell_init_env() fails */
wl_list_init(&shell->seat_created_listener.link);
wl_list_init(&shell->output_created_listener.link);
wl_list_init(&shell->output_resized_listener.link);
wl_list_init(&shell->output_moved_listener.link);
if (!weston_compositor_add_destroy_listener_once(ec,
&shell->destroy_listener,
lua_shell_destroy)) {
free(shell);
return 0;
}
shell->transform_listener.notify = transform_handler;
wl_signal_add(&ec->transform_signal, &shell->transform_listener);
config_file = weston_config_get_name_from_env();
shell->config = weston_config_parse(config_file);
parse_options(options, ARRAY_LENGTH(options), argc, argv);
shell->desktop = weston_desktop_create(ec, &lua_shell_desktop_api,
shell);
if (!shell->desktop)
return -1;
if (shell->config)
shell_section = weston_config_get_section(shell->config, "shell", NULL, NULL);
if (!script && shell_section)
weston_config_section_get_string(shell_section, "lua-script", &script, NULL);
if (!script) {
weston_log("No LUA script\n");
return -1;
}
if (lua_shell_init_env(shell, script)) {
free(script);
return -1;
}
free(script);
wl_list_for_each(seat, &ec->seat_list, link)
lua_shell_seat_create(shell, seat);
shell->seat_created_listener.notify = lua_shell_handle_seat_created;
wl_signal_add(&ec->seat_created_signal, &shell->seat_created_listener);
wl_list_for_each(output, &ec->output_list, link)
lua_shell_output_create(shell, output);
shell->output_created_listener.notify = lua_shell_handle_output_created;
wl_signal_add(&ec->output_created_signal, &shell->output_created_listener);
shell->output_resized_listener.notify = lua_shell_handle_output_resized;
wl_signal_add(&ec->output_resized_signal, &shell->output_resized_listener);
shell->output_moved_listener.notify = lua_shell_handle_output_moved;
wl_signal_add(&ec->output_moved_signal, &shell->output_moved_listener);
screenshooter_create(ec);
return 0;
}