Recent

Author Topic: Sendkeys  (Read 28398 times)

kris

  • Jr. Member
  • **
  • Posts: 59
Re: Sendkeys
« Reply #15 on: December 03, 2017, 07:08:01 am »
so far, I rewrote the code from https://mail.gnome.org/archives/gtk-app-devel-list/2004-July/msg00016.html into Lazarus, but I'm still getting crash error on gtk_main_do_event(Event);

I also learned that gtk_main_do_event is already defined in the unit gtk2.pas, so you do not need to define it yourself (i shared the declaration in the previous answer).

This is the code I have so far:
Code: Pascal  [Select][+][-]
  1.   Procedure TGtkTerm.SendKey(AKey: char);
  2.    var
  3.      Event      : PGdkEvent;
  4.      hw_keycode : ctypes.cuint16;
  5.      keys       : PGdkKeymapKey;
  6.      n_keys     : ctypes.cint;
  7.    begin
  8.      if(self.Vte=nil)then exit;
  9.      write('gdk_keymap_get_entries_for_keyval... ');
  10.      if gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), ord(AKey), keys, @n_keys)
  11.         then try
  12.                hw_keycode                  := keys[0].keycode;
  13.                writeln('done.');
  14.                writeln('hw_keycode=',hw_keycode,'; keys freed.');
  15.              finally
  16.                g_free(keys);
  17.                writeln('failed.');
  18.              end
  19.         else exit;
  20.  
  21.      write('gdk_event_new (GDK_KEY_PRESS)... ');
  22.      Event := gdk_event_new(GDK_KEY_PRESS);
  23.      if(Event=nil)
  24.        then writeln('failed.')
  25.        else writeln('done.');
  26.      if(Event=nil)then exit;
  27.      try
  28.        Event^.key.window           := self.Vte^.window;
  29.        Event^.key.state            := 0;//GDK_MOD1_MASK; {alt}
  30.        Event^.key.hardware_keycode := hw_keycode;
  31.  
  32.        Event^.key.keyval           := gdk_unicode_to_keyval(ord(AKey));
  33.        Event^.key.length           := 1;
  34.        writeln('key.keyval=',Event^.key.keyval);
  35.  
  36.        Event^.key.send_event       := 0;
  37.        Event^.key.time             := GDK_CURRENT_TIME;
  38.  
  39.        write('gtk_main_do_event(Event);... ');
  40.        try
  41.          gtk_main_do_event(Event);
  42.          writeln('done.');
  43.        except
  44.          writeln('failed.');
  45.        end;
  46.      finally
  47.        gdk_event_free(Event);
  48.      end;
  49.    end;
  50.  
    Remarks:

    • this is in a method of my own class wrapper around vte_terminal. I want to send keys into the terminal widget, so basically that is why I googled this thread in the first place.
    • the "self.Vte^.window;" is in my case (Vte is PGtkWidget), you may wish to use your own widget's window.
    • i use write/writeln to print out some debugging info, you don't need these at all.
    • the units you will end up needing for sure are: gtk2, gdk2, ctypes, and maybe (not sure) glib2

Will try to make it work, but at least this is what I have for now, and at least i translated the lnked sample code into Lazarus for your convenience. If any of you ever get to this point and solves the error, pls share. If I fix the error, I'll share as well.
« Last Edit: December 03, 2017, 07:09:53 am by kris »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Sendkeys
« Reply #16 on: December 03, 2017, 12:01:39 pm »
Will try to make it work, but at least this is what I have for now, and at least i translated the lnked sample code into Lazarus for your convenience. If any of you ever get to this point and solves the error, pls share. If I fix the error, I'll share as well.
A 10 year old excavation. Nice! Assuming you are looking for a way to send keys, I recommend to take a look on the mouseandkeyInput package. It claims to support sending keys.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

kris

  • Jr. Member
  • **
  • Posts: 59
Re: Sendkeys
« Reply #17 on: December 03, 2017, 04:41:28 pm »
Thank you VERY much for the hint on http://wiki.freepascal.org/MouseAndKeyInput !!
I just wanted to share that before I saw your reply, I already followed similar solution on my own. Also depending on a 3rd party library, but in my case I used libxdo, used by xdotool:
Also on the wiki about MouseAndKeyInput, under Gtk chapter it reads:
Quote
(...) ALT key pressing is not supported (...)
and libxdo does not have that limitation.

For my needs I only had to be able to send keyboard key events, no mouse. libxdo has plenty of exported functions, but I only ported these:
Code: Pascal  [Select][+][-]
  1.  
  2.   function xdo_new(display:Pchar): pxdo_t;cdecl;external 'xdo' name 'xdo_new';
  3.   function xdo_send_keysequence_window(xdo:Pxdo_t; window: window_t; keysequence:Pchar; delay:useconds_t):longint; cdecl;external 'xdo' name 'xdo_send_keysequence_window';
  4.   function xdo_send_keysequence_window_up(xdo:Pxdo_t; window: window_t; keysequence:Pchar; delay:useconds_t):longint;cdecl;external 'xdo' name 'xdo_send_keysequence_window_up';
  5.   function xdo_send_keysequence_window_down(xdo:Pxdo_t; window: window_t; keysequence:Pchar; delay:useconds_t):longint;cdecl;external 'xdo' name 'xdo_send_keysequence_window_down';
  6.  
and then I added my own wrapper function:
Code: Pascal  [Select][+][-]
  1. (...)
  2. implementation
  3. { API }
  4.   var
  5.     FXDO : pxdo_t = nil;
  6.  
  7.   Function SendKey(const WindowId: longint; const sequence: string): Boolean;
  8.    var
  9.      pc : pchar;
  10.    begin
  11.      if(FXDO=nil)then FXDO := xdo_new(nil);
  12.      if(FXDO=nil)then exit;
  13.      pc := pchar(sequence);
  14.      Result := xdo_send_keysequence_window(FXDO, WindowId, pc, 0)=0;
  15.    end;
  16. (...)
  17.  

As you can see in the external functions headers, there are some proprietary types, but I used FPC included h2pas tool to auto-convert the xdo.h file (https://github.com/jordansissel/xdotool/blob/master/xdo.h) from c to pascal, and it did the job. I had to:
  • add units unixtype and ctypes to the uses type
  • install dev package for xdo: $ sudo apt-get install libxdo-dev

it really works flawlessly!!!

kris

  • Jr. Member
  • **
  • Posts: 59
Re: Sendkeys
« Reply #18 on: December 03, 2017, 04:53:08 pm »
I thought that if someone would like to also use libxdo, I should extend this info by how to pass valid WindowId to the calls I shared.

Manually, when using xdotool from terminal, I usually find window id with "wmctrl -l" command. Wmctrl is not installed in Ubuntu by default, so: $ sudo apt-get install wmctrl.

"wmctrl -l" will list all the windows currently managed by the x11 window manager, and the very first column will be window id. The Id is a number, wmctrl prints it in hex. The format used is to prefix hex number with "0x", but Object Pascal uses '$' as default hex prefix. So, for example, "0xfefe" in Object Pascal would be:
Code: Pascal  [Select][+][-]
  1. const
  2.   C_HEX_VALUE = $fefe; //0xfefe
  3.  
also, the last column of "wmctrl -l" output is the window's title. So I wrote a function that can return window's id by window's title:
Code: Pascal  [Select][+][-]
  1. (...)
  2. uses
  3.   (...), Process, (...);
  4.  
  5.   Procedure grep(var s: string; const grepstr: string);
  6. implementation
  7.   Procedure grep(var s: string; const grepstr: string);
  8.    var
  9.      SL : TStringList;
  10.    begin
  11.      if(grepstr='')then
  12.        begin
  13.          s := '';
  14.          exit;
  15.        end;
  16.      SL := TStringList.Create;
  17.      try
  18.        SL.Text := s; s := '';
  19.        while(SL.Count>0)do
  20.          begin
  21.            if Pos(grepstr,SL[0])>0 then s := s+SL[0]+sLineBreak;
  22.            SL.Delete(0);
  23.          end;
  24.      finally
  25.        FreeAndNil(SL);
  26.        s := trim(s);
  27.      end;
  28.    end;
  29.  
  30. (...)
  31.  
  32. Procedure TForm1.cmdButtonClick(Sender: TObject);
  33.   var
  34.     WindowId : longint;
  35.     s,c            : string;
  36.     p              : integer;
  37.   begin
  38.      if not Process.RunCommand('wmctrl',['-l'],s)then exit;
  39.      grep(s,'VTE Terminal');
  40.      { because the window I wanted the Id of has that title, you should use whatever applies to your case }
  41.      p := Pos(' ',s);
  42.      if(p>0)
  43.        then s := trim(Copy(s,1,p-1))
  44.        else s := '';
  45.      if Copy(s,1,2)='0x'
  46.        then Delete(s,1,2)
  47.        else exit;
  48.      WindowId := StrToIntDef('$'+s,0);
  49.      if(WindowId<=0)then exit;
  50.      writeln('WindowId='+s);
  51.      while(c<>'')do
  52.        begin
  53.          libXDO.SendKey(WindowId, Copy(c,1,1));
  54.          Delete(c,1,1);
  55.        end;
  56.      libXDO.SendKey(WindowId, 'KP_Enter');}
  57.   end;
  58.  

just remember that this depends on wmctrl being installed on the system, which it is not by default on Ubuntu. That's just one way though, you could use x11 APIs from lazarus, but this way was faster for me.
I once found lazarus units that have functions for listing windows and their Id, so it is definitely possible not to depend on wmctrl, but there it is, as a reference.

kris

  • Jr. Member
  • **
  • Posts: 59
Re: Sendkeys
« Reply #19 on: December 03, 2017, 06:05:32 pm »

bytebites

  • Hero Member
  • *****
  • Posts: 633
Re: Sendkeys
« Reply #20 on: December 03, 2017, 06:19:11 pm »

Also on the wiki about MouseAndKeyInput, under Gtk chapter it reads:
Quote
(...) ALT key pressing is not supported (...)


Code: Pascal  [Select][+][-]
  1.   VK_MENU: Result := XK_VoidSymbol; // alt key crashes app, XK_Alt_R;

Actually  Alt-left works, if you change code in xkeyinput-file:

Code: Pascal  [Select][+][-]
  1. VK_MENU: Result := XK_Alt_L;

kris

  • Jr. Member
  • **
  • Posts: 59
Re: Sendkeys
« Reply #21 on: December 03, 2017, 06:40:44 pm »
very interesting how this topic came back to life after all these years.
Kudos to bytebites for sharing the patch ;)

I could only wish for someone knowledgeable to find this thread and also help me to figure out why the approach using gtk_main_do_event did not work for me.

Zoran

  • Hero Member
  • *****
  • Posts: 1829
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Sendkeys
« Reply #22 on: December 03, 2017, 11:11:22 pm »
A 10 year old excavation. Nice! Assuming you are looking for a way to send keys, I recommend to take a look on the mouseandkeyInput package. It claims to support sending keys.

Or better don't take a look at it, unless you want to license your code under GPL (and you will have to, if you take any piece of code from it).
MouseAndKeyInput package is under GPL, which makes it unusable for many Lazarus users.
So, another solution with more permissive license will be welcomed.

Thaddy

  • Hero Member
  • *****
  • Posts: 14214
  • Probably until I exterminate Putin.
Re: Sendkeys
« Reply #23 on: January 20, 2018, 12:33:15 pm »
Kris, you are responding to an 11 year old thread.

Better to look at the wiki under keupress http://wiki.lazarus.freepascal.org/LCL_Key_Handling

That's cross-platform.
Specialize a type, not a var.

 

TinyPortal © 2005-2018