unit UnitFocusManager;
//
// Handle all the focus utility routines
// Mainly used to set focus back on the target control or target program
//
interface

uses Classes;

type TFocusManager = class(TObject)
    public
        class procedure SetOn(ForeGround, FocusControl : cardinal);
        class function ForceForeground(h : THandle) : boolean;
        class procedure RemoveFocusFromSelf;
end;

implementation

uses UnitThreadAttach, UnitFrmDebug, Windows, UnitMisc, StrUtils, SysUtils;

class procedure TFocusManager.SetOn(ForeGround, FocusControl : cardinal);
var
    ta : TThreadAttach;
    h2, h3 : THandle;
begin

    ta := TThreadAttach.Create(ForeGround);
    if (ta.IsAttached) then begin
        FrmDebug.AppendLog('Focusing: '+WindowHandleToEXEName(ForeGround));
        Windows.SetForegroundWindow(ForeGround);
        if GetForegroundWindow <> ForeGround then begin
            FrmDebug.AppendLog('focuse failed 1');
        end;

        if (focuscontrol <> 0) then begin
            Windows.SetFocus(focuscontrol);
        end;

        h2 := FocusControl;
        repeat
            h3 := h2;
            h2 := GetParent(h3);
        until h2 = 0;
        FrmDebug.AppendLog('parent: '+WindowHandleToEXEName(h3));

        if GetForegroundWindow <> focuscontrol then begin
            FrmDebug.AppendLog('focuse failed 2');
        end;
    end else begin
        // default case, just try to focus the window
        Windows.SetForegroundWindow(Foreground);
    end;
    ta.free;
end;
class function TFocusManager.ForceForeground(h : THandle) : boolean;
var b : boolean;
    fore : THandle;

    dw : DWORD;
    dwnull : DWORD;
    ta : TThreadAttach;
begin
    result := true;
    fore := GetForegroundWindow;
    if fore = h then EXIT;

    if (fore = 0) then begin
        fore := GetDesktopWindow;
    end;
    ta := TThreadAttach.Create(fore);
    Windows.BringWindowToTop(h);
    Windows.SetForegroundWindow(h);
    myfree(ta);

    fore := GetForegroundWindow;
    result := (fore<>h);
    if (not result) then begin
        // temporarily disable the foreground lock
        dwnull := 0;
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @dw, 0);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(dwnull),SPIF_SENDCHANGE);
        Windows.BringWindowToTop(h);
        SetForegroundWindow(h);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(dw), SPIF_SENDCHANGE);

        result := h = GetForegroundWindow;
    end;
end;
class procedure TFocusManager.RemoveFocusFromSelf;
var name : string;
    h : THandle;
    ta : TThreadAttach;
const selfexe = 'arsclip.exe';
begin
    h := GetForegroundWindow;
    name := WindowHandleToEXEName(h);
    if lowercase(name) = selfexe then begin
        h := UnitMisc.GetNextWindow(h);
    end;
    name := WindowHandleToEXEName(h);
    if lowercase(name) = selfexe then begin
        h := UnitMisc.GetNextWindow(h);
    end;
    name := WindowHandleToEXEName(h);
    if lowercase(name) = selfexe then begin
        h := UnitMisc.GetNextWindow(h);
    end;

    ta := TThreadAttach.Create(GetForegroundWindow);
    Windows.SetForegroundWindow(h);
    myfree(ta);
end;

end.
