HotKey unter Ubuntu nutzen

Antworten
wennerer
Beiträge: 507
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

HotKey unter Ubuntu nutzen

Beitrag von wennerer »

Hallo,
wie in einem vorangegangenen Beitrag von mir (Linux, XGrabKey, Fenster minimieren) vielleicht ersichtlich war ich auf der Suche nach einer Möglichkeit einen HotKey unter Ubuntu zu nutzen. Hintergrund ist der, das ich schon länger immer mal wieder an einem kleinen Malprogramm, das ich nur selber nutze, herum bastle. Jetzt möchte ich es um so eine Art Snipping Tool ergänzen um Screenshots einfügen zu können. Auf der Suche nach einer Lösung habe ich recht schnell sehen müssen das das unter Linux nicht ganz so einfach wird. Bei einigen Beiträge hier im Forum wurde auf die Möglichkeit mittels XLib und XGrabKey verwiesen. In diese Richtung habe ich längere Zeit recherchiert. Leider fand ich aber keinerlei funktionierenden Beispielcode unter Pascal, was ich fand waren mehrere Codeschnipsel in C. Ich habe mich da dann mal durchgewühlt und ein Grundgerüst erstellt das zumindest bei mir (Ubuntu 16.04 32Bit) problemlos funktioniert. Ich weiss zwar nicht ob sich ausser mir noch irgend jemand dafür interessiert, da ich aber wirklich nicht sehr viel zu dem Thema (in Pascal) fand möchte ich meine Lösung hier vorstellen.
Wer Tastendruck und Mausbutton in Linux simulieren will benötigt vorab noch das optionale Paket libxtst-dev. Im Terminal mit „sudo apt-get install libxtst-dev“ leicht zu installieren. Im Lazarusordner befinden sich unter components/mouseandkeyinput mehrere Dateien die den Zugriff auf das neu installierte Paket ermöglichen. Wer einen Tastendruck simulieren möchte benötigt KeyInputIntf.pas und xkeyinput.pas. Wer Maustasten oder -bewegung simulieren möchte der benötigt mouseandkeyinput.pas, mouseinputintf.pas und xmouseinput.pas. Also Pfad einbinden oder die Dateien einfach ins Verzeichnis kopieren. Bitte beachtet die Lizenzen, wie gesagt ich nehme es nur für mich selber.
Hier noch meine Erkenntnisquellen:
https://www.lazarusforum.de/
forum.lazarus.freepascal.org/
https://stackoverflow.com/
The Xlib Manual – Christophe Tronche
Auf der Seite von Rheinwerk openbook findet man folgendes: Linux-Unix-Programmierung von Jürgen Wolf. Hier wird in deutsch der Umgang mit Xlib beschrieben.
Kurz noch zu meinem Beispiel.
Es wird ein Fenster geöffnet. Beim Drücken des Buttons „StartHotKeyTest“ wird das Fenster in den Starter minimiert. Man kann jetzt in andere Dokumente wechseln bis zu der Seite wo der Screenshot gemacht werden soll. Jetzt soll Shift+STRG+P gedrückt werden. Das Fenster öffnet sich wieder und es wird der Screenshot angezeigt. Der Übersichtlichkeit halber habe ich hier alles weg gelassen was nicht nötig war. Sollte es Jemand unter 64bit probieren würde mich interessieren ob es funktioniert. Und zuletzt natürlich probieren auf eigene Gefahr!
Viele Grüße Bernd

Code: Alles auswählen

 
unit unit_TestHotkey;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  LCLIntf, LCLType, ExtCtrls,
  //hier die benötigten X_units
  x,Xlib,KeySym,XKBlib,XKB, XKeyInput;
  {folgende Dateien findet man unter Lazarus/components/mouseandkeyinput:
   KeyInputIntf.pas,xkeyinput.pas für Tastendruck simulieren}

 
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Button1 : TButton;
    Frame1  : TFrame;
    Bmp1    : TBitmap;
    Timer1  : TTimer;
    procedure FormCreate(Sender: TObject);
    procedure StartHotKeyTest(Sender: TObject);
    procedure Frame1Paint(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
 
 
var
  Form1          : TForm1;
  win            : hwnd;
implementation
 
{$R *.lfm}
 
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxx  hotkey  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx//
function  HotKey:TBitmap;
var
  dpy          : PDisplay;
  root         : TWindow;
  ev           : TXEvent;
  modifiers    : LongInt;
  keycode      : dword;
  grab_window  : TWindow;
  owner_events : LongInt;
  pointer_mode : Integer;
  keyboard_mode: Integer;
  HK           : boolean;
  ScreenDC     : HDC;
  stateC       : cardinal;
  stateN       : cardinal;
  is_press     : Boolean;
  mywin        : TWindow;
  scr          : integer;
  xwa          : TXWindowAttributes ;
begin
 dpy     := XOpenDisplay(nil)//Verbindung zum X Server herstellen
 if dpy = nil then showmessage('Fehler beim Verbinden mit dem X Server');
 root    := DefaultRootWindow(dpy); //gibt den gesamten Bildschirm zurück
 
 //Hier wird geprüft ob Caps- und Numlock aktiv ist, falls aktiv wird ausgeschaltet
 (XkbGetIndicatorState (dpy, XkbUseCoreKbd, @stateC));   //überprüft u.a. Capslock-,Numlockstatus
 if (stateC and 1) = 1 then                              //state and 1 liefert Capslock,1 bedeutet gedrückt
  begin
   keycode        := XKeysymToKeycode(dpy,XK_Caps_Lock); //liefert den keycode einer Taste
   is_press := true;
   XTestFakeKeyEvent(dpy, keycode, is_press,0);          //Taste drücken
   XFlush(dpy);                                          //an X Server senden
   is_press := FALSE;
   XTestFakeKeyEvent(dpy, keycode, is_press,0);          //Taste loslassen
   XFlush(dpy);
   end;
 
 (XkbGetIndicatorState (dpy,XkbUseCoreKbd, @stateN));
 if (stateN and 2) = 2 then                             //state and 2 liefert Numlock,2 bedeutet gedrückt
  begin
   keycode         := XKeysymToKeycode(dpy,XK_Num_Lock);
   is_press := true;
   XTestFakeKeyEvent(dpy, keycode, is_press,0);
   XFlush(dpy);
   is_press := FALSE;
   XTestFakeKeyEvent(dpy, keycode, is_press,0);
   XFlush(dpy);
  end;
 
 //Hier wird der Hotkey gesetzt, bei mir Strg+Shift+P
 HK              := false;
 modifiers       := ControlMask or ShiftMask;   //Taste Strg, Taste Shift
 keycode         := XKeysymToKeycode(dpy,XK_P); //Taste P
 grab_window     :=  root;
 owner_events    := 0;
 pointer_mode    := GrabModeAsync;
 keyboard_mode   := GrabModeAsync;
 
 XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode,
             keyboard_mode);
 
 
 XSelectInput(dpy, root, KeyPressMask);    //hier wird Tastendruck als event festgelegt
 //ab hier wird auf den Hotkey gewartet
 while (HK=false) do
  begin
   XNextEvent(dpy,@ev);
    case ev._type of KeyPress:
     begin
      ScreenDC := GetDC(0);               //Hier kann man das hinschreiben was getan werden soll
      HotKey.LoadFromDevice(ScreenDC);    //ich mach einen Screenshot
      ReleaseDC(0,ScreenDC);
      XUngrabKey(dpy,keycode,modifiers,grab_window)//Hotkey löschen
      HK:=true;
     end;
    end;
   end;
  //Hier wird ein X Fenster erzeugt
  scr   := XDefaultScreen (dpy);
  mywin := XCreateSimpleWindow (dpy,Root,0,0,screen.Width,screen.Height,5,
  BlackPixel(dpy, scr),WhitePixel (dpy,scr) );
 
  XMapWindow (dpy,mywin);    //Fenster zeichnen
 
  while (xwa.map_state <> IsViewable) do    //hier wird gewartet bis Fenster sichtbar
   begin
     XGetWindowAttributes(dpy,mywin, @xwa);
     sleep(10);
   end;
 
  XDestroyWindow(dpy,mywin);
  XDestroyWindow(dpy,root);
  XCloseDisplay(dpy);
  ShowWindow(win,SW_normal);
  Form1.Show;
end;
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxx  hotkey Ende xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx//
 
{ TForm1 }
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Caption    :='HotKeyTest --- Drücke Strg+Shift+P';
  Form1.Top        :=  50;
  Form1.Left       :=(Screen.DesktopWidth div 2)-400;
  Form1.Height     := 550;
  Form1.Width      := 800;
  Form1.Color      := clScrollBar;
 
 
  Button1          := TButton.Create(nil);
  Button1.Parent   := Form1;
  Button1.Top      :=  25;
  Button1.Left     :=  25;
  Button1.Height   :=  25;
  Button1.Width    := 150;
  Button1.Caption  := 'Start HotKeyTest';
  Button1.OnClick  := @StartHotKeyTest;
 
  Frame1           := TFrame.Create(nil);
  Frame1.Parent    := Form1;
  Frame1.Top       :=  75;
  Frame1.Left      :=  50;
  Frame1.Height    := 450;
  Frame1.Width     := 700;
  Frame1.Color     := clwhite;
  Frame1.OnPaint   := @Frame1Paint;
 
  Bmp1             := TBitmap.Create;
  Bmp1.SetSize(700,450);
  Bmp1.Canvas.Rectangle(0,0,700,450);
 
  Timer1            := TTimer.Create(nil);
  Timer1.Enabled    := false;
  Timer1.Interval   := 1000;
  Timer1.OnTimer    := @Timer1Timer;
 
end;
 
procedure TForm1.StartHotKeyTest(Sender: TObject);
begin
 win:=form1.Handle;
 ShowWindow(win,SW_minimize);
 Form1.Timer1.Enabled:=true;
end;
 
procedure TForm1.Frame1Paint(Sender: TObject);
begin
  Frame1.Canvas.Draw(0,0,Bmp1);
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Form1.Timer1.Enabled:=false;
  Bmp1.Canvas.StretchDraw(Rect(0,0,700,450),HotKey);
  Frame1.Invalidate;
end;
 
end.
 
 


Und hier noch ein kleines Beispiel zum Thema Maus:

Code: Alles auswählen

 
unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,StdCtrls,LCLIntf,ExtCtrls,
  x,Xlib,xmouseinput;
 
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Button1 : TButton;
    Button2 : TButton;
    Timer1  : TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Timer(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
 
var
  Form1        : TForm1;
  Handel       : longint;
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.FormCreate(Sender: TObject);
begin
 Form1.Width     := 360;
 Form1.Height    := 100;
 Form1.Caption   := 'MausKlick simulieren';
 
 
 Button1         := TButton.Create(self);
 Button1.Parent  := Form1;
 Button1.Left    := 20;
 Button1.Top     := 20;
 Button1.Width   := 150;
 Button1.Height  := 25;
 Button1.Caption := 'Start MausClick';
 Button1.OnClick := @Button1Click;
 
 Button2         := TButton.Create(self);
 Button2.Parent  := Form1;
 Button2.Left    := 190;
 Button2.Top     := 20;
 Button2.Width   := 150;
 Button2.Height  := 25;
 Button2.Caption := 'MausClick';
 Button2.OnClick := @Button2Click;
 
 Timer1         := TTimer.Create(self);
 Timer1.Interval:= 2000;
 Timer1.Enabled := false;
 Timer1.OnTimer := @Timer;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  dpy      : PDisplay;
  root     : TWindow;
  Control: TControl;
  P:TPoint;
begin
 
 dpy     := XOpenDisplay(nil);
 root:=XDefaultScreen(dpy);
 
 Control:=Button2;
 P:=Control.ClientToScreen(Point(5, 5));
 
 XTestFakeMotionEvent(dpy,root,p.X,p.Y,0);
 
 XTestFakeButtonEvent(Dpy,1, True, 0);
 XFlush(dpy);
 XTestFakeButtonEvent(Dpy,1, false, 0);
 XFlush(dpy);
 XCloseDisplay(dpy);
 
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
 showmessage('Ich wurde angeklickt');
end;
 
procedure TForm1.Timer(Sender: TObject);
var
  dpy      : PDisplay;
  root     : TWindow;
  Control  : TControl;
  P        : TPoint;
begin
 Timer1.Enabled:=false;
 dpy     := XOpenDisplay(nil);
 root:=XDefaultScreen(dpy);
 
 Control:=Form1;
 P:=Control.ClientToScreen(Point(5, 5));
 
 XTestFakeMotionEvent(dpy,root,p.X,p.Y,0);
 
 XTestFakeButtonEvent(Dpy,1, True, 0);
 XFlush(dpy);
 XTestFakeButtonEvent(Dpy,1, false, 0);
 XFlush(dpy);
 XCloseDisplay(dpy);
 showmessage('Ende');
end;
 
end.
 
 

Antworten