From cb2caff551335fd3dd194e7b98cad019418c6b08 Mon Sep 17 00:00:00 2001 From: Patrick Gaskin Date: Thu, 2 Jul 2020 00:25:00 -0400 Subject: [PATCH] Improved extensibility of nickel_setting, split browser stuff into nickel_browser action (closes #59) (#60) * Split browser actions from nickel_extra into nickel_browser * Improved extensibility of nickel_setting action Instead of nickel_setting: for toggling a setting and nickel_setting:: for setting it explicitly, use nickel_setting::. --- res/doc | 33 +++--- src/action.h | 1 + src/action_cc.cc | 257 +++++++++++++++++++++++------------------------ 3 files changed, 146 insertions(+), 145 deletions(-) diff --git a/res/doc b/res/doc index a91907f..04f1038 100644 --- a/res/doc +++ b/res/doc @@ -29,6 +29,7 @@ # kfmon - triggers a kfmon action # nickel_setting - toggles a boolean setting # nickel_extras - opens one of the beta features +# nickel_browser - opens the browser # nickel_misc - other stuff which isn't significant enough for its own category # nickel_open - opens a view # nickel_wifi - controls wifi (note: it doesn't wait for it to connect or disconnect, neither does it check for success) @@ -46,27 +47,29 @@ # kfmon - the filename of the KFMon watched item to launch. # This is actually the basename of the watch's filename as specified in its KFMon config (i.e., the png). # You can also check the output of the 'list' command via the kfmon-ipc tool. -# nickel_setting - one of: -# invert - toggles FeatureSettings.InvertScreen -# invert: - ^ same, but sets it to 'true'/'enabled'/'yes'/'on'/'1' or 'false'/'disabled'/'no'/'off'/'0' -# lockscreen - toggles PowerSettings.UnlockEnabled (4.12.12111+) -# lockscreen: - ^ ... -# screenshots - toggles FeatureSettings.Screenshots -# screenshots: - ^ ... -# force_wifi - toggles DeveloperSettings.ForceWifiOn (note: the setting doesn't apply until you toggle WiFi) -# force_wifi: - ^ ... +# nickel_setting - : +# action if one of: +# toggle - toggles between true/false +# enable - sets to true +# disable - sets to false +# setting is one of: +# invert - FeatureSettings.InvertScreen +# lockscreen - PowerSettings.UnlockEnabled (4.12.12111+) +# screenshots - FeatureSettings.Screenshots +# force_wifi - DeveloperSettings.ForceWifiOn (note: the setting doesn't apply until you toggle WiFi) # nickel_extras - the mimetype of the plugin, or one of: -# web_browser - opens the web browser to the default homepage -# web_browser: - opens the web browser to the specified URL -# web_browser: - opens the web browser to the specified URL and injects the specified CSS (which can contain spaces and colons) into all pages -# web_browser:modal - opens the web browser to the default homepage as a pop-up window -# web_browser:modal: - see above -# web_browser:modal: - see above # unblock_it # sketch_pad # solitaire # sudoku # word_scramble +# nickel_browser - one of: +# - opens the web browser to the default homepage (note that the line should end with a colon even though the argument is blank) +# - opens the web browser to the specified URL +# - opens the web browser to the specified URL and injects the specified CSS (which can contain spaces and colons) into all pages +# modal - opens the web browser to the default homepage as a pop-up window +# modal: - see above +# modal: - see above # nickel_misc - one of: # home - goes to the home screen # force_usb_connection - forces a usb connection dialog to be shown diff --git a/src/action.h b/src/action.h index 7873342..7d328c3 100644 --- a/src/action.h +++ b/src/action.h @@ -43,6 +43,7 @@ void nm_action_result_free(nm_action_result_t *res); X(kfmon_id) \ X(nickel_setting) \ X(nickel_extras) \ + X(nickel_browser) \ X(nickel_misc) \ X(nickel_open) \ X(nickel_wifi) \ diff --git a/src/action_cc.cc b/src/action_cc.cc index 3fba7eb..b856093 100644 --- a/src/action_cc.cc +++ b/src/action_cc.cc @@ -126,6 +126,26 @@ NM_ACTION_(nickel_open) { NM_ACTION_(nickel_setting) { #define NM_ERR_RET nullptr + enum { + mode_toggle, + mode_enable, + mode_disable, + } mode; + + char *tmp1 = strdupa(arg); // strsep and strtrim will modify it + char *arg1 = strtrim(strsep(&tmp1, ":")); + char *arg2 = strtrim(tmp1); + NM_ASSERT(arg2, "could not find a : in the argument"); + + if (!strcmp(arg1, "toggle")) + mode = mode_toggle; + else if (!strcmp(arg1, "enable")) + mode = mode_enable; + else if (!strcmp(arg1, "disable")) + mode = mode_disable; + else + NM_RETURN_ERR("unknown action '%s' for nickel_setting: expected 'toggle', 'enable', or 'disable'", arg1); + //libnickel 4.6 * _ZN6Device16getCurrentDeviceEv Device *(*Device_getCurrentDevice)(); reinterpret_cast(Device_getCurrentDevice) = dlsym(RTLD_DEFAULT, "_ZN6Device16getCurrentDeviceEv"); @@ -188,30 +208,15 @@ NM_ACTION_(nickel_setting) { NM_ASSERT(Settings_vtable, "could not dlsym the vtable for Settings"); NM_ASSERT(vtable_ptr(settings) == vtable_target(Settings_vtable), "unexpected vtable layout (expected class to start with a pointer to 8 bytes into the vtable)"); - bool v = false; + bool v = mode == mode_disable; // this gets inverted - char *tmp1 = strdupa(arg); // strsep and strtrim will modify it - char *arg1 = strtrim(strsep(&tmp1, ":")); - char *arg2 = strtrim(tmp1); - - if (arg2) { - // note: v gets inverted when setting the setting below - if (!strcmp(arg2, "true") || !strcmp(arg2, "enabled") || !strcmp(arg2, "yes") || !strcmp(arg2, "on") || !strcmp(arg2, "1")) { - v = false; - } else if (!strcmp(arg2, "false") || !strcmp(arg2, "disabled") || !strcmp(arg2, "no") || !strcmp(arg2, "off") || !strcmp(arg2, "0")) { - v = true; - } else { - NM_RETURN_ERR("invalid value '%s' for setting '%s', must be 'true'/'enabled'/'yes'/'on'/'1' or 'false'/'disabled'/'no'/'off'/'0' (arg: '%s')", arg2, arg1, arg); - } - } - - if (!strcmp(arg1, "invert") || !strcmp(arg1, "screenshots")) { + if (!strcmp(arg2, "invert") || !strcmp(arg2, "screenshots")) { //libnickel 4.6 * _ZTV15FeatureSettings void *FeatureSettings_vtable = dlsym(RTLD_DEFAULT, "_ZTV15FeatureSettings"); NM_ASSERT(FeatureSettings_vtable, "could not dlsym the vtable for FeatureSettings"); vtable_ptr(settings) = vtable_target(FeatureSettings_vtable); - if (!strcmp(arg1, "invert")) { + if (!strcmp(arg2, "invert")) { //libnickel 4.6 * _ZN15FeatureSettings12invertScreenEv bool (*FeatureSettings_invertScreen)(Settings*); reinterpret_cast(FeatureSettings_invertScreen) = dlsym(RTLD_DEFAULT, "_ZN15FeatureSettings12invertScreenEv"); @@ -222,7 +227,7 @@ NM_ACTION_(nickel_setting) { reinterpret_cast(FeatureSettings_setInvertScreen) = dlsym(RTLD_DEFAULT, "_ZN15FeatureSettings15setInvertScreenEb"); NM_ASSERT(FeatureSettings_setInvertScreen, "could not dlsym FeatureSettings::setInvertScreen"); - if (!arg2) { + if (mode == mode_toggle) { v = FeatureSettings_invertScreen(settings); vtable_ptr(settings) = vtable_target(FeatureSettings_vtable); } @@ -237,8 +242,8 @@ NM_ACTION_(nickel_setting) { NM_LOG("updating top-level window %p after invert", w); if (w) w->update(); // TODO: figure out how to make it update _after_ the menu item redraws itself - } else if (!strcmp(arg1, "screenshots")) { - if (!arg2) { + } else if (!strcmp(arg2, "screenshots")) { + if (mode == mode_toggle) { QVariant v1 = Settings_getSetting(settings, QStringLiteral("Screenshots"), QVariant(false)); vtable_ptr(settings) = vtable_target(FeatureSettings_vtable); v = v1.toBool(); @@ -250,54 +255,50 @@ NM_ACTION_(nickel_setting) { QVariant v2 = Settings_getSetting(settings, QStringLiteral("Screenshots"), QVariant(false)); vtable_ptr(settings) = vtable_target(FeatureSettings_vtable); } - } else if (!strcmp(arg1, "lockscreen")) { + } else if (!strcmp(arg2, "lockscreen")) { void *PowerSettings_vtable = dlsym(RTLD_DEFAULT, "_ZTV13PowerSettings"); NM_ASSERT(PowerSettings_vtable, "could not dlsym the vtable for PowerSettings"); vtable_ptr(settings) = vtable_target(PowerSettings_vtable); - if (!strcmp(arg1, "lockscreen")) { - //libnickel 4.12.12111 * _ZN13PowerSettings16getUnlockEnabledEv - bool (*PowerSettings__getUnlockEnabled)(Settings*); - reinterpret_cast(PowerSettings__getUnlockEnabled) = dlsym(RTLD_DEFAULT, "_ZN13PowerSettings16getUnlockEnabledEv"); - NM_ASSERT(PowerSettings__getUnlockEnabled, "could not dlsym PowerSettings::getUnlockEnabled"); + //libnickel 4.12.12111 * _ZN13PowerSettings16getUnlockEnabledEv + bool (*PowerSettings__getUnlockEnabled)(Settings*); + reinterpret_cast(PowerSettings__getUnlockEnabled) = dlsym(RTLD_DEFAULT, "_ZN13PowerSettings16getUnlockEnabledEv"); + NM_ASSERT(PowerSettings__getUnlockEnabled, "could not dlsym PowerSettings::getUnlockEnabled"); - //libnickel 4.12.12111 * _ZN13PowerSettings16setUnlockEnabledEb - bool (*PowerSettings__setUnlockEnabled)(Settings*, bool); - reinterpret_cast(PowerSettings__setUnlockEnabled) = dlsym(RTLD_DEFAULT, "_ZN13PowerSettings16setUnlockEnabledEb"); - NM_ASSERT(PowerSettings__setUnlockEnabled, "could not dlsym PowerSettings::setUnlockEnabled"); + //libnickel 4.12.12111 * _ZN13PowerSettings16setUnlockEnabledEb + bool (*PowerSettings__setUnlockEnabled)(Settings*, bool); + reinterpret_cast(PowerSettings__setUnlockEnabled) = dlsym(RTLD_DEFAULT, "_ZN13PowerSettings16setUnlockEnabledEb"); + NM_ASSERT(PowerSettings__setUnlockEnabled, "could not dlsym PowerSettings::setUnlockEnabled"); - if (!arg2) { - v = PowerSettings__getUnlockEnabled(settings); - vtable_ptr(settings) = vtable_target(PowerSettings_vtable); - } - - PowerSettings__setUnlockEnabled(settings, !v); - vtable_ptr(settings) = vtable_target(PowerSettings_vtable); - - NM_ASSERT(PowerSettings__getUnlockEnabled(settings) == !v, "failed to set setting"); + if (mode == mode_toggle) { + v = PowerSettings__getUnlockEnabled(settings); vtable_ptr(settings) = vtable_target(PowerSettings_vtable); } - } else if (!strcmp(arg1, "force_wifi")) { + + PowerSettings__setUnlockEnabled(settings, !v); + vtable_ptr(settings) = vtable_target(PowerSettings_vtable); + + NM_ASSERT(PowerSettings__getUnlockEnabled(settings) == !v, "failed to set setting"); + vtable_ptr(settings) = vtable_target(PowerSettings_vtable); + } else if (!strcmp(arg2, "force_wifi")) { //libnickel 4.6 * _ZTV11DevSettings void *PowerSettings_vtable = dlsym(RTLD_DEFAULT, "_ZTV11DevSettings"); NM_ASSERT(PowerSettings_vtable, "could not dlsym the vtable for DevSettings"); vtable_ptr(settings) = vtable_target(PowerSettings_vtable); - if (!strcmp(arg1, "force_wifi")) { - if (!arg2) { - QVariant v1 = Settings_getSetting(settings, QStringLiteral("ForceWifiOn"), QVariant(false)); - vtable_ptr(settings) = vtable_target(PowerSettings_vtable); - v = v1.toBool(); - } - - Settings_saveSetting(settings, QStringLiteral("ForceWifiOn"), QVariant(!v), false); - vtable_ptr(settings) = vtable_target(PowerSettings_vtable); - - QVariant v2 = Settings_getSetting(settings, QStringLiteral("ForceWifiOn"), QVariant(false)); + if (mode == mode_toggle) { + QVariant v1 = Settings_getSetting(settings, QStringLiteral("ForceWifiOn"), QVariant(false)); vtable_ptr(settings) = vtable_target(PowerSettings_vtable); + v = v1.toBool(); } + + Settings_saveSetting(settings, QStringLiteral("ForceWifiOn"), QVariant(!v), false); + vtable_ptr(settings) = vtable_target(PowerSettings_vtable); + + QVariant v2 = Settings_getSetting(settings, QStringLiteral("ForceWifiOn"), QVariant(false)); + vtable_ptr(settings) = vtable_target(PowerSettings_vtable); } else { - // TODO: more settings + // TODO: more settings? Settings_SettingsD(settings); NM_RETURN_ERR("unknown setting name '%s' (arg: '%s')", arg1, arg); } @@ -307,7 +308,7 @@ NM_ACTION_(nickel_setting) { Settings_SettingsD(settings); - NM_RETURN_OK(strcmp(arg1, "invert") // invert is obvious + NM_RETURN_OK(strcmp(arg2, "invert") // invert is obvious ? nm_action_result_toast("%s %s", v ? "disabled" : "enabled", arg1) : nm_action_result_silent()); #undef NM_ERR_RET @@ -316,82 +317,6 @@ NM_ACTION_(nickel_setting) { NM_ACTION_(nickel_extras) { #define NM_ERR_RET nullptr - if (!strncmp(arg, "web_browser", 11)) { - bool modal; - QUrl *url; - QString *css; - - if (!strcmp(arg, "web_browser")) { - modal = false; - url = new QUrl(); - css = new QString(""); - } else { - char *tmp1 = strdupa(arg); // strsep and strtrim will modify it - char *arg1 = strtrim(strsep(&tmp1, ":")); - char *arg2 = strtrim(tmp1); - - if (!arg2 || strcmp(arg1, "web_browser")) - NM_RETURN_ERR("unknown beta feature name or plugin mimetype '%s' (split: '%s')", arg, arg1); - - QString tmp = QString::fromUtf8(arg2).trimmed(); - - if (tmp.section(':', 0, 0).trimmed() == "modal") { - modal = true; - tmp = tmp.section(':', 1).trimmed(); - } else { - modal = false; - } - - if (tmp.contains(' ')) { - css = new QString(tmp.section(' ', 1).trimmed()); - url = new QUrl(tmp.section(' ', 0, 0).trimmed(), QUrl::ParsingMode::StrictMode); - if (!url->isValid() || url->isRelative()) - NM_RETURN_ERR("invalid url '%s' (argument: '%s') (note: if your url has spaces, they need to be escaped)", qPrintable(tmp.section(' ', 0, 0)), arg); - } else if (tmp.length()) { - url = new QUrl(tmp, QUrl::ParsingMode::StrictMode); - css = new QString(""); - if (!url->isValid() || url->isRelative()) - NM_RETURN_ERR("invalid url '%s' (argument: '%s') (note: if your url has spaces, they need to be escaped)", qPrintable(tmp.section(' ', 0, 0)), arg); - } else { - url = new QUrl(); - css = new QString(""); - } - } - - NM_LOG("web browser '%s' (modal=%d, url='%s', css='%s')", arg, modal, url->isValid() ? qPrintable(url->toString()) : "(home)", qPrintable(*css)); - - //libnickel 4.6 * _ZN22BrowserWorkflowManager14sharedInstanceEv _ZN22BrowserWorkflowManagerC1EP7QObject - BrowserWorkflowManager *(*BrowserWorkflowManager_sharedInstance)(); - void (*BrowserWorkflowManager_BrowserWorkflowManager)(BrowserWorkflowManager*, QObject*); // 4.11.11911+ - reinterpret_cast(BrowserWorkflowManager_sharedInstance) = dlsym(RTLD_DEFAULT, "_ZN22BrowserWorkflowManager14sharedInstanceEv"); - reinterpret_cast(BrowserWorkflowManager_BrowserWorkflowManager) = dlsym(RTLD_DEFAULT, "_ZN22BrowserWorkflowManagerC1EP7QObject"); - NM_ASSERT(BrowserWorkflowManager_sharedInstance || BrowserWorkflowManager_BrowserWorkflowManager, "could not dlsym BrowserWorkflowManager constructor (4.11.11911+) or sharedInstance"); - - //libnickel 4.6 * _ZN22BrowserWorkflowManager11openBrowserEbRK4QUrlRK7QString - void (*BrowserWorkflowManager_openBrowser)(BrowserWorkflowManager*, bool, QUrl*, QString*); // the bool is whether to open it as a modal, the QUrl is the URL to load(if !QUrl::isValid(), it loads the homepage), the string is CSS to inject - reinterpret_cast(BrowserWorkflowManager_openBrowser) = dlsym(RTLD_DEFAULT, "_ZN22BrowserWorkflowManager11openBrowserEbRK4QUrlRK7QString"); - NM_ASSERT(BrowserWorkflowManager_openBrowser, "could not dlsym BrowserWorkflowManager::openBrowser"); - - // note: everything must be on the heap since if it isn't connected, it - // passes it as-is to the connected signal, which will be used - // after this action returns. - - BrowserWorkflowManager *bwm; - - if (BrowserWorkflowManager_BrowserWorkflowManager) { - bwm = calloc(1, 128); // as of 4.20.14622, it's actually 20 bytes, but we're going to stay on the safe side - NM_ASSERT(bwm, "could not allocate memory for BrowserWorkflowManager"); - BrowserWorkflowManager_BrowserWorkflowManager(bwm, nullptr); - } else { - bwm = BrowserWorkflowManager_sharedInstance(); - NM_ASSERT(bwm, "could not get shared browser workflow manager pointer"); - } - - BrowserWorkflowManager_openBrowser(bwm, modal, url, css); - - NM_RETURN_OK(nm_action_result_silent()); - } - const char* mimetype; if (strchr(arg, '/')) mimetype = arg; else if (!strcmp(arg, "unblock_it")) mimetype = "application/x-games-RushHour"; @@ -411,6 +336,78 @@ NM_ACTION_(nickel_extras) { #undef NM_ERR_RET } +NM_ACTION_(nickel_browser) { + #define NM_ERR_RET nullptr + + bool modal; + QUrl *url; + QString *css; + + if (!arg || !*arg) { + modal = false; + url = new QUrl(); + css = new QString(""); + } else { + QString tmp = QString::fromUtf8(arg).trimmed(); + + if (tmp.section(':', 0, 0).trimmed() == "modal") { + modal = true; + tmp = tmp.section(':', 1).trimmed(); + } else { + modal = false; + } + + if (tmp.contains(' ')) { + css = new QString(tmp.section(' ', 1).trimmed()); + url = new QUrl(tmp.section(' ', 0, 0).trimmed(), QUrl::ParsingMode::StrictMode); + if (!url->isValid() || url->isRelative()) + NM_RETURN_ERR("invalid url '%s' (argument: '%s') (note: if your url has spaces, they need to be escaped)", qPrintable(tmp.section(' ', 0, 0)), arg); + } else if (tmp.length()) { + url = new QUrl(tmp, QUrl::ParsingMode::StrictMode); + css = new QString(""); + if (!url->isValid() || url->isRelative()) + NM_RETURN_ERR("invalid url '%s' (argument: '%s') (note: if your url has spaces, they need to be escaped)", qPrintable(tmp.section(' ', 0, 0)), arg); + } else { + url = new QUrl(); + css = new QString(""); + } + } + + NM_LOG("web browser '%s' (modal=%d, url='%s', css='%s')", arg, modal, url->isValid() ? qPrintable(url->toString()) : "(home)", qPrintable(*css)); + + //libnickel 4.6 * _ZN22BrowserWorkflowManager14sharedInstanceEv _ZN22BrowserWorkflowManagerC1EP7QObject + BrowserWorkflowManager *(*BrowserWorkflowManager_sharedInstance)(); + void (*BrowserWorkflowManager_BrowserWorkflowManager)(BrowserWorkflowManager*, QObject*); // 4.11.11911+ + reinterpret_cast(BrowserWorkflowManager_sharedInstance) = dlsym(RTLD_DEFAULT, "_ZN22BrowserWorkflowManager14sharedInstanceEv"); + reinterpret_cast(BrowserWorkflowManager_BrowserWorkflowManager) = dlsym(RTLD_DEFAULT, "_ZN22BrowserWorkflowManagerC1EP7QObject"); + NM_ASSERT(BrowserWorkflowManager_sharedInstance || BrowserWorkflowManager_BrowserWorkflowManager, "could not dlsym BrowserWorkflowManager constructor (4.11.11911+) or sharedInstance"); + + //libnickel 4.6 * _ZN22BrowserWorkflowManager11openBrowserEbRK4QUrlRK7QString + void (*BrowserWorkflowManager_openBrowser)(BrowserWorkflowManager*, bool, QUrl*, QString*); // the bool is whether to open it as a modal, the QUrl is the URL to load(if !QUrl::isValid(), it loads the homepage), the string is CSS to inject + reinterpret_cast(BrowserWorkflowManager_openBrowser) = dlsym(RTLD_DEFAULT, "_ZN22BrowserWorkflowManager11openBrowserEbRK4QUrlRK7QString"); + NM_ASSERT(BrowserWorkflowManager_openBrowser, "could not dlsym BrowserWorkflowManager::openBrowser"); + + // note: everything must be on the heap since if it isn't connected, it + // passes it as-is to the connected signal, which will be used + // after this action returns. + + BrowserWorkflowManager *bwm; + + if (BrowserWorkflowManager_BrowserWorkflowManager) { + bwm = calloc(1, 128); // as of 4.20.14622, it's actually 20 bytes, but we're going to stay on the safe side + NM_ASSERT(bwm, "could not allocate memory for BrowserWorkflowManager"); + BrowserWorkflowManager_BrowserWorkflowManager(bwm, nullptr); + } else { + bwm = BrowserWorkflowManager_sharedInstance(); + NM_ASSERT(bwm, "could not get shared browser workflow manager pointer"); + } + + BrowserWorkflowManager_openBrowser(bwm, modal, url, css); + + NM_RETURN_OK(nm_action_result_silent()); + #undef NM_ERR_RET +} + NM_ACTION_(nickel_misc) { #define NM_ERR_RET nullptr if (!strcmp(arg, "home")) {