kiwmi/kiwmi/luak/kiwmi_server.c
tiosgz 8693d2dd13 Add controls for verbosity level
This allows the config to be exactly as verbose as kiwmi, or to change
the verbosity level at runtime (e.g. using kiwmic).

It uses numbers, because they are much easier to handle on both sides
and add only little inconvenience to the user.
2021-08-13 13:46:35 +00:00

583 lines
15 KiB
C

/* Copyright (c), Charlotte Meyer <dev@buffet.sh>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "luak/kiwmi_server.h"
#include <stdlib.h>
#include <unistd.h>
#include <lauxlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/util/log.h>
#include "desktop/view.h"
#include "input/cursor.h"
#include "input/input.h"
#include "input/seat.h"
#include "luak/kiwmi_cursor.h"
#include "luak/kiwmi_keyboard.h"
#include "luak/kiwmi_lua_callback.h"
#include "luak/kiwmi_output.h"
#include "luak/kiwmi_view.h"
#include "luak/lua_compat.h"
#include "server.h"
static int
l_kiwmi_server_active_output(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
struct kiwmi_server *server = obj->object;
struct kiwmi_output *output = desktop_active_output(server);
lua_pushcfunction(L, luaK_kiwmi_output_new);
lua_pushlightuserdata(L, server->lua);
lua_pushlightuserdata(L, output);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 1;
}
static int
l_kiwmi_server_cursor(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
struct kiwmi_server *server = obj->object;
lua_pushcfunction(L, luaK_kiwmi_cursor_new);
lua_pushlightuserdata(L, server->lua);
lua_pushlightuserdata(L, server->input.cursor);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 1;
}
static int
l_kiwmi_server_focused_view(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
struct kiwmi_server *server = obj->object;
if (!server->input.seat->focused_view) {
return 0;
}
lua_pushcfunction(L, luaK_kiwmi_view_new);
lua_pushlightuserdata(L, server->lua);
lua_pushlightuserdata(L, server->input.seat->focused_view);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 1;
}
static int
l_kiwmi_server_output_at(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TNUMBER); // lx
luaL_checktype(L, 3, LUA_TNUMBER); // ly
struct kiwmi_server *server = obj->object;
double lx = lua_tonumber(L, 2);
double ly = lua_tonumber(L, 3);
struct wlr_output *wlr_output =
wlr_output_layout_output_at(server->desktop.output_layout, lx, ly);
if (wlr_output) {
lua_pushcfunction(L, luaK_kiwmi_output_new);
lua_pushlightuserdata(L, obj->lua);
lua_pushlightuserdata(L, wlr_output->data);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
} else {
lua_pushnil(L);
}
return 1;
}
static int
l_kiwmi_server_quit(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
struct kiwmi_server *server = obj->object;
wl_display_terminate(server->wl_display);
return 0;
}
static int
kiwmi_server_schedule_handler(void *data)
{
struct kiwmi_lua_callback *lc = data;
lua_State *L = lc->server->lua->L;
lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref);
lua_pushvalue(L, -1);
if (lua_pcall(L, 1, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
}
wl_event_source_remove(lc->event_source);
luaL_unref(L, LUA_REGISTRYINDEX, lc->callback_ref);
wl_list_remove(&lc->link);
free(lc);
return 0;
}
static int
l_kiwmi_server_schedule(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TNUMBER); // delay
luaL_checktype(L, 3, LUA_TFUNCTION); // callback
struct kiwmi_server *server = obj->object;
struct kiwmi_lua_callback *lc = malloc(sizeof(*lc));
if (!lc) {
return luaL_error(L, "failed to allocate kiwmi_lua_callback");
}
int delay = lua_tonumber(L, 2);
lc->event_source = wl_event_loop_add_timer(
server->wl_event_loop, kiwmi_server_schedule_handler, lc);
if (wl_event_source_timer_update(lc->event_source, delay) < 0) {
free(lc);
return luaL_error(L, "failed to arm timer");
}
lc->server = server;
lc->callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
wl_list_insert(&server->lua->scheduled_callbacks, &lc->link);
return 0;
}
static int
l_kiwmi_server_set_verbosity(lua_State *L)
{
luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TNUMBER);
int verbosity = lua_tointeger(L, 2);
if (verbosity < WLR_SILENT) {
verbosity = WLR_SILENT;
} else if (verbosity >= WLR_LOG_IMPORTANCE_LAST) {
verbosity = WLR_DEBUG;
}
wlr_log_init((enum wlr_log_importance)verbosity, NULL);
return 0;
}
static int
l_kiwmi_server_spawn(lua_State *L)
{
luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TSTRING);
const char *command = lua_tostring(L, 2);
pid_t pid = fork();
if (pid < 0) {
return luaL_error(L, "Failed to run command (fork)");
}
if (pid == 0) {
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
_exit(EXIT_FAILURE);
}
lua_pushinteger(L, pid);
return 1;
}
static int
l_kiwmi_server_stop_interactive(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
struct kiwmi_server *server = obj->object;
server->input.cursor->cursor_mode = KIWMI_CURSOR_PASSTHROUGH;
return 0;
}
static int
l_kiwmi_server_unfocus(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
struct kiwmi_server *server = obj->object;
seat_focus_view(server->input.seat, NULL);
return 0;
}
static int
l_kiwmi_server_verbosity(lua_State *L)
{
luaL_checkudata(L, 1, "kiwmi_server");
int verbosity = (int)wlr_log_get_verbosity();
lua_pushinteger(L, verbosity);
return 1;
}
static int
l_kiwmi_server_view_at(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TNUMBER); // lx
luaL_checktype(L, 3, LUA_TNUMBER); // ly
struct kiwmi_server *server = obj->object;
double lx = lua_tonumber(L, 2);
double ly = lua_tonumber(L, 3);
struct wlr_surface *surface;
double sx;
double sy;
struct kiwmi_view *view =
view_at(&server->desktop, lx, ly, &surface, &sx, &sy);
if (view) {
lua_pushcfunction(L, luaK_kiwmi_view_new);
lua_pushlightuserdata(L, obj->lua);
lua_pushlightuserdata(L, view);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
} else {
lua_pushnil(L);
}
return 1;
}
static const luaL_Reg kiwmi_server_methods[] = {
{"active_output", l_kiwmi_server_active_output},
{"cursor", l_kiwmi_server_cursor},
{"focused_view", l_kiwmi_server_focused_view},
{"on", luaK_callback_register_dispatch},
{"output_at", l_kiwmi_server_output_at},
{"quit", l_kiwmi_server_quit},
{"schedule", l_kiwmi_server_schedule},
{"set_verbosity", l_kiwmi_server_set_verbosity},
{"spawn", l_kiwmi_server_spawn},
{"stop_interactive", l_kiwmi_server_stop_interactive},
{"unfocus", l_kiwmi_server_unfocus},
{"verbosity", l_kiwmi_server_verbosity},
{"view_at", l_kiwmi_server_view_at},
{NULL, NULL},
};
static void
kiwmi_server_on_keyboard_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_lua_callback *lc = wl_container_of(listener, lc, listener);
struct kiwmi_server *server = lc->server;
lua_State *L = server->lua->L;
struct kiwmi_keyboard *keyboard = data;
lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref);
lua_pushcfunction(L, luaK_kiwmi_keyboard_new);
lua_pushlightuserdata(L, server->lua);
lua_pushlightuserdata(L, keyboard);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
return;
}
if (lua_pcall(L, 1, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
}
static void
kiwmi_server_on_output_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_lua_callback *lc = wl_container_of(listener, lc, listener);
struct kiwmi_server *server = lc->server;
lua_State *L = server->lua->L;
struct kiwmi_output *output = data;
lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref);
lua_pushcfunction(L, luaK_kiwmi_output_new);
lua_pushlightuserdata(L, server->lua);
lua_pushlightuserdata(L, output);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
return;
}
if (lua_pcall(L, 1, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
}
static void
kiwmi_server_on_request_active_output_notify(
struct wl_listener *listener,
void *data)
{
struct kiwmi_lua_callback *lc = wl_container_of(listener, lc, listener);
struct kiwmi_server *server = lc->server;
lua_State *L = server->lua->L;
struct kiwmi_output **output = data;
lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref);
if (lua_pcall(L, 0, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return;
}
if (!lua_isnil(L, -1)) {
struct kiwmi_object *obj;
struct kiwmi_object **objp;
if (!(objp = luaK_toudata(L, -1, "kiwmi_output"))) {
wlr_log(
WLR_ERROR,
"kiwmi_output expected, got %s",
luaL_typename(L, -1));
return;
}
obj = *objp;
if (!obj->valid) {
wlr_log(WLR_ERROR, "kiwmi_output no longer valid");
return;
}
*output = obj->object;
}
}
static void
kiwmi_server_on_view_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_lua_callback *lc = wl_container_of(listener, lc, listener);
struct kiwmi_server *server = lc->server;
lua_State *L = server->lua->L;
struct kiwmi_view *view = data;
lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref);
lua_pushcfunction(L, luaK_kiwmi_view_new);
lua_pushlightuserdata(L, server->lua);
lua_pushlightuserdata(L, view);
if (lua_pcall(L, 2, 1, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
return;
}
if (lua_pcall(L, 1, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
}
static int
l_kiwmi_server_on_keyboard(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TFUNCTION);
struct kiwmi_server *server = obj->object;
lua_pushcfunction(L, luaK_kiwmi_lua_callback_new);
lua_pushlightuserdata(L, server);
lua_pushvalue(L, 2);
lua_pushlightuserdata(L, kiwmi_server_on_keyboard_notify);
lua_pushlightuserdata(L, &server->input.events.keyboard_new);
lua_pushlightuserdata(L, obj);
if (lua_pcall(L, 5, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 0;
}
static int
l_kiwmi_server_on_output(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TFUNCTION);
struct kiwmi_server *server = obj->object;
lua_pushcfunction(L, luaK_kiwmi_lua_callback_new);
lua_pushlightuserdata(L, server);
lua_pushvalue(L, 2);
lua_pushlightuserdata(L, kiwmi_server_on_output_notify);
lua_pushlightuserdata(L, &server->desktop.events.new_output);
lua_pushlightuserdata(L, obj);
if (lua_pcall(L, 5, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 0;
}
static int
l_kiwmi_server_on_request_active_output(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TFUNCTION);
struct kiwmi_server *server = obj->object;
lua_pushcfunction(L, luaK_kiwmi_lua_callback_new);
lua_pushlightuserdata(L, server);
lua_pushvalue(L, 2);
lua_pushlightuserdata(L, kiwmi_server_on_request_active_output_notify);
lua_pushlightuserdata(L, &server->desktop.events.request_active_output);
lua_pushlightuserdata(L, obj);
if (lua_pcall(L, 5, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 0;
}
static int
l_kiwmi_server_on_view(lua_State *L)
{
struct kiwmi_object *obj =
*(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_server");
luaL_checktype(L, 2, LUA_TFUNCTION);
struct kiwmi_server *server = obj->object;
lua_pushcfunction(L, luaK_kiwmi_lua_callback_new);
lua_pushlightuserdata(L, server);
lua_pushvalue(L, 2);
lua_pushlightuserdata(L, kiwmi_server_on_view_notify);
lua_pushlightuserdata(L, &server->desktop.events.view_map);
lua_pushlightuserdata(L, obj);
if (lua_pcall(L, 5, 0, 0)) {
wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1));
return 0;
}
return 0;
}
static const luaL_Reg kiwmi_server_events[] = {
{"keyboard", l_kiwmi_server_on_keyboard},
{"output", l_kiwmi_server_on_output},
{"request_active_output", l_kiwmi_server_on_request_active_output},
{"view", l_kiwmi_server_on_view},
{NULL, NULL},
};
int
luaK_kiwmi_server_new(lua_State *L)
{
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); // kiwmi_lua
luaL_checktype(L, 2, LUA_TLIGHTUSERDATA); // kiwmi_server
struct kiwmi_lua *lua = lua_touserdata(L, 1);
struct kiwmi_server *server = lua_touserdata(L, 2);
struct kiwmi_object *obj = luaK_get_kiwmi_object(lua, server, NULL);
struct kiwmi_object **server_ud = lua_newuserdata(L, sizeof(*server_ud));
luaL_getmetatable(L, "kiwmi_server");
lua_setmetatable(L, -2);
*server_ud = obj;
return 1;
}
int
luaK_kiwmi_server_register(lua_State *L)
{
luaL_newmetatable(L, "kiwmi_server");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaC_setfuncs(L, kiwmi_server_methods, 0);
luaC_newlib(L, kiwmi_server_events);
lua_setfield(L, -2, "__events");
lua_pushcfunction(L, luaK_usertype_ref_equal);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, luaK_kiwmi_object_gc);
lua_setfield(L, -2, "__gc");
return 0;
}