Now Reading
Fullscreen apps above the MacBook notch — Alin Panaitiu

Fullscreen apps above the MacBook notch — Alin Panaitiu

2023-04-12 15:12:31

On MacBook laptops with a notch, the realm above the notch is reserved for the menu bar for many purposes. That’s good for regular window utilization, because the menubar now not takes valuable vertical house that may very well be used for the content material.

image

Going fullscreen although, solely makes that space black (in order to cover the notch) however apps will nonetheless render beneath the notch, leaving about 100k pixels ineffective.

image

So listed here are some directions on how one can fullscreen a window above the notch in probably the most hackish approach potential.

By the way in which, for those who want that previous MacBook and not using a notch design, my Lunar app can disable the notch by making use of a hidden decision.


# Outdated-style fullscreen

Each macOS window is definitely a NSWindow, and that class has a toggleFullScreen() technique.

However what that does is transfer the window to its personal particular Mission Management House and render it fullscreen beneath the notch. Not what I need.

There is a factor known as old-style fullscreen which some apps assist natively (Chic Textual content, kitty, IINA and so on.).

As an alternative of calling toggleFullScreen(), this technique does the next:

  • makes the window borderless
  • units up the app to auto-hide the menubar and dock
  • units the window body equal to the display body

This is how IINA does it for instance: iina/MainWindowController.swift · iina/iina · GitHub

The issue is that, whereas apps can change their very own home windows as they please, there is no approach for an app to alter these attributes on home windows of different apps.

# Injecting code

Utilizing Frida we will inject and run code contained in the context of any operating course of.

We first have to disable SIP although, as code injection is restricted by default for apparent safety causes.

Under we outline our Frida script, which we will save in ~/Paperwork/OverNotch.js for later utilization.

OverNotch.js

var NSApplicationPresentationAutoHideMenuBar = 1 << 2
var NSApplicationPresentationAutoHideDock = 1 << 0
var NSWindowStyleMaskBorderless = 0
var NSApplicationPresentationDefault = 0

world.app = ObjC.courses.NSApplication.sharedApplication()

world.arrayFromNSArray = (nsArray) => {
    var jsArray = []
    var rely = nsArray.rely()
    for (var i = 0; i < rely; i++) {
        jsArray[i] = nsArray.objectAtIndex_(i)
    }
    return jsArray
}

world.mainWindow = () => {
    if (world.app.mainWindow()) { return world.app.mainWindow() }
    if (world.app.keyWindow()) { return world.app.keyWindow() }

    var home windows = arrayFromNSArray(world.app.home windows())
    var index = Math.min(...home windows.map((w) => w.orderedIndex()))
    return home windows.discover((w) => w.orderedIndex() == index)
}

world.w = mainWindow()

world.toggleFullScreen = (windowNum) => {
    if (!windowNum) { windowNum = world.w.windowNumber() }

    var dict = ObjC.courses.NSThread.mainThread().threadDictionary()
    if (dict.valueForKey_(windowNum.toString())) {
        stopFullScreen(windowNum)
    } else {
        makeFullScreen(windowNum)
    }
}

world.stopFullScreen = (windowNum) => {
    Interceptor.revert(app.setPresentationOptions_.implementation)
    ObjC.schedule(ObjC.mainQueue, () => {
        world.app.setPresentationOptions_(NSApplicationPresentationDefault)
    })

    var window = windowNum ? world.app.windowWithWindowNumber_(windowNum) : world.w
    if (!window) {
        return console.log('Window not discovered')
    }

    ObjC.schedule(ObjC.mainQueue, () => {
        var key = window.windowNumber().toString()
        var dict = ObjC.courses.NSThread.mainThread().threadDictionary()
        if (dict.valueForKey_(key)) {
            window.setStyleMask_(dict.valueForKey_(key).unsignedLongLongValue())
            dict.removeObjectForKey_(key)
        }

        window.setFrameUsingName_(key)
        window.setIsMovable_(true)

        Interceptor.revert(app.setPresentationOptions_.implementation)
    })
}

world.makeFullScreen = (windowNum) => {
    var window = windowNum ? world.app.windowWithWindowNumber_(windowNum) : world.w
    if (!window) {
        return console.log('Window not discovered')
    }

    ObjC.schedule(ObjC.mainQueue, () => {
        var key = window.windowNumber().toString()
        var dict = ObjC.courses.NSThread.mainThread().threadDictionary()
        var worth = ObjC.courses.NSNumber.numberWithUnsignedLongLong_(window.styleMask())
        dict.setValue_forKey_(worth, key)
        window.saveFrameUsingName_(key)

        world.app.setPresentationOptions_(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)
        window.setStyleMask_(NSWindowStyleMaskBorderless)
        window.setFrame_display_(window.display().body(), true)
        window.setIsMovable_(false)

        Interceptor.substitute(app.setPresentationOptions_.implementation, new NativeCallback((masks) => {}))
    })
}

Then as a check, have a Safari window open, and run the next to make it fullscreen:

sudo frida -q -l ~/Paperwork/OverNotch.js -e "toggleFullScreen()" Safari

Word that this does not work with all home windows, and a few apps will crash when their window styleMask is modified.

# Hotkey

I want to bind fn - a to fullscreen the present window utilizing skhd.

I’d normally bind rcmd - a as it’s kind of simpler to press, however since I surrendered the ⌘ Proper Command key to my rcmd app switcher, I am utilizing fn for these system-wide hotkeys.

This is what I added in ~/.skhdrc to try this:

fn - a  : sudo frida -q -l ~/Paperwork/OverNotch.js -e "toggleFullScreen()" $(osascript -e 'inform software "System Occasions" to get unix id of first software course of whose frontmost is true')

# Why would you want such a factor?

I personally wished it for having Home windows 11 look as natively as potential by Parallels Desktop. I nonetheless want it sometimes for {hardware} that solely gives a Home windows configuration program.

You may as well use it to get an immersive view of particular webpages. This is NightDrive for instance:

Or perhaps have a extra centered zen-mode on observe taking apps. This is NotePlan in my case:

# SIP disabled? How about no?

Frida and different debuggers like lldb and gdb use the task_for_pid API for attaching to a operating course of.

That does not essentially want SIP being disabled. For task_for_pid to succeed on a course of, that course of wants to fulfill one of many following necessities:

  • be unsigned
  • be signed with out Hardened Runtime
  • embrace the com.apple.safety.get-task-allow entitlement

Fortuitously, we will alter the signature of any non-system app with out disabling SIP.

Sadly that is not very easy as a result of we have to make sure that we signal all the extra bundles contained in the app, and we have to handle not eradicating present entitlements.

As a result of I am utilizing fish shell, this is how I method this process:

operate resign-bundle -a bundle
    # Dump present entitlements as a PLIST
    codesign -d --entitlements - --xml "$bundle" >/tmp/entitlements.xml
    if not check -s /tmp/entitlements.xml
        # Signal with out Hardened Runtime
        codesign -fs $CODESIGN_CERT "$bundle"
        return
    finish

    # Add `get-task-allow` to the entitlements to permit debuggers
    /usr/libexec/PlistBuddy -c "Add :com.apple.safety.get-task-allow bool true" /tmp/entitlements.xml
    # Signal with Hardened Runtime and `get-task-allow`
    codesign -fs $CODESIGN_CERT -o runtime --timestamp --entitlements /tmp/entitlements.xml "$bundle"
finish

operate resign-app -a app
    # Backup present app
    mkdir -p ~/.cache/resign-app-backups/
    if not check -d "~/.cache/resign-app-backups/$(basename "$app")"
        rsync -avz "$app" ~/.cache/resign-app-backups/
    finish

    # Resign bundles contained in the app
    fd -uu '.(app|framework|dylib|xpc|appex)$' "$app" -j 1 -x fish -c 'resign-bundle {}'

    # Resign the app itself
    resign-bundle "$app"
finish

Testing this on RealVNC Viewer:

> resign-app "/Purposes/VNC Viewer.app"

Executable=/Purposes/VNC Viewer.app/Contents/MacOS/vncviewer
/Purposes/VNC Viewer.app: changing present signature

exhibiting how native fullscreen differs from legacy fullscreen

I additionally tried testing this on VirtualBuddy however as a result of it has a required entitlement (com.apple.vm.networking) and an embedded provisioning profile tied to the developer certificates, this received too sophisticated to warrant the hassle.

System apps can also’t work with this method as a result of a few of them dwell in immutable volumes known as Cryptexes. For instance this is the place Safari could be discovered on macOS Ventura:

# Safari lives at /System/Volumes/Preboot/Cryptexes/App/System/Purposes/Safari.app

> codesign --remove-signature '/System/Volumes/Preboot/Cryptexes/App/System/Purposes/Safari.app'
/System/Volumes/Preboot/Cryptexes/App/System/Purposes/Safari.app: inner error in Code Signing subsystem

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top