Pyrainput (FunKeyMonkey plugin + pyrainputctl) design


i think it would make sense to fire small scroll events at a low frequency when the nub is less off center, and just fire more frequently if the nub is further off center.
 
Analogue scroll events are actually a thing, think of touchpad scrolling. Touchpads are probably providing absolute instead of relative position information, though.
 
Yes, playing with my trackpad, the two-finger scrolling works as if it's sending a up/down arrow key every centimetre or so I move. Maybe a two zoned approach will be enough for everyone, where it scrollls just one tick if you only move it up to half way, but sends repeated clicks if you move it all of the way, at perhaps some user definable frequency if no happy solution can be found between all the users with their pyras thus far (and willing to contribute to this thread).
 
Analogue scroll events are actually a thing, think of touchpad scrolling. Touchpads are probably providing absolute instead of relative position information, though.
That's a good point, but they're probably handled separately. For example my laptop's touchpad looks like:

Code:
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 272 (BTN_LEFT)
    Event code 325 (BTN_TOOL_FINGER)
    Event code 328 (BTN_TOOL_QUINTTAP)
    Event code 330 (BTN_TOUCH)
    Event code 333 (BTN_TOOL_DOUBLETAP)
    Event code 334 (BTN_TOOL_TRIPLETAP)
    Event code 335 (BTN_TOOL_QUADTAP)
  Event type 3 (EV_ABS)
    Event code 0 (ABS_X)
      Value     42
      Min        0
      Max     1162
      Resolution      12
    Event code 1 (ABS_Y)
      Value    634
      Min        0
      Max      634
      Resolution      12
    Event code 47 (ABS_MT_SLOT)
      Value      0
      Min        0
      Max        4
    Event code 53 (ABS_MT_POSITION_X)
      Value      0
      Min        0
      Max     1162
      Resolution      12
    Event code 54 (ABS_MT_POSITION_Y)
      Value      0
      Min        0
      Max      634
      Resolution      12
    Event code 55 (ABS_MT_TOOL_TYPE)
      Value      0
      Min        0
      Max        2
    Event code 57 (ABS_MT_TRACKING_ID)
      Value      0
      Min        0
      Max    65535
  Event type 4 (EV_MSC)
    Event code 5 (MSC_TIMESTAMP)
Properties:
  Property type 0 (INPUT_PROP_POINTER)
  Property type 2 (INPUT_PROP_BUTTONPAD)

There's not specific scroll events, just touch and multitouch locations. The actual scrolling behavior is handled somewhere else.
 
i think it would make sense to fire small scroll events at a low frequency when the nub is less off center, and just fire more frequently if the nub is further off center.
It would probably need two limit values: when it switches to slow speed and when it changes to fast speed. If these are the same number there's an uncomfortable area where the speed would skip erratically due to the noise in the nubs. The principle is simple, but there's some tuning like this to make it feel nice.
 
probably unrelated but still I have some questions. Where can I find the used mapping? And how do I run the pyra input tester.

when i run the input tester I cant seem to get the nice keyboard layout that shows if my keys are working... tried running it from tty or terminal. but probably im doing it wrong. I suspect my upper right shoulder to not work, I think its mapped to alt but it doesnt show up/work. thats why I am curious about the mapping.

that and getting to know my pyra


Thanks!

bzar pointed me to xinput which you can install from the repo, it monitors if your keys give an input.
 
Last edited:
There's a stupid bug with the way SDL2 reports nub movements that I'm done trying to fix or find a workaround for now. It causes ED and/or the camera to move by itself if the nub is let go quickly. Annoying but still playable. If anyone can let me know of a fix I'd be eternally grateful
This bug undexsym mentions in the thread about his game is related to pyrainput. As a workaround it helps to set nubs.deadzone = -1.

As far as I think what the problem is: In pyrainput.cpp the function "handle" does only send the new position of the analog nubs to the virtual gamepas, if the new position is outside of the deadzone. That seems unintentional.
But I don't really know, if there is a reason for this check. Does it have any negative side effects to remove this?
1757451942762.png
 
Probably I'm wrong but it seems intended behavior to not send a location if it is in a dead zone. An alternative might be sending the previous location, im not sure how Dead zones normally work.
 
I think the intention was to help the nubs to go back to exactly zero. So I think the bug is a missing else block. It should look like this:
1757487388824.png

Instead of helping the nubs to go to exactly to zero, the old code did the opposite. The nubs could never go back to zero.

The else blocks already would fix the problem. But since this functionality never worked but nobody seemed to miss it in all this time, I think we should set the default value for nubs.deadzone to -1. If somebody wants to activate it (because his nubs have more drift than usual) he could still activate it by creating a "/etc/pyrainput.cfg".
Just to be clear: I'm only talking about the nubs.deadzone. This will not affect the deadzone used for mouse movements or scrolling since those are their own values (mouse.deadzone and mouse.wheel.deadzone). The deadzones for the mouse work exactly as expected and do the job.
 
I tested my bugfix on the bookworm branch of https://dev.pyra-handheld.com/packages/funkeymonkey-pyrainput . The bugfix seems to do the job. I haven't changed the default value for nubs.deadzone yet, since that would be a different commit and is not necessary for the bug to be fixed.

I created an account at https://dev.pyra-handheld.com/packages and wait now for the approval, so I can create a pull request later on.

Code:
#include <regex>
#include "pyrainput.h"

struct pyrainput_global global;

void init(char const** argv, unsigned int argc) {
    std::vector<unsigned int> keycodes;
    for(unsigned int i = FIRST_KEY; i <= LAST_KEY; ++i) {
        keycodes.push_back(i);
    }

    // Initialize data structures
    global.keyboard = new UinputDevice("/dev/uinput", BUS_USB, "pyraInput keyboard", 1, 1, 1, {
        { EV_KEY, keycodes }
    });

    global.behaviors = new KeyBehaviors();

    // Read settings
    // Must be done before the key behavior to take key remapping via file into account
    handleArgs(argv, argc, global.settings);
    if(!global.settings.configFile.empty()) {
        loadConfig(global.settings.configFile, global.settings);
    }

    // Set key behavior

    global.behaviors->altcomplex(KEY_MENU, &global.Fn.pressed, KEY_MENU, [](int value) {
        if(value) {
            setCommandMode(!global.commandMode);
        }
    });

    global.behaviors->gphat(pyraKeyMap["PYRA_KEY_UP"],        BTN_DPAD_UP);
    global.behaviors->gphat(pyraKeyMap["PYRA_KEY_DOWN"],    BTN_DPAD_DOWN);
    global.behaviors->gphat(pyraKeyMap["PYRA_KEY_LEFT"],    BTN_DPAD_LEFT);
    global.behaviors->gphat(pyraKeyMap["PYRA_KEY_RIGHT"],    BTN_DPAD_RIGHT);
    global.behaviors->gpmap2(pyraKeyMap["PYRA_KEY_LEFTALT"],    BTN_START, &global.Alt.left, &global.Alt);
    global.behaviors->gpmap2(pyraKeyMap["PYRA_KEY_LEFTCTRL"],    BTN_SELECT, &global.Ctrl.left, &global.Ctrl);
    global.behaviors->gpmap(pyraKeyMap["PYRA_KEY_HOME"],    BTN_A);
    global.behaviors->gpmap(pyraKeyMap["PYRA_KEY_END"],    BTN_B);
    global.behaviors->gpmap(pyraKeyMap["PYRA_KEY_PAGEDOWN"],    BTN_X);
    global.behaviors->gpmap(pyraKeyMap["PYRA_KEY_PAGEUP"],    BTN_Y);
    global.behaviors->gpmap2(pyraKeyMap["PYRA_KEY_RIGHTSHIFT"],BTN_TL, &global.Shift.right, &global.Shift);
    global.behaviors->gpmap2(pyraKeyMap["PYRA_KEY_RIGHTCTRL"],    BTN_TR, &global.Ctrl.right, &global.Ctrl);
    global.behaviors->gpmap2(pyraKeyMap["PYRA_KEY_RIGHTALT"],    BTN_TR2, &global.Alt.right, &global.Alt);
    global.behaviors->gpmap(pyraKeyMap["PYRA_KEY_INSERT"],    BTN_C);  //(I)
    global.behaviors->gpmap(pyraKeyMap["PYRA_KEY_DELETE"],    BTN_Z);  //(II)
  
    global.behaviors->complex(KEY_LEFTSHIFT, [](int value) {
        global.Shift.left = (value==1);
        global.Shift.pressed = global.Shift.left || global.Shift.right;
        global.keyboard->send(EV_KEY, KEY_LEFTSHIFT, value);
    });
    global.behaviors->complex(KEY_RIGHTMETA, [](int value) {
        global.Fn.right = (value==1);
        global.Fn.pressed = global.Fn.left || global.Fn.right;
        if (global.settings.exportGamepad) {
            global.gamepad->send(EV_KEY, BTN_TL2, value);
            global.gamepad->send(EV_SYN, 0, 0);
        }
    });
    global.behaviors->complex(KEY_LEFTMETA, [](int value) {
        global.Fn.left = (value==1);
        global.Fn.pressed = global.Fn.left || global.Fn.right;
    });

    global.behaviors->altmap(KEY_ESC,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_ESC"],    pyraKeyMap["ESC"]);
    global.behaviors->altmap(KEY_PAUSE,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_PAUSE"],    pyraKeyMap["PAUSE"]);
        global.behaviors->complex(KEY_BRIGHTNESSUP, handleBrightness);
    //global.behaviors->altmap(KEY_BRIGHTNESSUP,    &global.Fn.pressed, KEY_BRIGHTNESSUP,    KEY_BRIGHTNESSDOWN);
    global.behaviors->altmap(KEY_F11,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_F11"],    pyraKeyMap["PYRA_KEY_FN_F11"]);

        // Handler function generator function for number keys
        auto numKeyBehavior = [](unsigned int regular, unsigned int fn, Settings::ProfileIndex index) {
          return [regular, fn, index](int value) {
            if(global.commandMode) {
              if(value) {
                loadProfile(index, global.settings);
                setCommandMode(false);
              }
            } else if(global.Fn.pressed) {
              global.keyboard->send(EV_KEY, fn, value);
            } else {
              global.keyboard->send(EV_KEY, regular, value);
            }
          };
        };

    global.behaviors->complex(KEY_1, numKeyBehavior(pyraKeyMap["PYRA_KEY_1"], pyraKeyMap["PYRA_KEY_FN_1"], Settings::PROFILE_1));
    global.behaviors->complex(KEY_2, numKeyBehavior(pyraKeyMap["PYRA_KEY_2"], pyraKeyMap["PYRA_KEY_FN_2"], Settings::PROFILE_2));
    global.behaviors->complex(KEY_3, numKeyBehavior(pyraKeyMap["PYRA_KEY_3"], pyraKeyMap["PYRA_KEY_FN_3"], Settings::PROFILE_3));
    global.behaviors->complex(KEY_4, numKeyBehavior(pyraKeyMap["PYRA_KEY_4"], pyraKeyMap["PYRA_KEY_FN_4"], Settings::PROFILE_4));
    global.behaviors->complex(KEY_5, numKeyBehavior(pyraKeyMap["PYRA_KEY_5"], pyraKeyMap["PYRA_KEY_FN_5"], Settings::PROFILE_5));
    global.behaviors->complex(KEY_6, numKeyBehavior(pyraKeyMap["PYRA_KEY_6"], pyraKeyMap["PYRA_KEY_FN_6"], Settings::PROFILE_6));
    global.behaviors->complex(KEY_7, numKeyBehavior(pyraKeyMap["PYRA_KEY_7"], pyraKeyMap["PYRA_KEY_FN_7"], Settings::PROFILE_7));
    global.behaviors->complex(KEY_8, numKeyBehavior(pyraKeyMap["PYRA_KEY_8"], pyraKeyMap["PYRA_KEY_FN_8"], Settings::PROFILE_8));
    global.behaviors->complex(KEY_9, numKeyBehavior(pyraKeyMap["PYRA_KEY_9"], pyraKeyMap["PYRA_KEY_FN_9"], Settings::PROFILE_9));
    global.behaviors->complex(KEY_0, numKeyBehavior(pyraKeyMap["PYRA_KEY_0"], pyraKeyMap["PYRA_KEY_FN_0"], Settings::PROFILE_0));

    global.behaviors->altmap(KEY_TAB,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_TAB"],    pyraKeyMap["PYRA_KEY_FN_TAB"]);
    global.behaviors->altmap(KEY_Q,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_Q"],    pyraKeyMap["PYRA_KEY_FN_Q"]);            // I120 // ok
    global.behaviors->altmap(KEY_W,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_W"],    pyraKeyMap["PYRA_KEY_FN_W"]);            // I129 // ok
    global.behaviors->altmap(KEY_E,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_E"],    pyraKeyMap["PYRA_KEY_FN_E"]);            // I149 // ok
    global.behaviors->altmap(KEY_R,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_R"],    pyraKeyMap["PYRA_KEY_FN_R"]);        // I154 // ok
    global.behaviors->altmap(KEY_T,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_T"],    pyraKeyMap["PYRA_KEY_FN_T"]);            // I168 // ok
    global.behaviors->altmap(KEY_Y,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_Y"],    pyraKeyMap["PYRA_KEY_FN_Y"]);            // I178 // ok
    global.behaviors->altmap(KEY_U,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_U"],    pyraKeyMap["PYRA_KEY_FN_U"]);            // I183 // ok
    global.behaviors->altmap(KEY_I,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_I"],    pyraKeyMap["PYRA_KEY_FN_I"]);            // I184 // ok
    global.behaviors->altmap(KEY_O,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_O"],    pyraKeyMap["PYRA_KEY_FN_O"]);            // I230 // ok
    global.behaviors->altmap(KEY_P,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_P"],    pyraKeyMap["PYRA_KEY_FN_P"]);            // I217 // ok
    global.behaviors->altmap(KEY_APOSTROPHE,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_APOSTROPHE"],    pyraKeyMap["PYRA_KEY_FN_APOSTROPHE"]);    // I247 // ok
    global.behaviors->altmap(KEY_A,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_A"],    pyraKeyMap["PYRA_KEY_FN_A"]);            // I222 // ok
    global.behaviors->altmap(KEY_S,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_S"],    pyraKeyMap["PYRA_KEY_FN_S"]);            // I248 // ok
    global.behaviors->altmap(KEY_D,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_D"],    pyraKeyMap["PYRA_KEY_FN_D"]);            // I221 // ok
    global.behaviors->altmap(KEY_F,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_F"],    pyraKeyMap["PYRA_KEY_FN_F"]);            // I219 // ok
    global.behaviors->altmap(KEY_G,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_G"],    pyraKeyMap["PYRA_KEY_FN_G"]);            // I249- AB11 (89)
    global.behaviors->altmap(KEY_H,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_H"],    pyraKeyMap["PYRA_KEY_FN_H"]);            // I250- JPCM (95)
    global.behaviors->altmap(KEY_J,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_J"],    pyraKeyMap["PYRA_KEY_FN_J"]);            // I251- AE13 (124)
    global.behaviors->altmap(KEY_K,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_K"],    pyraKeyMap["PYRA_KEY_FN_K"]);            // I252- FK19 (189)
    global.behaviors->altmap(KEY_L,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_L"],    pyraKeyMap["PYRA_KEY_FN_L"]);            // I253- FK24 (194)
    global.behaviors->altmap(KEY_COMMA,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_COMMA"],    pyraKeyMap["PYRA_KEY_FN_COMMA"]);
    global.behaviors->altmap(KEY_DOT,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_DOT"],    pyraKeyMap["PYRA_KEY_FN_DOT"]);
    global.behaviors->altmap(KEY_Z,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_Z"],    pyraKeyMap["PYRA_KEY_FN_Z"]);
    global.behaviors->altmap(KEY_X,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_X"],    pyraKeyMap["PYRA_KEY_FN_X"]);
    global.behaviors->altmap(KEY_C,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_C"],    pyraKeyMap["PYRA_KEY_FN_C"]);
    global.behaviors->altmap(KEY_V,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_V"],    pyraKeyMap["PYRA_KEY_FN_V"]);
    global.behaviors->altmap(KEY_B,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_B"],    pyraKeyMap["PYRA_KEY_FN_B"]);
    global.behaviors->altmap(KEY_N,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_N"],    pyraKeyMap["PYRA_KEY_FN_N"]);
    global.behaviors->altmap(KEY_M,        &global.Fn.pressed, pyraKeyMap["PYRA_KEY_M"],    pyraKeyMap["PYRA_KEY_FN_M"]);            // 228- MDSW (195)
    global.behaviors->altmap(KEY_SPACE,    &global.Fn.pressed, pyraKeyMap["PYRA_KEY_SPACE"],    pyraKeyMap["PYRA_KEY_SPACE"]);
  
    global.gamepad = new PyraJoystick("/dev/uinput", BUS_USB, "pyraInput Gamepad", 1, 1, 1, {
        { EV_KEY, {
            BTN_A, BTN_B, BTN_X, BTN_Y,
            BTN_TL, BTN_TR, BTN_TL2, BTN_TR2,
            BTN_SELECT, BTN_START, BTN_C, BTN_Z,
            BTN_THUMBL, BTN_THUMBR
        } },
        { EV_ABS, { ABS_HAT0X, ABS_HAT0Y, ABS_X, ABS_Y, ABS_RX, ABS_RY } }
    }, {
        { ABS_X, -255, 255, 3, 30 },
        { ABS_Y, -255, 255, 3, 30 },
        { ABS_RX, -255, 255, 3, 30 },
        { ABS_RY, -255, 255, 3, 30 },
        { ABS_HAT0X, -65535, 65535, 0, 0 },
        { ABS_HAT0Y, -65535, 65535, 0, 0 },
    });
    global.mouse = new Mouse {
        UinputDevice("/dev/uinput", BUS_USB, "pyraInput Mouse", 1, 1, 1, {
            { EV_KEY, { BTN_LEFT, BTN_RIGHT } },
            { EV_REL, { REL_X, REL_Y, REL_HWHEEL, REL_WHEEL, REL_WHEEL_HI_RES, REL_HWHEEL_HI_RES } }
        }), 0, 0, 0, 0, {}, {}
    };
    global.mouseThread  = std::move(std::thread(handleMouse, global.mouse, &global.settings, &global.stop));
    global.rumbleThread = std::move(std::thread(handleRumble, global.gamepad, &global.stop));

    global.settings.scripts.brightnessNormal = "/usr/share/pyra/scripts/pyra-brightkey.sh screen up";
    global.settings.scripts.brightnessShift = "/usr/share/pyra/scripts/pyra-brightkey.sh screen down";
    global.settings.scripts.brightnessFn = "/usr/share/pyra/scripts/pyra-brightkey.sh key up";
    global.settings.scripts.brightnessFnShift = "/usr/share/pyra/scripts/pyra-brightkey.sh key down";
}

void handle(input_event const& e, unsigned int role) {
    switch(e.type) {
    case EV_ABS:
        if (role == ROLE_LEFT_NUB) {
            switch(e.code) {
            case ABS_X:
                if (global.settings.exportGamepad) {
                    if (e.value>global.settings.joyDeadzone || e.value<-global.settings.joyDeadzone) {
                        global.gamepad->send(EV_ABS, ABS_X, e.value);
                    } else {
                        global.gamepad->send(EV_ABS, ABS_X, 0);
                    }
                    global.gamepad->send(EV_SYN, 0, 0);
                }
                for(auto mode : global.settings.leftNubModeX) {
                    handleNubAxis(mode, e.value, global.mouse, global.gamepad, global.settings);
                }
                break;
            case ABS_Y:
                if (global.settings.exportGamepad) {
                    if (e.value>global.settings.joyDeadzone || e.value<-global.settings.joyDeadzone) {
                        global.gamepad->send(EV_ABS, ABS_Y, e.value);
                    } else {
                        global.gamepad->send(EV_ABS, ABS_Y, 0);
                    }
                    global.gamepad->send(EV_SYN, 0, 0);
                }
                for(auto mode : global.settings.leftNubModeY) {
                    handleNubAxis(mode, e.value, global.mouse, global.gamepad, global.settings);
                }
                break;
            default: break;
            };
        } else if (role == ROLE_RIGHT_NUB) {
            switch(e.code) {
            case ABS_X:
                if (global.settings.exportGamepad) {
                    if (e.value>global.settings.joyDeadzone || e.value<-global.settings.joyDeadzone) {
                        global.gamepad->send(EV_ABS, ABS_RX, e.value);
                    } else {
                        global.gamepad->send(EV_ABS, ABS_RX, 0);
                    }
                    global.gamepad->send(EV_SYN, 0, 0);
                }
                for(auto mode : global.settings.rightNubModeX) {
                    handleNubAxis(mode, e.value, global.mouse, global.gamepad, global.settings);
                }
                break;
            case ABS_Y:
                if (global.settings.exportGamepad) {
                    if (e.value>global.settings.joyDeadzone||e.value<-global.settings.joyDeadzone) {
                        global.gamepad->send(EV_ABS, ABS_RY, e.value);
                    } else {
                        global.gamepad->send(EV_ABS, ABS_RY, 0);
                    }
                    global.gamepad->send(EV_SYN, 0, 0);
                }
                for(auto mode : global.settings.rightNubModeY) {
                    handleNubAxis(mode, e.value, global.mouse, global.gamepad, global.settings);
                }
                break;
            default: break;
        };
        }
        break;
    case EV_KEY:
        switch(e.code) {
            case BTN_LEFT: // Right nub click
            if (role == ROLE_GPIO) {
                for(auto mode : global.settings.rightNubClickMode) {
                    handleNubClick(mode, e.value, global.mouse, global.gamepad, global.settings);
                }
            }
            break;
        case BTN_RIGHT: // Left nub click
            if (role == ROLE_GPIO) {
                for(auto mode : global.settings.leftNubClickMode) {
                    handleNubClick(mode, e.value, global.mouse, global.gamepad, global.settings);
                }
            }
            break;

        default:
            global.behaviors->handle(e.code, e.value);
            global.keyboard->send(EV_SYN, 0, 0);
            break;
        }
        break;
    case EV_REL:
        /*
         * One of the nub is in mouse mode
         * TODO: Should forward the event to the mouse
        global.mouse->device.send(EV_REL, e.code, e.value);
        global.mouse->signal.notify_all();
        printf("REL: type=%d, code=%d, value=%d\n", e.type, e.code, e.value);
         */
        break;
    }
}

void destroy() {
    global.stop = true;
    global.mouse->signal.notify_all();

    if(global.gamepad) {
        delete global.gamepad;
    }
    if(global.keyboard) {
        delete global.keyboard;
    }
    if(global.behaviors) {
        delete global.behaviors;
    }

    global.mouseThread.join();
    global.rumbleThread.join();
}

void user1() {
    resetSettings(global.settings);
    loadConfig(global.settings.configFile, global.settings);
}
void user2() {
}

void handleArgs(char const** argv, unsigned int argc, Settings& settings) {
    std::string configFile;
    std::regex configRegex("config=(.*)");
  
    for(unsigned int i = 0; i < argc; ++i) {
        std::string arg(argv[i]);
        std::smatch match;
        if(std::regex_match(arg, match, configRegex)) {
            settings.configFile = match[1];
        }
    }
}

void setCommandMode(bool value) {
  if(global.commandMode != value) {
    global.commandMode = value;

    if(global.commandMode) {
      if(!global.settings.scripts.commandModeOn.empty()) {
        system(global.settings.scripts.commandModeOn.c_str());
      }
    } else {
      if(!global.settings.scripts.commandModeOff.empty()) {
        system(global.settings.scripts.commandModeOff.c_str());
      }
    }
  }
}

void handleNubAxis(Settings::NubAxisMode mode, int value, Mouse* mouse, PyraJoystick* gamepad, Settings const& settings) {
    switch(mode) {
    case Settings::MOUSE_X:
        mouse->dx = value;
        if(mouse->dx > settings.mouseDeadzone || mouse->dx < -settings.mouseDeadzone)
            mouse->signal.notify_all();
        break;
    case Settings::MOUSE_Y:
        mouse->dy = value;
        if(mouse->dy > settings.mouseDeadzone || mouse->dy < -settings.mouseDeadzone)
            mouse->signal.notify_all();
        break;
    case Settings::SCROLL_X:
        mouse->dwx = value;
        if(mouse->dwx > settings.mouseDeadzone || mouse->dwx < -settings.mouseDeadzone)
            mouse->signal.notify_all();
        break;
    case Settings::SCROLL_Y:
        mouse->dwy = value;
        if(mouse->dwy > settings.mouseDeadzone  || mouse->dwy < -settings.mouseDeadzone)
            mouse->signal.notify_all();
        break;
    case Settings::MOUSE_BTN: {
        int new_val = 0;
        if (value < -settings.mouseClickDeadzone)
            new_val = -1;
        else if (value > settings.mouseClickDeadzone)
            new_val = 1;
        if (global.mouseBtn!=new_val && global.settings.exportMouse) {
            if (global.mouseBtn == -1)
                mouse->device.send(EV_KEY, BTN_LEFT, 0);
            else if (global.mouseBtn == 1)
                mouse->device.send(EV_KEY, BTN_RIGHT, 0);
            if (new_val == -1 )
                mouse->device.send(EV_KEY, BTN_LEFT, 1);
            else if (new_val == 1 )
                mouse->device.send(EV_KEY, BTN_RIGHT, 1);
            mouse->device.send(EV_SYN, 0, 0);
            global.mouseBtn=new_val;
        }
        }
        break;
    case Settings::UNKNOWN_NUB_AXIS_MODE:
        break;
    }
}

void handleNubClick(Settings::NubClickMode mode, int value, Mouse* mouse, PyraJoystick* gamepad, Settings const& settings) {
    switch(mode) {
    case Settings::MOUSE_MIDDLE: {
        if (global.settings.exportMouse) {
            std::lock_guard<std::mutex> lk(mouse->mutex);
            mouse->device.send(EV_KEY, BTN_MIDDLE, value);
            mouse->device.send(EV_SYN, 0, 0);
        }
        break;
    }
    case Settings::MOUSE_LEFT: {
        if (global.settings.exportMouse) {
            std::lock_guard<std::mutex> lk(mouse->mutex);
            mouse->device.send(EV_KEY, BTN_LEFT, value);
            mouse->device.send(EV_SYN, 0, 0);
        }
        break;
    }
    case Settings::MOUSE_RIGHT: {
        if (global.settings.exportMouse) {
            std::lock_guard<std::mutex> lk(mouse->mutex);
            mouse->device.send(EV_KEY, BTN_RIGHT, value);
            mouse->device.send(EV_SYN, 0, 0);
        }
        break;
    }
    case Settings::NUB_CLICK_LEFT:
        if (global.settings.exportGamepad) {
            gamepad->send(EV_KEY, BTN_THUMBL, value);
            gamepad->send(EV_SYN, 0, 0);
        }
        break;
    case Settings::NUB_CLICK_RIGHT:
        if (global.settings.exportGamepad) {
            gamepad->send(EV_KEY, BTN_THUMBR, value);
            gamepad->send(EV_SYN, 0, 0);
        }
        break;
    case Settings::UNKNOWN_NUB_CLICK_MODE:
        break;
    }
}

void handleBrightness(int value) {
    if (value!=1) return;
    int ret = 0;
    if (global.Shift.pressed && global.Ctrl.pressed && global.Fn.pressed) {
        if (!global.settings.scripts.brightnessFnShiftCtrl.empty())
            ret=system(global.settings.scripts.brightnessFnShiftCtrl.c_str());
    } else if (global.Shift.pressed && global.Alt.pressed && global.Fn.pressed) {
        if (!global.settings.scripts.brightnessFnShiftAlt.empty())
            ret=system(global.settings.scripts.brightnessFnShiftAlt.c_str());
    } else if (global.Shift.pressed && global.Alt.pressed) {
        if (!global.settings.scripts.brightnessShiftAlt.empty())
            ret=system(global.settings.scripts.brightnessShiftAlt.c_str());
    } else if (global.Shift.pressed && global.Ctrl.pressed) {
        if (!global.settings.scripts.brightnessShiftCtrl.empty())
            ret=system(global.settings.scripts.brightnessShiftCtrl.c_str());
    } else if (global.Fn.pressed && global.Alt.pressed) {
        if (!global.settings.scripts.brightnessFnAlt.empty())
            ret=system(global.settings.scripts.brightnessFnAlt.c_str());
    } else if (global.Fn.pressed && global.Ctrl.pressed) {
        if (!global.settings.scripts.brightnessFnCtrl.empty())
            ret=system(global.settings.scripts.brightnessFnCtrl.c_str());
    } else if (global.Alt.pressed && global.Ctrl.pressed) {
        if (!global.settings.scripts.brightnessAltCtrl.empty())
            ret=system(global.settings.scripts.brightnessAltCtrl.c_str());
    } else if (global.Fn.pressed && global.Shift.pressed) {
        if (!global.settings.scripts.brightnessFnShift.empty())
            ret=system(global.settings.scripts.brightnessFnShift.c_str());
    } else if (global.Alt.pressed) {
        if (!global.settings.scripts.brightnessAlt.empty())
            ret=system(global.settings.scripts.brightnessAlt.c_str());
    } else if (global.Alt.pressed) {
        if (!global.settings.scripts.brightnessAlt.empty())
            ret=system(global.settings.scripts.brightnessAlt.c_str());
    } else if (global.Shift.pressed) {
        if (!global.settings.scripts.brightnessShift.empty())
            ret=system(global.settings.scripts.brightnessShift.c_str());
    } else if (global.Fn.pressed) {
        if (!global.settings.scripts.brightnessFn.empty())
            ret=system(global.settings.scripts.brightnessFn.c_str());
    } else {
        if (!global.settings.scripts.brightnessNormal.empty())
            ret=system(global.settings.scripts.brightnessNormal.c_str());
    }
}

void handleRumble(PyraJoystick* gamepad, bool* stop) {
    while(!*stop && gamepad->is_ready()) {
        gamepad->rumble_handle();
        std::this_thread::sleep_for(std::chrono::milliseconds(16));
    }
}


void handleMouse(Mouse* mouse, Settings* settings, bool* stop) {
    while(!*stop) {
        if((mouse->dx > settings->mouseDeadzone || mouse->dx < -settings->mouseDeadzone || mouse->dy > settings->mouseDeadzone || mouse->dy < -settings->mouseDeadzone || mouse->dwx > settings->mouseClickDeadzone || mouse->dwx < -settings->mouseClickDeadzone || mouse->dwy > settings->mouseWheelDeadzone || mouse->dwy < -settings->mouseWheelDeadzone)&&global.settings.exportMouse) {
            std::lock_guard<std::mutex> lk(mouse->mutex);

            if(mouse->dx > settings->mouseDeadzone) {
                mouse->device.send(EV_REL, REL_X, (mouse->dx - settings->mouseDeadzone) * settings->mouseSensitivity / 1000);
            } else if(mouse->dx < -settings->mouseDeadzone) {
                mouse->device.send(EV_REL, REL_X, (mouse->dx + settings->mouseDeadzone) * settings->mouseSensitivity / 1000);
            }

            if(mouse->dy > settings->mouseDeadzone) {
                mouse->device.send(EV_REL, REL_Y, (mouse->dy - settings->mouseDeadzone) * settings->mouseSensitivity / 1000);
            } else if(mouse->dy < -settings->mouseDeadzone) {
                mouse->device.send(EV_REL, REL_Y, (mouse->dy + settings->mouseDeadzone) * settings->mouseSensitivity / 1000);
            }


            int hwheelChange = 0;
            if(mouse->dwx > settings->mouseWheelDeadzone) {
                hwheelChange = (mouse->dwx - settings->mouseWheelDeadzone) * settings->mouseWheelSensitivity / 100;
            } else if(mouse->dwx < -settings->mouseWheelDeadzone) {
                hwheelChange = (mouse->dwx + settings->mouseWheelDeadzone) * settings->mouseWheelSensitivity / 100;
            }
            if(hwheelChange != 0) {
                mouse->hwheelPosition += hwheelChange;
                mouse->device.send(EV_REL, REL_HWHEEL_HI_RES, hwheelChange);
                int notches = mouse->hwheelPosition / mouse->WHEEL_HI_RES_PER_NOTCH;
                if(notches != 0) {
                    mouse->device.send(EV_REL, REL_HWHEEL, notches);
                    mouse->hwheelPosition = mouse->hwheelPosition % mouse->WHEEL_HI_RES_PER_NOTCH;
                }
            }

            int wheelChange = 0;
            if(mouse->dwy > settings->mouseWheelDeadzone) {
                wheelChange = -(mouse->dwy - settings->mouseWheelDeadzone) * settings->mouseWheelSensitivity / 100;
            } else if(mouse->dwy < -settings->mouseWheelDeadzone) {
                wheelChange = -(mouse->dwy + settings->mouseWheelDeadzone) * settings->mouseWheelSensitivity / 100;
            }
            if(wheelChange != 0) {
                mouse->wheelPosition += wheelChange;
                mouse->device.send(EV_REL, REL_WHEEL_HI_RES, wheelChange);
                int notches = mouse->wheelPosition / mouse->WHEEL_HI_RES_PER_NOTCH;
                if(notches != 0) {
                    mouse->device.send(EV_REL, REL_WHEEL, notches);
                    mouse->wheelPosition = mouse->wheelPosition % mouse->WHEEL_HI_RES_PER_NOTCH;
                }
            }

            mouse->device.send(EV_SYN, 0, 0);
        } else {
            std::unique_lock<std::mutex> lk(mouse->mutex);
            mouse->signal.wait(lk);
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(16));
    }
    delete mouse;
}
 
Back
Top