Toggle Full-Screen Status of Front Window

Hey Folks,

This AppleScript shows how to detect whether or not the front window is zoomed to full-screen and how to toggle full-screen on/off based upon that knowledge.

* Tested on OSX 10.11.4 with Keyboard Maestro 7.0.4d5.

-Chris


Toggle Full-Screen Mode of Front Window.kmmacros (3.0 KB)

5 Likes

Or, if you would like to experiment with an Execute JavaScript for Automation action, you might try something like:

(function () {
    'use strict';

    // fullScreenAttribute :: () -> Maybe attribute
    function maybeFullScreenAttribute() {
        var lstProc = Application('System Events')
            .applicationProcesses
            .whose({
                frontmost: true
            }),

            // Frontmost standard window ?
            procFront = lstProc.length ? lstProc.at(0) : undefined,
            lstWins = procFront ? procFront.windows
            .whose({
                subrole: 'AXStandardWindow'
            }) : [];

        return lstWins.length ? lstWins[0]
            .attributes.AXFullScreen : undefined;
    }
    

    var fullScreen =  maybeFullScreenAttribute();

    if (fullScreen !== undefined) {
    
        // Toggle ...
        fullScreen.value = !fullScreen.value();
    }

})();
1 Like

Thanks for sharing, Chris.
Another great example of using System Events to handle all apps.

How did you get this update?

On my Mac, it shows KM 7.0.3 with no updates available.

Model Identifier: MacBookPro10,1
System Version: OS X 10.10.5 (14F1713)

Thanks for sharing another great JXA example, Rob.
It always helps to have a direct comparison between AppleScript and JXA.

One question please:

What does this mean?

What is the significance of "Maybe attribute"?

Thanks for your help.

The same here, only 7.0.3. Probably a top secret dev version :weary:

maybe in the return type expresses the fact that the function might return undefined rather than an attribute (in the absence of the right kind of processes and windows at run-time).

The AppleScript version is using error handling, while the JavaScript version is using conditional assignment - some languages formalise support for using the type system

but you can always do it manually, returning something like undefined, or null, when the input to a function is invalid (or, to put it another way, no output is defined for a given input).

Generally, the appeal of avoiding error handling is that the latter tends to disrupt the composition of functions. If a deeply nested function gets an out of range input and trips an error, the outer functions will choke. Option types provide a way of handling such cases without abandoning compositional patterns of structuring.

A more general (reusable) approach to attribute reading here might, incidentally, be:

 (function () {
     'use strict';

     // maybeFrontWinAttributes :: () -> Maybe attributes
     function maybeFrontWinAttributes() {
         var procs = Application('System Events')
             .applicationProcesses
             .whose({
                 frontmost: true
             }),

             // Frontmost standard window ?
             proc = procs.length ? procs.at(0) : undefined,
             lstWins = proc ? proc.windows
             .whose({
                 subrole: 'AXStandardWindow'
             }) : [];

         return lstWins.length ? lstWins[0]
             .attributes : undefined;
     }

     var mbAttribs = maybeFrontWinAttributes();
	 
     if (mbAttribs) {
         var fullScreen = mbAttribs.byName('AXFullScreen');

         fullScreen.value = !fullScreen.value();
     }

 })();

Given that there are some other useful attributes in the full set:

     return Object.keys(mbAttribs)
         .map(function (k) {
             return mbAttribs[k].name();
         })

–>

["AXFocused", "AXFullScreen", "AXTitle", 
"AXPosition", "AXGrowArea", "AXMinimizeButton", 
"AXDocument", "AXSections", "AXCloseButton", 
"AXMain", "AXFullScreenButton", "AXProxy", 
"AXDefaultButton", "AXMinimized", "AXChildren", 
"AXRole", "AXParent", "AXTitleUIElement", 
"AXCancelButton", "AXModal", "AXSubrole",
 "AXZoomButton", "AXRoleDescription", "AXSize",
 "AXToolbarButton", "AXFrame", "AXIdentifier"]

Thank you! That looks very helpful and useful! :+1:

Sorry, I still don't understand this. Where is maybe associated with the return type?

return lstWins.length ? lstWins[0]
            .attributes.AXFullScreen : undefined;

See the link to https://en.wikipedia.org/wiki/Option_type

( the value returned may be an attribute, but may also be undefined )

I see you updated your post after my reply to clarify maybe. Good idea.

However, you state "maybe in the return type" but I don't see anything explicit in the function or return that indicates "maybe".

Perhaps you meant "maybe" in the function name, rather than in the "return type"? Your function name: maybeFrontWinAttributes

Of course, a simple comment (in the script) on what the function returns would clear up the mystery, and make it easier to use for most of us.

Since the name of a function or variable has no effect on its type or usage, is this just a way of visually indicating to the reader that it is a "maybe type"? Similar to using "myVarStr" to indicate it is intended to be string type?

To be clear to all, JavaScript does NOT have a built-in type called "maybe". That is a pure programmers construct.

I do like the concept of using "undefined" rather than throwing an error.

For other readers, everyone should know that in JavaScript, a IF test on a variable that is "undefined" will return "false" (boolean false), as in:

var myVar = undefined
if (myVar) {
   // do something because myVar is NOT undefined
	 console.log('myVar has a value')
}
else {	// myVar IS undefined
	console.log('myVar is undefined')
}

We each have our own style. After decades of using a variable prefix that indicates type, I have recently changed to adding a suffix that indicates type. When I read a variable, I am more interested in what it contains than what type it is.

So I would use:

function frontWinAttributesMB() {
}

Nothing wrong with your style. It's just a matter of preference. :wink:

Rob, thanks again for all that you share. It has helped me learn a lot. Sometimes though, it takes a few questions to understand your code.

This is a nice list to have, and to know how to get.
Thanks.

Anyone have any ideas on how to get the official definition of these attributes? I've done a lot of searching and have found nothing. They are NOT in the System Events SDEF.

the official definition of these attributes

If we are to learn anything from the Galilean moment, it is that experiment yields more nourishing (and more dependable) harvests than authority :sunny:

No - the thing to notice is that the return type of the function is not attribute, it’s ‘possibly/potentially’ attribute – specifically, the option pair of (undefined | attribute). Maybe is just shorter than potentially and more widely used in this context.

Why name the function by its return value ? Because nesting expressions (rather than sequencing statements) gives a simple and easily refactored way of structuring quickly sketched and disposable scripts. We are more interested in what the function evaluates to than what it ‘does’.

Why does JavaScript lend itself well to structuring by function nesting ? Mainly because we have map, reduce and filter (+ other Array methods) built-in, and these lend themselves well to a compositional (or ‘nested’) structure.

Incidentally,

If (x) then DO x else DO y

doesn’t fit easily with this approach because an if statement is not an expression (it has no usable return value).

The JS ternary syntax is useful because it yields a conditional expression rather than a conditional statement, and therefore fits into a simple nest of functions. It also gives a convenient way of conditionally binding names in a var statement.

1 Like

That's nice, but if every person had to rediscover and document for him/herself every scientific (or programming) fact, then we would never advance because we would all be reinventing the wheel.

I understand that. My point is that "maybe" is just something someone made up that uses a programing language. It is NOT a native, intrinsic JavaScript variable type.

It is a good construct, but is largely esoteric for the needs of most users who are writing (or trying to use) a short script (as opposed to those that are building large systems).

Your use of the phrase "maybe in the return type" may be clear to you, but I suspect that it is not to most readers in this forum.

When writing a function a choice the programmer always has to make is what to return in case of an error.

It may not be as compact and compounded as the statement that you would write, but I submit it works well and is more readable to most users.

"maybe" is just something someone made up

An intriguing interpretation of the history of modal logic : - )

a choice the programmer always has to make is what to return in case of an error

No. The point of functions which return modal or monadic values like (attributes | undefined) is that error-handling doesn't arise. The idea is that all input values and conditions return a well-defined output value like Bottom, Nothing, or Undefined, and no error condition arises. It's an alternative to error handling, and allows for functional composition. ( Error trapping doesn't )

not be as compact and compounded as the statement that you would write

A misunderstanding there, I think. The issue is not compactness, it's that you can't use a statement if you are composing functions.

( The ternary conditional is not a statement – it's an expression )

1 Like

Really? It seemed to work for you here:

which is inside your function().

BTW, in my original post where I used the if (x) then DO x else DO y example, it is just to clearly illustrate to other readers that JavaScript treats undefined as a logical false when used in any type of logical construct.

(That’s not composition – if then else works fine at the IO statement stage. Because statements return no value, however, they don’t compose inside a nest or chain of functions).

My first question is, though, whether you feel it is really polite to badger those who submit posts here with (at best) your own instructions on what they should post next, or (at worst) apparently interminable objections to the inadequate quality of how they write, or the inadequate quantity of explanation and detail about what they write ?

I have to confess that I do find this wearing, and it does erode my willingness to contribute material to this forum. This morning, for example, I reluctantly decided against posting here, and sent a script elsewhere. I’m afraid that that is likely to become my habit for at least a while.

More generally, since you do seem keen to learn, it is, of course, a very natural response, and one which we probably all have had from time to time, to, respond to any difficulty in understanding something by focusing on feelings that the book is badly written, or the explanation poorly made – it must be someone else’s fault …

Understandable from time to time, but probably not a good habit – I’m sure you don’t really feel that habitually berating the explainer, or blaming the book, is likely to prove the best way to learn ? The best use of time ? Much better, I suspect, to get one’s hands dirty, try a few experiments, and solve the problems as they arise. If one explanation doesn’t work for you, then just look for another. No need to feel continually obliged to write in and complain to the author : - ) Give yourself a break …

Sorry that you feel "badgered". That certainly is not my intent.

I don't know what your purpose is for posting scripts in a public forum. But it seems to me that doing so invites questions from others who are trying to understand your script.

I give you a lot of credit for posting some great scripts. But if I can't use them beyond the immediate application then they are of much less value to me.

It is not uncommon in Internet public forums for the poster of code to be subject to the review and feedback of other readers in that forum. My only objective is to understand and learn from what you have posted.

Forgive me if I do not accept/swallow everything you post or say without question. You can feel free to ignore my questions if that makes your life more comfortable.

That's very mature of you. Sounds vaguely like a threat.

Critical review of others work is a long-standing process in the scientific world, and that would include computer science. I personally welcome critical review of all of my code because it almost always results in improving my code and my knowledge.

Have a good day, sir.

This is great. Even after 8 years, this code still works. I modernized the JS syntax slightly.

"use strict";

const maybeFrontWinAttributes = () => {
  const procs = Application("System Events").applicationProcesses.whose({
    frontmost: true,
  });
  const proc = procs.length ? procs.at(0) : undefined;
  const lstWins = proc?.windows.whose({ subrole: "AXStandardWindow" }) ?? [];

  return lstWins.length ? lstWins[0].attributes : undefined;
};

const mbAttribs = maybeFrontWinAttributes();

if (mbAttribs) {
  const fullScreen = mbAttribs.byName("AXFullScreen");
  fullScreen.value = !fullScreen.value();
}

1 Like

Thanks, it looks good !

( is there a slight glitch on the 'lstWins = ' line as pasted there ? )


FWIW my Keyboard Maestro 11 copy has this kind of shape:

Toggle Full-Screen status of front window.kmmacros (4.0 KB)

Expand disclosure triangle to view JS source
return (() => {
    "use strict";

    // Rob Trew @2024
    // Ver 0.2

    // Toggle full-screen status of front window.
    const main = () =>
        either(
            message => message
        )(
            attribs => {
                const
                    state = attribs.byName("AXFullScreen"),
                    newValue = !state.value();

                return (
                    state.value = newValue,
                    newValue
                        ? "Full-screen ON"
                        : "Full-screen OFF"
                );
            }
        )(
            frontWinAttribsLR()
        );

    // frontWinAttribsLR :: IO () -> Either String Attributes
    const frontWinAttribsLR = () => {
        const
            proc = Application("System Events")
            .applicationProcesses.whose({
                frontmost: true
            })
            .at(0);

        return proc.exists()
            ? (() => {
                const
                    win = proc.windows.whose({
                        subrole: "AXStandardWindow"
                    })
                    .at(0);

                return win.exists()
                    ? Right(win.attributes)
                    : Left(
                        "No frontmost application window found."
                    );
            })()
            : Left("No frontmost application found.");
    };


    // --------------------- GENERIC ---------------------

    // Left :: a -> Either a b
    const Left = x => ({
        type: "Either",
        Left: x
    });


    // Right :: b -> Either a b
    const Right = x => ({
        type: "Either",
        Right: x
    });


    // either :: (a -> c) -> (b -> c) -> Either a b -> c
    const either = fl =>
    // Application of the function fl to the
    // contents of any Left value in e, or
    // the application of fr to its Right value.
        fr => e => "Left" in e
            ? fl(e.Left)
            : fr(e.Right);


    // MAIN ---
    return main();
})();