Skip to content

Commit d7b1ff4

Browse files
committedAug 13, 2024·
experimental Lua backend
1 parent bfc3dd2 commit d7b1ff4

25 files changed

+300
-17
lines changed
 

‎.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ env:
55
BUILD_TYPE: Release
66
ARCH: x64
77
VCPKG_CONFIG: Release
8-
VCPKG_HASH: 1dc5ee30eb1032221d29f281f4a94b73f06b4284
8+
VCPKG_HASH: e590c2b30c08caf1dd8d612ec602a003f9784b7d
99
DOC_INSTANCE: wrs/bt
1010
DOC_ARTIFACT: webHelpBT2-all.zip
1111
DOC_ALGOLIA_ARTIFACT: algolia-indexes-BT.zip

‎CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ if(MSVC AND CMAKE_BUILD_TYPE MATCHES Release)
3838
endif()
3939

4040
add_subdirectory(grey/grey)
41+
add_subdirectory(grey/demo/desktop)
4142
add_subdirectory(bt)
4243
add_subdirectory(test)

‎bt/CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ find_package(fmt CONFIG REQUIRED)
1212
find_package(nlohmann_json CONFIG REQUIRED)
1313
find_path(P_RANAV_CSV2_INCLUDE_DIRS "csv2/mio.hpp")
1414
find_package(tinyxml2 CONFIG REQUIRED)
15+
find_package(Lua REQUIRED)
1516

1617
file(GLOB core_src CONFIGURE_DEPENDS
1718
"app/*.cpp"
@@ -55,8 +56,13 @@ target_link_libraries(bt
5556
fmt::fmt-header-only
5657
nlohmann_json::nlohmann_json
5758
tinyxml2::tinyxml2
59+
${LUA_LIBRARIES}
5860
)
59-
target_include_directories(${APP_NAME} PRIVATE "../common" "../grey/grey" ${P_RANAV_CSV2_INCLUDE_DIRS})
61+
target_include_directories(${APP_NAME} PRIVATE
62+
"../common"
63+
"../grey/grey"
64+
${P_RANAV_CSV2_INCLUDE_DIRS}
65+
${LUA_INCLUDE_DIR})
6066

6167
target_sources(bt PRIVATE dpi-aware.manifest)
6268

‎bt/app/script_site.cpp

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#include "script_site.h"
2+
#include <fstream>
3+
4+
using namespace std;
5+
6+
namespace bt {
7+
8+
const string FileName = "rules.lua";
9+
10+
script_site::script_site(const string& path_or_code, bool is_path) :
11+
path_or_code{path_or_code}, is_path{is_path} {
12+
reload();
13+
}
14+
15+
script_site::~script_site() {
16+
if(L) {
17+
lua_close(L);
18+
}
19+
}
20+
21+
void script_site::reload() {
22+
error.clear();
23+
24+
// reinintialise interpreter
25+
if(L) {
26+
lua_close(L);
27+
L = nullptr;
28+
}
29+
30+
L = luaL_newstate();
31+
luaL_openlibs(L);
32+
33+
// load code into string
34+
{
35+
if(is_path) {
36+
ifstream fs(path_or_code);
37+
if(fs.is_open()) {
38+
code = string(istreambuf_iterator<char>(fs), istreambuf_iterator<char>());
39+
}
40+
} else {
41+
code = path_or_code;
42+
}
43+
}
44+
45+
// load code into interpreter
46+
if(luaL_loadstring(L, code.c_str()) || lua_pcall(L, 0, 0, 0)) {
47+
error = lua_tostring(L, -1);
48+
// pop error message from the stack
49+
lua_pop(L, 1);
50+
return;
51+
}
52+
}
53+
54+
void script_site::set_code(const std::string& code) {
55+
// save code to file
56+
{
57+
if(is_path) {
58+
ofstream fs(path_or_code);
59+
if(fs.is_open()) {
60+
fs << code;
61+
}
62+
}
63+
}
64+
65+
reload();
66+
}
67+
68+
bool script_site::call_rule(url_payload& up, const string& function_name) {
69+
70+
// set global table "p" with 3 members: url, window_title, process_name
71+
lua_newtable(L);
72+
lua_pushstring(L, up.match_url.c_str());
73+
lua_setfield(L, -2, "url");
74+
lua_pushstring(L, up.window_title.c_str());
75+
lua_setfield(L, -2, "wt");
76+
lua_pushstring(L, up.process_name.c_str());
77+
lua_setfield(L, -2, "pn");
78+
lua_setglobal(L, "p");
79+
80+
// call function
81+
lua_getglobal(L, function_name.c_str());
82+
if(lua_pcall(L, 0, 1, 0)) {
83+
// get error message from the stack
84+
error = lua_tostring(L, -1);
85+
lua_pop(L, 1);
86+
return false;
87+
}
88+
89+
// read return value
90+
bool r = lua_toboolean(L, -1);
91+
lua_pop(L, 1);
92+
93+
// read back "url" from global table "p" (in case it's changed)
94+
lua_getglobal(L, "p");
95+
lua_getfield(L, -1, "url");
96+
//up.match_url = lua_tostring(L, -1);
97+
lua_pop(L, 1);
98+
99+
return r;
100+
}
101+
}

‎bt/app/script_site.h

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
#include "lua.hpp"
3+
#include <string>
4+
#include "url_payload.h"
5+
6+
namespace bt {
7+
class script_site {
8+
public:
9+
script_site(const std::string& path_or_code, bool is_path);
10+
~script_site();
11+
12+
void reload();
13+
14+
std::string get_path() const { return is_path ? path_or_code : ""; }
15+
std::string get_error() const { return error; }
16+
17+
// code as string manipulation
18+
std::string get_code() const { return code; }
19+
void set_code(const std::string& code);
20+
21+
// bt specific functions
22+
bool call_rule(url_payload& up, const std::string& function_name);
23+
24+
25+
private:
26+
bool is_path;
27+
std::string path_or_code;
28+
std::string code;
29+
std::string error;
30+
lua_State* L{nullptr};
31+
};
32+
}

‎bt/app/ui/config_app.cpp

+43-6
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,18 @@ namespace bt::ui {
2323
title{string{APP_LONG_NAME} + " " + APP_VERSION},
2424
wnd_config{title, &is_open},
2525
wnd_about{"About"},
26-
wnd_subs{"Substitutions", &show_subs} {
26+
wnd_subs{"Substitutions", &show_subs},
27+
wnd_scripting{"Scripting", &show_scripting} {
2728

28-
app = grey::app::make(title, 900, 450);
29+
app = grey::app::make(title, 900, 500);
2930
app->initial_theme_id = g_config.theme_id;
3031
app->win32_can_resize = false;
3132
app->win32_center_on_screen = true;
3233

3334
wnd_config
34-
//.size(900, 450)
3535
.has_menubar()
3636
.no_titlebar()
37-
//.center()
38-
.no_border()
37+
.border(0)
3938
.no_resize()
4039
.no_collapse()
4140
.fill_viewport()
@@ -44,12 +43,19 @@ namespace bt::ui {
4443
wnd_about
4544
.size(310, 355)
4645
.center()
46+
.border(1)
4747
.no_resize();
4848

4949
wnd_subs
5050
.size(600, 300)
51+
.border(1)
5152
.center();
5253

54+
wnd_scripting
55+
.size(600, 600)
56+
.border(1)
57+
.no_scroll();
58+
5359
float padding_bottom = 20 * app->scale;
5460
w_left_panel = w::container{250 * app->scale, -padding_bottom};
5561
w_right_panel = w::container{0, -padding_bottom}.border();
@@ -124,6 +130,9 @@ namespace bt::ui {
124130
if(show_subs)
125131
render_subs_window();
126132

133+
if(show_scripting)
134+
render_scripting_window();
135+
127136
render_dashboard();
128137

129138
w::notify_render_frame();
@@ -239,6 +248,9 @@ namespace bt::ui {
239248
if(w::mi("Substitutions...", true, ICON_MD_FIND_REPLACE)) {
240249
show_subs = !show_subs;
241250
}
251+
if(w::mi("Scripting...", true, ICON_MD_CODE)) {
252+
show_scripting = !show_scripting;
253+
}
242254
}
243255

244256
if(w::menu m{"Help"}; m) {
@@ -510,7 +522,7 @@ It super fast, extremely light on resources, completely free and open source.)",
510522
w::tooltip("URL that will be opened in the browser");
511523
}
512524

513-
w::sl(820 * app->scale);
525+
w::sl(800 * app->scale);
514526
if (w::button(ICON_MD_CLEAR_ALL " clear", w::emphasis::error)) {
515527
url_tester_up.clear();
516528
}
@@ -527,6 +539,31 @@ It super fast, extremely light on resources, completely free and open source.)",
527539
test_url();
528540
}
529541
}
542+
543+
void config_app::render_scripting_window() {
544+
w::guard gw{wnd_scripting};
545+
546+
if(!script_initialised) {
547+
script_editor.set_text(g_script.get_code());
548+
script_initialised = true;
549+
}
550+
551+
if(!g_script.get_error().empty()) {
552+
w::label(g_script.get_error(), w::emphasis::error);
553+
}
554+
555+
if(w::accordion("Test")) {
556+
w::input(scripting_up.url, ICON_MD_LINK " URL", true);
557+
w::input(scripting_up.window_title, ICON_MD_WINDOW " window", true);
558+
w::input(scripting_up.process_name, ICON_MD_MEMORY " process", true);
559+
}
560+
561+
if(w::button(ICON_MD_SAVE " save", w::emphasis::primary)) {
562+
g_script.set_code(script_editor.get_text());
563+
}
564+
565+
script_editor.render();
566+
}
530567

531568
void config_app::render_status_bar() {
532569
w::status_bar sb;

‎bt/app/ui/config_app.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,17 @@ namespace bt::ui {
6060

6161
// "Substitutions" window
6262
bool show_subs{false};
63-
grey::widgets::window wnd_subs{"Substitutions"};
63+
grey::widgets::window wnd_subs;
6464
std::vector<std::string> replacer_kinds{"string", "regex"};
6565
url_payload url_subs_up;
6666

67+
// "Script" window
68+
bool show_scripting{false};
69+
bool script_initialised{false};
70+
grey::widgets::window wnd_scripting{"Scripting"};
71+
grey::widgets::text_editor script_editor;
72+
url_payload scripting_up;
73+
6774
// URL Tester
6875
bool show_url_tester{false};
6976
size_t url_tester_payload_version{0};
@@ -86,6 +93,7 @@ namespace bt::ui {
8693
void render_subs_window();
8794
void render_dashboard();
8895
void render_url_tester_input();
96+
void render_scripting_window();
8997

9098
void render_status_bar();
9199
void render_no_browsers();

‎bt/app/ui/picker_app.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ namespace bt::ui {
7575
//.size(wnd_width, wnd_height_normal)
7676
.no_titlebar()
7777
.no_resize()
78-
.no_border()
78+
.border(0)
7979
.fill_viewport()
8080
.no_scroll();
8181
};

‎bt/app/url_pipeline.h

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#pragma once
2-
#include <string>
32
#include <memory>
43
#include <vector>
54
#include "url_pipeline_step.h"

‎bt/bt.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
alg::tracker t{APP_SHORT_NAME, APP_VERSION};
1919
bt::config g_config;
2020
bt::url_pipeline g_pipeline{g_config};
21+
bt::script_site g_script{bt::config::get_data_file_path("rules.lua"), true};
2122

2223
using namespace std;
2324

‎bt/globals.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <string>
33
#include "app/url_pipeline.h"
44
#include "app/config.h"
5+
#include "app/script_site.h"
56
#include "../common/ext/alg_tracker.h"
67

78
const std::string AppDescription("Redirects open URLs to a browser of your choice.");
@@ -22,4 +23,6 @@ extern alg::tracker t;
2223

2324
extern bt::config g_config;
2425

25-
extern bt::url_pipeline g_pipeline;
26+
extern bt::url_pipeline g_pipeline;
27+
28+
extern bt::script_site g_script;

‎docs/release-notes.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 5.0.0
2+
3+
### New features
4+
- BT can write log file (todo: docs)
5+
- Lua scripting.
6+
17
## 4.0.3
28

39
New features:

‎test/CMakeLists.txt

+8-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
55
project(test)
66

77
find_package(fmt CONFIG REQUIRED)
8+
find_package(Lua REQUIRED)
89
find_package(GTest CONFIG REQUIRED)
910

1011
# For Windows: Prevent overriding the parent project's compiler/linker settings
@@ -16,7 +17,8 @@ file(GLOB cpps CONFIGURE_DEPENDS
1617
"*.cpp"
1718
"../common/*.cpp"
1819
"../bt/app/match_rule.cpp"
19-
"../bt/app/security/*.cpp")
20+
"../bt/app/security/*.cpp"
21+
"../bt/app/script_site.cpp")
2022

2123
add_executable(test ${cpps})
2224

@@ -25,6 +27,9 @@ gtest_discover_tests(test)
2527

2628
target_link_libraries(test PRIVATE
2729
fmt::fmt-header-only
28-
GTest::gtest GTest::gtest_main GTest::gmock GTest::gmock_main)
30+
GTest::gtest GTest::gtest_main GTest::gmock GTest::gmock_main
31+
${LUA_LIBRARIES})
2932

30-
target_include_directories(test PRIVATE "../common")
33+
target_include_directories(test PRIVATE
34+
"../common"
35+
${LUA_INCLUDE_DIR})

‎test/playground.lua

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
-- initial payload for testing
2+
p = {
3+
url = "https://microsoft.com/other",
4+
wt = "Slack",
5+
pn = "slack.exe"
6+
}
7+
8+
function work_slack()
9+
10+
-- extract domain part from p.url
11+
domain = string.match(p.url, "https://(.-)/")
12+
print("domain is " .. domain)
13+
14+
-- check if domain is microsoft.com and process name is slack.exe, then return true
15+
if domain == "microsoft.com" and p.pn == "slack.exe" then
16+
p.url = p.url .. "?authuser=3"
17+
return true
18+
end
19+
20+
return false
21+
end
22+
23+
print("--- Before: ---")
24+
print("url: " .. p.url)
25+
print("wt: " .. p.wt)
26+
print("pn: " .. p.pn)
27+
28+
matches = work_slack()
29+
print("matches: " .. tostring(matches))
30+
31+
print("--- After: ---")
32+
print("url: " .. p.url)
33+
print("wt: " .. p.wt)
34+
print("pn: " .. p.pn)

‎test/script_site.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <gtest/gtest.h>
2+
#include "../bt/app/script_site.h"
3+
4+
using namespace std;
5+
using namespace bt;
6+
7+
TEST(Script, Loads) {
8+
bt::script_site ss{R"()", false};
9+
EXPECT_EQ(ss.get_error(), "");
10+
}
11+
12+
TEST(Script, SimpleMatch) {
13+
bt::script_site ss{R"(
14+
function test1()
15+
return true;
16+
end
17+
)", false};
18+
19+
EXPECT_EQ(ss.get_error(), "");
20+
21+
url_payload up{"http://test.com"};
22+
bool matches = ss.call_rule(up, "test1");
23+
EXPECT_TRUE(matches);
24+
}

‎vcpkg-configuration.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"default-registry": {
33
"kind": "git",
4-
"baseline": "1dc5ee30eb1032221d29f281f4a94b73f06b4284",
4+
"baseline": "e590c2b30c08caf1dd8d612ec602a003f9784b7d",
55
"repository": "https://github.com/microsoft/vcpkg"
66
},
77
"registries": [

‎vcpkg.json

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"simpleini",
3131
"p-ranav-csv2",
3232
"tinyxml2",
33+
"lua",
3334
"gtest"
3435
]
3536
}

‎wrs/bt.tree

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<toc-element topic="rules.md"/>
1919
<toc-element topic="picker.md"/>
2020
<toc-element topic="url-proc.md">
21+
<toc-element topic="url-proc-scripting.md"/>
2122
</toc-element>
2223
<toc-element topic="commandline.md"/>
2324
<toc-element topic="Browser-Extensions.md">

‎wrs/images/audit00.png

-50.3 KB
Binary file not shown.

‎wrs/images/default-sb.png

-11 KB
Binary file not shown.

‎wrs/images/default.png

-53.6 KB
Binary file not shown.

‎wrs/images/no-browsers.png

-69.4 KB
Binary file not shown.

‎wrs/topics/url-proc-scripting.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Scripting
2+
3+
Start typing here...

‎wrs/topics/url-proc.md

+21
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ The functionality is available from the **Pipeline** menu.
66

77
<img src="url-pipeline-menu.png" height="120" alt="URL Pipeline menu"/>
88

9+
To understand how URL processing works, let's see how %product% processes URLs:
10+
11+
```mermaid
12+
stateDiagram-v2
13+
direction LR
14+
RP: Rule processor
15+
B: Open browser
16+
state Pipeline {
17+
O365 --> Unshorten
18+
Unshorten --> Substitute
19+
}
20+
[*] --> Pipeline
21+
Pipeline --> RP
22+
RP --> B
23+
B --> [*]
24+
```
25+
26+
Every time you click on a link, it first goes via a "Pipeline" that makes optional transformations on it. This includes unwrapping Office 365 links, un-shortening URLs, and applying substitutions, all described below.
27+
28+
Rule processor then applies rules to the URL and opens the browser based on the rule.
29+
930
## Un-shortening
1031

1132
<warning>

0 commit comments

Comments
 (0)
Please sign in to comment.