mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-06 17:05:34 +00:00
Now builds from a clean repo: developer/src/build.sh configure build test publish * Splits kmbrowserhost into kmdbrowserhost for Developer; this means that Developer Browser Host now inherits the Developer settings rather than the Keyman for Windows settings, and simplifies distribution and management. The only difference between the two is in the startup code so this seems like a good split. * Cleanup of various build scripts and dependencies.
456 lines
14 KiB
ObjectPascal
456 lines
14 KiB
ObjectPascal
unit KeymanPaths;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils;
|
|
|
|
type
|
|
EKeymanPath = class(Exception);
|
|
|
|
TKeymanPaths = class
|
|
private
|
|
const S_CEF_DebugPath = 'Debug_CEFPath';
|
|
const S_CEF_EnvVar = 'KEYMAN_CEF4DELPHI_ROOT';
|
|
const S_CEF_SubFolder = 'cef\';
|
|
const S_CEF_LibCef = 'libcef.dll';
|
|
const S_CEF_SubProcess = 'kmbrowserhost.exe';
|
|
const S_CEF_SubProcess_Developer = 'kmdbrowserhost.exe';
|
|
const S_CustomisationFilename = 'desktop_pro.pxx';
|
|
public
|
|
const S_KMShell = 'kmshell.exe';
|
|
const S_TSysInfoExe = 'tsysinfo.exe';
|
|
const S_KeymanExe = 'keyman.exe';
|
|
const S_CfgIcon = 'cfgicon.ico';
|
|
const S_AppIcon = 'appicon.ico';
|
|
const S_KeymanMenuTitleImage = 'menutitle.png';
|
|
const S_FallbackKeyboardPath = 'Keyboards\';
|
|
const S__Package = '_Package\';
|
|
const S_MCompileExe = 'mcompile.exe';
|
|
class function ErrorLogPath(const app: string = ''): string; static;
|
|
class function KeymanHelpPath(const HelpFile: string): string; static;
|
|
class function KeymanDesktopInstallPath(const filename: string = ''): string; static;
|
|
class function KeymanEngineInstallPath(const filename: string = ''): string; static;
|
|
class function KeymanDesktopInstallDir: string; static;
|
|
class function KeymanEngineInstallDir: string; static;
|
|
class function KeyboardsInstallPath(const filename: string = ''): string; static;
|
|
class function KeyboardsInstallDir: string; static;
|
|
class function KeymanConfigStaticHttpFilesPath(const filename: string = ''): string; static;
|
|
class function KeymanCustomisationPath: string; static;
|
|
class function KeymanCoreLibraryPath(const Filename: string): string; static;
|
|
class function CEFPath: string; static; // Chromium Embedded Framework
|
|
class function CEFDataPath(const mode: string): string; static;
|
|
class function CEFSubprocessPath(IsDeveloper: Boolean): string; static;
|
|
|
|
class function RunningFromSource(var keyman_root: string): Boolean; static;
|
|
end;
|
|
|
|
function GetFolderPath(csidl: Integer): string;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Winapi.Windows,
|
|
Winapi.ActiveX,
|
|
Winapi.ShlObj,
|
|
System.Classes,
|
|
System.Win.Registry,
|
|
|
|
DebugPaths,
|
|
RegistryKeys;
|
|
|
|
|
|
// Duplicated from utilsystem to avoid importing other units
|
|
function GetFolderPath(csidl: Integer): string;
|
|
var
|
|
buf: array[0..260] of Char;
|
|
idl: PItemIDList;
|
|
begin
|
|
Result := '';
|
|
if SUCCEEDED(SHGetFolderLocation(0, csidl, 0, 0, idl)) then
|
|
begin
|
|
if SHGetPathFromIDList(idl, buf) then
|
|
Result := buf;
|
|
ILFree(idl);
|
|
end;
|
|
|
|
if (Result = '') and (csidl = CSIDL_PROGRAM_FILES) then
|
|
with TRegistry.Create do // I2890
|
|
try
|
|
RootKey := HKEY_LOCAL_MACHINE;
|
|
if not OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion') then // I2890
|
|
RaiseLastOSError(LastError);
|
|
Result := ReadString('ProgramFilesDir');
|
|
finally
|
|
Free;
|
|
end;
|
|
if Result <> '' then
|
|
if Result[Length(Result)] <> '\' then Result := Result + '\';
|
|
end;
|
|
|
|
|
|
class function TKeymanPaths.KeymanDesktopInstallDir: string;
|
|
begin
|
|
Result := ExtractFileDir(KeymanDesktopInstallPath);
|
|
end;
|
|
|
|
type
|
|
TRegSzHelper = class helper for TRegistry
|
|
function ReadMultiSz(const name: string; strings: TStrings): Boolean;
|
|
end;
|
|
|
|
function TRegSzHelper.ReadMultiSz(const name: string; Strings: TStrings): boolean;
|
|
var
|
|
iSizeInWChar, iSizeInByte: integer;
|
|
Buffer: array of WChar;
|
|
p, EndBuffer: PWChar;
|
|
begin
|
|
iSizeInByte := GetDataSize(name);
|
|
iSizeInWChar := iSizeInByte div SizeOf(WChar);
|
|
if iSizeInByte > 0 then
|
|
begin
|
|
SetLength(Buffer, iSizeInWChar);
|
|
iSizeInWChar := ReadBinaryData(name, Buffer[0], iSizeInByte) div sizeof(WChar);
|
|
p := @Buffer[0];
|
|
EndBuffer := p;
|
|
Inc(EndBuffer, iSizeInWChar);
|
|
while p < EndBuffer do
|
|
begin
|
|
Strings.Append(p);
|
|
p := StrScan(p, #0);
|
|
Inc(p);
|
|
end;
|
|
Result := True;
|
|
end
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
class function TKeymanPaths.KeymanDesktopInstallPath(const filename: string = ''): string;
|
|
var
|
|
s: TStrings;
|
|
begin
|
|
//
|
|
// If the current executable is kmshell.exe, then return the folder it is in (unless
|
|
// a debug path overrides it).
|
|
//
|
|
// Otherwise, Keyman takes precedence if installed. If it is not installed,
|
|
// check the installed OEM product registry and the first installed OEM product is
|
|
// used. Currently, I assess that the likelihood of two OEM products being installed
|
|
// without Keyman being installed is low, and we can live with the first one taking
|
|
// precedence.
|
|
//
|
|
// Finally, if no OEM product can be found, then abort.
|
|
//
|
|
|
|
Result := '';
|
|
|
|
if SameText(ExtractFileName(ParamStr(0)), TKeymanPaths.S_KMShell) then
|
|
begin
|
|
// Just for kmshell.exe, use the current folder
|
|
Result := ExtractFilePath(ParamStr(0));
|
|
end;
|
|
|
|
if Result = '' then
|
|
begin
|
|
// If Keyman is installed, then use its install path.
|
|
with TRegistry.Create do // I2890
|
|
try
|
|
RootKey := HKEY_LOCAL_MACHINE;
|
|
if OpenKeyReadOnly(SRegKey_KeymanDesktop_LM) and ValueExists(SRegValue_RootPath) then
|
|
Result := ReadString(SRegValue_RootPath);
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
if Result = '' then
|
|
begin
|
|
// Look in the registry for any installed OEM product; this is a multistring,
|
|
// but we only want the first value
|
|
with TRegistry.Create do
|
|
try
|
|
RootKey := HKEY_LOCAL_MACHINE;
|
|
if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Engine_OEMProductPath) then
|
|
begin
|
|
s := TStringList.Create;
|
|
try
|
|
ReadMultiSz(SRegValue_Engine_OEMProductPath, s);
|
|
if s.Count > 0 then
|
|
Result := s[0];
|
|
finally
|
|
s.Free;
|
|
end;
|
|
end;
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
Result := GetDebugPath('KeymanDesktop', Result);
|
|
|
|
if Result = '' then
|
|
raise EKeymanPath.Create('Unable to find the Keyman directory. You should reinstall the product.');
|
|
|
|
Result := IncludeTrailingPathDelimiter(Result) + filename;
|
|
end;
|
|
|
|
class function TKeymanPaths.KeymanEngineInstallDir: string;
|
|
begin
|
|
Result := ExtractFileDir(KeymanEngineInstallPath);
|
|
end;
|
|
|
|
class function TKeymanPaths.KeymanEngineInstallPath(const filename: string = ''): string;
|
|
begin
|
|
with TRegistry.Create do // I2890
|
|
try
|
|
RootKey := HKEY_LOCAL_MACHINE;
|
|
if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_RootPath) then
|
|
Result := ReadString(SRegValue_RootPath);
|
|
finally
|
|
Free;
|
|
end;
|
|
|
|
Result := GetDebugPath('KeymanEngine', Result);
|
|
|
|
if Result = '' then
|
|
raise EKeymanPath.Create('Unable to find the Keyman Engine directory. You should reinstall the product.');
|
|
|
|
Result := IncludeTrailingPathDelimiter(Result) + filename;
|
|
end;
|
|
|
|
class function TKeymanPaths.CEFPath: string;
|
|
|
|
function AppendSlash(const path: string): string;
|
|
begin
|
|
Result := path;
|
|
if Result <> '' then
|
|
Result := IncludeTrailingPathDelimiter(path);
|
|
end;
|
|
|
|
function IsValidPath(const path: string): Boolean;
|
|
begin
|
|
Result := (path <> '') and FileExists(path + S_CEF_LibCef);
|
|
end;
|
|
|
|
begin
|
|
// cef\ subfolder of executable path
|
|
Result := ExtractFilePath(ParamStr(0)) + S_CEF_SubFolder;
|
|
if IsValidPath(Result) then
|
|
Exit;
|
|
|
|
// Debug_CEFPath registry setting
|
|
Result := AppendSlash(GetDebugPath(S_CEF_DebugPath, ''));
|
|
if IsValidPath(Result) then
|
|
Exit;
|
|
|
|
// KEYMAN_CEF4DELPHI_ROOT environment variable
|
|
Result := AppendSlash(GetEnvironmentVariable(S_CEF_EnvVar));
|
|
if IsValidPath(Result) then
|
|
Exit;
|
|
|
|
// Same folder as executable
|
|
Result := ExtractFilePath(ParamStr(0));
|
|
if IsValidPath(Result) then
|
|
Exit;
|
|
|
|
// Keyman Desktop installation folder + cef\
|
|
try
|
|
Result := KeymanDesktopInstallPath+S_CEF_SubFolder
|
|
except
|
|
on E:EKeymanPath do
|
|
Result := '';
|
|
end;
|
|
if IsValidPath(Result) then
|
|
Exit;
|
|
|
|
// Failed, could not find libcef.dll
|
|
Result := '';
|
|
end;
|
|
|
|
class function TKeymanPaths.CEFSubprocessPath(IsDeveloper: Boolean): string;
|
|
var
|
|
keyman_root: string;
|
|
begin
|
|
// Same folder as executable
|
|
|
|
// TODO: make this a little cleaner by passing in expected subprocess name
|
|
if IsDeveloper
|
|
then Result := ExtractFilePath(ParamStr(0)) + S_CEF_SubProcess_Developer
|
|
else Result := ExtractFilePath(ParamStr(0)) + S_CEF_SubProcess;
|
|
|
|
if FileExists(Result) then Exit;
|
|
|
|
// On developer machines, if we are running within the source repo, then use
|
|
// those paths
|
|
if TKeymanPaths.RunningFromSource(keyman_root) then
|
|
begin
|
|
// Source repo, bin folder
|
|
Result := keyman_root + 'windows\bin\desktop\' + S_CEF_SubProcess;
|
|
if FileExists(Result) then Exit;
|
|
|
|
// Source repo, source folder
|
|
Result := keyman_root + 'windows\bin\desktop\kmbrowserhost\win32\debug\' + S_CEF_SubProcess;
|
|
if FileExists(Result) then Exit;
|
|
|
|
Result := keyman_root + 'windows\bin\desktop\kmbrowserhost\win32\release\' + S_CEF_SubProcess;
|
|
if FileExists(Result) then Exit;
|
|
end;
|
|
|
|
// Check final install location - in Keyman for Windows install folder
|
|
try
|
|
Result := KeymanDesktopInstallPath(S_CEF_SubProcess);
|
|
except
|
|
on E:EKeymanPath do
|
|
Result := '';
|
|
end;
|
|
if (Result <> '') and FileExists(Result) then Exit;
|
|
|
|
Result := '';
|
|
end;
|
|
|
|
class function TKeymanPaths.CEFDataPath(const mode: string): string;
|
|
begin
|
|
Result := GetFolderPath(CSIDL_APPDATA) + SFolderCEFBrowserData + '\' + mode;
|
|
end;
|
|
|
|
class function TKeymanPaths.KeyboardsInstallDir: string;
|
|
begin
|
|
Result := ExcludeTrailingPathDelimiter(KeyboardsInstallPath);
|
|
end;
|
|
|
|
class function TKeymanPaths.KeyboardsInstallPath(const filename: string): string;
|
|
begin
|
|
with TRegistry.Create do // I2890
|
|
try
|
|
// TODO: We only want to use %CommonAppData%\Keyman now for all keyboards
|
|
// So we should eliminate use of RootKeyboardAdminPath and S_FallbackKeyboardPath
|
|
RootKey := HKEY_LOCAL_MACHINE;
|
|
if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_RootKeyboardAdminPath) then
|
|
begin
|
|
Result := ReadString(SRegValue_RootKeyboardAdminPath)
|
|
end
|
|
else
|
|
begin
|
|
Result := GetFolderPath(CSIDL_COMMON_APPDATA);
|
|
if Result = '' then
|
|
Result := TKeymanPaths.KeymanEngineInstallPath(TKeymanPaths.S_FallbackKeyboardPath)
|
|
else
|
|
Result := Result + SFolderKeymanKeyboard;
|
|
end;
|
|
finally
|
|
Free;
|
|
end;
|
|
|
|
Result := GetDebugPath('Keyboards', Result, True);
|
|
if Result = '' then
|
|
raise EKeymanPath.Create('Unable to find the Keyboards directory. You should reinstall the product.');
|
|
|
|
Result := Result + Filename;
|
|
end;
|
|
|
|
class function TKeymanPaths.ErrorLogPath(const app: string): string;
|
|
begin
|
|
Result := GetFolderPath(CSIDL_LOCAL_APPDATA) + SFolderKeymanEngineDiag + '\';
|
|
ForceDirectories(Result); // I2768
|
|
if app <> '' then
|
|
Result := Result + app + '-' + IntToStr(GetCurrentProcessId) + '.log';
|
|
end;
|
|
|
|
class function TKeymanPaths.KeymanConfigStaticHttpFilesPath(const filename: string): string;
|
|
var
|
|
keyman_root: string;
|
|
begin
|
|
// Look up KEYMAN_ROOT development variable -- if found and executable
|
|
// within that path then use that as source path
|
|
if TKeymanPaths.RunningFromSource(keyman_root) then
|
|
begin
|
|
Exit(keyman_root + 'windows\src\desktop\kmshell\xml\' + filename);
|
|
end;
|
|
|
|
Result := GetDebugPath('KeymanConfigStaticHttpFilesPath', '');
|
|
if Result = '' then
|
|
begin
|
|
Result := ExtractFilePath(ParamStr(0));
|
|
|
|
// The xml files may be in the same folder as the executable
|
|
// for some 3rd party distributions of Keyman files.
|
|
if FileExists(Result + 'xml\strings.xml') then
|
|
Result := Result + 'xml\';
|
|
end;
|
|
|
|
Result := Result + filename;
|
|
end;
|
|
|
|
class function TKeymanPaths.KeymanCustomisationPath: string;
|
|
var
|
|
keyman_root: string;
|
|
begin
|
|
// On developer machines, if we are running within the source repo, then use
|
|
// those paths
|
|
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT');
|
|
if (keyman_root <> '') and SameText(keyman_root, ParamStr(0).Substring(0, keyman_root.Length)) then
|
|
begin
|
|
// Source repo, bin folder
|
|
Result := IncludeTrailingPathDelimiter(keyman_root) + 'windows\src\desktop\branding';
|
|
if DirectoryExists(Result) then
|
|
Exit(Result + '\' + S_CustomisationFilename);
|
|
end;
|
|
|
|
Result := TKeymanPaths.KeymanDesktopInstallPath(S_CustomisationFilename);
|
|
end;
|
|
|
|
class function TKeymanPaths.KeymanHelpPath(const HelpFile: string): string;
|
|
var
|
|
keyman_root: string;
|
|
begin
|
|
// On developer machines, if we are running within the source repo, then use
|
|
// those paths
|
|
if TKeymanPaths.RunningFromSource(keyman_root) then
|
|
begin
|
|
// Source repo, bin folder
|
|
Result := keyman_root + 'windows\bin\desktop\' + HelpFile;
|
|
if FileExists(Result) then Exit;
|
|
end;
|
|
|
|
// Same folder as executable
|
|
Result := ExtractFilePath(ParamStr(0)) + HelpFile;
|
|
if FileExists(Result) then Exit;
|
|
|
|
Result := TKeymanPaths.KeymanDesktopInstallPath(HelpFile);
|
|
if FileExists(Result) then Exit;
|
|
|
|
Result := '';
|
|
end;
|
|
|
|
class function TKeymanPaths.RunningFromSource(var keyman_root: string): Boolean;
|
|
begin
|
|
// On developer machines, if we are running within the source repo, then use
|
|
// those paths
|
|
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT');
|
|
if keyman_root <> '' then
|
|
keyman_root := IncludeTrailingPathDelimiter(keyman_root);
|
|
Result := (keyman_root <> '') and SameText(keyman_root, ParamStr(0).Substring(0, keyman_root.Length));
|
|
end;
|
|
|
|
class function TKeymanPaths.KeymanCoreLibraryPath(const Filename: string): string;
|
|
var
|
|
keyman_root: string;
|
|
begin
|
|
// Look up KEYMAN_ROOT development variable -- if found and executable
|
|
// within that path then use that as source path
|
|
if TKeymanPaths.RunningFromSource(keyman_root) then
|
|
begin
|
|
Exit(keyman_root + 'core\build\x86\debug\src\' + Filename);
|
|
end;
|
|
|
|
Result := GetDebugPath('KeymanCoreLibraryPath', '');
|
|
if Result = '' then
|
|
begin
|
|
Result := ExtractFilePath(ParamStr(0));
|
|
end;
|
|
|
|
Result := IncludeTrailingPathDelimiter(Result) + Filename;
|
|
end;
|
|
|
|
end.
|