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.
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.