Based on a user request, I had to figure out if a screen is locked or not.
So with some tinkering (thank you StackExchange!) I found 2 ways to handle this.
- Check if the screen is locked or not with a simple one time call
- With the system notifying my application that the lock state changed (so we do not have to poll if a screen is locked or not)
So for this I made a little unit - feel free to use/modify/etc:
unit t4a_macOS_ScreenLocked;
{$mode ObjFPC}{$H+}
{$modeswitch objectivec1}
interface
uses
Classes, SysUtils, MacOSAll, Forms, process, CocoaAll, CocoaUtils;
type
TScreenLockUnlockCallback = procedure(isScreenLocked:boolean) of object;
{ T4ALIB - Check if the screen is locked or not (eg. Login screen shows). }
function CheckIfScreenLocked: Boolean;
{ Use Notifications by system}
procedure ScreenLockCallback(Center: CFNotificationCenterRef;
Observer: Pointer;
Name: CFStringRef;
Object_: Pointer;
UserInfo: CFDictionaryRef); mwpascal;
procedure RegisterScreenLockNotifications(OptionalCallbackProcedure:TScreenLockUnlockCallback = nil);
procedure UnregisterScreenLockNotifications;
var
ScreenIsLocked : boolean; // used for notifications
CallbackProcedure : TScreenLockUnlockCallback;
implementation
// Onetime call/check
function CheckIfScreenLocked: Boolean;
var
SessionDict: CFDictionaryRef;
ValueRef: CFTypeRef;
begin
Result := False;
SessionDict := CGSessionCopyCurrentDictionary;
if SessionDict <> nil then
begin
if CFDictionaryGetValueIfPresent(
SessionDict,
CFSTR('CGSSessionScreenIsLocked'),
@ValueRef
) then
begin
if CFGetTypeID(ValueRef) = CFBooleanGetTypeID then
Result := CFBooleanGetValue(CFBooleanRef(ValueRef));
end;
CFRelease(SessionDict);
end;
ScreenIsLocked := Result;
end;
// Notification by system
procedure ScreenLockCallback(Center: CFNotificationCenterRef;
Observer: Pointer;
Name: CFStringRef;
Object_: Pointer;
UserInfo: CFDictionaryRef); mwpascal;
var
NotificationName: String;
begin
NotificationName := CFStringToString(Name);
ScreenIsLocked := (NotificationName = 'com.apple.screenIsLocked') and
(NotificationName <> 'com.apple.screenIsUnlocked');
if Assigned(CallbackProcedure) then CallbackProcedure(ScreenIsLocked);
end;
procedure RegisterScreenLockNotifications(OptionalCallbackProcedure:TScreenLockUnlockCallback = nil);
var
Center: CFNotificationCenterRef;
begin
CallbackProcedure := OptionalCallbackProcedure;
Center := CFNotificationCenterGetDistributedCenter;
CFNotificationCenterAddObserver(
Center,
nil, // observer
@ScreenLockCallback,
CFSTR('com.apple.screenIsLocked'),
nil,
CFNotificationSuspensionBehaviorDeliverImmediately
);
CFNotificationCenterAddObserver(
Center,
nil,
@ScreenLockCallback,
CFSTR('com.apple.screenIsUnlocked'),
nil,
CFNotificationSuspensionBehaviorDeliverImmediately
);
end;
procedure UnregisterScreenLockNotifications;
var
Center: CFNotificationCenterRef;
begin
Center := CFNotificationCenterGetDistributedCenter;
CFNotificationCenterRemoveObserver(Center, nil, nil, nil);
end;
finalization
UnregisterScreenLockNotifications;
end.
Use:
1. One time check:
Just call "CheckIfScreenLocked", which returns TRUE if the screen is locked.
2. Using notifications:
- define a public function that can be called when the screen locked state changes:
// your own procedure
procdure TForm1.ScreenLockChanged(locked:boolean); { public! }
begin
if locked the writeln('locked');
end;
- In the onCreate event of your form, activate the notification observer
procedure TForm1.FormCreate(Sender: TObject);
begin
RegisterScreenLockNotifications(@ScreenLockChanged);
end;
(the observer will be unregistered automatically when your application closes, or you can call "UnregisterScreenLockNotifications")
Hope this is of use to someone out there 😉