Started implementing config, implemented uninstall flag
This commit is contained in:
14
res/doc
14
res/doc
@@ -9,7 +9,7 @@
|
|||||||
#
|
#
|
||||||
# Place your configuration files in this folder (/mnt/onboard/.kobo/.adds/nmi).
|
# Place your configuration files in this folder (/mnt/onboard/.kobo/.adds/nmi).
|
||||||
# They can be named anything, and should consist of multiple lines either
|
# They can be named anything, and should consist of multiple lines either
|
||||||
# starting with # for a comment, or in the one of the following formats:
|
# starting with # for a comment, or in the the following format:
|
||||||
#
|
#
|
||||||
# menu_item:<location>:<label>:<subsystem>:<arg>
|
# menu_item:<location>:<label>:<subsystem>:<arg>
|
||||||
# Adds a menu item (spaces around fields are ignored).
|
# Adds a menu item (spaces around fields are ignored).
|
||||||
@@ -31,11 +31,17 @@
|
|||||||
# invert - toggles FeatureSettings.InvertScreen (all versions)
|
# invert - toggles FeatureSettings.InvertScreen (all versions)
|
||||||
# screenshots - toggles FeatureSettings.Screenshots (all versions)
|
# screenshots - toggles FeatureSettings.Screenshots (all versions)
|
||||||
#
|
#
|
||||||
# uninstall
|
|
||||||
# Makes nickel-menu-inject uninstall itself.
|
|
||||||
#
|
|
||||||
# For example, you might have a configuration file in KOBOeReader/.adds/nmi/mystuff like:
|
# For example, you might have a configuration file in KOBOeReader/.adds/nmi/mystuff like:
|
||||||
#
|
#
|
||||||
# menu_item :main :Show an error :dbg_error :This is an error message!
|
# menu_item :main :Show an error :dbg_error :This is an error message!
|
||||||
# menu_item :reader :Invert Screen :nickel_setting :invert
|
# menu_item :reader :Invert Screen :nickel_setting :invert
|
||||||
#
|
#
|
||||||
|
# If there is a error in the configuration, an item which displays it will be
|
||||||
|
# added to the main menu. If an internal error occurs, it is written to syslog,
|
||||||
|
# which can be viewed over telnet or SSH (the username is root) with the command
|
||||||
|
# logread.
|
||||||
|
#
|
||||||
|
# To uninstall nickel-menu-inject, create a file named KOBOeReader/.adds/nmi/uninstall,
|
||||||
|
# or manually uninstall it by deleting libnmi.so. You can also uninstall it by
|
||||||
|
# triggering the failsafe mechanism by turning your Kobo off within 20 seconds
|
||||||
|
# of turning it on.
|
||||||
78
src/config.c
78
src/config.c
@@ -1,4 +1,6 @@
|
|||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -9,40 +11,54 @@
|
|||||||
#include "subsys_c.h"
|
#include "subsys_c.h"
|
||||||
#include "subsys_cc.h"
|
#include "subsys_cc.h"
|
||||||
|
|
||||||
// TODO: actually parse it
|
typedef enum {
|
||||||
|
NMI_CONFIG_TYPE_MENU_ITEM = 1,
|
||||||
|
} nmi_config_type_t;
|
||||||
|
|
||||||
nmi_menu_entry_t *nmi_config_parse(size_t *n, char **err_out) {
|
struct nmi_config_t {
|
||||||
|
nmi_config_type_t type;
|
||||||
|
union {
|
||||||
|
nmi_menu_item_t *menu_item;
|
||||||
|
} value;
|
||||||
|
nmi_config_t *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
nmi_config_t *nmi_config_parse(char **err_out) {
|
||||||
#define NMI_ERR_RET NULL
|
#define NMI_ERR_RET NULL
|
||||||
NMI_ASSERT(n, "required argument is null");
|
|
||||||
nmi_menu_entry_t *me = calloc(5, sizeof(nmi_menu_entry_t));
|
|
||||||
|
|
||||||
me[0].loc = NMI_MENU_LOCATION_MAIN_MENU;
|
NMI_RETURN_ERR("not implemented");
|
||||||
me[0].lbl = strdup("Test Log (main)");
|
|
||||||
me[0].arg = strdup("It worked!");
|
|
||||||
me[0].execute = nmi_subsys_dbgsyslog;
|
|
||||||
|
|
||||||
me[1].loc = NMI_MENU_LOCATION_READER_MENU;
|
|
||||||
me[1].lbl = strdup("Test Log (reader)");
|
|
||||||
me[1].arg = strdup("It worked!");
|
|
||||||
me[1].execute = nmi_subsys_dbgsyslog;
|
|
||||||
|
|
||||||
me[2].loc = NMI_MENU_LOCATION_MAIN_MENU;
|
|
||||||
me[2].lbl = strdup("Test Error (main)");
|
|
||||||
me[2].arg = strdup("It's a fake error!");
|
|
||||||
me[2].execute = nmi_subsys_dbgerror;
|
|
||||||
|
|
||||||
me[3].loc = NMI_MENU_LOCATION_READER_MENU;
|
|
||||||
me[3].lbl = strdup("Test Error (reader)");
|
|
||||||
me[3].arg = strdup("It's a fake error!");
|
|
||||||
me[3].execute = nmi_subsys_dbgerror;
|
|
||||||
|
|
||||||
me[4].loc = NMI_MENU_LOCATION_READER_MENU;
|
|
||||||
me[4].lbl = strdup("Invert Screen");
|
|
||||||
me[4].arg = "invert";
|
|
||||||
me[4].execute = nmi_subsys_nickelsetting;
|
|
||||||
|
|
||||||
*n = 5;
|
|
||||||
NMI_RETURN_OK(me);
|
|
||||||
#undef NMI_ERR_RET
|
#undef NMI_ERR_RET
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nmi_menu_item_t **nmi_config_get_menu(nmi_config_t *cfg, size_t *n_out) {
|
||||||
|
*n_out = 0;
|
||||||
|
for (nmi_config_t *cur = cfg; cur; cur = cfg->next)
|
||||||
|
if (cur->type == NMI_CONFIG_TYPE_MENU_ITEM)
|
||||||
|
(*n_out)++;
|
||||||
|
|
||||||
|
nmi_menu_item_t **it = calloc(*n_out, sizeof(nmi_menu_item_t*));
|
||||||
|
if (!it)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nmi_menu_item_t **tmp = it;
|
||||||
|
for (nmi_config_t *cur = cfg; cur; cur = cfg->next)
|
||||||
|
if (cur->type == NMI_CONFIG_TYPE_MENU_ITEM)
|
||||||
|
*(tmp++) = cur->value.menu_item;
|
||||||
|
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nmi_config_free(nmi_config_t *cfg) {
|
||||||
|
while (cfg) {
|
||||||
|
nmi_config_t *n = cfg->next;
|
||||||
|
|
||||||
|
if (cfg->type == NMI_CONFIG_TYPE_MENU_ITEM) {
|
||||||
|
free(cfg->value.menu_item->lbl);
|
||||||
|
free(cfg->value.menu_item->arg);
|
||||||
|
free(cfg->value.menu_item);
|
||||||
|
}
|
||||||
|
free(cfg);
|
||||||
|
|
||||||
|
cfg = n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
17
src/config.h
17
src/config.h
@@ -4,10 +4,23 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
|
||||||
// TODO
|
typedef struct nmi_config_t nmi_config_t;
|
||||||
nmi_menu_entry_t *nmi_config_parse(size_t *n, char **err_out);
|
|
||||||
|
// nmi_config_parse parses the configuration files in /mnt/onboard/.adds/nmi.
|
||||||
|
// An error is returned if there are syntax errors, file access errors, or
|
||||||
|
// invalid subsystem names for menu_item.
|
||||||
|
nmi_config_t *nmi_config_parse(char **err_out);
|
||||||
|
|
||||||
|
// nmi_config_get_menu gets a malloc'd array of pointers to the menu items
|
||||||
|
// defined in the config. These pointers will be valid until nmi_config_free is
|
||||||
|
// called.
|
||||||
|
nmi_menu_item_t **nmi_config_get_menu(nmi_config_t *cfg, size_t *n_out);
|
||||||
|
|
||||||
|
// nmi_config_free frees all allocated memory.
|
||||||
|
void nmi_config_free(nmi_config_t *cfg);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,12 @@ static void *_nmi_failsafe_destroy(void* _fs) {
|
|||||||
void nmi_failsafe_destroy(nmi_failsafe_t *fs, int delay) {
|
void nmi_failsafe_destroy(nmi_failsafe_t *fs, int delay) {
|
||||||
fs->delay = delay;
|
fs->delay = delay;
|
||||||
|
|
||||||
NMI_LOG("failsafe: starting restore thread");
|
NMI_LOG("failsafe: scheduling restore");
|
||||||
pthread_t t;
|
pthread_t t;
|
||||||
pthread_create(&t, NULL, _nmi_failsafe_destroy, fs);
|
pthread_create(&t, NULL, _nmi_failsafe_destroy, fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nmi_failsafe_uninstall(nmi_failsafe_t *fs) {
|
||||||
|
NMI_LOG("failsafe: deleting %s", fs->tmp);
|
||||||
|
unlink(fs->tmp);
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ nmi_failsafe_t *nmi_failsafe_create(char **err_out);
|
|||||||
// after a delay. The nmi_failsafe_t must not be used afterwards.
|
// after a delay. The nmi_failsafe_t must not be used afterwards.
|
||||||
void nmi_failsafe_destroy(nmi_failsafe_t *fs, int delay);
|
void nmi_failsafe_destroy(nmi_failsafe_t *fs, int delay);
|
||||||
|
|
||||||
|
// nmi_failsafe_uninstall uninstalls the lib. The nmi_failsafe_t must not be
|
||||||
|
// used afterwards.
|
||||||
|
void nmi_failsafe_uninstall(nmi_failsafe_t *fs);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
33
src/init.c
33
src/init.c
@@ -5,10 +5,12 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "failsafe.h"
|
#include "failsafe.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
#include "subsys_c.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
__attribute__((constructor)) void nmi_init() {
|
__attribute__((constructor)) void nmi_init() {
|
||||||
@@ -27,14 +29,33 @@ __attribute__((constructor)) void nmi_init() {
|
|||||||
goto stop;
|
goto stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle uninstall
|
NMI_LOG("init: checking for uninstall flag");
|
||||||
|
if (!access("/mnt/onboard/.adds/nmi/uninstall", F_OK)) {
|
||||||
|
NMI_LOG("init: flag found, uninstalling");
|
||||||
|
nmi_failsafe_uninstall(fs);
|
||||||
|
unlink("/mnt/onboard/.adds/nmi/uninstall");
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
|
||||||
NMI_LOG("init: parsing config");
|
NMI_LOG("init: parsing config");
|
||||||
size_t entries_n;
|
size_t items_n;
|
||||||
nmi_menu_entry_t *entries;
|
nmi_menu_item_t **items;
|
||||||
if (!(entries = nmi_config_parse(&entries_n, &err)) && err) {
|
nmi_config_t *cfg;
|
||||||
NMI_LOG("error: could not parse config: %s, stopping", err);
|
if (!(cfg = nmi_config_parse(&err)) && err) {
|
||||||
|
NMI_LOG("error: could not parse config: %s, creating error item in main menu instead", err);
|
||||||
|
|
||||||
|
items_n = 1;
|
||||||
|
items = calloc(items_n, sizeof(nmi_menu_item_t*));
|
||||||
|
*items = calloc(items_n, sizeof(nmi_menu_item_t));
|
||||||
|
|
||||||
|
items[0]->loc = NMI_MENU_LOCATION_MAIN_MENU;
|
||||||
|
items[0]->lbl = strdup("Config Error");
|
||||||
|
items[0]->arg = strdup(err);
|
||||||
|
items[0]->execute = nmi_subsys_dbgerror;
|
||||||
|
|
||||||
free(err);
|
free(err);
|
||||||
|
} else if (!(items = nmi_config_get_menu(cfg, &items_n))) {
|
||||||
|
NMI_LOG("error: could not allocate memory, stopping");
|
||||||
goto stop_fs;
|
goto stop_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +67,7 @@ __attribute__((constructor)) void nmi_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NMI_LOG("init: hooking libnickel");
|
NMI_LOG("init: hooking libnickel");
|
||||||
if (nmi_menu_hook(libnickel, entries, entries_n, &err) && err) {
|
if (nmi_menu_hook(libnickel, items, items_n, &err) && err) {
|
||||||
NMI_LOG("error: could not hook libnickel: %s, stopping", err);
|
NMI_LOG("error: could not hook libnickel: %s, stopping", err);
|
||||||
free(err);
|
free(err);
|
||||||
goto stop_fs;
|
goto stop_fs;
|
||||||
|
|||||||
30
src/menu.cc
30
src/menu.cc
@@ -33,10 +33,10 @@ static QAction* (*AbstractNickelMenuController_createAction)(void*, QMenu*, QWid
|
|||||||
// a signal handler).
|
// a signal handler).
|
||||||
static void (*ConfirmationDialogFactory_showOKDialog)(QString const&, QString const&);
|
static void (*ConfirmationDialogFactory_showOKDialog)(QString const&, QString const&);
|
||||||
|
|
||||||
static nmi_menu_entry_t *_entries;
|
static nmi_menu_item_t **_items;
|
||||||
static size_t _entries_n;
|
static size_t _items_n;
|
||||||
|
|
||||||
extern "C" int nmi_menu_hook(void *libnickel, nmi_menu_entry_t *entries, size_t entries_n, char **err_out) {
|
extern "C" int nmi_menu_hook(void *libnickel, nmi_menu_item_t **items, size_t items_n, char **err_out) {
|
||||||
#define NMI_ERR_RET 1
|
#define NMI_ERR_RET 1
|
||||||
reinterpret_cast<void*&>(AbstractNickelMenuController_createMenuTextItem) = dlsym(libnickel, "_ZN28AbstractNickelMenuController18createMenuTextItemEP5QMenuRK7QStringbbS4_");
|
reinterpret_cast<void*&>(AbstractNickelMenuController_createMenuTextItem) = dlsym(libnickel, "_ZN28AbstractNickelMenuController18createMenuTextItemEP5QMenuRK7QStringbbS4_");
|
||||||
reinterpret_cast<void*&>(AbstractNickelMenuController_createAction) = dlsym(libnickel, "_ZN22AbstractMenuController12createActionEP5QMenuP7QWidgetbbb");
|
reinterpret_cast<void*&>(AbstractNickelMenuController_createAction) = dlsym(libnickel, "_ZN22AbstractMenuController12createActionEP5QMenuP7QWidgetbbb");
|
||||||
@@ -53,8 +53,8 @@ extern "C" int nmi_menu_hook(void *libnickel, nmi_menu_entry_t *entries, size_t
|
|||||||
reinterpret_cast<void*&>(AbstractNickelMenuController_createMenuTextItem_orig) = nmi_dlhook(libnickel, "_ZN28AbstractNickelMenuController18createMenuTextItemEP5QMenuRK7QStringbbS4_", nmh, &err);
|
reinterpret_cast<void*&>(AbstractNickelMenuController_createMenuTextItem_orig) = nmi_dlhook(libnickel, "_ZN28AbstractNickelMenuController18createMenuTextItemEP5QMenuRK7QStringbbS4_", nmh, &err);
|
||||||
NMI_ASSERT(AbstractNickelMenuController_createMenuTextItem_orig, "failed to hook _ZN28AbstractNickelMenuController18createMenuTextItemEP5QMenuRK7QStringbbS4_: %s", err);
|
NMI_ASSERT(AbstractNickelMenuController_createMenuTextItem_orig, "failed to hook _ZN28AbstractNickelMenuController18createMenuTextItemEP5QMenuRK7QStringbbS4_: %s", err);
|
||||||
|
|
||||||
_entries = entries;
|
_items = items;
|
||||||
_entries_n = entries_n;
|
_items_n = items_n;
|
||||||
|
|
||||||
NMI_RETURN_OK(0);
|
NMI_RETURN_OK(0);
|
||||||
#undef NMI_ERR_RET
|
#undef NMI_ERR_RET
|
||||||
@@ -73,25 +73,25 @@ extern "C" MenuTextItem* _nmi_menu_hook(void* _this, QMenu* menu, QString const&
|
|||||||
if ((isrm = (label == trrm) && !checkable))
|
if ((isrm = (label == trrm) && !checkable))
|
||||||
NMI_LOG("Intercepting reader menu (label=Dictionary, checkable=false)...");
|
NMI_LOG("Intercepting reader menu (label=Dictionary, checkable=false)...");
|
||||||
|
|
||||||
for (size_t i = 0; i < _entries_n; i++) {
|
for (size_t i = 0; i < _items_n; i++) {
|
||||||
nmi_menu_entry_t *ent = &_entries[i];
|
nmi_menu_item_t *it = _items[i];
|
||||||
if (ent->loc == NMI_MENU_LOCATION_MAIN_MENU && !ismm)
|
if (it->loc == NMI_MENU_LOCATION_MAIN_MENU && !ismm)
|
||||||
continue;
|
continue;
|
||||||
if (ent->loc == NMI_MENU_LOCATION_READER_MENU && !isrm)
|
if (it->loc == NMI_MENU_LOCATION_READER_MENU && !isrm)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
NMI_LOG("Adding item '%s'...", ent->lbl);
|
NMI_LOG("Adding item '%s'...", it->lbl);
|
||||||
|
|
||||||
MenuTextItem* item = AbstractNickelMenuController_createMenuTextItem_orig(_this, menu, QString::fromUtf8(ent->lbl), false, false, "");
|
MenuTextItem* item = AbstractNickelMenuController_createMenuTextItem_orig(_this, menu, QString::fromUtf8(it->lbl), false, false, "");
|
||||||
QAction* action = AbstractNickelMenuController_createAction(_this, menu, item, true, true, true);
|
QAction* action = AbstractNickelMenuController_createAction(_this, menu, item, true, true, true);
|
||||||
|
|
||||||
// note: we're capturing by value, i.e. the pointer to the global variable, rather then the stack variable, so this is safe
|
// note: we're capturing by value, i.e. the pointer to the global variable, rather then the stack variable, so this is safe
|
||||||
QObject::connect(action, &QAction::triggered, std::function<void(bool)>([ent](bool checked){
|
QObject::connect(action, &QAction::triggered, std::function<void(bool)>([it](bool checked){
|
||||||
NMI_LOG("Item '%s' pressed...", ent->lbl);
|
NMI_LOG("Item '%s' pressed...", it->lbl);
|
||||||
char *err;
|
char *err;
|
||||||
if (ent->execute(ent->arg, &err) && err) {
|
if (it->execute(it->arg, &err) && err) {
|
||||||
NMI_LOG("Got error %s, displaying...", err);
|
NMI_LOG("Got error %s, displaying...", err);
|
||||||
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(ent->lbl), QString::fromUtf8(err));
|
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QString::fromUtf8(err));
|
||||||
free(err);
|
free(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,17 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nmi_menu_location_t loc;
|
nmi_menu_location_t loc;
|
||||||
const char *lbl;
|
char *lbl;
|
||||||
const char *arg;
|
char *arg;
|
||||||
int (*execute)(const char *arg, char **out_err); // can block, must return 0 on success, nonzero with out_err set to the malloc'd error message on error
|
int (*execute)(const char *arg, char **out_err); // can block, must return 0 on success, nonzero with out_err set to the malloc'd error message on error
|
||||||
} nmi_menu_entry_t;
|
} nmi_menu_item_t;
|
||||||
|
|
||||||
// nmi_menu_hook hooks a dlopen'd libnickel handle to add the specified menus,
|
// nmi_menu_hook hooks a dlopen'd libnickel handle to add the specified menus,
|
||||||
// and returns 0 on success, or 1 with err_out (if provided) set to the malloc'd
|
// and returns 0 on success, or 1 with err_out (if provided) set to the malloc'd
|
||||||
// error message on error. The provided configuration and all pointers it
|
// error message on error. The provided configuration and all pointers it
|
||||||
// references must remain valid for the lifetime of the program (i.e. not stack
|
// references must remain valid for the lifetime of the program (i.e. not stack
|
||||||
// allocated). It MUST NOT be called more than once.
|
// allocated). It MUST NOT be called more than once.
|
||||||
int nmi_menu_hook(void *libnickel, nmi_menu_entry_t *entries, size_t entries_n, char **err_out);
|
int nmi_menu_hook(void *libnickel, nmi_menu_item_t **items, size_t items_n, char **err_out);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user