diff --git a/src/plugins/lua/CMakeLists.txt b/src/plugins/lua/CMakeLists.txt index 7235e963995..2557c5c8fe5 100644 --- a/src/plugins/lua/CMakeLists.txt +++ b/src/plugins/lua/CMakeLists.txt @@ -15,6 +15,7 @@ add_qtc_plugin(Lua bindings/inheritance.h bindings/install.cpp bindings/json.cpp + bindings/localsocket.cpp bindings/messagemanager.cpp bindings/qtcprocess.cpp bindings/settings.cpp @@ -35,6 +36,7 @@ add_qtc_plugin(Lua meta/gui.lua meta/json.lua meta/install.lua + meta/localsocket.lua meta/lsp.lua meta/messagemanager.lua meta/process.lua diff --git a/src/plugins/lua/bindings/localsocket.cpp b/src/plugins/lua/bindings/localsocket.cpp new file mode 100644 index 00000000000..ef3ffa2bf66 --- /dev/null +++ b/src/plugins/lua/bindings/localsocket.cpp @@ -0,0 +1,112 @@ + +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "../luaengine.h" + +#include +#include + +namespace Lua::Internal { + +class LocalSocket : public QLocalSocket +{ +public: + using QLocalSocket::QLocalSocket; +}; + +void addLocalSocketModule() +{ + LuaEngine::registerProvider("LocalSocket", [](sol::state_view lua) -> sol::object { + sol::table async = lua.script("return require('async')", "_localsocket_").get(); + sol::function wrap = async["wrap"]; + + sol::table result = lua.create_table(); + + sol::usertype socketType + = result.new_usertype("LocalSocket", sol::no_constructor); + + result["create"] = [lua](const QString &name) { + auto socket = std::make_unique(); + socket->setServerName(name); + return socket; + }; + + socketType["isConnected"] = [](LocalSocket *socket) { + return socket->state() == QLocalSocket::ConnectedState; + }; + + socketType["connectToServer_cb"] = [](LocalSocket *socket, sol::function cb) { + if (socket->state() != QLocalSocket::UnconnectedState) + throw sol::error("socket is not in UnconnectedState"); + + auto connection = new QMetaObject::Connection; + auto connectionError = new QMetaObject::Connection; + + *connection = QObject::connect( + socket, &QLocalSocket::connected, socket, [connectionError, connection, cb]() { + qDebug() << "CONNECTED"; + auto res = LuaEngine::void_safe_call(cb, true); + QTC_CHECK_EXPECTED(res); + QObject::disconnect(*connection); + delete connection; + QObject::disconnect(*connectionError); + delete connectionError; + }); + *connectionError = QObject::connect( + socket, + &QLocalSocket::errorOccurred, + socket, + [socket, connection, connectionError, cb]() { + qDebug() << "CONNECT ERROR"; + auto res = LuaEngine::void_safe_call(cb, false, socket->errorString()); + QTC_CHECK_EXPECTED(res); + QObject::disconnect(*connection); + delete connection; + QObject::disconnect(*connectionError); + delete connectionError; + }); + + socket->connectToServer(); + }; + + socketType["connectToServer"] + = wrap(socketType["connectToServer_cb"].get()).get(); + + socketType["write"] = [](LocalSocket *socket, const std::string &data) { + if (socket->state() != QLocalSocket::ConnectedState) + throw sol::error("socket is not in ConnectedState"); + + return socket->write(data.c_str(), data.size()); + }; + + socketType["read_cb"] = [](LocalSocket *socket, sol::function cb) { + if (socket->state() != QLocalSocket::ConnectedState) + throw sol::error("socket is not in ConnectedState"); + + if (socket->bytesAvailable() > 0) { + QTimer::singleShot(0, [cb, socket]() { + LuaEngine::void_safe_call(cb, socket->readAll().toStdString()); + }); + return; + } + + auto connection = new QMetaObject::Connection; + *connection = QObject::connect( + socket, &QLocalSocket::readyRead, socket, [connection, cb, socket]() { + auto res = LuaEngine::void_safe_call(cb, socket->readAll().toStdString()); + QTC_CHECK_EXPECTED(res); + QObject::disconnect(*connection); + delete connection; + }); + }; + + socketType["read"] = wrap(socketType["read_cb"].get()).get(); + + socketType["close"] = [](LocalSocket *socket) { socket->close(); }; + + return result; + }); +} + +} // namespace Lua::Internal diff --git a/src/plugins/lua/lua.qbs b/src/plugins/lua/lua.qbs index d382a1be09a..1b9bdb74e04 100644 --- a/src/plugins/lua/lua.qbs +++ b/src/plugins/lua/lua.qbs @@ -41,6 +41,7 @@ QtcPlugin { "inheritance.h", "install.cpp", "json.cpp" + "localsocket.cpp", "messagemanager.cpp", "qtcprocess.cpp", "settings.cpp", @@ -71,6 +72,7 @@ QtcPlugin { "gui.lua", "install.lua", "json.lua", + "localsocket.lua", "lsp.lua", "messagemanager.lua", "process.lua", diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index 9ff52293b53..ade60166f3e 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -41,6 +41,7 @@ void addGuiModule(); void addHookModule(); void addInstallModule(); void addJsonModule(); +void addLocalSocketModule(); void addMessageManagerModule(); void addProcessModule(); void addQtModule(); @@ -256,6 +257,7 @@ public: addHookModule(); addInstallModule(); addJsonModule(); + addLocalSocketModule(); addMessageManagerModule(); addProcessModule(); addQtModule(); diff --git a/src/plugins/lua/meta/localsocket.lua b/src/plugins/lua/meta/localsocket.lua new file mode 100644 index 00000000000..6394102ef50 --- /dev/null +++ b/src/plugins/lua/meta/localsocket.lua @@ -0,0 +1,42 @@ +---@meta LocalSocket + +---@class LocalSocket +LocalSocket = {} + + +---Creates a new Socket to the given socketFileName +---@param socketFileName string The name of the socket file. +---@return LocalSocket localSocket The created socket. +function LocalSocket.create(socketFileName) end + +---Returns true if the socket is connected. +---@return boolean isConnected True if the socket is connected. +function LocalSocket:isConnected() end + +---@async +---Connects the socket to the server. +---@return boolean success True if the connection was successful. +---@return string errorString The error string if the connection failed. +function LocalSocket:connectToServer() end + +---Connects the socket to the server +---@param callback function The callback function. The callback function should take two arguments: success (boolean) and errorString (string). +function LocalSocket:connectToServer_cb(callback) end + +---Sends the data to the server. +---@param data string The data to send. +---@return integer bytesWritten The number of bytes written. +function LocalSocket:write(data) end + +---@async +---Reads the data from the server. Waits until data is available. +---@return string data The data read from the server. +function LocalSocket:read() end + +---Reads the data from the server. Calls "callback" once data is available. +---@param callback function The callback function. The callback function should take one argument: data (string). +function LocalSocket:read_cb(callback) end +---Closes the socket. +function LocalSocket:close() end + +return LocalSocket