mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-07 17:36:00 +00:00
125 lines
3 KiB
ObjectPascal
125 lines
3 KiB
ObjectPascal
unit Keyman.System.SharedBuffers;
|
|
|
|
interface
|
|
|
|
uses
|
|
Winapi.Windows;
|
|
|
|
const
|
|
MAX_SELECTKEYBOARDTSF_CIRCULARBUFFER_SIZE = 16;
|
|
GLOBAL_SHAREDBUFFER_FILE_MAPPING_NAME = 'KeymanEngine_SharedBuffer';
|
|
|
|
type
|
|
{$ALIGN 4}
|
|
TSelectKeyboardBuffer = record
|
|
LangID: WORD;
|
|
CLSID: TGUID;
|
|
GUIDProfile: TGUID;
|
|
end;
|
|
|
|
{$ALIGN 4}
|
|
TSharedBuffer = record
|
|
SelectKeyboardBuffer: array[0..MAX_SELECTKEYBOARDTSF_CIRCULARBUFFER_SIZE-1] of TSelectKeyboardBuffer;
|
|
end;
|
|
|
|
PSharedBuffer = ^TSharedBuffer;
|
|
|
|
TSharedBufferManager = class
|
|
private
|
|
class var FIdentity: TSharedBufferManager;
|
|
private
|
|
FSelectKeyboardIndex: Integer;
|
|
hMMF: THandle;
|
|
pSharedData: PSharedBuffer;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function WriteSelectKeyboardBuffer(skb: TSelectKeyboardBuffer): Integer;
|
|
class function Identity: TSharedBufferManager;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.SysUtils,
|
|
|
|
Keyman.System.Security;
|
|
|
|
{ TSharedBufferManager }
|
|
|
|
constructor TSharedBufferManager.Create;
|
|
begin
|
|
inherited Create;
|
|
hMMF := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE or SEC_COMMIT, 0, sizeof(TSharedBuffer),
|
|
PChar(GLOBAL_SHAREDBUFFER_FILE_MAPPING_NAME));
|
|
if hMMF = 0 then
|
|
RaiseLastOSError;
|
|
|
|
SetObjectToLowIntegrity(hMMF);
|
|
// Most apps only need read access. TODO: reduce general access permission to READ for other processes;
|
|
// but doing so for application packages is a good start
|
|
GrantPermissionToAllApplicationPackages(hMMF, FILE_MAP_READ);
|
|
|
|
pSharedData := PSharedBuffer(MapViewOfFile(hMMF, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TSharedBuffer)));
|
|
if not Assigned(pSharedData) then
|
|
RaiseLastOSError;
|
|
end;
|
|
|
|
destructor TSharedBufferManager.Destroy;
|
|
begin
|
|
if Assigned(pSharedData) then
|
|
begin
|
|
if not UnmapViewOfFile(pSharedData) then
|
|
RaiseLastOSError;
|
|
end;
|
|
|
|
if hMMF <> 0 then
|
|
begin
|
|
if not CloseHandle(hMMF) then
|
|
RaiseLastOSError;
|
|
end;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
class function TSharedBufferManager.Identity: TSharedBufferManager;
|
|
begin
|
|
if not Assigned(FIdentity) then
|
|
FIdentity := TSharedBufferManager.Create;
|
|
Result := FIdentity;
|
|
end;
|
|
|
|
//
|
|
// This comes from the Windows SDK and is a simple way of guaranteeing
|
|
// that the processor cache is flushed on a multiprocessor machine, so
|
|
// that we can be assured that the circular buffer is coherent before
|
|
// we notify the target thread. (Given that we post a message to the
|
|
// target thread and don't use synchronisation primitives, this is
|
|
// probably not going to be a problem anyway but it's the right way to
|
|
// do it.
|
|
//
|
|
procedure MemoryBarrier; inline;
|
|
var
|
|
Barrier: LONG;
|
|
begin
|
|
InterlockedOr(Barrier, 0);
|
|
end;
|
|
|
|
function TSharedBufferManager.WriteSelectKeyboardBuffer(
|
|
skb: TSelectKeyboardBuffer): Integer;
|
|
begin
|
|
Result := FSelectKeyboardIndex;
|
|
|
|
pSharedData.SelectKeyboardBuffer[Result] := skb;
|
|
|
|
MemoryBarrier;
|
|
|
|
Inc(FSelectKeyboardIndex);
|
|
if FSelectKeyboardIndex = MAX_SELECTKEYBOARDTSF_CIRCULARBUFFER_SIZE then
|
|
FSelectKeyboardIndex := 0;
|
|
end;
|
|
|
|
initialization
|
|
finalization
|
|
FreeAndNil(TSharedBufferManager.FIdentity);
|
|
end.
|