9

How to define a global shortcut Ctrl+a 1 with AutoKey which sends the same keystroke Ctrl+a 1 to the window with title 'gnu screen' ? If there is no such titled window it should show a popup message "Couldn't find destination window".

zuba
  • 2,393

3 Answers3

5
  1. Install libnotify-bin which provides notify-send

    sudo apt-get install libnotify-bin
    
  2. Create new script:

    import time
    import subprocess
    
    #no need
    #keyboard.release_key("<ctrl>")
    
    # wait_for_keypress does not return any thing to distinguish between target key pressed or timeout reached.
    # So if time is less than the timeout then it was key press.
    start_time = time.time()
    keyboard.wait_for_keypress("1", timeOut=1)
    if (time.time()-start_time < 0.9):
        time.sleep(0.2)
        window.activate("gnu screen")
        time.sleep(0.1)
        active_title = window.get_active_title()
        # if it doesn't get same title, then no window titled as gnu screen
        # it sends a notify message otherwise send the key sequence.
        if (active_title == "gnu screen"):
            keyboard.press_key("<ctrl>")
            keyboard.send_key("a")
            keyboard.release_key("<ctrl>")
            keyboard.send_key("1")
        else:
            subprocess.Popen(['notify-send', "Couldn't find destination window"])
    
  3. Setup its hot key as: Ctrl+a

To trigger it: Ctrl+a then 1 (in <1sec)


Debug

  1. Launch X Event Tester in separate terminal window

    xev -event keyboard
    
  2. Check it's window title, mine shows Event Tester

    $ wmctrl -l
    0x03000012 -1       N/A Desktop — Plasma
    0x030000c1 -1       N/A Desktop — Plasma
    0x0300001b -1       N/A Plasma
    0x06a00098  0        PC User User - Ask Ubuntu - Mozilla Firefox
    0x01a00067  0       N/A user : screen
    0x01a000cd  0       N/A user : xev
    0x04600001  0       N/A Event Tester
    0x01e0015d  0        PC AutoKey
    
  3. Modify script to target Event Tester window.

    import time
    import subprocess
    
    start_time = time.time()
    keyboard.wait_for_keypress("1", timeOut=1)
    if (time.time()-start_time < 0.9):
        time.sleep(0.2)
        window.activate("Event Tester")
        time.sleep(0.1)
        keyboard.press_key("<ctrl>")
        keyboard.send_key("a")
        keyboard.release_key("<ctrl>")
        keyboard.send_key("1")
    
  4. If xev receives the key sequence. Its output should be some thing similar to:

    KeyPress event, serial 34, synthetic NO, window 0x4600001,
        root 0xb2, subw 0x0, time 55057700, (1053,140), root:(945,303),
        state 0x0, keycode 105 (keysym 0xffe4, Control_R), same_screen YES,
        XLookupString gives 0 bytes: 
        XmbLookupString gives 0 bytes: 
        XFilterEvent returns: False
    
    KeyPress event, serial 34, synthetic YES, window 0x4600001,
        root 0xb2, subw 0x0, time 0, (1,1), root:(1,1),
        state 0x0, keycode 38 (keysym 0x61, a), same_screen YES,
        XLookupString gives 1 bytes: (61) "a"
        XmbLookupString gives 1 bytes: (61) "a"
        XFilterEvent returns: False
    
    KeyRelease event, serial 35, synthetic YES, window 0x4600001,
        root 0xb2, subw 0x0, time 0, (1,1), root:(1,1),
        state 0x0, keycode 38 (keysym 0x61, a), same_screen YES,
        XLookupString gives 1 bytes: (61) "a"
        XFilterEvent returns: False
    
    KeyRelease event, serial 35, synthetic NO, window 0x4600001,
        root 0xb2, subw 0x0, time 55057701, (1053,140), root:(945,303),
        state 0x4, keycode 105 (keysym 0xffe4, Control_R), same_screen YES,
        XLookupString gives 0 bytes: 
        XFilterEvent returns: False
    
    KeyPress event, serial 35, synthetic YES, window 0x4600001,
        root 0xb2, subw 0x0, time 0, (1,1), root:(1,1),
        state 0x0, keycode 10 (keysym 0x31, 1), same_screen YES,
        XLookupString gives 1 bytes: (31) "1"
        XmbLookupString gives 1 bytes: (31) "1"
        XFilterEvent returns: False
    
    KeyRelease event, serial 35, synthetic YES, window 0x4600001,
        root 0xb2, subw 0x0, time 0, (1,1), root:(1,1),
        state 0x0, keycode 10 (keysym 0x31, 1), same_screen YES,
        XLookupString gives 1 bytes: (31) "1"
        XFilterEvent returns: False
    

Note, I have removed the if condition to check active title. The script activates/raises xev window but the check doesn't find the correct title. I got just the notification message.

user.dz
  • 48,105
  • @zuba, window.activate("gnu screen") should activate the target window but if it doesn't then either the window doesn't exist or title is wrong. Try also window.activate("gnu screen") or window.activate("gnu screen", switchDesktop=True) in separate script with another shortcut, see if it does work. If you don't mind post wmctrl -l so we can verify the exact title. – user.dz Jun 22 '15 at 08:33
  • Ok, I see it founds and activates the right window, Great! But it doesn't work with gnu screen window - it simply prints a1, while it should make screen switch to the firt window. Can you fix that? – zuba Jun 22 '15 at 09:01
  • @zuba, I couldn't get same behavior. If you are using byobu, try with F12 instead of Ctrl+a – user.dz Jun 22 '15 at 18:13
  • Does that mean you can switch gnu screen with the script? – zuba Jun 23 '15 at 09:33
  • @zuba, yes, I'm using kubuntu 15.04. Your case seems like pressing too key a without modifier <ctrl>. – user.dz Jun 23 '15 at 10:27
  • @zuba, I have added a debug step, to break down the problem. see if you can get same output. if you have time to join chat, may be i could help. – user.dz Jun 24 '15 at 21:13
  • @zuba, Do you still have your "a" - without "Ctrl" problem? I have the strong feeling that you need to increase time.sleep(0.1) before sending composed "Ctrl" to much more (like 0.5 or even 1 sec) especially if you need to shift another desktop/viewport! – V-Mark Dec 15 '15 at 19:52
1

I was thinking along the same lines as "Mostafa Najafiyazdi" and I used his wmctrl function as a base for first script version of the function with some modifications.

This is not autokey specific and as such it's not a python script. I use similar setup to activate, switch and do some stuff on my media-pc (mythtv) with a single remotecontrol button.

Setting windows shortcut/autokey to run these script with combined keys is difficult and ctrl+a is usually globally something different. If you decide to use something ctrl+f7 for the activation key, then calling this script should do what you want.

This requies xdotool and libnotify-bin. This will not change active window to "gnu screen", see commented section to change to the same functionality as with the activate_window_and_send_keys script.

~/bin/focus_window_and_send_keys "gnu screen" "ctrl+a+1"

And the script:

#!/bin/bash
## copy these files to ~/bin = (~ = is your home directory)
### finding window and keys
## requires xdotool
### notify
## libnotify-bin

# Usage:
# ~/bin/focus_window_and_send_keys "window title" "keys" "morekeys" ...
# ~/bin/focus_window_and_send_keys "gnu screen" "ctrl+a+1"

title=${1:-NOT_FOUND_EMPTY_TITLE}
shift
## get current window
CURWIN=$(xdotool getactivewindow)
TARGETWIN=$(xdotool search --name "$title" | head -n 1)
if [ -z $TARGETWIN ]; then
 notify-send -i face-crying "Can't find specified window!"
else
  ## use this to activate window
  #xdotool windowactive "$TARGETWIN"
  ## changes focus, does not change to the window on display
  xdotool windowfocus "$TARGETWIN"
  ## send keys if window was found
  for keypress in "$@"
  do
    xdotool key --window "$TARGETWIN" "$keypress"
  done
fi
## comment next line if focus should stay with activated window
xdotool windowfocus "$CURWIN"

My first version required these and is functional (below), but uses extra wrapper and needs window to be active. xdotool version does not require that.

This requires xautomation libnotify-bin and wmctrl

this is ~/bin/activate_window_and_send_keys scipt To do what you want from cmdline, you'd call this with

# this is press ctrl, a, 1, release ctrl
~/bin/activate_window_and_send_keys "gnu screen" "ctrl+a+1"
# this is press ctrl, a, release ctrl, 1
~/bin/activate_window_and_send_keys "gnu screen" "ctrl+a" "1"

Remember to chmod u+x on your ~/bin/* scripts

#!/bin/bash
## copy these files to ~/bin (~ = is your home directory)
### finding window
## requires wmctrl
### notify
## libnotify-bin
### tool to send keypresses
## requires xautomation

# Usage:
# ~/bin/activate_window_and_send_keys "window title" "keys" "morekeys" ...
# ~/bin/activate_window_and_send_keys "gnu screen" "ctrl+a+1"

export PATH=$PATH:~/bin


## helper function to find correct window
## modified version of Mostafa Najafiyazdi answer
## http://askubuntu.com/a/637897/41757
function find_window_and_activate_window {
  # Get the list of all windows
  # and select the line containing a substring given as
  # an argument to the script
  title=$1
  window_found=`wmctrl -l | grep "$title" | awk '{print $3}'`
  # If nothing is found, echo a message
  if [ -z "$window_found" ]; then
    notify-send -i face-crying "Can't find specified window!"
    return -1
  else
    wmctrl -a "$title"
  fi
}

title=${1:-NOT_FOUND_EMPTY_TITLE}
shift
echo "$title"
find_window_and_activate_window "$title" && {
   ## send keys if window was found
   for keypress in "$@"
   do
     sendkey "$keypress"
   done
}

This is ~/bin/sendkey It is simply a wrapper around xte to simplify xte syntax

#!/usr/bin/perl

my @keys=@ARGV;
for my $key (@keys) {
  my @keycomb=split(/\+/, $key);
  my $k = pop(@keycomb);
  #print "$k\n";
  my $modup   = "";
  my $moddown = "";
  my $press   = " 'key $k' ";
  for my $m (@keycomb) {
    $m =~ s/ctrl/Control_L/gi;
    $m =~ s/alt/Alt_L/gi;
    $m =~ s/shift/Shift_L/gi;
    $moddown = $moddown.' "keydown '.$m.'"';
    $modup   = ' "keyup '.$m.'"'.$modup;
  }
  system("xte $moddown $press $modup");
}
Manwe
  • 755
  • Thank you for such complete answer! I'm going to try it this evening – zuba Jun 24 '15 at 09:33
  • Manwe, as I see you've implemented titled window activation and sendind it key presses. The thing I'm interested in the most is triggering a script by the key sequence ctrl+a 1. That is why I assign the bounty to another answer, which almost does the trick. Thank you very much for your scripts and explanations! – zuba Jun 24 '15 at 20:03
0

The first thing that comes to my mind is to break what you want to do in two parts:

  1. Writing a bash function which checks for a window titled whatever you want, for you it would be 'gnu screen', if not found pops up a message saying "Couldn't find destination window!" For this you would first need to install wmctrl

    sudo apt-get install wmctrl
    

    It is a windows controller which you can use to get windows information including its PID, title, position, size, desktop number, etc. Add the following code in your .bash_aliases:

    function find_window {
      # Get the list of all windows 
      # and select the line containing a substring given as 
      # an argument to the script
      title=$1
    
      window_found=`wmctrl -l | grep $title | awk '{print $3}'`
    
      # If nothing is found, echo a message
      if [ -z "$window_found" ]; then
        notify-send -i face-crying "Can't find specified window!"
      else
        wmctrl -a $1
      fi
    }
    

    You can change how the notification looks like. Just see the manpage for notify-send.

  2. Define a custom short using your system settings. See here Simple add find_window "gnu screen" as the command to run. You define keyboard sequences as you like.

EDIT: If the window exists, it will switch to that window! That's what the else in the if then else does.

  • 1
    Thank you for your time, Mostafa. The script you provided solves one of 3 parts of the question. It supposed to activate titled window, while left not implemented sending to the window the key sequence and triggering the script system wide. These 2 parts compose the essense of the question. I'm looking forward to see the complete solution from you. – zuba Jun 18 '15 at 07:16
  • Ok, now I get what you mean. Should the whole key sequence Ctrl+a-1 be passed or just the last number, i.e. 1. – Mostafa Najafiyazdi Jun 19 '15 at 05:34
  • Yes, the whole sequence – zuba Jun 19 '15 at 05:36