Tastatureingaben an aktives Fenster senden

In dieser Linux-Ecke dürfen nur Themen rund um Linux geschrieben werden.
Beiträge, die plattformübergreifend sind, gehören ins 'Allgemein'-Forum.
Benutzeravatar
Programie
Beiträge: 1280
Registriert: 06.08.2005 22:56
Computerausstattung: https://www.sysprofile.de/id160800
Wohnort: Gernsbach
Kontaktdaten:

Tastatureingaben an aktives Fenster senden

Beitrag von Programie »

In meinem aktuellen PB Projekt Keyboard Mapper moechte ich implementieren, dass Tastatureingaben in das aktuell aktive Fenster einer fremden Anwendung gesendet werden. Unter Windows funktioniert das mit keybd_event_() aus der WinAPI. Gibt es unter Linux eine vergleichbare Funktion?

Ich moechte zwei Varianten implementieren: Senden von einfachen Tastatureingaben und Senden eines kompletten Strings (z.B. per Clipboard + Ctrl+V im aktiven Fenster).

Hat jemand eine Idee wie man das in PB unter Linux implementieren kann?
BildBildBildBild
Benutzeravatar
RSBasic
Admin
Beiträge: 8022
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: Tastatureingaben an aktives Fenster senden

Beitrag von RSBasic »

Es gibt gdk_event_put_(). Damit kannst du auch Keycodes simulieren. Leider konnte ich in diesem Forum kein Beispielcode finden.
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
Benutzeravatar
Programie
Beiträge: 1280
Registriert: 06.08.2005 22:56
Computerausstattung: https://www.sysprofile.de/id160800
Wohnort: Gernsbach
Kontaktdaten:

Re: Tastatureingaben an aktives Fenster senden

Beitrag von Programie »

OK, danke. Mal schauen ob ich damit weiter komme.
BildBildBildBild
Omi
Beiträge: 143
Registriert: 25.03.2013 09:59

Re: Tastatureingaben an aktives Fenster senden

Beitrag von Omi »

Hi Programie,

da Linux hier sehr widerborstig ist, schau Dir vielleicht mal wmctrl oder (vielleicht noch geeigneter) xdotool für Linux X11 an.
Kann sein dass Du hier schneller ans Ziel kommst.

Code: Alles auswählen

;Ein paar Links zu xdotool ...
;http://manpages.ubuntu.com/manpages/xenial/man1/xdotool.1.html
;https://www.linux.org/threads/xdotool-keyboard.10528/
;http://xmodulo.com/simulate-key-press-mouse-movement-linux.html

Delay(2000)
;Klicke auf ein Fremdfenster ...
; Debug RunProgram ("xdotool", "getactivewindow key ctrl+Q", "");     schließt (meist) das aktive Fenster
Debug RunProgram ("xdotool", "getactivewindow key ctrl+Down", "");   scrollt (meist) das aktive Fenster nach unten
Gruß, Charly
PureBasic Linux-API-Library: http://www.chabba.de
Benutzeravatar
Programie
Beiträge: 1280
Registriert: 06.08.2005 22:56
Computerausstattung: https://www.sysprofile.de/id160800
Wohnort: Gernsbach
Kontaktdaten:

Re: Tastatureingaben an aktives Fenster senden

Beitrag von Programie »

OK, einen Aufruf von xdotool oder so mit RunProgram() wollte ich verhindern. Da kann man auch gleich alles mit einem Shell Script erschlagen. :mrgreen:

Ich hab da in der Zwischenzeit auch ein bischen mit gdk_event_put() experimentiert. Aber das Event wird wohl nur an die eigenen Fenster gesendet: https://mail.gnome.org/archives/gtk-app ... 00226.html

Hab da auch den Hinweis auf Xlib gefunden und mal damit experimentiert. Allerdings hab ich es noch nicht soweit geschafft, dass irgendein Fenster irgendetwas macht (nichtmal meine eigenen), stattdessen crasht meine Test Anwendung immer mit einem Invalid Memory Access. :cry:
BildBildBildBild
Benutzeravatar
Programie
Beiträge: 1280
Registriert: 06.08.2005 22:56
Computerausstattung: https://www.sysprofile.de/id160800
Wohnort: Gernsbach
Kontaktdaten:

Re: Tastatureingaben an aktives Fenster senden

Beitrag von Programie »

So, habe es jetzt zumindest mal geschafft, dass mein Programm nicht mehr durch einen Invalid Memory Access crashed. Aber es funktioniert einfach nicht. Es kommen keine Tastatureingaben an (auch nicht in den StringGadgets von dem Programm selbst)...

Hier ist mein aktueller Stand:

Code: Alles auswählen

EnableExplicit

ImportC ""
  gdk_screen_get_active_window(*screen)
  gdk_screen_get_default()
  gdk_x11_window_get_xid(*window)
EndImport

ImportC "-lX11"
  XOpenDisplay(*display)
  XSendEvent(display, xWindow, propagate, event_mask, event_send)
  XStringToKeysym(string.p-utf8)
  XFlush(display)
EndImport

Structure XKeyEvent
  type.i       ; KeyPress or KeyRelease
  serial.l     ; # of last request processed by server
  send_event.i ; true if this came from a SendEvent request
  *display     ; Display the event was read from
  window.i     ; ''event'' window it is reported relative to
  root.i       ; root window that the event occurred on
  subwindow.i  ; child window
  time.i       ; milliseconds
  x.i          ; pointer x coordinate in event window
  y.i          ; pointer y coordinate in event window
  x_root.i     ; x coordinate relative to root
  y_root.i     ; y coordinate relative to root
  state.i      ; key or button mask
  keycode.i    ; detail
  same_screen.i; same screen flag
EndStructure

Structure charcodemap
  key.w
  code.a
  symbol.l
  group.i
  modmask.i
  needs_binding.i
EndStructure

#KeyPressMask = 1
#KeyPress = 2
#KeyRelease = 3

Procedure SendKey(*display, *key.charcodemap, modstate)
  Protected *gdkWindow = gdk_screen_get_active_window(gdk_screen_get_default())
  Debug *gdkWindow
  
  Protected xWindow = gdk_x11_window_get_xid(*gdkWindow)
  Debug xWindow
  
  Protected mask = modstate | *key\modmask

  Protected keyEvent.XKeyEvent
  keyEvent\display = *display
  keyEvent\subwindow = 0
  keyEvent\time = 0
  keyEvent\same_screen = #True
  
  keyEvent\window = xWindow
  keyEvent\keycode = *key\code
  keyEvent\state = mask | *key\group << 13
  
  keyEvent\type = #KeyPress
  XSendEvent(*display, keyEvent\window, #True, #KeyPressMask, keyEvent)
  XFlush(*display)
  
  Delay(100)
  
  keyEvent\type = #KeyRelease
  XSendEvent(*display, keyEvent\window, #True, #KeyPressMask, keyEvent)
  XFlush(*display)
EndProcedure

Procedure SendKeyLoop(param)
  Protected *display = XOpenDisplay(0)
  
  Repeat
    Protected symbol = XStringToKeysym("A")
    Debug symbol
    
    Protected key.charcodemap
    key\code = 0
    key\symbol = symbol
    key\needs_binding = 1
    SendKey(*display, key, 0)
    
    Delay(1000)
  ForEver
EndProcedure

If OpenWindow(0, 0, 0, 500, 130, "Test", #PB_Window_ScreenCentered)
  StringGadget(0, 10, 10, 480, 30, "")
  StringGadget(1, 10, 50, 480, 30, "")
  StringGadget(2, 10, 90, 480, 30, "")
  SetActiveGadget(0)
  CreateThread(@SendKeyLoop(), 0)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Einiges davon habe ich mir aus dem Code vom xdotool zusammengebastelt.
BildBildBildBild
Benutzeravatar
Programie
Beiträge: 1280
Registriert: 06.08.2005 22:56
Computerausstattung: https://www.sysprofile.de/id160800
Wohnort: Gernsbach
Kontaktdaten:

Re: Tastatureingaben an aktives Fenster senden

Beitrag von Programie »

Habe es jetzt geschafft Tastatureingaben an das aktive Fenster zu senden. :D

Ganz einfach: mit XTestFakeKeyEvent (so macht es auch xdotool wenn das Zielfenster den Fokus hat)

Code: Alles auswählen

EnableExplicit

ImportC "-lX11"
  XOpenDisplay(*display)
  XStringToKeysym(string.p-utf8)
  XKeysymToKeycode(*display, keysym)
  XFlush(display)
EndImport

ImportC "/usr/lib/x86_64-linux-gnu/libXtst.so.6"
  XTestFakeKeyEvent(display, keycode, is_press, delay)
EndImport

Procedure SendKeyLoop(param)
  Protected *display = XOpenDisplay(0)
  
  Repeat
    Protected symbol = XStringToKeysym("A")
    Protected code = XKeysymToKeycode(*display, symbol)
    Debug symbol
    Debug code
    
    XTestFakeKeyEvent(*display, code, #True, 0)
    XTestFakeKeyEvent(*display, code, #False, 0)
    XFlush(*display)
    
    Delay(1000)
  ForEver
EndProcedure

If OpenWindow(0, 0, 0, 500, 130, "Test", #PB_Window_ScreenCentered)
  StringGadget(0, 10, 10, 480, 30, "")
  StringGadget(1, 10, 50, 480, 30, "")
  StringGadget(2, 10, 90, 480, 30, "")
  SetActiveGadget(0)
  CreateThread(@SendKeyLoop(), 0)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Was mich an dem Code aktuell noch stoert: Der absolute Pfad zur libXtst.so.6. Wenn ich nur den Dateiname ohne Pfad beim Import angebe bekomme ich einen Linker Error (gcc: error: libXtst.so.6: No such file or directory).

Allerdings funktioniert es mit OpenLibrary ohne absoluten Pfad. Eventuell werde ich einfach die Funktion mit einem Prototype + GetFunction ansprechen.
BildBildBildBild
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Tastatureingaben an aktives Fenster senden

Beitrag von ccode_new »

@Programie

Klasse!

Nicht schlecht!
Programie hat geschrieben:Was mich an dem Code aktuell noch stoert: Der absolute Pfad zur libXtst.so.6. Wenn ich nur den Dateiname ohne Pfad beim Import angebe bekomme ich einen Linker Error (gcc: error: libXtst.so.6: No such file or directory).
Ähm! Schon mal:

ImportC "-lXtst"
XTestFakeKeyEvent(display, keycode, is_press, delay)
EndImport

probiert ?
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
Programie
Beiträge: 1280
Registriert: 06.08.2005 22:56
Computerausstattung: https://www.sysprofile.de/id160800
Wohnort: Gernsbach
Kontaktdaten:

Re: Tastatureingaben an aktives Fenster senden

Beitrag von Programie »

ccode_new hat geschrieben:Ähm! Schon mal:

ImportC "-lXtst"
XTestFakeKeyEvent(display, keycode, is_press, delay)
EndImport

probiert ?
Ah... soweit hab ich nicht gedacht. Aber damit funktioniert es, danke!

Was genau macht denn das "-l" beim Import? Das andere mit "-lX11" hab ich auch nur im englischen Forum gefunden.
BildBildBildBild
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Tastatureingaben an aktives Fenster senden

Beitrag von ccode_new »

Das -l ist Präfix für lib.

Ist eine C-Linker-Anweisung für den gcc-Linker (ld).
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Antworten