From 8341187ce6fde1795af6d7b713f80707510a83f7 Mon Sep 17 00:00:00 2001 From: Cam Date: Sat, 18 Mar 2023 03:08:29 -0400 Subject: [PATCH 1/4] Add files via upload --- install.sh | 10 +- keyMap.txt | 2 + src/fakeKeysX11.hpp | 262 +++++++++++++++++ src/getactivewindowX11.hpp | 128 +++++++++ src/nagaX11.cpp | 567 +++++++++++++++++++++++++++++++++++++ src/nagaXinputStart.sh | 12 +- 6 files changed, 970 insertions(+), 11 deletions(-) create mode 100644 src/fakeKeysX11.hpp create mode 100644 src/getactivewindowX11.hpp create mode 100644 src/nagaX11.cpp diff --git a/install.sh b/install.sh index 705288e..a1795cf 100755 --- a/install.sh +++ b/install.sh @@ -16,8 +16,8 @@ command -v g++ >/dev/null 2>&1 || { tput setaf 1; echo >&2 "I require g++ but it reset echo "Compiling code..." -cd src -g++ naga.cpp -o naga -pthread -Ofast --std=c++2a -lX11 -lXtst -lXmu +cd src || exit +g++ nagaX11.cpp -o naga -pthread -Ofast --std=c++2b -lX11 -lXtst -lXmu if [ ! -f ./naga ]; then tput setaf 1; echo "Error at compile! Ensure you have g++ installed. !!!Aborting!!!" @@ -47,11 +47,11 @@ for u in $(sudo awk -F'[/:]' '{if ($3 >= 1000 && $3 != 65534) print $1}' /etc/pa do sudo gpasswd -a "$u" razer _dir="/home/${u}/.naga" - sudo mkdir -p $_dir + sudo mkdir -p "$_dir" if [ -d "$_dir" ] then sudo cp -r -n -v "keyMap.txt" "$_dir" - sudo chown -R $(id -un $u):users "$_dir" + sudo chown -R "$(id -un "$u"):users" "$_dir" fi done if [ -d "/root" ]; @@ -67,5 +67,5 @@ sudo mv /tmp/80-naga.rules /etc/udev/rules.d/80-naga.rules naga start -tput setaf 2; echo "Please add (naga.desktop or a script with naga start) to be executed\nwhen your window manager starts." +tput setaf 2; printf "Please add (naga.desktop or a script with naga start) to be executed\nwhen your window manager starts." tput sgr0; diff --git a/keyMap.txt b/keyMap.txt index 530d0a8..9c22c4e 100644 --- a/keyMap.txt +++ b/keyMap.txt @@ -54,6 +54,8 @@ configWindow=Gnome-terminal 3 - string=gnome@terminal.com 4 - run2=if [ $(pgrep -c spotify) -eq 0 ]; then setsid spotify; fi 5 - run=gnome-terminal +6 - specialPressOnPress=È +6 - specialReleaseOnRelease=È 7 - key=XF86AudioLowerVolume 8 - key=XF86AudioPlay 9 - key=XF86AudioRaiseVolume diff --git a/src/fakeKeysX11.hpp b/src/fakeKeysX11.hpp new file mode 100644 index 0000000..df7beb9 --- /dev/null +++ b/src/fakeKeysX11.hpp @@ -0,0 +1,262 @@ +#include +#include +#include + +#include +#include + +#define N_MODIFIER_INDEXES (Mod5MapIndex + 1) + +struct FakeKey +{ + Display *xdpy; + int min_keycode, max_keycode, n_keysyms_per_keycode, held_keycode, held_state_flags, alt_mod_index; + KeySym *keysyms; + KeyCode modifier_table[N_MODIFIER_INDEXES]; +}; + +static void deleteFakeKey(FakeKey *aKeyFaker) +{ + XFree(aKeyFaker->keysyms); + free(aKeyFaker); +} + +static int utf8_to_ucs4(const unsigned char *src_orig, unsigned int *dst, int len) +{ + const unsigned char *src = src_orig; + unsigned char s; + int extra; + unsigned int result; + + if (len == 0) + return 0; + + s = *src++; + len--; + + if (!(s & 0x80)) + { + result = s; + extra = 0; + } + else if (!(s & 0x40)) + { + return -1; + } + else if (!(s & 0x20)) + { + result = s & 0x1f; + extra = 1; + } + else if (!(s & 0x10)) + { + result = s & 0xf; + extra = 2; + } + else if (!(s & 0x08)) + { + result = s & 0x07; + extra = 3; + } + else if (!(s & 0x04)) + { + result = s & 0x03; + extra = 4; + } + else if (!(s & 0x02)) + { + result = s & 0x01; + extra = 5; + } + else + { + return -1; + } + if (extra > len) + return -1; + + while (extra--) + { + result <<= 6; + s = *src++; + + if ((s & 0xc0) != 0x80) + return -1; + + result |= s & 0x3f; + } + *dst = result; + return src - src_orig; +} + +FakeKey *fakekey_init(Display *xdpy) +{ + FakeKey *fk = NULL; + int event, error, major, minor, mod_index, mod_key; + XModifierKeymap *modifiers; + KeyCode *kp; + + if (xdpy == NULL || !XTestQueryExtension(xdpy, &event, &error, &major, &minor)) + return NULL; + + + fk = (FakeKey *)malloc(sizeof(FakeKey)); + memset(fk, 0, sizeof(FakeKey)); + + fk->xdpy = xdpy; + + /* Find keycode limits */ + + XDisplayKeycodes(fk->xdpy, &fk->min_keycode, &fk->max_keycode); + + /* Get the mapping */ + + fk->keysyms = XGetKeyboardMapping(fk->xdpy, fk->min_keycode, fk->max_keycode - fk->min_keycode + 1, &fk->n_keysyms_per_keycode); + + modifiers = XGetModifierMapping(fk->xdpy); + + kp = modifiers->modifiermap; + + for (mod_index = 0; mod_index < 8; mod_index++) + { + fk->modifier_table[mod_index] = 0; + + for (mod_key = 0; mod_key < modifiers->max_keypermod; mod_key++) + { + int keycode = kp[mod_index * modifiers->max_keypermod + mod_key]; + + if (keycode != 0) + { + fk->modifier_table[mod_index] = keycode; + break; + } + } + } + + if (modifiers) + XFreeModifiermap(modifiers); + + return fk; +} + +int fakekey_send_keyevent(FakeKey *fk, KeyCode keycode, Bool is_press, int flags) +{ + if (flags) + { + if (flags & LockMask) + XTestFakeKeyEvent(fk->xdpy, fk->modifier_table[ShiftMapIndex], + is_press, CurrentTime); + + if (flags & ControlMask) + XTestFakeKeyEvent(fk->xdpy, fk->modifier_table[ControlMapIndex], + is_press, CurrentTime); + + if (flags & Mod1Mask) //ALT MASK + XTestFakeKeyEvent(fk->xdpy, fk->modifier_table[Mod1MapIndex], + is_press, CurrentTime); + + XSync(fk->xdpy, False); + } + + XTestFakeKeyEvent(fk->xdpy, keycode, is_press, CurrentTime); + + XSync(fk->xdpy, False); + + return 1; +} + +int fakekey_press_keysym(FakeKey *fk, KeySym keysym, int flags) +{ + static int modifiedkey; + KeyCode code = 0; + + if ((code = XKeysymToKeycode(fk->xdpy, keysym)) != 0) + { + if (XkbKeycodeToKeysym(fk->xdpy, code, 0, 0) != keysym) + { + if (XkbKeycodeToKeysym(fk->xdpy, code, 0, 1) == keysym) + flags |= LockMask; + else + code = 0; + } + else + { + flags &= ~LockMask; + } + } + + if (!code) + { + modifiedkey = (modifiedkey + 1) % 10; + + int index = (fk->max_keycode - fk->min_keycode - modifiedkey - 1) * fk->n_keysyms_per_keycode; + + fk->keysyms[index] = keysym; + + XChangeKeyboardMapping(fk->xdpy, + fk->min_keycode, + fk->n_keysyms_per_keycode, + fk->keysyms, + (fk->max_keycode - fk->min_keycode)); + + XSync(fk->xdpy, False); + + code = fk->max_keycode - modifiedkey - 1; + + if (XkbKeycodeToKeysym(fk->xdpy, code, 0, 0) != keysym && XkbKeycodeToKeysym(fk->xdpy, code, 0, 1) == keysym) + { + flags |= LockMask; + } + } + + if (code != 0) + { + fakekey_send_keyevent(fk, code, True, flags); + + fk->held_state_flags = flags; + fk->held_keycode = code; + + return 1; + } + + fk->held_state_flags = 0; + fk->held_keycode = 0; + + return 0; +} + +int fakekey_press(FakeKey *fk, const unsigned char *utf8_char_in, int len_bytes, int flags) +{ + unsigned int ucs4_out; + + if (fk->held_keycode) /* key is already held down */ + return 0; + + + + if (len_bytes < 0) + { + len_bytes = strlen((const char *)utf8_char_in); + } + + if (utf8_to_ucs4(utf8_char_in, &ucs4_out, len_bytes) < 1) + { + return 0; + } + + if (ucs4_out > 0x00ff) /* < 0xff assume Latin-1 1:1 mapping */ + ucs4_out = ucs4_out | 0x01000000; /* This gives us the magic X keysym */ + + return fakekey_press_keysym(fk, (KeySym)ucs4_out, flags); +} + +void fakekey_release(FakeKey *fk) +{ + if (!fk->held_keycode) + return; + + fakekey_send_keyevent(fk, fk->held_keycode, False, fk->held_state_flags); + + fk->held_state_flags = 0; + fk->held_keycode = 0; +} diff --git a/src/getactivewindowX11.hpp b/src/getactivewindowX11.hpp new file mode 100644 index 0000000..2863ce6 --- /dev/null +++ b/src/getactivewindowX11.hpp @@ -0,0 +1,128 @@ +#include +#include +#include + +#include // `apt-get install libx11-dev` +#include // `apt-get install libxmu-dev` + +Bool xerror = False; + +Display *open_display() +{ + Display *d = XOpenDisplay(NULL); + if (d == NULL) + { + printf("fail\n"); + exit(1); + } + return d; +} + +int handle_error(Display *display, XErrorEvent *error) +{ + printf("ERROR: X11 error\n"); + xerror = True; + return 1; +} + +Window get_focus_window(Display *d) +{ + Window w; + int revert_to; + XGetInputFocus(d, &w, &revert_to); // see man + if (xerror) + { + printf("fail\n"); + return 0; + } + else if (w == None) + { + printf("no focus window\n"); + return 0; + } + else + { + return w; + } +} + +// get the top window. +// a top window have the following specifications. +// * the start window is contained the descendent windows. +// * the parent window is the root window. +Window get_top_window(Display *d, Window start) +{ + Window w = start; + Window parent = start; + Window root = None; + Window *children; + unsigned int nchildren; + Status s; + while (parent != root) + { + w = parent; + s = XQueryTree(d, w, &root, &parent, &children, &nchildren); // see man + + if (s) + XFree(children); + + if (xerror) + { + printf("fail\n"); + return 0; + } + } + return w; +} + +// search a named window (that has a WM_STATE prop) +// on the descendent windows of the argment Window. +Window get_named_window(Display *d, Window start) +{ + Window w; + w = XmuClientWindow(d, start); // see man + return w; +} + +char *print_window_class(Display *d, Window w) +{ + Status s; + XClassHint *clas; + + clas = XAllocClassHint(); // see man + if (xerror) + { + printf("ERROR: XAllocClassHint\n"); + return new char[1]('E'); + } + + s = XGetClassHint(d, w, clas); // see man + XCloseDisplay(d); + if (xerror || s) + { + return clas->res_class; + } + else + { + printf("ERROR: XGetClassHint\n"); + return new char[1]('E'); + } +} + +char *getActiveWindow() +{ + Display *d; + Window w; + + // for XmbTextPropertyToTextList + setlocale(LC_ALL, ""); // see man locale + + d = open_display(); + XSetErrorHandler(handle_error); + + // get active window + w = get_focus_window(d); + w = get_top_window(d, w); + w = get_named_window(d, w); + return print_window_class(d, w); +} \ No newline at end of file diff --git a/src/nagaX11.cpp b/src/nagaX11.cpp new file mode 100644 index 0000000..50ead44 --- /dev/null +++ b/src/nagaX11.cpp @@ -0,0 +1,567 @@ +// This is lostallmymoney's remake of RaulPPelaez's original tool. +// RaulPPelaez, et. al wrote the original file. As long as you retain this notice you +// can do whatever you want with this stuff. + +#include "fakeKeysX11.hpp" +#include "getactivewindowX11.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +typedef pair CharAndChar; +typedef pair CharAndFakeKey; +static mutex fakeKeyFollowUpsMutex, configSwitcherMutex; +static vector *const fakeKeyFollowUps = new vector(); +static int fakeKeyFollowCount = 0; + +class configKey +{ +private: + const string *const prefix; + const bool onKeyPressed; + const void (*const internalFunction)(const string *const c); + +public: + const bool &IsOnKeyPressed() const { return onKeyPressed; } + const void runInternal(const string *const content) const { internalFunction(content); } + const string *const Prefix() const { return prefix; } + + configKey(const bool tonKeyPressed, const void (*const tinternalF)(const string *cc) = NULL, const string tcontent = "") : prefix(new string(tcontent)), onKeyPressed(tonKeyPressed), internalFunction(tinternalF) + { + } +}; + +typedef pair stringAndConfigKey; + +class MacroEvent +{ +private: + const configKey *const keyType; + const string *const content; + +public: + const configKey *KeyType() const { return keyType; } + const string *Content() const { return content; } + + MacroEvent(const configKey *tkeyType, const string *tcontent) : keyType(tkeyType), content(new string(*tcontent)) + { + } +}; + +typedef vector MacroEventVector; + +class configSwitchScheduler +{ +private: + bool scheduledReMap = false, aWindowConfigActive = false; + string scheduledReMapString = "", temporaryWindowConfigName = "", backupConfigName = ""; + +public: + vector configWindowsNamesVector; + + const string &RemapString() const + { + return scheduledReMapString; + } + const string &temporaryWindowName() const + { + return temporaryWindowConfigName; + } + const string &getBackupConfigName() const + { + return backupConfigName; + } + const bool &isAWindowConfigActive() const + { + return aWindowConfigActive; + } + const bool &isRemapScheduled() const + { + return scheduledReMap; + } + const void scheduleReMap(const string *reMapString) + { + scheduledReMapString = *reMapString; + scheduledReMap = true; + aWindowConfigActive = false; + temporaryWindowConfigName = ""; + backupConfigName = ""; + } + const void scheduleWindowReMap(const string *reMapString) + { + if (!aWindowConfigActive) + { + backupConfigName = scheduledReMapString; + } + scheduledReMapString = *reMapString; + temporaryWindowConfigName = *reMapString; + scheduledReMap = true; + aWindowConfigActive = true; + } + const void unScheduleReMap() + { + scheduledReMap = false; + } +}; + +static configSwitchScheduler *const configSwitcher = new configSwitchScheduler(); + +class NagaDaemon +{ +private: + const string conf_file = string(getenv("HOME")) + "/.naga/keyMap.txt"; + + map configKeysMap; + map>> macroEventsKeyMaps; + + string currentConfigName; + struct input_event ev1[64]; + const int size = sizeof(struct input_event) * 64; + vector devices; + bool areSideBtnEnabled = true, areExtraBtnEnabled = true, areWindowConfigsInitialised = false; + + void loadConf(const string configName, bool silent = false) + { + configSwitcher->unScheduleReMap(); + if (!macroEventsKeyMaps.contains(configName)) + { + ifstream in(conf_file.c_str(), ios::in); + if (!in) + { + cerr << "Cannot open " << conf_file << ". Exiting." << endl; + exit(1); + } + bool found1 = false, found2 = false; + int pos, configLine, configEndLine; + string commandContent; + + if (!areWindowConfigsInitialised) + { + for (int readingLine = 1; getline(in, commandContent) && !found2; readingLine++) + { + if (commandContent.find("configWindow=") != string::npos) + { + commandContent.erase(0, 13); + (*configSwitcher).configWindowsNamesVector.emplace_back(new string(commandContent)); + } + } + areWindowConfigsInitialised = true; + } + + in.clear(); + in.seekg(0, ios::beg); // reset file reading + + for (int readingLine = 1; getline(in, commandContent) && !found2; readingLine++) + { + if (!found1) + { + if (commandContent.find("config=" + configName) != string::npos || commandContent.find("configWindow=" + configName) != string::npos) // finding configname + { + configLine = readingLine; + found1 = true; + } + } + else + { + if (commandContent.find("configEnd") != string::npos) // finding configEnd + { + configEndLine = readingLine; + found2 = true; + } + } + } + if (!found1 || !found2) + { + clog << "Error with config names and configEnd : " << configName << ". Exiting." << endl; + exit(1); + } + in.clear(); + in.seekg(0, ios::beg); // reset file reading + + for (int readingLine = 1; getline(in, commandContent) && readingLine < configEndLine; readingLine++) + { + if (readingLine > configLine) + { + if (commandContent[0] == '#' || commandContent.find_first_not_of(' ') == string::npos) + continue; // Ignore comments, empty lines + pos = commandContent.find('='); + string *commandType = new string(commandContent.substr(0, pos)); // commandType = numbers + command type + commandContent.erase(0, pos + 1); // commandContent = command content + commandType->erase(remove(commandType->begin(), commandType->end(), ' '), commandType->end()); // Erase spaces inside 1st part of the line + pos = commandType->find("-"); + const string *const buttonNumber = new string(commandType->substr(0, pos)); // Isolate button number + commandType = new string(commandType->substr(pos + 1)); // Isolate command type + for (char &c : *commandType) + c = tolower(c); + + const auto stoiNumber = [&](const string *const numberString) + { + try + { + return stoi(*numberString); + } + catch (...) + { + clog << "At config line " << readingLine << ": expected a number" << endl; + exit(1); + } + }; + + if (configKeysMap.contains(*commandType)) + { // filter out bad types + if (*configKeysMap[*commandType]->Prefix() != "") + commandContent = *configKeysMap[*commandType]->Prefix() + commandContent; + macroEventsKeyMaps[configName][stoiNumber(buttonNumber)][configKeysMap[*commandType]->IsOnKeyPressed()].emplace_back(new MacroEvent(configKeysMap[*commandType], &commandContent)); + // Encode and store mapping v3 + } + else if (*commandType == "key") + { + if (commandContent.size() == 1) + { + commandContent = hexChar(commandContent[0]); + } + const string *const commandContent2 = new string(*configKeysMap["keyreleaseonrelease"]->Prefix() + commandContent); + commandContent = *configKeysMap["keypressonpress"]->Prefix() + commandContent; + macroEventsKeyMaps[configName][stoiNumber(buttonNumber)][true].emplace_back(new MacroEvent(configKeysMap["keypressonpress"], &commandContent)); + macroEventsKeyMaps[configName][stoiNumber(buttonNumber)][false].emplace_back(new MacroEvent(configKeysMap["keyreleaseonrelease"], commandContent2)); + } + } + } + in.close(); + } + currentConfigName = configName; + if (!silent) + { + (void)!(system(("notify-send -t 200 'New config :' '" + configName + "'").c_str())); + } + } + + string hexChar(const char a) + { + stringstream hexedChar; + hexedChar << "0x00" << hex << (int)(a); + return hexedChar.str(); + } + + int side_btn_fd, extra_btn_fd; + input_event *ev11; + fd_set readset; + + bool checkForWindowConfig() + { + char *c = getActiveWindow(); + clog << "CurrentWindowNameLog : " << c << endl; + if (configSwitcher->temporaryWindowName() == "" || strcmp(c, configSwitcher->temporaryWindowName().c_str()) != 0) + { + bool found = false; + for (string *configWindowName : (*configSwitcher).configWindowsNamesVector) + { + if (strcmp(c, configWindowName->c_str()) == 0) + { + lock_guard guard(configSwitcherMutex); + configSwitcher->scheduleWindowReMap(configWindowName); + loadConf(configSwitcher->RemapString(), true); // change config for macroEvents[ii]->Content() + found = true; + return true; + } + } + if (!found && configSwitcher->isAWindowConfigActive()) + { + lock_guard guard(configSwitcherMutex); + configSwitcher->scheduleReMap(&configSwitcher->getBackupConfigName()); + loadConf(configSwitcher->RemapString(), true); // change config for macroEvents[ii]->Content() + return true; + } + } + return false; + } + + void run() + { + map> currenConfigPtr = macroEventsKeyMaps[currentConfigName]; + + if (areSideBtnEnabled) + ioctl(side_btn_fd, EVIOCGRAB, 1); // Give application exclusive control over side buttons. + ev11 = &ev1[1]; + while (1) + { + if (configSwitcher->isRemapScheduled()) + { + lock_guard guard(configSwitcherMutex); // remap + loadConf(configSwitcher->RemapString()); // change config for macroEvents[ii]->Content() + currenConfigPtr = macroEventsKeyMaps[currentConfigName]; + } + + FD_ZERO(&readset); + if (areSideBtnEnabled) + FD_SET(side_btn_fd, &readset); + if (areExtraBtnEnabled) + FD_SET(extra_btn_fd, &readset); + if (select(FD_SETSIZE, &readset, NULL, NULL, NULL) == -1) + exit(2); + if (areSideBtnEnabled && FD_ISSET(side_btn_fd, &readset)) // Side buttons + { + if (read(side_btn_fd, ev1, size) == -1) + exit(2); + if (ev1[0].value != ' ' && ev11->type == EV_KEY) + { // Key event (press or release) + switch (ev11->code) + { + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + if (checkForWindowConfig()) + currenConfigPtr = macroEventsKeyMaps[currentConfigName]; + thread(runActions, ¤ConfigPtr[ev11->code - 1][ev11->value == 1]).detach(); // real key number = ev11->code - 1 + break; + } + } + } + if (areExtraBtnEnabled && FD_ISSET(extra_btn_fd, &readset)) // Extra buttons + { + if (read(extra_btn_fd, ev1, size) == -1) + exit(2); + if (ev11->type == 1) + { // Only extra buttons + switch (ev11->code) + { + case 275: + case 276: + if (checkForWindowConfig()) + currenConfigPtr = macroEventsKeyMaps[currentConfigName]; + thread(runActions, ¤ConfigPtr[ev11->code - 262][ev11->value == 1]).detach(); // real key number = ev11->code - OFFSET (#262) + break; + } + } + } + } + } + + // Functions that can be given to configKeys + const static void writeStringNow(const string *macroContent) + { + lock_guard guard(fakeKeyFollowUpsMutex); + FakeKey *const aKeyFaker = fakekey_init(XOpenDisplay(NULL)); + const int strSize = macroContent->size(); + for (int z = 0; z < strSize; z++) + { + fakekey_press(aKeyFaker, (unsigned char *)&(*macroContent)[z], 8, 0); + fakekey_release(aKeyFaker); + } + XFlush(aKeyFaker->xdpy); + XCloseDisplay(aKeyFaker->xdpy); + deleteFakeKey(aKeyFaker); + } + + const static void specialPressNow(const string *const macroContent) + { + lock_guard guard(fakeKeyFollowUpsMutex); + FakeKey *const aKeyFaker = fakekey_init(XOpenDisplay(NULL)); + fakekey_press(aKeyFaker, (unsigned char *)&(*macroContent)[0], 8, 0); + XFlush(aKeyFaker->xdpy); + fakeKeyFollowUps->emplace_back(new CharAndFakeKey(&(*macroContent)[0], aKeyFaker)); + fakeKeyFollowCount++; + } + + const static void specialReleaseNow(const string *const macroContent) + { + if (fakeKeyFollowCount > 0) + { + lock_guard guard(fakeKeyFollowUpsMutex); + for (int vectorId = fakeKeyFollowUps->size() - 1; vectorId >= 0; vectorId--) + { + CharAndFakeKey *const aKeyFollowUp = (*fakeKeyFollowUps)[vectorId]; + if (*get<0>(*aKeyFollowUp) == (*macroContent)[0]) + { + FakeKey *const aKeyFaker = get<1>(*aKeyFollowUp); + fakekey_release(aKeyFaker); + XFlush(aKeyFaker->xdpy); + XCloseDisplay(aKeyFaker->xdpy); + fakeKeyFollowUps->erase(fakeKeyFollowUps->begin() + vectorId); + fakeKeyFollowCount--; + deleteFakeKey(aKeyFaker); + } + delete aKeyFollowUp; + } + } + else + clog << "No candidate for key release" << endl; + } + + const static void chmapNow(const string *const macroContent) + { + lock_guard guard(configSwitcherMutex); + configSwitcher->scheduleReMap(macroContent); // schedule config switch/change + } + + const static void sleepNow(const string *const macroContent) + { + usleep(stoul(*macroContent) * 1000); // microseconds make me dizzy in keymap.txt + } + + const static void executeNow(const string *const macroContent) + { + (void)!(system(macroContent->c_str())); + } + // end of configKeys functions + + static void runActions(MacroEventVector *const relativeMacroEventsPointer) + { + for (MacroEvent *const macroEventPointer : *relativeMacroEventsPointer) + { // run all the events at Key + macroEventPointer->KeyType()->runInternal(macroEventPointer->Content()); + } + } + +public: + NagaDaemon(const string mapConfig = "defaultConfig") + { + if (daemon(0, 1)) + perror("Couldn't daemonise from unistd"); + // modulable device files list + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic-event-mouse"); // NAGA EPIC + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Dock-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Dock-event-mouse"); // NAGA EPIC DOCK + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_2014-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_2014-event-mouse"); // NAGA 2014 + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga-event-mouse"); // NAGA MOLTEN + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma-event-mouse"); // NAGA EPIC CHROMA + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma_Dock-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma_Dock-event-mouse"); // NAGA EPIC CHROMA DOCK + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Chroma-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Chroma-event-mouse"); // NAGA CHROMA + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Hex-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Hex-event-mouse"); // NAGA HEX + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Hex_V2-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Hex_V2-event-mouse"); // NAGA HEX v2 + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Trinity_00000000001A-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Trinity_00000000001A-event-mouse"); // NAGA Trinity + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Left_Handed_Edition-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Left_Handed_Edition-event-mouse"); // NAGA Left Handed + devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Pro_000000000000-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Pro_000000000000-event-mouse"); // NAGA PRO WIRELESS + devices.emplace_back("/dev/input/by-id/usb-1532_Razer_Naga_Pro_000000000000-if02-event-kbd", "/dev/input/by-id/usb-1532_Razer_Naga_Pro_000000000000-event-mouse"); // NAGA PRO + // devices.emplace_back("/dev/input/by-id/YOUR_DEVICE_FILE", "/dev/input/by-id/YOUR_DEVICE_FILE#2"); // DUMMY EXAMPLE ~ ONE CAN BE EMPTY LIKE SUCH : "" (for devices with no extra buttons) + + for (CharAndChar &device : devices) + { // Setup check + side_btn_fd = open(device.first, O_RDONLY), extra_btn_fd = open(device.second, O_RDONLY); + if (side_btn_fd != -1 || extra_btn_fd != -1) + { + if (side_btn_fd == -1) + { + if (extra_btn_fd == -1) + { + cerr << "No naga devices found or you don't have permission to access them." << endl; + exit(1); + } + clog << "Reading from: " << device.second << endl; + areSideBtnEnabled = false; + } + else if (extra_btn_fd == -1) + { + clog << "Reading from: " << device.first << endl; + areExtraBtnEnabled = false; + } + else + clog << "Reading from: " << device.first << endl + << " and " << device.second << endl; + break; + } + } + + // modulable options list to manage internals inside runActions method arg1:COMMAND, arg2:onKeyPressed?, arg3:function to send prefix+config content. + + configKeysMap.insert(stringAndConfigKey("chmap", new configKey(true, chmapNow))); // change keymap + configKeysMap.insert(stringAndConfigKey("chmaprelease", new configKey(false, chmapNow))); + + configKeysMap.insert(stringAndConfigKey("sleep", new configKey(true, sleepNow))); + configKeysMap.insert(stringAndConfigKey("sleeprelease", new configKey(false, sleepNow))); + + configKeysMap.insert(stringAndConfigKey("run", new configKey(true, executeNow, "setsid "))); + configKeysMap.insert(stringAndConfigKey("run2", new configKey(true, executeNow))); + + configKeysMap.insert(stringAndConfigKey("runrelease", new configKey(false, executeNow, "setsid "))); + configKeysMap.insert(stringAndConfigKey("runrelease2", new configKey(false, executeNow))); + + configKeysMap.insert(stringAndConfigKey("keypressonpress", new configKey(true, executeNow, "setsid xdotool keydown --window getactivewindow "))); + configKeysMap.insert(stringAndConfigKey("keypressonrelease", new configKey(false, executeNow, "setsid xdotool keydown --window getactivewindow "))); + + configKeysMap.insert(stringAndConfigKey("keyreleaseonpress", new configKey(true, executeNow, "setsid xdotool keyup --window getactivewindow "))); + configKeysMap.insert(stringAndConfigKey("keyreleaseonrelease", new configKey(false, executeNow, "setsid xdotool keyup --window getactivewindow "))); + + configKeysMap.insert(stringAndConfigKey("keyclick", new configKey(true, executeNow, "setsid xdotool key --window getactivewindow "))); + configKeysMap.insert(stringAndConfigKey("keyclickrelease", new configKey(false, executeNow, "setsid xdotool key --window getactivewindow "))); + + configKeysMap.insert(stringAndConfigKey("string", new configKey(true, writeStringNow))); + configKeysMap.insert(stringAndConfigKey("stringrelease", new configKey(false, writeStringNow))); + + configKeysMap.insert(stringAndConfigKey("specialpressonpress", new configKey(true, specialPressNow))); + configKeysMap.insert(stringAndConfigKey("specialpressonrelease", new configKey(false, specialPressNow))); + + configKeysMap.insert(stringAndConfigKey("specialreleaseonpress", new configKey(true, specialReleaseNow))); + configKeysMap.insert(stringAndConfigKey("specialreleaseonrelease", new configKey(false, specialReleaseNow))); + + configSwitcher->scheduleReMap(&mapConfig); + loadConf(mapConfig); // Initialize config + run(); + } +}; + +void stopD() +{ + clog << "Stopping possible naga daemon" << endl; + (void)!(system(("/usr/local/bin/Naga_Linux/nagaKillroot.sh " + to_string((int)getpid())).c_str())); +}; + +// arguments manage +int main(const int argc, const char *const argv[]) +{ + if (argc > 1) + { + if (strstr(argv[1], "start") != NULL) + { + stopD(); + clog << "Starting naga daemon in hidden mode, keep the window for the logs..." << endl; + usleep(40000); + (void)!(system("/usr/local/bin/Naga_Linux/nagaXinputStart.sh")); + + if (argc > 2) + NagaDaemon(string(argv[2]).c_str()); + else + NagaDaemon(); + } + else if (strstr(argv[1], "kill") != NULL || strstr(argv[1], "stop") != NULL) + { + stopD(); + } + else if (strstr(argv[1], "uninstall") != NULL) + { + string answer; + clog << "Are you sure you want to uninstall ? y/n" << endl; + cin >> answer; + if (answer.length() != 1 || (answer[0] != 'y' && answer[0] != 'Y')) + { + clog << "Aborting" << endl; + } + else + { + (void)!(system("bash /usr/local/bin/Naga_Linux/nagaUninstall.sh")); + } + } + } + else + { + clog << "Possible arguments : \n -start Starts the daemon in hidden mode. (stops it before)\n -stop Stops the daemon.\n -uninstall Uninstalls the daemon." << endl; + } + return 0; +} diff --git a/src/nagaXinputStart.sh b/src/nagaXinputStart.sh index 2b7cc37..132ddf9 100644 --- a/src/nagaXinputStart.sh +++ b/src/nagaXinputStart.sh @@ -1,11 +1,11 @@ -#!/bin/bash +#!/bin/sh NAGAID2=$(xinput | grep Naga | grep pointer | cut -d= -f2 | cut -f1) -if [[ `echo $NAGAID2 | wc -w` -eq 2 ]]; then - if [[ `xinput get-button-map $(echo $NAGAID2 | awk '{print $1}') | grep 10 | wc -l` -eq 1 ]]; then - xinput set-button-map $(echo $NAGAID2 | awk '{print $1}') 1 2 3 4 5 6 7 11 10 8 9 13 14 15 275 276 +if [ "$(echo "$NAGAID2" | wc -w)" -eq "2" ]; then + if [ "$(xinput get-button-map "$(echo "$NAGAID2" | head -n 1)" | grep -c 10)" -eq 1 ]; then + xinput set-button-map "$(echo "$NAGAID2" | head -n 1)" 1 2 3 4 5 6 7 11 10 8 9 13 14 15 275 276 else - xinput set-button-map $(echo $NAGAID2 | awk '{print $2}') 1 2 3 4 5 6 7 11 10 8 9 13 14 15 275 276 + xinput set-button-map "$(echo "$NAGAID2" | tail -n 1)" 1 2 3 4 5 6 7 11 10 8 9 13 14 15 275 276 fi else - xinput set-button-map $NAGAID2 1 2 3 4 5 6 7 11 10 8 9 13 14 15 275 276 + xinput set-button-map "$NAGAID2" 1 2 3 4 5 6 7 11 10 8 9 13 14 15 275 276 fi From b7e0f6700082a9aff57ea7f77e46b3c7ad2d73dd Mon Sep 17 00:00:00 2001 From: Cam Date: Sat, 18 Mar 2023 03:08:44 -0400 Subject: [PATCH 2/4] Delete getactivewindow.hpp --- src/getactivewindow.hpp | 128 ---------------------------------------- 1 file changed, 128 deletions(-) delete mode 100644 src/getactivewindow.hpp diff --git a/src/getactivewindow.hpp b/src/getactivewindow.hpp deleted file mode 100644 index 2863ce6..0000000 --- a/src/getactivewindow.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include - -#include // `apt-get install libx11-dev` -#include // `apt-get install libxmu-dev` - -Bool xerror = False; - -Display *open_display() -{ - Display *d = XOpenDisplay(NULL); - if (d == NULL) - { - printf("fail\n"); - exit(1); - } - return d; -} - -int handle_error(Display *display, XErrorEvent *error) -{ - printf("ERROR: X11 error\n"); - xerror = True; - return 1; -} - -Window get_focus_window(Display *d) -{ - Window w; - int revert_to; - XGetInputFocus(d, &w, &revert_to); // see man - if (xerror) - { - printf("fail\n"); - return 0; - } - else if (w == None) - { - printf("no focus window\n"); - return 0; - } - else - { - return w; - } -} - -// get the top window. -// a top window have the following specifications. -// * the start window is contained the descendent windows. -// * the parent window is the root window. -Window get_top_window(Display *d, Window start) -{ - Window w = start; - Window parent = start; - Window root = None; - Window *children; - unsigned int nchildren; - Status s; - while (parent != root) - { - w = parent; - s = XQueryTree(d, w, &root, &parent, &children, &nchildren); // see man - - if (s) - XFree(children); - - if (xerror) - { - printf("fail\n"); - return 0; - } - } - return w; -} - -// search a named window (that has a WM_STATE prop) -// on the descendent windows of the argment Window. -Window get_named_window(Display *d, Window start) -{ - Window w; - w = XmuClientWindow(d, start); // see man - return w; -} - -char *print_window_class(Display *d, Window w) -{ - Status s; - XClassHint *clas; - - clas = XAllocClassHint(); // see man - if (xerror) - { - printf("ERROR: XAllocClassHint\n"); - return new char[1]('E'); - } - - s = XGetClassHint(d, w, clas); // see man - XCloseDisplay(d); - if (xerror || s) - { - return clas->res_class; - } - else - { - printf("ERROR: XGetClassHint\n"); - return new char[1]('E'); - } -} - -char *getActiveWindow() -{ - Display *d; - Window w; - - // for XmbTextPropertyToTextList - setlocale(LC_ALL, ""); // see man locale - - d = open_display(); - XSetErrorHandler(handle_error); - - // get active window - w = get_focus_window(d); - w = get_top_window(d, w); - w = get_named_window(d, w); - return print_window_class(d, w); -} \ No newline at end of file From 9114c0eebce0443e314418b61c78973b006c11aa Mon Sep 17 00:00:00 2001 From: Cam Date: Sat, 18 Mar 2023 03:08:53 -0400 Subject: [PATCH 3/4] Delete naga.cpp --- src/naga.cpp | 572 --------------------------------------------------- 1 file changed, 572 deletions(-) delete mode 100644 src/naga.cpp diff --git a/src/naga.cpp b/src/naga.cpp deleted file mode 100644 index 774b323..0000000 --- a/src/naga.cpp +++ /dev/null @@ -1,572 +0,0 @@ -// This is lostallmymoney's remake of RaulPPelaez's original tool. -// RaulPPelaez, et. al wrote the original file. As long as you retain this notice you -// can do whatever you want with this stuff. - -#include "fakeKeys.hpp" -#include "getactivewindow.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; - -typedef pair CharAndChar; -typedef pair CharAndFakeKey; -static mutex fakeKeyFollowUpsMutex, configSwitcherMutex; -static vector *const fakeKeyFollowUps = new vector(); -static int fakeKeyFollowCount = 0; - -class configKey -{ -private: - const string *const prefix; - const bool onKeyPressed; - const void (*const internalFunction)(const string *const c); - -public: - const bool &IsOnKeyPressed() const { return onKeyPressed; } - const void runInternal(const string *const content) const { internalFunction(content); } - const string *const Prefix() const { return prefix; } - - configKey(const bool tonKeyPressed, const void (*const tinternalF)(const string *cc) = NULL, const string tcontent = "") : prefix(new string(tcontent)), onKeyPressed(tonKeyPressed), internalFunction(tinternalF) - { - } -}; - -typedef pair stringAndConfigKey; - -class MacroEvent -{ -private: - const configKey *const keyType; - const string *const content; - -public: - const configKey *KeyType() const { return keyType; } - const string *Content() const { return content; } - - MacroEvent(const configKey *tkeyType, const string *tcontent) : keyType(tkeyType), content(new string(*tcontent)) - { - } -}; - -typedef vector MacroEventVector; - -class configSwitchScheduler -{ -private: - bool scheduledReMap = false, aWindowConfigActive = false; - string scheduledReMapString = "", temporaryWindowConfigName = "", backupConfigName = ""; - -public: - vector configWindowsNamesVector; - - const string &RemapString() const - { - return scheduledReMapString; - } - const string &temporaryWindowName() const - { - return temporaryWindowConfigName; - } - const string &getBackupConfigName() const - { - return backupConfigName; - } - const bool &isAWindowConfigActive() const - { - return aWindowConfigActive; - } - const bool &isRemapScheduled() const - { - return scheduledReMap; - } - const void scheduleReMap(const string *reMapString) - { - scheduledReMapString = *reMapString; - scheduledReMap = true; - aWindowConfigActive = false; - temporaryWindowConfigName = ""; - backupConfigName = ""; - } - const void scheduleWindowReMap(const string *reMapString) - { - if (!aWindowConfigActive) - { - backupConfigName = scheduledReMapString; - } - scheduledReMapString = *reMapString; - temporaryWindowConfigName = *reMapString; - scheduledReMap = true; - aWindowConfigActive = true; - } - const void unScheduleReMap() - { - scheduledReMap = false; - } -}; - -static configSwitchScheduler *const configSwitcher = new configSwitchScheduler(); - -class NagaDaemon -{ -private: - const string conf_file = string(getenv("HOME")) + "/.naga/keyMap.txt"; - - map configKeysMap; - map>> macroEventsKeyMaps; - - string currentConfigName; - struct input_event ev1[64]; - const int size = sizeof(struct input_event) * 64; - vector devices; - bool areSideBtnEnabled = true, areExtraBtnEnabled = true, areWindowConfigsInitialised = false; - - void loadConf(const string configName, bool silent = false) - { - configSwitcher->unScheduleReMap(); - if (!macroEventsKeyMaps.contains(configName)) - { - ifstream in(conf_file.c_str(), ios::in); - if (!in) - { - cerr << "Cannot open " << conf_file << ". Exiting." << endl; - exit(1); - } - bool found1 = false, found2 = false; - int pos, configLine, configEndLine; - string commandContent; - - if (!areWindowConfigsInitialised) - { - for (int readingLine = 1; getline(in, commandContent) && !found2; readingLine++) - { - if (commandContent.find("configWindow=") != string::npos) - { - commandContent.erase(0, 13); - (*configSwitcher).configWindowsNamesVector.emplace_back(new string(commandContent)); - } - } - areWindowConfigsInitialised = true; - } - - in.clear(); - in.seekg(0, ios::beg); // reset file reading - - for (int readingLine = 1; getline(in, commandContent) && !found2; readingLine++) - { - if (!found1) - { - if (commandContent.find("config=" + configName) != string::npos || commandContent.find("configWindow=" + configName) != string::npos) // finding configname - { - configLine = readingLine; - found1 = true; - } - } - else - { - if (commandContent.find("configEnd") != string::npos) // finding configEnd - { - configEndLine = readingLine; - found2 = true; - } - } - } - if (!found1 || !found2) - { - clog << "Error with config names and configEnd : " << configName << ". Exiting." << endl; - exit(1); - } - in.clear(); - in.seekg(0, ios::beg); // reset file reading - - for (int readingLine = 1; getline(in, commandContent) && readingLine < configEndLine; readingLine++) - { - if (readingLine > configLine) - { - if (commandContent[0] == '#' || commandContent.find_first_not_of(' ') == string::npos) - continue; // Ignore comments, empty lines - pos = commandContent.find('='); - string *commandType = new string(commandContent.substr(0, pos)); // commandType = numbers + command type - commandContent.erase(0, pos + 1); // commandContent = command content - commandType->erase(remove(commandType->begin(), commandType->end(), ' '), commandType->end()); // Erase spaces inside 1st part of the line - pos = commandType->find("-"); - const string *const buttonNumber = new string(commandType->substr(0, pos)); // Isolate button number - commandType = new string(commandType->substr(pos + 1)); // Isolate command type - for (char &c : *commandType) - c = tolower(c); - - const auto stoiNumber = [&](const string *const numberString) - { - try - { - return stoi(*numberString); - } - catch (...) - { - clog << "At config line " << readingLine << ": expected a number" << endl; - exit(1); - } - }; - - if (configKeysMap.contains(*commandType)) - { // filter out bad types - if (*configKeysMap[*commandType]->Prefix() != "") - commandContent = *configKeysMap[*commandType]->Prefix() + commandContent; - macroEventsKeyMaps[configName][stoiNumber(buttonNumber)][configKeysMap[*commandType]->IsOnKeyPressed()].emplace_back(new MacroEvent(configKeysMap[*commandType], &commandContent)); - // Encode and store mapping v3 - } - else if (*commandType == "key") - { - if (commandContent.size() == 1) - { - commandContent = hexChar(commandContent[0]); - } - const string *const commandContent2 = new string(*configKeysMap["keyreleaseonrelease"]->Prefix() + commandContent); - commandContent = *configKeysMap["keypressonpress"]->Prefix() + commandContent; - map *const mEKMCB = ¯oEventsKeyMaps[configName][stoiNumber(buttonNumber)]; - (*mEKMCB)[true].emplace_back(new MacroEvent(configKeysMap["keypressonpress"], &commandContent)); - (*mEKMCB)[false].emplace_back(new MacroEvent(configKeysMap["keyreleaseonrelease"], commandContent2)); - } - } - } - in.close(); - } - currentConfigName = configName; - if (!silent) - { - (void)!(system(("notify-send -t 200 'New config :' '" + configName + "'").c_str())); - } - } - - string hexChar(const char a) - { - stringstream hexedChar; - hexedChar << "0x00" << hex << (int)(a); - return hexedChar.str(); - } - - int side_btn_fd, extra_btn_fd; - input_event *ev11; - fd_set readset; - - void checkForWindowConfig() - { - char *c; - try - { - c = getActiveWindow(); - } - catch (...) - { - return; - } - clog << "CurrentWindowNameLog : " << c << endl; - bool found = false; - if (configSwitcher->temporaryWindowName() == "" || strcmp(c, configSwitcher->temporaryWindowName().c_str()) != 0) - { - for (string *configWindowName : (*configSwitcher).configWindowsNamesVector) - { - if (strcmp(c, configWindowName->c_str()) == 0) - { - lock_guard guard(configSwitcherMutex); - configSwitcher->scheduleWindowReMap(configWindowName); - loadConf(configSwitcher->RemapString(), true); // change config for macroEvents[ii]->Content() - found = true; - break; - } - } - if (!found && configSwitcher->isAWindowConfigActive()) - { - lock_guard guard(configSwitcherMutex); - configSwitcher->scheduleReMap(&configSwitcher->getBackupConfigName()); - loadConf(configSwitcher->RemapString(), true); // change config for macroEvents[ii]->Content() - } - } - } - - void run() - { - if (areSideBtnEnabled) - ioctl(side_btn_fd, EVIOCGRAB, 1); // Give application exclusive control over side buttons. - ev11 = &ev1[1]; - while (1) - { - if (configSwitcher->isRemapScheduled()) - { - lock_guard guard(configSwitcherMutex); // remap - loadConf(configSwitcher->RemapString()); // change config for macroEvents[ii]->Content() - } - - FD_ZERO(&readset); - if (areSideBtnEnabled) - FD_SET(side_btn_fd, &readset); - if (areExtraBtnEnabled) - FD_SET(extra_btn_fd, &readset); - if (select(FD_SETSIZE, &readset, NULL, NULL, NULL) == -1) - exit(2); - if (areSideBtnEnabled && FD_ISSET(side_btn_fd, &readset)) // Side buttons - { - if (read(side_btn_fd, ev1, size) == -1) - exit(2); - if (ev1[0].value != ' ' && ev11->type == EV_KEY) - { // Key event (press or release) - switch (ev11->code) - { - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - checkForWindowConfig(); - thread(runActions, ¯oEventsKeyMaps[currentConfigName][ev11->code - 1][ev11->value == 1]).detach(); // real key number = ev11->code - 1 - break; - } - } - } - if (areExtraBtnEnabled && FD_ISSET(extra_btn_fd, &readset)) // Extra buttons - { - if (read(extra_btn_fd, ev1, size) == -1) - exit(2); - if (ev11->type == 1) - { // Only extra buttons - switch (ev11->code) - { - case 275: - case 276: - case 277: - case 278: - checkForWindowConfig(); - thread(runActions, ¯oEventsKeyMaps[currentConfigName][ev11->code - 262][ev11->value == 1]).detach(); // real key number = ev11->code - OFFSET (#262) - break; - } - } - } - } - } - - // Functions that can be given to configKeys - const static void writeStringNow(const string *macroContent) - { - lock_guard guard(fakeKeyFollowUpsMutex); - FakeKey *const aKeyFaker = fakekey_init(XOpenDisplay(NULL)); - const int strSize = macroContent->size(); - for (int z = 0; z < strSize; z++) - { - fakekey_press(aKeyFaker, (unsigned char *)&(*macroContent)[z], 8, 0); - fakekey_release(aKeyFaker); - } - XFlush(aKeyFaker->xdpy); - XCloseDisplay(aKeyFaker->xdpy); - deleteFakeKey(aKeyFaker); - } - - const static void specialPressNow(const string *const macroContent) - { - lock_guard guard(fakeKeyFollowUpsMutex); - FakeKey *const aKeyFaker = fakekey_init(XOpenDisplay(NULL)); - fakekey_press(aKeyFaker, (unsigned char *)&(*macroContent)[0], 8, 0); - XFlush(aKeyFaker->xdpy); - fakeKeyFollowUps->emplace_back(new CharAndFakeKey(&(*macroContent)[0], aKeyFaker)); - fakeKeyFollowCount++; - } - - const static void specialReleaseNow(const string *const macroContent) - { - if (fakeKeyFollowCount > 0) - { - lock_guard guard(fakeKeyFollowUpsMutex); - for (int vectorId = fakeKeyFollowUps->size() - 1; vectorId >= 0; vectorId--) - { - CharAndFakeKey *const aKeyFollowUp = (*fakeKeyFollowUps)[vectorId]; - if (*get<0>(*aKeyFollowUp) == (*macroContent)[0]) - { - FakeKey *const aKeyFaker = get<1>(*aKeyFollowUp); - fakekey_release(aKeyFaker); - XFlush(aKeyFaker->xdpy); - XCloseDisplay(aKeyFaker->xdpy); - fakeKeyFollowUps->erase(fakeKeyFollowUps->begin() + vectorId); - fakeKeyFollowCount--; - deleteFakeKey(aKeyFaker); - } - delete aKeyFollowUp; - } - } - else - clog << "No candidate for key release" << endl; - } - - const static void chmapNow(const string *const macroContent) - { - lock_guard guard(configSwitcherMutex); - configSwitcher->scheduleReMap(macroContent); // schedule config switch/change - } - - const static void sleepNow(const string *const macroContent) - { - usleep(stoul(*macroContent) * 1000); // microseconds make me dizzy in keymap.txt - } - - const static void executeNow(const string *const macroContent) - { - (void)!(system(macroContent->c_str())); - } - // end of configKeys functions - - static void runActions(MacroEventVector *const relativeMacroEventsPointer) - { - for (MacroEvent *const macroEventPointer : *relativeMacroEventsPointer) - { // run all the events at Key - macroEventPointer->KeyType()->runInternal(macroEventPointer->Content()); - } - } - -public: - NagaDaemon(const string mapConfig = "defaultConfig") - { - if (daemon(0, 1)) - perror("Couldn't daemonise from unistd"); - // modulable device files list - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic-event-mouse"); // NAGA EPIC - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Dock-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Dock-event-mouse"); // NAGA EPIC DOCK - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_2014-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_2014-event-mouse"); // NAGA 2014 - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga-event-mouse"); // NAGA MOLTEN - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma-event-mouse"); // NAGA EPIC CHROMA - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma_Dock-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Epic_Chroma_Dock-event-mouse"); // NAGA EPIC CHROMA DOCK - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Chroma-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Chroma-event-mouse"); // NAGA CHROMA - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Hex-if01-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Hex-event-mouse"); // NAGA HEX - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Hex_V2-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Hex_V2-event-mouse"); // NAGA HEX v2 - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Trinity_00000000001A-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Trinity_00000000001A-event-mouse"); // NAGA Trinity - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Left_Handed_Edition-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Left_Handed_Edition-event-mouse"); // NAGA Left Handed - devices.emplace_back("/dev/input/by-id/usb-Razer_Razer_Naga_Pro_000000000000-if02-event-kbd", "/dev/input/by-id/usb-Razer_Razer_Naga_Pro_000000000000-event-mouse"); // NAGA PRO WIRELESS - devices.emplace_back("/dev/input/by-id/usb-1532_Razer_Naga_Pro_000000000000-if02-event-kbd", "/dev/input/by-id/usb-1532_Razer_Naga_Pro_000000000000-event-mouse"); // NAGA PRO - // devices.emplace_back("/dev/input/by-id/YOUR_DEVICE_FILE", "/dev/input/by-id/YOUR_DEVICE_FILE#2"); // DUMMY EXAMPLE ~ ONE CAN BE EMPTY LIKE SUCH : "" (for devices with no extra buttons) - - for (CharAndChar &device : devices) - { // Setup check - side_btn_fd = open(device.first, O_RDONLY), extra_btn_fd = open(device.second, O_RDONLY); - if (side_btn_fd != -1 || extra_btn_fd != -1) - { - if (side_btn_fd == -1) - { - if (extra_btn_fd == -1) - { - cerr << "No naga devices found or you don't have permission to access them." << endl; - exit(1); - } - clog << "Reading from: " << device.second << endl; - areSideBtnEnabled = false; - } - else if (extra_btn_fd == -1) - { - clog << "Reading from: " << device.first << endl; - areExtraBtnEnabled = false; - } - else - clog << "Reading from: " << device.first << endl - << " and " << device.second << endl; - break; - } - } - - // modulable options list to manage internals inside runActions method arg1:COMMAND, arg2:onKeyPressed?, arg3:function to send prefix+config content. - - configKeysMap.insert(stringAndConfigKey("chmap", new configKey(true, chmapNow))); // change keymap - configKeysMap.insert(stringAndConfigKey("chmaprelease", new configKey(false, chmapNow))); - - configKeysMap.insert(stringAndConfigKey("sleep", new configKey(true, sleepNow))); - configKeysMap.insert(stringAndConfigKey("sleeprelease", new configKey(false, sleepNow))); - - configKeysMap.insert(stringAndConfigKey("run", new configKey(true, executeNow, "setsid "))); - configKeysMap.insert(stringAndConfigKey("run2", new configKey(true, executeNow))); - - configKeysMap.insert(stringAndConfigKey("runrelease", new configKey(false, executeNow, "setsid "))); - configKeysMap.insert(stringAndConfigKey("runrelease2", new configKey(false, executeNow))); - - configKeysMap.insert(stringAndConfigKey("keypressonpress", new configKey(true, executeNow, "setsid xdotool keydown --window getactivewindow "))); - configKeysMap.insert(stringAndConfigKey("keypressonrelease", new configKey(false, executeNow, "setsid xdotool keydown --window getactivewindow "))); - - configKeysMap.insert(stringAndConfigKey("keyreleaseonpress", new configKey(true, executeNow, "setsid xdotool keyup --window getactivewindow "))); - configKeysMap.insert(stringAndConfigKey("keyreleaseonrelease", new configKey(false, executeNow, "setsid xdotool keyup --window getactivewindow "))); - - configKeysMap.insert(stringAndConfigKey("keyclick", new configKey(true, executeNow, "setsid xdotool key --window getactivewindow "))); - configKeysMap.insert(stringAndConfigKey("keyclickrelease", new configKey(false, executeNow, "setsid xdotool key --window getactivewindow "))); - - configKeysMap.insert(stringAndConfigKey("string", new configKey(true, writeStringNow))); - configKeysMap.insert(stringAndConfigKey("stringrelease", new configKey(false, writeStringNow))); - - configKeysMap.insert(stringAndConfigKey("specialpressonpress", new configKey(true, specialPressNow))); - configKeysMap.insert(stringAndConfigKey("specialpressonrelease", new configKey(false, specialPressNow))); - - configKeysMap.insert(stringAndConfigKey("specialreleaseonpress", new configKey(true, specialReleaseNow))); - configKeysMap.insert(stringAndConfigKey("specialreleaseonrelease", new configKey(false, specialReleaseNow))); - - configSwitcher->scheduleReMap(&mapConfig); - loadConf(mapConfig); // Initialize config - run(); - } -}; - -void stopD() -{ - clog << "Stopping possible naga daemon" << endl; - (void)!(system(("/usr/local/bin/Naga_Linux/nagaKillroot.sh " + to_string((int)getpid())).c_str())); -}; - -// arguments manage -int main(const int argc, const char *const argv[]) -{ - if (argc > 1) - { - if (strstr(argv[1], "start") != NULL) - { - stopD(); - clog << "Starting naga daemon in hidden mode, keep the window for the logs..." << endl; - usleep(40000); - (void)!(system("/usr/local/bin/Naga_Linux/nagaXinputStart.sh")); - - if (argc > 2) - NagaDaemon(string(argv[2]).c_str()); - else - NagaDaemon(); - } - else if (strstr(argv[1], "kill") != NULL || strstr(argv[1], "stop") != NULL) - { - stopD(); - } - else if (strstr(argv[1], "uninstall") != NULL) - { - string answer; - clog << "Are you sure you want to uninstall ? y/n" << endl; - cin >> answer; - if (answer.length() != 1 || (answer[0] != 'y' && answer[0] != 'Y')) - { - clog << "Aborting" << endl; - } - else - { - (void)!(system("bash /usr/local/bin/Naga_Linux/nagaUninstall.sh")); - } - } - } - else - { - clog << "Possible arguments : \n -start Starts the daemon in hidden mode. (stops it before)\n -stop Stops the daemon.\n -uninstall Uninstalls the daemon." << endl; - } - return 0; -} From d94273d7ed83783605f2cd2324b9f546c64a8f86 Mon Sep 17 00:00:00 2001 From: Cam Date: Sat, 18 Mar 2023 03:08:58 -0400 Subject: [PATCH 4/4] Delete fakeKeys.hpp --- src/fakeKeys.hpp | 262 ----------------------------------------------- 1 file changed, 262 deletions(-) delete mode 100644 src/fakeKeys.hpp diff --git a/src/fakeKeys.hpp b/src/fakeKeys.hpp deleted file mode 100644 index a330a09..0000000 --- a/src/fakeKeys.hpp +++ /dev/null @@ -1,262 +0,0 @@ -#include -#include -#include - -#include -#include - -#define N_MODIFIER_INDEXES (Mod5MapIndex + 1) - -struct FakeKey -{ - Display *xdpy; - int min_keycode, max_keycode, n_keysyms_per_keycode, held_keycode, held_state_flags, alt_mod_index; - KeySym *keysyms; - KeyCode modifier_table[N_MODIFIER_INDEXES]; -}; - -static void deleteFakeKey(FakeKey *aKeyFaker) -{ - XFree(aKeyFaker->keysyms); - free(aKeyFaker); -} - -static int utf8_to_ucs4(const unsigned char *src_orig, unsigned int *dst, int len) -{ - const unsigned char *src = src_orig; - unsigned char s; - int extra; - unsigned int result; - - if (len == 0) - return 0; - - s = *src++; - len--; - - if (!(s & 0x80)) - { - result = s; - extra = 0; - } - else if (!(s & 0x40)) - { - return -1; - } - else if (!(s & 0x20)) - { - result = s & 0x1f; - extra = 1; - } - else if (!(s & 0x10)) - { - result = s & 0xf; - extra = 2; - } - else if (!(s & 0x08)) - { - result = s & 0x07; - extra = 3; - } - else if (!(s & 0x04)) - { - result = s & 0x03; - extra = 4; - } - else if (!(s & 0x02)) - { - result = s & 0x01; - extra = 5; - } - else - { - return -1; - } - if (extra > len) - return -1; - - while (extra--) - { - result <<= 6; - s = *src++; - - if ((s & 0xc0) != 0x80) - return -1; - - result |= s & 0x3f; - } - *dst = result; - return src - src_orig; -} - -FakeKey *fakekey_init(Display *xdpy) -{ - FakeKey *fk = NULL; - int event, error, major, minor, mod_index, mod_key; - XModifierKeymap *modifiers; - KeyCode *kp; - - if (xdpy == NULL || !XTestQueryExtension(xdpy, &event, &error, &major, &minor)) - return NULL; - - - fk = (FakeKey *)malloc(sizeof(FakeKey)); - memset(fk, 0, sizeof(FakeKey)); - - fk->xdpy = xdpy; - - /* Find keycode limits */ - - XDisplayKeycodes(fk->xdpy, &fk->min_keycode, &fk->max_keycode); - - /* Get the mapping */ - - fk->keysyms = XGetKeyboardMapping(fk->xdpy, fk->min_keycode, fk->max_keycode - fk->min_keycode + 1, &fk->n_keysyms_per_keycode); - - modifiers = XGetModifierMapping(fk->xdpy); - - kp = modifiers->modifiermap; - - for (mod_index = 0; mod_index < 8; mod_index++) - { - fk->modifier_table[mod_index] = 0; - - for (mod_key = 0; mod_key < modifiers->max_keypermod; mod_key++) - { - int keycode = kp[mod_index * modifiers->max_keypermod + mod_key]; - - if (keycode != 0) - { - fk->modifier_table[mod_index] = keycode; - break; - } - } - } - - if (modifiers) - XFreeModifiermap(modifiers); - - return fk; -} - -int fakekey_send_keyevent(FakeKey *fk, KeyCode keycode, Bool is_press, int flags) -{ - if (flags) - { - if (flags & LockMask) - XTestFakeKeyEvent(fk->xdpy, fk->modifier_table[ShiftMapIndex], - is_press, CurrentTime); - - if (flags & ControlMask) - XTestFakeKeyEvent(fk->xdpy, fk->modifier_table[ControlMapIndex], - is_press, CurrentTime); - - if (flags & Mod1Mask) //ALT MASK - XTestFakeKeyEvent(fk->xdpy, fk->modifier_table[Mod1MapIndex], - is_press, CurrentTime); - - XSync(fk->xdpy, False); - } - - XTestFakeKeyEvent(fk->xdpy, keycode, is_press, CurrentTime); - - XSync(fk->xdpy, False); - - return 1; -} - -int fakekey_press_keysym(FakeKey *fk, KeySym keysym, int flags) -{ - static int modifiedkey; - KeyCode code = 0; - - if ((code = XKeysymToKeycode(fk->xdpy, keysym)) != 0) - { - if (XkbKeycodeToKeysym(fk->xdpy, code, 0, 0) != keysym) - { - if (XkbKeycodeToKeysym(fk->xdpy, code, 0, 1) == keysym) - flags |= LockMask; - else - code = 0; - } - else - { - flags &= ~LockMask; - } - } - - if (!code) - { - modifiedkey = (modifiedkey + 1) % 10; - - int index = (fk->max_keycode - fk->min_keycode - modifiedkey - 1) * fk->n_keysyms_per_keycode; - - fk->keysyms[index] = keysym; - - XChangeKeyboardMapping(fk->xdpy, - fk->min_keycode, - fk->n_keysyms_per_keycode, - fk->keysyms, - (fk->max_keycode - fk->min_keycode)); - - XSync(fk->xdpy, False); - - code = fk->max_keycode - modifiedkey - 1; - - if (XkbKeycodeToKeysym(fk->xdpy, code, 0, 0) != keysym && XkbKeycodeToKeysym(fk->xdpy, code, 0, 1) == keysym) - { - flags |= LockMask; - } - } - - if (code != 0) - { - fakekey_send_keyevent(fk, code, True, flags); - - fk->held_state_flags = flags; - fk->held_keycode = code; - - return 1; - } - - fk->held_state_flags = 0; - fk->held_keycode = 0; - - return 0; -} - -int fakekey_press(FakeKey *fk, const unsigned char *utf8_char_in, int len_bytes, int flags) -{ - unsigned int ucs4_out; - - if (fk->held_keycode) /* key is already held down */ - return 0; - - - - if (len_bytes < 0) - { - len_bytes = strlen((const char *)utf8_char_in); - } - - if (utf8_to_ucs4(utf8_char_in, &ucs4_out, len_bytes) < 1) - { - return 0; - } - - if (ucs4_out > 0x00ff) /* < 0xff assume Latin-1 1:1 mapping */ - ucs4_out = ucs4_out | 0x01000000; /* This gives us the magic X keysym */ - - return fakekey_press_keysym(fk, (KeySym)ucs4_out, flags); -} - -void fakekey_release(FakeKey *fk) -{ - if (!fk->held_keycode) - return; - - fakekey_send_keyevent(fk, fk->held_keycode, False, fk->held_state_flags); - - fk->held_state_flags = 0; - fk->held_keycode = 0; -}