unit form_mousepos;

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

{$WARN 5023 off : Unit "$1" not used in $2}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 {$IFDEF WINDOWS} shellapi, {$ENDIF}
 sysutils, classes, math,
 LCLIntf, LCLType, LMessages,
 Graphics, Controls, Forms, Dialogs,
 ExtCtrls, StdCtrls, IniFiles,
 _crw_alloc, _crw_wmctrl, _crw_spcfld,
 _crw_sesman;

type
  TFormMousePos = class(TForm)
    PanelMousePos: TPanel;
    LabelMousePosX: TLabel;
    LabelMousePosY: TLabel;
    LabelMousePosW: TLabel;
    LabelMousePosH: TLabel;
    TimerMousePos: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure TimerMousePosTimer(Sender: TObject);
    procedure PanelMousePosClick(Sender: TObject);
  private
    { Private declarations }
    IniFileName : LongString;
    procedure LoadIniFile;
    procedure SaveIniFile;
    function  FindIniFileName:LongString;
    procedure UpdateSize;
    procedure UpdateHint;
    procedure UpdateCurs;
    procedure HookMouse;
    procedure UnhookMouse;
  public
    { Public declarations }
  end;

var
 FormMousePos: TFormMousePos;

function FindAppPid(WinClass,AppTitle:LongString):TPid;
function FindAppActivate(WinClass,AppTitle:LongString; Activate:Boolean=true):Boolean;

function MousePosWndClass:LongString;
function MousePosWndTitle:LongString;

implementation

{$R *.lfm}

const
 PtMono  = 'PT Mono';
 _CTRL_  : Boolean = false;
 DragOn  : Boolean = false;
 DragPos : TPoint  = ( x:0; y:0 );

 ///////////////////
 // Utility routines
 ///////////////////

function MousePosWndClass:LongString;
begin
 Result:='';
 if IsWindows then Result:='Window';
 if IsUnix then Result:='mousepos.Mousepos';
end;

function MousePosWndTitle:LongString;
begin
 Result:='MousePos';
end;

function FindAppPid(WinClass,AppTitle:LongString):TPid;
var wnd:HWND;
begin
 Result:=0;
 wnd:=wmctrl.FindWindow(0,WinClass,AppTitle);
 if wmctrl.IsWindow(wnd) then Result:=wmctrl.WindowPid(wnd);
end;

function FindAppActivate(WinClass,AppTitle:LongString; Activate:Boolean=true):Boolean;
var wnd:HWND; ad:Integer;
begin
 wnd:=wmctrl.FindWindow(0,WinClass,AppTitle);
 Result:=wmctrl.IsWindow(wnd);
 if Activate and Result then begin
  if IsUnix then begin
   ad:=wmctrl.ActiveDesktop;
   wmctrl.SetWindowDesktop(wnd,ad);
  end;
  wmctrl.ActivateWindow(wnd);
 end;
end;

 ///////////////////////////////
 // TFormMousePos implementation
 ///////////////////////////////

procedure TFormMousePos.FormCreate(Sender: TObject);
begin
 inherited;
 Caption:=MousePosWndTitle;
 Application.Title:=MousePosWndTitle;
 if (Screen.Fonts.IndexOf(PtMono)>=0) then Font.Name:=PtMono;
 UpdateSize; UpdateHint; UpdateCurs;
 IniFileName:=FindIniFileName;
 LoadIniFile;
 HookMouse;
end;

procedure TFormMousePos.FormDestroy(Sender: TObject);
begin
 UnhookMouse;
 SaveIniFile;
 IniFileName:='';
 inherited;
end;

procedure TFormMousePos.UpdateSize;
const space1=0; space2=2;
var dw,dh:Integer;
begin
 dw:=Width-PanelMousePos.ClientWidth;
 dh:=Height-PanelMousePos.ClientHeight;
 LabelMousePosY.Top:=LabelMousePosX.BoundsRect.Bottom+space1;
 LabelMousePosW.Top:=LabelMousePosY.BoundsRect.Bottom+space2;
 LabelMousePosH.Top:=LabelMousePosW.BoundsRect.Bottom+space1;
 Width:=dw+LabelMousePosX.Width+24;
 Height:=dh+LabelMousePosH.BoundsRect.Bottom+8;
end;

procedure TFormMousePos.UpdateHint;
begin
 LabelMousePosX.Hint:='Mouse X position on screen.';
 LabelMousePosY.Hint:='Mouse Y position on screen.';
 LabelMousePosW.Hint:='Mouse selection Width.';
 LabelMousePosH.Hint:='Mouse selection Height.';
end;

procedure TFormMousePos.UpdateCurs;
begin
 PanelMousePos.Cursor:=crHelp;
 LabelMousePosX.Cursor:=crArrow;
 LabelMousePosY.Cursor:=crArrow;
 LabelMousePosW.Cursor:=crArrow;
 LabelMousePosH.Cursor:=crArrow;
end;

procedure MouseDrag(mode:Integer);
begin
 if (mode<>0) then begin
  DragPos:=Mouse.CursorPos;
  DragOn:=true;
 end else begin
  DragPos:=Point(0,0);
  DragOn:=false;
 end;
end;

procedure TFormMousePos.HookMouse;
begin
 MouseDrag(0);
end;

procedure TFormMousePos.UnhookMouse;
begin
 MouseDrag(0);
end;

procedure TFormMousePos.TimerMousePosTimer(Sender: TObject);
var p:TPoint; ctrl:Boolean; trap:Boolean;
begin
 p:=Mouse.CursorPos;
 LabelMousePosX.Caption:=Format('X: %5d',[p.x]);
 LabelMousePosY.Caption:=Format('Y: %5d',[p.y]);
 if DragOn then begin
  p.x:=p.x-DragPos.x; p.y:=p.y-DragPos.y;
  LabelMousePosW.Caption:=Format('W: %5d',[p.x]);
  LabelMousePosH.Caption:=Format('H: %5d',[p.y]);
  LabelMousePosW.Enabled:=true;
  LabelMousePosH.Enabled:=true;
 end else begin
  LabelMousePosW.Caption:=Format('W: %5s',['-']);
  LabelMousePosH.Caption:=Format('H: %5s',['-']);
  LabelMousePosW.Enabled:=false;
  LabelMousePosH.Enabled:=false;
 end;
 ctrl:=(ssCtrl in GetKeyShiftState);
 trap:=(ctrl<>_Ctrl_);
 if trap then _Ctrl_:=ctrl;
 if trap then MouseDrag(IfThen(DragOn,0,1));
end;

procedure TFormMousePos.LoadIniFile;
var IniFile:TIniFile; sx,sy:LongString;
begin
 if (IniFileName='') then Exit;
 IniFile:=TIniFile.Create(IniFileName);
 try
  sx:=IniFile.ReadString(Name,'Left',IntToStr(Left));
  sy:=IniFile.ReadString(Name,'Top',IntToStr(Top));
  Left:=Max(0,Min(Screen.DesktopWidth-Width,StrToIntDef(sx,Left)));
  Top:=Max(0,Min(Screen.DesktopHeight-Height,StrToIntDef(sy,Top)));
 finally
  IniFile.Free;
 end;
end;

procedure TFormMousePos.SaveIniFile;
var IniFile:TIniFile;
begin
 if (IniFileName='') then Exit;
 IniFile:=TIniFile.Create(IniFileName);
 try
  if DirectoryExists(ExtractFilePath(IniFileName))
  or ForceDirectories(ExtractFilePath(IniFileName)) then begin
   IniFile.WriteString(Name,'Left',IntToStr(Left));
   IniFile.WriteString(Name,'Top',IntToStr(Top));
   IniFile.UpdateFile;
  end;
 finally
  IniFile.Free;
 end;
end;

function TFormMousePos.FindIniFileName:LongString;
begin
 Result:=SessionManager.LocalDataFile('mousepos.ini');;
end;

procedure TFormMousePos.PanelMousePosClick(Sender: TObject);
begin
 Application.MessageBox('MousePos tool to view Mouse Position:'+EOL+EOL
                       +' X: horizontal mouse coordinate on screen.'+EOL
                       +' Y: vertical   mouse coordinate on screen.'+EOL
                       +' W: mouse selection width  (with CTRL pressed).'+EOL
                       +' H: mouse selection height (with CTRL pressed).'+EOL+EOL
                       +'On CTRL press: start mouse selection.'+EOL
                       +'On single click over window: show this help.'+EOL+EOL
                       +'Copyright (c) 2022-2024 Alexey Kuryakin daqgroup@mail.ru',
                       'About MousePos',MB_OK);
end;

end.

//////////////
// END OF FILE
//////////////

