aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2025-03-23 16:59:16 +0530
committerAkshay Nair <phenax5@gmail.com>2025-03-23 16:59:34 +0530
commit18b3e4939931c0d2bf779d2e52cb0c90e282ec97 (patch)
treee9afabef77660b8cfcd8a37006c9db38c4bc8aa6
parent1f018902a8e1d1138c9a56106bce7aa7982a247b (diff)
downloadnull-browser-18b3e4939931c0d2bf779d2e52cb0c90e282ec97.tar.gz
null-browser-18b3e4939931c0d2bf779d2e52cb0c90e282ec97.zip
Fix segfault with evaluate, add keymap accidentally popping wrong value
+ add demnu config.lua example
-rw-r--r--config.lua28
-rw-r--r--flake.nix23
-rw-r--r--src/AsyncEventLoop.cpp33
-rw-r--r--src/AsyncEventLoop.hpp3
-rw-r--r--src/LuaRuntime.cpp103
-rw-r--r--src/LuaRuntime.hpp14
-rw-r--r--src/widgets/MainWindow.cpp18
-rw-r--r--src/widgets/MainWindow.hpp1
8 files changed, 107 insertions, 116 deletions
diff --git a/config.lua b/config.lua
index 38a07cb..4f4aee3 100644
--- a/config.lua
+++ b/config.lua
@@ -6,40 +6,29 @@ web = web
uv = uv
local Dmenu = {}
-function Dmenu.select(list, callback)
+function Dmenu.select(list, opts, callback)
local selection = nil
local stdin = uv.new_pipe();
local stdout = uv.new_pipe();
- uv.spawn('dmenu', { stdio = { stdin, stdout, nil } }, function(code)
- print('exit')
+ local args = { '-p', opts.prompt or '>' }
+ uv.spawn('dmenu', { args = args, stdio = { stdin, stdout, nil } }, function(code)
uv.close(stdout)
- print('exit close out')
uv.close(stdin)
- print('exit close in')
if code == 0 then
callback(nil, selection)
else
callback('Exit with status code: ' .. code, selection)
end
- print('done exit')
end)
- uv.read_start(stdout, function(err, data)
- print('-> out', err, data)
- if err ~= nil then
- callback(err, nil)
- elseif data then
- selection = data
- end
+ uv.read_start(stdout, function(_err, data)
+ if data then selection = data end
end)
for _, value in ipairs(list) do
- print('input write')
uv.write(stdin, value .. '\n')
end
- print('shit')
uv.shutdown(stdin)
- print('end')
end
local function trim(s)
@@ -56,21 +45,22 @@ local urls = {
-- Open in new tab
web.keymap.set('n', 't', function()
- Dmenu.select(urls, function(err, result)
+ Dmenu.select(urls, { prompt = 'Open tab:' }, function(err, result)
if err or not result then return end
web.tabopen(trim(result))
end)
end)
-- Open in current tab
web.keymap.set('n', 'o', function()
- Dmenu.select(urls, function(err, result)
+ Dmenu.select(urls, { prompt = 'Open:' }, function(err, result)
if err or not result then return end
web.open(trim(result))
end)
end)
+
-- Run lua code
web.keymap.set('n', 'q', function()
- Dmenu.select({}, function(err, result)
+ Dmenu.select({}, { prompt = 'Lua >' }, function(err, result)
if err or not result then return end
local run, run_err = load(result)
if run_err then print(run_err) end
diff --git a/flake.nix b/flake.nix
index 5834f8b..34c8509 100644
--- a/flake.nix
+++ b/flake.nix
@@ -9,11 +9,23 @@
let
pkgs = import nixpkgs { inherit system; };
+ lua-libluv = with pkgs; luajitPackages.libluv.overrideAttrs (self: rec {
+ version = "1.50.0-1";
+ knownRockspec = (fetchurl {
+ url = "mirror://luarocks/luv-${version}.rockspec";
+ sha256 = "sha256-IL2EejtmT0pw0cAupMz0gvP3a19NPsc45W1RaoeGJgY=";
+ }).outPath;
+ src = fetchurl {
+ url = "https://github.com/luvit/luv/releases/download/${version}/luv-${version}.tar.gz";
+ sha256 = "sha256-2GfDAk2cmB1U8u3YPhP9bcEVjwYIY197HA9rVYa1vDQ=";
+ };
+ });
+
dependencies = with pkgs; [
qt6.full
luajit
libuv
- luajitPackages.libluv
+ lua-libluv
# libcef
# nss
];
@@ -25,7 +37,7 @@
clang-tools
pkg-config
gdb
- # vcpkg
+ valgrind
] ++ dependencies;
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}";
@@ -39,7 +51,12 @@
buildInputs = with pkgs; [
qt6.qtbase
] ++ dependencies;
- nativeBuildInputs = with pkgs; [ cmake qt6.wrapQtAppsHook pkg-config ];
+
+ nativeBuildInputs = with pkgs; [
+ cmake
+ qt6.wrapQtAppsHook
+ pkg-config
+ ];
};
});
}
diff --git a/src/AsyncEventLoop.cpp b/src/AsyncEventLoop.cpp
index 6c9b42d..6c6333f 100644
--- a/src/AsyncEventLoop.cpp
+++ b/src/AsyncEventLoop.cpp
@@ -3,8 +3,6 @@
#include <mutex>
#include <queue>
#include <thread>
-#include <utility>
-#include <uv.h>
#include "AsyncEventLoop.hpp"
@@ -23,6 +21,8 @@ AsyncEventLoop::AsyncEventLoop() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
+void AsyncEventLoop::wake() { uv_async_send(&asyncHandle); }
+
void AsyncEventLoop::processTasks() {
std::queue<std::function<void()>> tasks;
@@ -41,10 +41,9 @@ void AsyncEventLoop::processTasks() {
void AsyncEventLoop::runLoop() {
isLoopRunning = true;
while (isLoopRunning) {
- /* int _result = */ uv_run(loop, UV_RUN_NOWAIT);
- // qDebug() << "Tasks handled:" << result;
- // uv_print_active_handles(loop, stdout);
- // if (result == 0)
+ // qDebug() << "loop iteration" << uv_loop_alive(loop);
+ uv_run(loop, UV_RUN_NOWAIT);
+ // qDebug() << "uv_run() returned: " << result;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
@@ -54,33 +53,28 @@ AsyncEventLoop::~AsyncEventLoop() {
return;
isLoopRunning = false;
- // Clear the tasks queue
+ // Wake it up. Stab it to death. (clear async queue)
+ wake();
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
{
std::lock_guard<std::mutex> lock(tasksQueueMutex);
std::queue<std::function<void()>>().swap(tasksQueue);
}
-
- // Wake it up. Stab it to death.
- uv_async_send(&asyncHandle);
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
uv_stop(loop);
// Close all handles
AsyncEventLoop::closeHandle(reinterpret_cast<uv_handle_t *>(&asyncHandle));
uv_walk(loop, AsyncEventLoop::closeHandle, nullptr);
- while (uv_run(loop, UV_RUN_ONCE) != 0)
- ;
+ while (uv_run(loop, UV_RUN_NOWAIT) != 0)
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
// TODO: Fix pending handler case (setTimeout(100) wait(20) close() -> error)
- qDebug() << "join start";
if (loopThread.joinable())
loopThread.join();
- qDebug() << "join done";
- while (uv_loop_close(loop) == UV_EBUSY) {
- uv_walk(loop, AsyncEventLoop::closeHandle, nullptr);
- uv_run(loop, UV_RUN_NOWAIT);
- }
+ while (uv_loop_close(loop))
+ uv_run(loop, UV_RUN_DEFAULT);
+
free(loop);
loop = nullptr;
}
@@ -93,5 +87,6 @@ void AsyncEventLoop::asyncHandleCallback(uv_async_t *handle) {
void AsyncEventLoop::closeHandle(uv_handle_t *handle, void *) {
if (!uv_is_closing(handle)) {
uv_close(handle, [](uv_handle_t *h) { h->data = nullptr; });
+ uv_unref(handle);
}
}
diff --git a/src/AsyncEventLoop.hpp b/src/AsyncEventLoop.hpp
index 29c2958..5718909 100644
--- a/src/AsyncEventLoop.hpp
+++ b/src/AsyncEventLoop.hpp
@@ -15,6 +15,7 @@ public:
AsyncEventLoop();
~AsyncEventLoop();
+ void wake();
DEFINE_GETTER(getUVLoop, loop)
template <typename F> void queueTask(F &&task) {
@@ -22,7 +23,7 @@ public:
std::lock_guard<std::mutex> lock(tasksQueueMutex);
tasksQueue.push(std::forward<F>(task));
}
- uv_async_send(&asyncHandle);
+ wake();
}
protected:
diff --git a/src/LuaRuntime.cpp b/src/LuaRuntime.cpp
index cdf95b0..274ec29 100644
--- a/src/LuaRuntime.cpp
+++ b/src/LuaRuntime.cpp
@@ -13,23 +13,7 @@ const char *web_global_name = "web";
LuaRuntime::LuaRuntime() {
state = luaL_newstate();
luaL_openlibs(state);
-
- // Load `web`
- luaL_Reg weblib[] = {
- {"open", &LuaRuntime::lua_onUrlOpen},
- {"tabopen", &LuaRuntime::lua_onUrlTabOpen},
- {nullptr, nullptr},
- };
- luaL_newlib(state, weblib);
- lua_setglobal(state, web_global_name);
-
- lua_getglobal(state, web_global_name);
- luaL_Reg keymaplib[] = {
- {"set", &LuaRuntime::lua_addKeymap},
- };
- luaL_newlib(state, keymaplib);
- lua_setfield(state, -2, "keymap");
- lua_pop(state, 1);
+ preserveTop(state, { initWebLib(); })
}
void LuaRuntime::startEventLoop() {
@@ -46,10 +30,10 @@ void LuaRuntime::startEventLoop() {
}
void LuaRuntime::stopEventLoop() {
- if (eventLoop == nullptr)
- return;
- delete eventLoop;
- eventLoop = nullptr;
+ if (eventLoop != nullptr) {
+ delete eventLoop;
+ eventLoop = nullptr;
+ }
// Clear the uv global
lua_pushnil(state);
@@ -59,28 +43,26 @@ void LuaRuntime::stopEventLoop() {
void LuaRuntime::evaluate(QString code) {
eventLoop->queueTask([this, code]() {
- if (luaL_dostring(state, code.toStdString().c_str()) != LUA_OK) {
- const char *value = lua_tostring(state, -1);
- lua_pop(state, 1);
-
- qDebug() << "Lua Error: " << value;
- emit evaluationFailed(value);
- } else {
- QVariant value = getValue(-1);
- lua_pop(state, 1);
-
- qDebug() << "result: " << value;
- emit evaluationCompleted(value);
- }
+ preserveTop(state, {
+ if (luaL_dostring(state, code.toStdString().c_str()) != LUA_OK) {
+ const char *value = lua_tostring(state, -1);
+ qDebug() << "Lua Error: " << value;
+ emit evaluationFailed(value);
+ } else {
+ QVariant value = getLuaValue(-1);
+ qDebug() << "result: " << value;
+ emit evaluationCompleted(value);
+ }
+ })
});
}
QVariant LuaRuntime::evaluateSync(QString code) {
luaL_dostring(state, code.toStdString().c_str());
- return getValue(-1); // TODO: error handling
+ return getLuaValue(-1); // TODO: error handling
}
-QVariant LuaRuntime::getValue(int idx) {
+QVariant LuaRuntime::getLuaValue(int idx) {
if (lua_isstring(state, idx))
return lua_tostring(state, idx);
@@ -117,15 +99,13 @@ int LuaRuntime::lua_addKeymap(lua_State *state) {
lua_pushvalue(state, 3);
int functionRef = luaL_ref(state, LUA_REGISTRYINDEX);
auto action = [state, functionRef]() {
- qDebug() << "key action";
-
- lua_rawgeti(state, LUA_REGISTRYINDEX, functionRef);
- if (lua_pcall(state, 0, 0, 0) != LUA_OK) {
- const char *error = lua_tostring(state, -1);
- qDebug() << "Error calling Lua function:" << error;
- lua_pop(state, 1);
- }
- lua_pop(state, 1);
+ preserveTop(state, {
+ lua_rawgeti(state, LUA_REGISTRYINDEX, functionRef);
+ if (lua_pcall(state, 0, 0, 0) != LUA_OK) {
+ const char *error = lua_tostring(state, -1);
+ qDebug() << "Error calling Lua function:" << error;
+ }
+ })
};
// TODO: Cleanup function ref on after keymap clear
@@ -136,11 +116,34 @@ int LuaRuntime::lua_addKeymap(lua_State *state) {
}
void LuaRuntime::loadFile(QString path) {
- qDebug() << "Loading: " << path;
- if (luaL_dofile(state, path.toStdString().c_str()) != LUA_OK) {
- qDebug() << "Load file error:" << lua_tostring(state, -1);
- lua_pop(state, 1);
- }
+ queueTask([this, path]() {
+ preserveTop(state, {
+ qDebug() << "Loading: " << path;
+ if (luaL_dofile(state, path.toStdString().c_str()) != LUA_OK) {
+ qDebug() << "Load file error:" << lua_tostring(state, -1);
+ }
+ })
+ });
+}
+
+void LuaRuntime::initWebLib() {
+ // web
+ luaL_Reg weblib[] = {
+ {"open", &LuaRuntime::lua_onUrlOpen},
+ {"tabopen", &LuaRuntime::lua_onUrlTabOpen},
+ {nullptr, nullptr},
+ };
+ luaL_newlib(state, weblib);
+ lua_setglobal(state, web_global_name);
+
+ // web.keymap
+ lua_getglobal(state, web_global_name);
+ luaL_Reg keymaplib[] = {
+ {"set", &LuaRuntime::lua_addKeymap},
+ };
+ luaL_newlib(state, keymaplib);
+ lua_setfield(state, -2, "keymap");
+ // lua_pop(state, 1);
}
LuaRuntime::~LuaRuntime() {
diff --git a/src/LuaRuntime.hpp b/src/LuaRuntime.hpp
index 9f672e9..3d44282 100644
--- a/src/LuaRuntime.hpp
+++ b/src/LuaRuntime.hpp
@@ -7,6 +7,11 @@
#include "AsyncEventLoop.hpp"
#include "widgets/WebViewStack.hpp"
+#define preserveTop(STATE, BODY) \
+ int __top = lua_gettop(STATE); \
+ BODY; \
+ lua_settop(STATE, __top);
+
class LuaRuntime : public QObject {
Q_OBJECT
@@ -17,16 +22,14 @@ public:
}
void evaluate(QString code);
- QVariant evaluateSync(QString code);
-
void loadFile(QString path);
+ QVariant evaluateSync(QString code);
void stopEventLoop();
void startEventLoop();
-
- QVariant getValue(int idx);
-
DELEGATE(eventLoop, queueTask, queueTask)
+
+ QVariant getLuaValue(int idx);
DEFINE_GETTER(getState, state)
signals:
@@ -39,6 +42,7 @@ signals:
protected:
LuaRuntime();
~LuaRuntime();
+ void initWebLib();
static int lua_onUrlOpen(lua_State *state);
static int lua_onUrlTabOpen(lua_State *state);
static int lua_addKeymap(lua_State *state);
diff --git a/src/widgets/MainWindow.cpp b/src/widgets/MainWindow.cpp
index ce03bce..df8da53 100644
--- a/src/widgets/MainWindow.cpp
+++ b/src/widgets/MainWindow.cpp
@@ -51,26 +51,8 @@ MainWindow::MainWindow() {
keymapEvaluator->addKeymap(KeyMode::Insert, "<esc>", [keymapEvaluator]() {
keymapEvaluator->setCurrentMode(KeyMode::Normal);
});
-
keymapEvaluator->addKeymap(KeyMode::Normal, "<c-t>a",
[]() { qDebug() << "Stuff"; });
- keymapEvaluator->addKeymap(KeyMode::Normal, "<c-t>b",
- []() { qDebug() << "Else"; });
-}
-
-void MainWindow::keyPressEvent(QKeyEvent *event) {
- auto combo = event->keyCombination();
-
- if (combo.key() == Qt::Key_J &&
- combo.keyboardModifiers().testFlag(Qt::ControlModifier)) {
- inputMediator->nextWebView();
- } else if (combo.key() == Qt::Key_K &&
- combo.keyboardModifiers().testFlag(Qt::ControlModifier)) {
- inputMediator->previousWebView();
- } else if (combo.key() == Qt::Key_W &&
- combo.keyboardModifiers().testFlag(Qt::ControlModifier)) {
- inputMediator->closeCurrentWebView();
- }
}
bool MainWindow::eventFilter(QObject *, QEvent *event) {
diff --git a/src/widgets/MainWindow.hpp b/src/widgets/MainWindow.hpp
index 2bbc42e..c3118fc 100644
--- a/src/widgets/MainWindow.hpp
+++ b/src/widgets/MainWindow.hpp
@@ -10,7 +10,6 @@ public:
MainWindow();
private:
- void keyPressEvent(QKeyEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override;
private: