Pandora Making the Pandora key act more like the Windows key


Mycohl

Still Fresh
Joined
Oct 3, 2009
Messages
19
So, being a Windows luser, I enjoy being able to use the menu key to run things and also open the menu. I wanted to make the Pandora key do something similar, but I ran into a few stumbling blocks. The event daemon only runs key scripts when you release the key, so I had to modify that a little. Also, I am not a programmer, so my implementation is probably very stupid and inefficient. I will post it here though, so you guys can use it or make improvements or whatever.


For those who don't want to muck about with compiling things, I will attach both files (pndevmapperd and pndhotkey) in an archive. You will want to



Code:
sudo tar -xzf hotkey.tar.gz /usr/bin/



You will want to edit your /etc/pandora/conf/eventmap file to run the pndhotkey immediately when the pandora key is pressed. To do this just change the 'pandora' line:


here.



/usr/pandora/scripts/snapsnap.sh





Code:
pandora /usr/bin/pndhotkey -1


(That is a 'negative one' for the hold time, indicating it will be run on key down instead of up.)



Then, you will need to create a configuration file for pndhotkey at /etc/pandora/conf/pndhotkey.conf

I currently have the following (and you can add whatever else you like):


Code:
# pndhotkey configuration file - keys listed under minimenu will only run 

#                                under minimenu, under desktop will only

#                                run under xfce/whatever, and general will

#                                run in either


[config]


# this is the command to check if the full desktop is up

desktop-check        pidof xfce4-session


# this is the command to run in order to show/hide the desktop menu

desktop-menu         /usr/pandora/scripts/op_menu_toggle.sh


[minimenu]

Q                    /usr/pandora/scripts/op_pndkillall.sh


[desktop]

R                    xfrun4

L                    xflock4

K                    /usr/pandora/scripts/op_xfcekill.sh

T                    Terminal


[general]

W                    sudo /usr/pandora/scripts/op_wifi.sh

B                    sudo /usr/pandora/scripts/op_bluetooth.sh

S                    /usr/pandora/scripts/snapsnap.sh




Then, you'll need to create any of those scripts you'd like to include in the config file. You will definitely want the menu toggle (or some replacement). Don't forget to chmod a+x on all script files you create yourself.



(These were shamelessly stolen from skeezix's premade scripts.)



/usr/pandora/scripts/op_menu_toggle.sh


Code:
#!/bin/bash

# this script determines whether the popup menu is displayed,

# and shows/hides it


# note that this doesn't actually work, since xfce4-popup-menu

# does not stay resident. i have yet to find a work-around, but

# i'm still investigating some things


popuppid=$(pidof xfce4-popup-menu)

if [ $popuppid ]; then

    # menu is open, let's close it

    kill $popupid

else

    # no open menu found

    /bin/bash -c 'DISPLAY=:0.0 xfce4-popup-menu'

fi




/usr/pandora/scripts/op_pndkillall.sh


Code:
#!/bin/bash

# this script kills all processes that were launched

# with the pnd_run script


pidlist=$(pstree -lpA | grep pnd_run.sh | sed -ne 's/.*(\([0-9]\+\))/\1/p')


for PID in $pidlist

do

  kill -9 $PID

done




/usr/pandora/scripts/op_xfcekill.sh


Code:
#!/bin/bash

# this script opens a list of running apps and prompts

# the user to terminate any of them


pidlist=$(pstree -lpA | grep pnd_run.sh | sed -ne 's/.*-\(.*\)(\([0-9]\+\))/\2\n \1/p' | /bin/bash -c 'DISPLAY=:0.0 zenity --list --multiple --column "pid" --column "name" --title "kill" --text "which apps should be killed"' | sed 's/|/\n/')


for PID in $pidlist

do

  kill -9 $PID

done




This one was stolen from SomeGuy99's guide
Code:
#!/bin/bash

# this script saves a timestamped screen shot

# to the current user's home folder


fbgrab -d /dev/fb0 ~/screen`date +%y%m%d-%H%M%S`-00.png

fbgrab -d /dev/fb1 ~/screen`date +%y%m%d-%H%M%S`-01.png

notify-send snapsnap


# EDIT: Seems like most games (SDL?) are using fb1 instead 

# of the default. Easy fix, just grab both and sort them 

# later, although it will cause more nand writes. Someone 

# can come up with a better system, I'm sure.




So, hopefully people will find some use in this. I will post the source here for pndhotkey and also the changes I made to pndevmapperd.

Again, just to make it absolutely clear, I am not a programmer (or a very good scripter, really), so please don't expect too much.



pndhotkey.c


Code:
#include <stdio.h>

#include <stdlib.h>

#include <linux/input.h>

#include <time.h>

#include <string.h>

#include <X11/Xlib.h>


#include "pnd_conf.h"

#include "pnd_container.h"

#include "pnd_pxml.h"

#include "pnd_utility.h"


#define KEY_PRESSED(key,map) (map[key / 8] & (1 << (key % 8)))


typedef struct {

  int keycode;

  char *keyname;

} keycode_t;


keycode_t keycodes[] = {

  { KEY_A, "A" }, { KEY_B, "B" }, { KEY_C, "C" }, { KEY_D, "D" },

  { KEY_E, "E" }, { KEY_F, "F" }, { KEY_G, "G" }, { KEY_H, "H" },

  { KEY_I, "I" }, { KEY_J, "J" }, { KEY_K, "K" }, { KEY_L, "L" },

  { KEY_M, "M" }, { KEY_N, "N" }, { KEY_O, "O" }, { KEY_P, "P" },

  { KEY_Q, "Q" }, { KEY_R, "R" }, { KEY_S, "S" }, { KEY_T, "T" },

  { KEY_U, "U" }, { KEY_V, "V" }, { KEY_W, "W" }, { KEY_X, "X" },

  { KEY_Y, "Y" }, { KEY_Z, "Z" }, { KEY_1, "1" }, { KEY_2, "2" },

  { KEY_3, "3" }, { KEY_4, "4" }, { KEY_5, "5" }, { KEY_6, "6" },

  { KEY_7, "7" }, { KEY_8, "8" }, { KEY_9, "9" }, { KEY_0, "0" },

  { KEY_COMMA, "COMMA" }, { KEY_DOT, "FULLSTOP" }, { KEY_ENTER, "ENTER" },

  { KEY_DELETE, "DELETE" }, { KEY_UP, "UP" }, { KEY_DOWN, "DOWN" },

  { KEY_LEFT, "LEFT" }, { KEY_RIGHT, "RIGHT" }, { KEY_HOME, "BUTTONA" },

  { KEY_END, "BUTTONB" }, { KEY_PAGEDOWN, "BUTTONX" }, { KEY_PAGEUP, "BUTTONY" },

  { KEY_RIGHTSHIFT, "BUTTONL" }, { KEY_RIGHTCTRL, "BUTTONR" },

  { KEY_LEFTALT, "SELECT" }, { KEY_LEFTCTRL, "START" }, { -1, NULL }

};


char username[128];


keycode_t* getKeyCode(char *keystr) {

    keycode_t *p = keycodes;

    while (p->keycode != -1) {

        if (strcasecmp (p->keyname,keystr) == 0 ) {

            return p;

        }

        p++;

    }

    return p;

}


int main(int argc, char *argv[])

{

    FILE *log;

    // shut off logging unless -l switch is used

    if (argc > 1 && strncmp(argv[1], "-", 1) == 0) {

        if (strcmp(argv[1], "-l") == 0 || strcmp(argv[1], "--log") == 0) {

            log = fopen("/tmp/pndhotkey.log", "w");

        } else {

            printf("Unknown option: %s\nUsage: pndhotkey [options]\nCurrent options:\n-l, --log    Enable logging to /tmp/pndhotkey.log\n", argv[1]);

            exit(0);

        }

    } else {

        log = fopen("/dev/null", "w");

    }

    stdout = log;

    stderr = log;

    //fprintf(log, "Begin logging...\n");


    FILE *keypad;

    FILE *gpio;

    keypad = fopen("/dev/input/by-path/platform-i2c_omap.1-platform-twl4030_keypad-event", "r");

    gpio = fopen("/dev/input/by-path/platform-gpio-keys-event", "r");


    if (keypad == NULL) {

        fprintf(log, "Failed to open keypad input, exiting.");

        exit(1);

    } else if (gpio == NULL) {

        fprintf(log, "Failed to open gpio input, exiting.");

        exit(1);

    }


    char keypadmap[KEY_MAX/8 + 1];

    char gpiomap[KEY_MAX/8 + 1];


    memset(keypadmap, 0, sizeof(keypadmap));

    memset(gpiomap, 0, sizeof(gpiomap));

    if (ioctl(fileno(keypad), EVIOCGKEY(sizeof(keypadmap)), keypadmap) == -1) {

        fprintf(log, "Can't get keypad state using EVIOCGKEY, exiting.");

        exit(1);

    }

    if (ioctl(fileno(gpio), EVIOCGKEY(sizeof(gpiomap)), gpiomap) == -1) {

        fprintf(log, "Can't get gpio state using EVIOCGKEY, exiting.");

        exit(1);

    }


    pnd_conf_handle hotkeyconf;

    hotkeyconf = pnd_conf_fetch_by_path("/etc/pandora/conf/pndhotkey.conf");

    if (!hotkeyconf) {

        fprintf(log, "Failed getting handle to conf file, exiting.");

        exit(1);

    }


    // determine whether we are running with wm

    void *node;

    node = pnd_box_find_by_key(hotkeyconf, "config.desktop-check");

    int wmcheck;

    char wmcmd[(strlen((char*)node) + 13)];

    sprintf(wmcmd, "%s > /dev/null", (char*)node);

    wmcheck = system(wmcmd);

    // check if process could not be created or if file not found

    if (wmcheck == -1 || wmcheck == 32512) {

        node = pnd_box_find_by_key(hotkeyconf, "config.desktop-check");

        fprintf(log, "Could not launch window manager check [%s], exiting.", (char*)node);

        exit(1);

    } else {

        wmcheck = !wmcheck;

    }


    // check username to run commands as

    pnd_check_login(username, 127);


    struct timespec ts;


    // we are grabbing all keys

    Display *dpy;

    fprintf(log, "Begin capture of X Display\n");

    if ((dpy = XOpenDisplay(":0.0")) == NULL) {

        // possibly first run, might not be able to connect as root

        fprintf(log, "Could not open default X Display, attempting to run xhost...\n");

        char xhostcmd[(strlen(username) + 50)];

        sprintf(xhostcmd, "su -c 'DISPLAY=:0.0 /usr/bin/xhost local:root' - %s", username);

        system(xhostcmd);

        if ((dpy = XOpenDisplay(":0.0")) == NULL) {

            fprintf(log, "Could not open default X Display, exiting");

            exit(1);

        }

    }

    XGrabKeyboard(dpy, DefaultRootWindow(dpy), False, GrabModeAsync, GrabModeAsync, CurrentTime);


    // loop while pandora key is held

    while (KEY_PRESSED(KEY_MENU,gpiomap)) {


        node = pnd_box_get_head(hotkeyconf);

        // loop through conf entries looking for keys

        while (node) {

            char *key = pnd_box_get_key(node);

            //fprintf(log, "Key: %s => %s\n", key, (char*)node);

            if (wmcheck && (strncmp(key,"desktop.",8) == 0)) {

                key += 8;

            } else if (!wmcheck && (strncmp(key,"minimenu.",9) == 0)) {

                key += 9;

            } else if (strncmp(key,"general.",8) == 0) {

                key += 8;

            } else {

                key = NULL;

            }


            // if we found a valid hotkey, check if it is pressed and run it

            if (key != NULL) {

                keycode_t *code = getKeyCode(key);

                if (code != NULL && (KEY_PRESSED(code->keycode,keypadmap) || KEY_PRESSED(code->keycode,gpiomap))) {

                    fprintf(log, "Found hotkey [%s] for [%s].\n", key, (char*)node);


                    char cmd[(strlen((char*)node) + strlen(username) + 54)];


                    // this is a stupid fix because 'su' munges the path, would be better

                    // to detect full path from .profile or .bashrc or something (??)

                    // also, would probably be more elegant to fork instead of using '&'

                    sprintf(cmd, "su -c 'DISPLAY=:0.0 PATH=$PATH:/sbin:/usr/sbin %s &' - %s", (char*)node, username);


                    int result = system(cmd);

                    XUngrabKeyboard(dpy, CurrentTime);

                    if (result == -1 || result == 32512) {

                        fprintf(log, "Could not launch [%s] - exit status [%i] - exiting.", cmd, result);

                        exit(1);

                    } else {

                        exit(0);

                    }

                }

            }

            node = pnd_box_get_next(node);

        }


        // don't want to lock our pandy up while key is held, we'll wait 10ms

        ts.tv_sec = 0;

        ts.tv_nsec = 10000000;

        nanosleep(&ts, NULL);


        // refresh the global keystate

        memset(keypadmap, 0, sizeof(keypadmap));

        memset(gpiomap, 0, sizeof(gpiomap));

        ioctl(fileno(keypad), EVIOCGKEY(sizeof(keypadmap)), keypadmap);

        ioctl(fileno(gpio), EVIOCGKEY(sizeof(gpiomap)), gpiomap);

    }


    // if we're still running at this point, user has released pandora key

    // without pressing any other key, we should open the menu if wm is running

    if (wmcheck) {

        node = pnd_box_find_by_key(hotkeyconf, "config.desktop-menu");

        if ((wmcheck = system((char*)node)) == -1 || wmcheck == 32512) {

            fprintf(log, "Could not launch window manager menu toggle [%s] - exit status [%i] - exiting.", (char*)node, wmcheck);

            XUngrabKeyboard(dpy, CurrentTime);

            exit(1);

        }

    }


    XUngrabKeyboard(dpy, CurrentTime);

    exit(0);

}




pndevmapperd.c changes:


Code:
(line 105?) change:

  unsigned int maxhold;

to:

  int maxhold;


(line 687?) add:

        if ( g_evmap [ i ].maxhold < 0 ) {

          invoke_it = 1;

        }

before:

        g_evmap [ i ].keydown_time = time ( NULL );
 

Attachments

  • hotkey.tar.gz
    23.1 KB · Views: 243
Last edited by a moderator:
Woot for hacking around :) Thats why its all out there in the open :)


Note that it looks like you didn't release the source; do so soon as you can.


I generally don't see much point in watching key-up (since it just introduces a delay), but I guess you're adding _chording_ here? ie: so you press Pandora+Q to 'force quit' instead of just pressing pandora?


(If not, then probably going to collide with other users of the keys, so I assume so :)


If chording is useful, then I can add something into pndevmapperd for official hotfix, let me know. But first you have to post source, since you're deriving from something I GPL'd and I want to see your implementation details :)


jeff
 
Woot for hacking around :) Thats why its all out there in the open :)


Note that it looks like you didn't release the source; do so soon as you can.


[...]


If chording is useful, then I can add something into pndevmapperd for official hotfix, let me know. But first you have to post source, since you're deriving from something I GPL'd and I want to see your implementation details :)


jeff

I did post the source. Everything is spoiler'd to prevent eyeball asplosions.


Or do you mean that I neglected to include the GPL header? I guess I can edit that in, although pndevmapperd.c doesn't have it, so I figured it wasn't necessary.


Yes, chording combined with menu toggling was what I was going for.
 
Ah, you are right, sorry about that!; somehow I missed those ones at the bottom. (I should add in headers carefully myself .. I made the choice to make libpnd/etc all LGPL as the best choice for Pandora, but we made that call after a lot of that had been written already (pre-release, post-writing :) )


I blame being in 6 hours of meetings today, with my retinas wanting to climb out of my head and run for it :)


Remind me (PM?) after hf5 ships, maybe we can build some chording support in for hf6 for ya, or add some of these sorts of hotkeys in as standard. I'll check out your patch, see if we can slide her right in or something.


Awesome hack though :)


jeff
 
Last edited by a moderator:
Back
Top