mirror of
https://github.com/MezzalunaWM/Mezzaluna.git
synced 2026-03-07 19:49:53 -05:00
Merge branch 'mez_remote_lua' into dev
This commit is contained in:
commit
1f2e333846
7 changed files with 293 additions and 39 deletions
|
|
@ -27,7 +27,9 @@ pub fn build(b: *std.Build) void {
|
|||
scanner.addSystemProtocol("stable/tablet/tablet-v2.xml");
|
||||
scanner.addSystemProtocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml");
|
||||
scanner.addCustomProtocol(b.path("protocols/wlr-layer-shell-unstable-v1.xml"));
|
||||
scanner.addCustomProtocol(b.path("protocols/mez-remote-lua-unstable-v1.xml"));
|
||||
|
||||
scanner.generate("zmez_remote_lua_manager_v1", 1);
|
||||
scanner.generate("wl_compositor", 6);
|
||||
scanner.generate("wl_subcompositor", 1);
|
||||
scanner.generate("wl_shm", 1);
|
||||
|
|
|
|||
89
protocols/mez-remote-lua-unstable-v1.xml
Normal file
89
protocols/mez-remote-lua-unstable-v1.xml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<protocol name="zmez_remote_lua_v1">
|
||||
<copyright>
|
||||
Copyright © 2025 mezzaluna team
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
</copyright>
|
||||
<description summary="Remote lua interface for Mezzaluna">
|
||||
This protocol allows clients to receive lua errors from mez and execute lua
|
||||
on a running mez server with the lua mez api.
|
||||
|
||||
Warning! The protocol described in this file is experimental and backward
|
||||
incompatible changes may be made. Backward compatible changes may be added
|
||||
together with the corresponding interface version bump. Backward
|
||||
incompatible changes are done by bumping the version number in the protocol
|
||||
and interface names and resetting the interface version. Once the protocol
|
||||
is to be declared stable, the 'z' prefix and the version number in the
|
||||
protocol and interface names are removed and the interface version number
|
||||
is reset.
|
||||
</description>
|
||||
|
||||
<interface name="zmez_remote_lua_manager_v1" version="2">
|
||||
<description summary="manage mez layout objects">
|
||||
A global factory for zmez_remote_lua_v1 objects.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the mez_remote_lua_manager object">
|
||||
This request indicates that the client will not use the
|
||||
mez_remote_lua_manager object any more. Objects that have been created
|
||||
through this instance are not affected.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="get_remote">
|
||||
<description summary="create a mez_remote_lua_v1 object">
|
||||
This creates a new mez_remote_lua_v1 object.
|
||||
All lua related communication is done through this interface.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zmez_remote_lua_v1"/>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zmez_remote_lua_v1" version="2">
|
||||
<description summary="receive lua logs and send lua code to execute">
|
||||
This interface allows clients to receive lua logs from the compositor.
|
||||
Additionally it allow for the client to send lua code for the compositor
|
||||
to execute.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the mez_remote_lua_v1 object">
|
||||
This request indicates that the client will not use the
|
||||
mez_remote_lua_v1 object any more.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="new_log_entry">
|
||||
<description summary="the compositor has a new log entry">
|
||||
The compositor sends this event to inform the client that it has a new
|
||||
log entry for the client.
|
||||
|
||||
The text contains the lua log information the server generates when
|
||||
executing lua code. The server does not hold logs from before the client
|
||||
connect, and therefore you will only recieve log information from the
|
||||
point that you start listening for them and on.
|
||||
</description>
|
||||
<arg name="text" type="string" summary="the log text"/>
|
||||
</event>
|
||||
|
||||
<request name="push_lua">
|
||||
<description summary="propose dimensions of the next view">
|
||||
This request sends stringified lua code for the server to run.
|
||||
The output, if any, will be sent through the new_log_entry event.
|
||||
</description>
|
||||
<arg name="lua_chunk" type="string" summary="stringified lua code"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
||||
103
src/RemoteLua.zig
Normal file
103
src/RemoteLua.zig
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
const RemoteLua = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const zlua = @import("zlua");
|
||||
const wayland = @import("wayland");
|
||||
const Utils = @import("Utils.zig");
|
||||
const Lua = @import("lua/Lua.zig");
|
||||
const wl = wayland.server.wl;
|
||||
const mez = wayland.server.zmez;
|
||||
|
||||
const gpa = std.heap.c_allocator;
|
||||
const server = &@import("main.zig").server;
|
||||
|
||||
node: std.DoublyLinkedList.Node,
|
||||
remote_lua_v1: *mez.RemoteLuaV1,
|
||||
L: *zlua.Lua,
|
||||
|
||||
pub fn sendNewLogEntry(str: [*:0]const u8) void {
|
||||
var node = server.remote_lua_clients.first;
|
||||
while (node) |n| {
|
||||
const data: ?*RemoteLua = @fieldParentPtr("node", n);
|
||||
if (data) |d| d.remote_lua_v1.sendNewLogEntry(str);
|
||||
node = n.next;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(client: *wl.Client, version: u32, id: u32) !void {
|
||||
const remote_lua_v1 = try mez.RemoteLuaV1.create(client, version, id);
|
||||
|
||||
const node = try gpa.create(RemoteLua);
|
||||
errdefer gpa.destroy(node);
|
||||
node.* = .{
|
||||
.remote_lua_v1 = remote_lua_v1,
|
||||
.node = .{},
|
||||
.L = try zlua.Lua.init(gpa),
|
||||
};
|
||||
errdefer node.L.deinit();
|
||||
node.L.openLibs();
|
||||
Lua.openLibs(node.L);
|
||||
// TODO: replace stdout and stderr with buffers we can send to the clients
|
||||
|
||||
server.remote_lua_clients.prepend(&node.node);
|
||||
|
||||
remote_lua_v1.setHandler(*RemoteLua, handleRequest, handleDestroy, node);
|
||||
}
|
||||
|
||||
fn handleRequest(
|
||||
remote_lua_v1: *mez.RemoteLuaV1,
|
||||
request: mez.RemoteLuaV1.Request,
|
||||
remote: *RemoteLua,
|
||||
) void {
|
||||
const L = remote.L;
|
||||
switch (request) {
|
||||
.destroy => remote_lua_v1.destroy(),
|
||||
.push_lua => |req| {
|
||||
const chunk: [:0]const u8 = std.mem.sliceTo(req.lua_chunk, 0);
|
||||
|
||||
const str = std.mem.concatWithSentinel(gpa, u8, &[_][]const u8{
|
||||
"return ",
|
||||
chunk,
|
||||
";",
|
||||
}, 0) catch return catchLuaFail(remote);
|
||||
defer gpa.free(str);
|
||||
|
||||
zlua.Lua.loadBuffer(L, str, "=repl", zlua.Mode.text) catch {
|
||||
L.pop(L.getTop());
|
||||
L.loadString(chunk) catch {
|
||||
catchLuaFail(remote);
|
||||
L.pop(-1);
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
L.protectedCall(.{ .results = zlua.mult_return, }) catch {
|
||||
catchLuaFail(remote);
|
||||
L.pop(1);
|
||||
};
|
||||
|
||||
var i: i32 = 1;
|
||||
const nresults = L.getTop();
|
||||
while (i <= nresults) : (i += 1) {
|
||||
// TODO: support lua5.1 and luajit?
|
||||
sendNewLogEntry(L.toStringEx(i));
|
||||
L.pop(-1);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handleDestroy(_: *mez.RemoteLuaV1, remote_lua: *RemoteLua) void {
|
||||
if (remote_lua.node.prev) |p| {
|
||||
if (remote_lua.node.next) |n| n.prev.? = p;
|
||||
p.next = remote_lua.node.next;
|
||||
} else server.remote_lua_clients.first = remote_lua.node.next;
|
||||
|
||||
remote_lua.L.deinit();
|
||||
gpa.destroy(remote_lua);
|
||||
}
|
||||
|
||||
fn catchLuaFail(remote: *RemoteLua) void {
|
||||
const err: [:0]const u8 = remote.L.toStringEx(-1);
|
||||
sendNewLogEntry(std.mem.sliceTo(err, 0));
|
||||
}
|
||||
49
src/RemoteLuaManager.zig
Normal file
49
src/RemoteLuaManager.zig
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
const RemoteLuaManager = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const wayland = @import("wayland");
|
||||
const Utils = @import("Utils.zig");
|
||||
const RemoteLua = @import("RemoteLua.zig");
|
||||
const wl = wayland.server.wl;
|
||||
const mez = wayland.server.zmez;
|
||||
|
||||
const gpa = std.heap.c_allocator;
|
||||
const server = &@import("main.zig").server;
|
||||
|
||||
global: *wl.Global,
|
||||
|
||||
pub fn init() !?*RemoteLuaManager {
|
||||
const self = try gpa.create(RemoteLuaManager);
|
||||
|
||||
self.global = try wl.Global.create(server.wl_server, mez.RemoteLuaManagerV1, 1, ?*anyopaque, null, bind);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn bind(client: *wl.Client, _: ?*anyopaque, version: u32, id: u32) void {
|
||||
const remote_lua_manager_v1 = mez.RemoteLuaManagerV1.create(client, version, id) catch {
|
||||
client.postNoMemory();
|
||||
Utils.oomPanic();
|
||||
};
|
||||
remote_lua_manager_v1.setHandler(?*anyopaque, handleRequest, null, null);
|
||||
}
|
||||
|
||||
fn handleRequest(
|
||||
remote_lua_manager_v1: *mez.RemoteLuaManagerV1,
|
||||
request: mez.RemoteLuaManagerV1.Request,
|
||||
_: ?*anyopaque,
|
||||
) void {
|
||||
switch (request) {
|
||||
.destroy => remote_lua_manager_v1.destroy(),
|
||||
.get_remote => |req| {
|
||||
RemoteLua.create(
|
||||
remote_lua_manager_v1.getClient(),
|
||||
remote_lua_manager_v1.getVersion(),
|
||||
req.id,
|
||||
) catch {
|
||||
remote_lua_manager_v1.getClient().postNoMemory();
|
||||
Utils.oomPanic();
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@ const Keymap = @import("types/Keymap.zig");
|
|||
const Hook = @import("types/Hook.zig");
|
||||
const Events = @import("types/Events.zig");
|
||||
const Popup = @import("Popup.zig");
|
||||
const RemoteLua = @import("RemoteLua.zig");
|
||||
const RemoteLuaManager = @import("RemoteLuaManager.zig");
|
||||
|
||||
const gpa = std.heap.c_allocator;
|
||||
const server = &@import("main.zig").server;
|
||||
|
|
@ -26,6 +28,7 @@ renderer: *wlr.Renderer,
|
|||
backend: *wlr.Backend,
|
||||
event_loop: *wl.EventLoop,
|
||||
session: ?*wlr.Session,
|
||||
remote_lua_manager: ?*RemoteLuaManager,
|
||||
|
||||
shm: *wlr.Shm,
|
||||
xdg_shell: *wlr.XdgShell,
|
||||
|
|
@ -44,6 +47,7 @@ cursor: Cursor,
|
|||
keymaps: std.AutoHashMap(u64, Keymap),
|
||||
hooks: std.ArrayList(*Hook),
|
||||
events: Events,
|
||||
remote_lua_clients: std.DoublyLinkedList,
|
||||
|
||||
// Backend listeners
|
||||
new_input: wl.Listener(*wlr.InputDevice) = .init(handleNewInput),
|
||||
|
|
@ -98,9 +102,11 @@ pub fn init(self: *Server) void {
|
|||
.root = undefined,
|
||||
.seat = undefined,
|
||||
.cursor = undefined,
|
||||
.remote_lua_manager = RemoteLuaManager.init() catch Utils.oomPanic(),
|
||||
.keymaps = .init(gpa),
|
||||
.hooks = try .initCapacity(gpa, 10), // TODO: choose how many slots to start with
|
||||
.events = try .init(gpa),
|
||||
.remote_lua_clients = .{},
|
||||
};
|
||||
|
||||
self.renderer.initServer(wl_server) catch {
|
||||
|
|
|
|||
|
|
@ -45,49 +45,53 @@ fn loadConfigDir(self: *Lua) !void {
|
|||
try self.state.doFile(path);
|
||||
}
|
||||
|
||||
pub fn openLibs(self: *zlua.Lua) void {
|
||||
{
|
||||
self.newTable();
|
||||
defer _ = self.setGlobal("mez");
|
||||
{
|
||||
self.newTable();
|
||||
defer _ = self.setField(-2, "path");
|
||||
}
|
||||
{
|
||||
const fs_funcs = zlua.fnRegsFromType(Fs);
|
||||
self.newLib(fs_funcs);
|
||||
self.setField(-2, "fs");
|
||||
}
|
||||
{
|
||||
const input_funcs = zlua.fnRegsFromType(Input);
|
||||
self.newLib(input_funcs);
|
||||
self.setField(-2, "input");
|
||||
}
|
||||
{
|
||||
const hook_funcs = zlua.fnRegsFromType(Hook);
|
||||
self.newLib(hook_funcs);
|
||||
self.setField(-2, "hook");
|
||||
}
|
||||
{
|
||||
const api_funcs = zlua.fnRegsFromType(Api);
|
||||
self.newLib(api_funcs);
|
||||
self.setField(-2, "api");
|
||||
}
|
||||
{
|
||||
const view_funcs = zlua.fnRegsFromType(View);
|
||||
self.newLib(view_funcs);
|
||||
self.setField(-2, "view");
|
||||
}
|
||||
{
|
||||
const output_funcs = zlua.fnRegsFromType(Output);
|
||||
self.newLib(output_funcs);
|
||||
self.setField(-2, "output");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(self: *Lua) !void {
|
||||
self.state = try zlua.Lua.init(gpa);
|
||||
errdefer self.state.deinit();
|
||||
self.state.openLibs();
|
||||
|
||||
{
|
||||
self.state.newTable();
|
||||
defer _ = self.state.setGlobal("mez");
|
||||
{
|
||||
self.state.newTable();
|
||||
defer _ = self.state.setField(-2, "path");
|
||||
}
|
||||
{
|
||||
const fs_funcs = zlua.fnRegsFromType(Fs);
|
||||
self.state.newLib(fs_funcs);
|
||||
self.state.setField(-2, "fs");
|
||||
}
|
||||
{
|
||||
const input_funcs = zlua.fnRegsFromType(Input);
|
||||
self.state.newLib(input_funcs);
|
||||
self.state.setField(-2, "input");
|
||||
}
|
||||
{
|
||||
const hook_funcs = zlua.fnRegsFromType(Hook);
|
||||
self.state.newLib(hook_funcs);
|
||||
self.state.setField(-2, "hook");
|
||||
}
|
||||
{
|
||||
const api_funcs = zlua.fnRegsFromType(Api);
|
||||
self.state.newLib(api_funcs);
|
||||
self.state.setField(-2, "api");
|
||||
}
|
||||
{
|
||||
const view_funcs = zlua.fnRegsFromType(View);
|
||||
self.state.newLib(view_funcs);
|
||||
self.state.setField(-2, "view");
|
||||
}
|
||||
{
|
||||
const output_funcs = zlua.fnRegsFromType(Output);
|
||||
self.state.newLib(output_funcs);
|
||||
self.state.setField(-2, "output");
|
||||
}
|
||||
}
|
||||
openLibs(self.state);
|
||||
|
||||
loadRuntimeDir(self) catch |err| {
|
||||
if (err == error.LuaRuntime) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const wlr = @import("wlroots");
|
|||
const zlua = @import("zlua");
|
||||
|
||||
const Event = @import("Events.zig");
|
||||
const RemoteLua = @import("../RemoteLua.zig");
|
||||
const Lua = &@import("../main.zig").lua;
|
||||
|
||||
events: std.ArrayList([]const u8), // a list of events
|
||||
|
|
@ -39,7 +40,7 @@ pub fn callback(self: *const Hook, args: anytype) void {
|
|||
}
|
||||
|
||||
Lua.state.protectedCall(.{ .args = i }) catch {
|
||||
// TODO: add a callback to remote lua when that gets merged
|
||||
RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable);
|
||||
};
|
||||
Lua.state.pop(-1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue