Skip to content

Instantly share code, notes, and snippets.

@smx-smx
Created June 26, 2025 01:38
Show Gist options
  • Save smx-smx/26b340d8bc11dd9d68e59291b6743cc5 to your computer and use it in GitHub Desktop.
Save smx-smx/26b340d8bc11dd9d68e59291b6743cc5 to your computer and use it in GitHub Desktop.
// kiosk.js
// forces a macOS app to be in presentation mode 3 (hide dock, hide menu bar)
// tracks fullscreen enter/exit and alt-tab, restoring the desired state in case it was changed by the OS
'use strict';
console.log("Frida script starting (presentation10.js - with fullscreen hooks)...");
if (ObjC.available) {
console.log("Objective-C runtime is available.");
try {
// --- Assume standard setup is here (NSApplication, NSNotificationCenter, NSOperationQueue, etc.) ---
const NSApplication = ObjC.classes.NSApplication;
if (!NSApplication) { throw new Error("NSApplication class not found."); }
const app = NSApplication.sharedApplication();
if (!app || app.isNull()) { throw new Error("NSApplication.sharedApplication() is null or invalid."); }
const NSNotificationCenter = ObjC.classes.NSNotificationCenter;
if (!NSNotificationCenter) { throw new Error("NSNotificationCenter class not found."); }
const defaultCenter = NSNotificationCenter.defaultCenter();
if (!defaultCenter || defaultCenter.isNull()) { throw new Error("NSNotificationCenter.defaultCenter() is null or invalid."); }
const NSOperationQueue = ObjC.classes.NSOperationQueue;
if (!NSOperationQueue) { throw new Error("NSOperationQueue class not found."); }
const mainQueue = NSOperationQueue.mainQueue();
if (!mainQueue || mainQueue.isNull()) { throw new Error("NSOperationQueue.mainQueue() is null or invalid."); }
// --- Define Presentation Options (same as before) ---
const NSApplicationPresentationHideDock = (1 << 1); // 2
const NSApplicationPresentationHideMenuBar = (1 << 3); // 8
const desiredOptions = NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; // 10
console.log("Frida: Using desiredOptions value: " + desiredOptions);
// --- applyPresentationOptions function (same as before) ---
function applyPresentationOptions(reason) {
const currentApp = NSApplication.sharedApplication();
if (currentApp && !currentApp.isNull()) {
console.log(`Frida: [applyPresentationOptions on ${reason}] Attempting: ${desiredOptions}`);
currentApp.setPresentationOptions_(desiredOptions);
const newOptions = currentApp.presentationOptions();
console.log(`Frida: [applyPresentationOptions on ${reason}] Current after set: ${newOptions}`);
if (newOptions != desiredOptions) {
// Check if fullscreen is part of the new options
const isFullScreenActive = (newOptions & (1 << 10)) === (1 << 10); // NSApplicationPresentationFullScreen
// Check if our specific HideMenuBar flag is still there or got overridden
const isMenuBarHidden = (newOptions & NSApplicationPresentationHideMenuBar) === NSApplicationPresentationHideMenuBar;
console.warn(`Frida: [applyPresentationOptions on ${reason}] Options changed. Current: ${newOptions} (FullScreenActive: ${isFullScreenActive}, MenuBarExplicitlyHidden: ${isMenuBarHidden}), Desired: ${desiredOptions}`);
}
} else {
console.error(`Frida: [applyPresentationOptions on ${reason}] NSApplication.sharedApplication() is null.`);
}
}
// --- Initial application of options (same as before) ---
console.log("Frida: Scheduling initial applyPresentationOptions call...");
ObjC.schedule(ObjC.mainQueue, function() { // Using ObjC.mainQueue (GCD) for initial call
console.log("Frida: [ObjC.mainQueue] Initial applyPresentationOptions call.");
applyPresentationOptions("initial load via ObjC.mainQueue");
console.log("Frida: [ObjC.mainQueue] Initial call finished.");
});
// --- Helper to create NSString for notification names ---
function getNSString(jsString) {
if (ObjC.classes.NSString) {
const cString = Memory.allocUtf8String(jsString);
const nsString = ObjC.classes.NSString.stringWithUTF8String_(cString);
if (!nsString || nsString.isNull()) {
throw new Error("Failed to create NSString for: " + jsString);
}
console.log("Frida: Created NSString for '" + jsString + "': " + nsString);
return nsString;
} else {
throw new Error("NSString class not found.");
}
}
// --- Helper to add an observer ---
function addNotificationObserver(notificationNameString, callbackReason) {
const nsNotificationName = getNSString(notificationNameString);
const callbackBlock = new ObjC.Block({
retType: 'void',
argTypes: ['object'], // NSNotification *
implementation: function(notification) {
console.log("Frida: Received " + notificationNameString + "!");
// The block is already called on the 'mainQueue' (NSOperationQueue) we specify.
// The notification object might contain the window, useful for debugging:
// console.log("Frida: Notification object: " + notification.toString());
// console.log("Frida: Window from notification: " + notification.object().toString());
applyPresentationOptions(callbackReason);
}
});
console.log("Frida: Created ObjC.Block for " + notificationNameString);
defaultCenter.addObserverForName_object_queue_usingBlock_(
nsNotificationName,
null, // object: observe notifications from any sender (any window)
mainQueue, // queue: NSOperationQueue on which the block should be executed
callbackBlock
);
console.log("Frida: Subscribed to " + notificationNameString);
}
// --- Observe NSApplicationDidBecomeActiveNotification (your existing working observer) ---
addNotificationObserver("NSApplicationDidBecomeActiveNotification", "AppDidBecomeActive");
// --- Observe Window Fullscreen Notifications ---
addNotificationObserver("NSWindowDidEnterFullScreenNotification", "WindowDidEnterFullscreen");
addNotificationObserver("NSWindowDidExitFullScreenNotification", "WindowDidExitFullscreen");
// Optional: Observe WillEnter/WillExit if DidEnter/DidExit aren't responsive enough
// addNotificationObserver("NSWindowWillEnterFullScreenNotification", "WindowWillEnterFullscreen");
// addNotificationObserver("NSWindowWillExitFullScreenNotification", "WindowWillExitFullscreen");
console.log("Frida: Script setup complete. Monitoring for activation and fullscreen events.");
} catch (error) {
console.error("Frida: Top-level script error: " + error.message + "\nStack:\n" + error.stack);
}
} else {
console.log("Frida: Objective-C runtime not available.");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment