Closed ckruse closed 2 years ago
@ckruse 👋 Hello! Sorry to hear. Quite possible, since the Spaces features use private APIs from Apple. Would you mind posting a snippet of what you are doing? Have you debugged what call doesn’t work?
Hey there,
I tried debugging the issue, but it just seems that Space.addWindows()
and Space.removeWindows()
doesn't have any effect.
This is the Script I'm using to move screens to different spaces in single screen config:
const singleScreenConfig = [
["iTerm2", 2],
["Element", 3],
["Signal", 3],
["Nachrichten", 3],
["Google Chat", 3],
["Mastonaut", 3],
["Tweetbot", 3],
["MailMate", 4],
["Things", 4],
];
function moveToTargetSpace(window, targetSpace) {
const otherSpaces = window.spaces();
targetSpace.addWindows([window]);
otherSpaces.forEach((space) => {
if (space.hash() != targetSpace.hash()) {
space.removeWindows([window]);
}
});
console.log(
targetSpace.hash(),
otherSpaces.map((space) => space.hash()),
window.spaces().map((space) => space.hash())
);
}
function setSingleScreenConfig() {
const spaces = Space.all();
console.log(spaces.map((space) => space.hash()));
singleScreenConfig.forEach(([appName, spaceIdx]) => {
const app = App.get(appName);
if (!app) {
Phoenix.log("app not found", appName);
return;
}
console.log(appName);
moveToTargetSpace(app.mainWindow(), spaces[spaceIdx]);
});
}
Event.on("screensDidChange", function () {
const noOfScreens = Screen.all().length;
if (noOfScreens == 1) {
setSingleScreenConfig();
}
});
Key.on("p", ["control", "alt", "cmd"], setSingleScreenConfig);
The output of the console.log
in moveToTargetSpace
is e.g. this:
2022-01-27 20:35:47.042116+0100 0x7bc87 Default 0x0 40828 0 Phoenix: iTerm2
2022-01-27 20:35:47.051998+0100 0x7bc87 Default 0x0 40828 0 Phoenix: 4 3 3
So iTerm's spaces configuration doesn't change at all, does it?
I also ran into this issue right after updating to 12.2. I have a simple movement function bound to a shortcut:
function moveToSpace(direction, window) {
window = window || Window.focused();
if (!window) {
return;
}
const space = Space.active();
if (!space) {
return;
}
const targetSpace = direction == RIGHT ? space.next() : space.previous();
if (!targetSpace) {
return;
}
targetSpace.addWindows([window]);
space.removeWindows([window]);
window.focus();
}
It was working fine until the 12.2 update. No errors or warnings come up in the logs, but the addWindows
and removeWindows
functions have no apparent effect.
Amethyst appears to be having similar issues: https://github.com/ianyh/Amethyst/issues/1192
Thanks! Need to dig what Apple has changed. 😄
I was able to use both addWindows
and removeWindows
from a master build successfully. So my guess is that you need to build the app on macOS 12.2 to link the private APIs. Could you try building the app from source to confirm this? If this is true, then the next release should fix this.
didn't work for me.
xcodebuild -showsdks
shows:
macOS SDKs:
macOS 12.3 -sdk macosx12.3
sw_vers
:
ProductName: macOS
ProductVersion: 12.2
BuildVersion: 21D49
built with
xcodebuild -workspace Phoenix.xcworkspace \
-scheme Phoenix \
-configuration Release \
SYMROOT="$PWD/build/" \
clean build
(also with -sdk macosx12.3
)
function moveToTargetSpace(window, targetSpace) {
const otherSpaces = window.spaces();
targetSpace.addWindows([window]);
otherSpaces.forEach((space) => {
if (space.hash() != targetSpace.hash()) {
space.removeWindows([window]);
}
});
console.log(
targetSpace.hash(),
otherSpaces.map((space) => space.hash()),
window.spaces().map((space) => space.hash())
);
}
Key.on("p", ["alt", "cmd"], () => {
const spaces = Space.all();
const space = spaces[spaces.length - 1];
console.log("moving to space", space.hash());
moveToTargetSpace(Window.focused(), space);
});
Can confirm. Doesn't work on master. I built it like documented, but still:
2022-01-29 19:06:23.373598+0100 0x18a565 Default 0x0 60317 0 Phoenix: iTerm2
2022-01-29 19:06:23.378843+0100 0x18a565 Default 0x0 60317 0 Phoenix: 4 1 1
(Second log row: Target space, current space, current space after move)
@kaatt Intriguing. The config you provided does work for me from a master build, but it only works when moving from the first space to the second space. I think it must be something else than the pure addWindows
and removeWindows
functions.
@kasper the config @kaatt provided doesn't do anything for me with a master build 🤯
@kasper
21D49
?xcodebuild -showsdks
)@ckruse @kaatt Sorry, I somehow missed the x.2 completely. 🙄 I’m on 12.1. So just to confirm, this has worked in Monterey, but the latest 12.2 update broke it? I guess I need to update... 😂
Correct. It worked with Monterey but stopped with 12.2
Thanks! Classic, I bet Apple broke something, would be weird if they changed the APIs in between, but I’ll run the investigations on 12.2 then.
might be worth looking into the SkyLight framework which is what exports the CGS APIs. as of macOS 12.1, CGS were all aliases for SLS
stark (phoenix like tool) uses SLS APIs: https://github.com/tombell/stark/blob/main/Stark/Source/API/Space.swift
diffing
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight.tbd
and
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight.tbd
results in:
> _SLSDisplayCreateImage,
775a777
> _SLSHMDEnabled,
781a784,789
> _SLSHMDModeConfigurationCopyCurrentCGSDisplayMode,
> _SLSHMDModeConfigurationTransactionAbort,
> _SLSHMDModeConfigurationTransactionCommit,
> _SLSHMDModeConfigurationTransactionInitialize,
> _SLSHMDModeConfigurationTransactionSetCGSDisplayMode,
> _SLSHMDModeConfigurationTransactionValidate,
783a792
> _SLSHMDSetEnabled,
980a990
> _SLSScreenCaptureAccessCheck,
1393a1404
> _SLSTransactionSystemStatusBarSetItemPrivacyIndicator,
1557a1569
> _kSLCaptureStreamAlwaysScaleToFit,
1561a1574,1585
> _kSLContentStreamAlwaysScaleToFitKey,
> _kSLContentStreamBackgroundColorKey,
> _kSLContentStreamColorSpaceKey,
> _kSLContentStreamDestinationRectKey,
> _kSLContentStreamFrameSizeKey,
> _kSLContentStreamMinimumFrameTimeKey,
> _kSLContentStreamPixelFormatKey,
> _kSLContentStreamPreserveAspectRatioKey,
> _kSLContentStreamQueueDepthKey,
> _kSLContentStreamShowCursorKey,
> _kSLContentStreamSourceRectKey,
> _kSLContentStreamYCbCrMatrixKey,
1633a1658
> _kSLSHMDCapabilityCGDisplayModes,
1735a1761,1763
> SLContentFilter,
> SLContentStream,
> SLContentStreamUpdate,
however, /System/Library/Frameworks/CoreGraphics.framework/Versions/Current/CoreGraphics.tbd
hasn't been changed between 12.1 and 12.3
@kaatt Indeed, thanks! I was checking it out, but I can’t find anything that would be relevant to the existing APIs used by Phoenix.
i tried replacing CGSAddWindowsToSpaces/CGSRemoveWindowsFromSpaces
with SLSAddWindowsToSpaces/SLSRemoveWindowsFromSpaces
but it had no effect.
also tried stark's add/remove windows from spaces function but it had no effect either. stark exclusively uses SLS functions (no CGS)
perhaps @tombell may have suggestions on what to do here?
I think SLS*
and CGS*
should still map one-to-one. The function headers seem identical. 🤔
maybe it's a regression? in https://developer.apple.com/documentation/macos-release-notes/macos-12_3-release-notes, apple initially wrote SkyLight instead of ScreenCaptureKit (now fixed)
they must have modified SkyLight for ScreenCaptureKit
I think
SLS*
andCGS*
should still map one-to-one. The function headers seem identical. 🤔
This is correct, SkyLight is just what the library is called now, as far as I know.
perhaps @tombell may have suggestions on what to do here?
I've updated to Monterey 12.2, and my Stark code doesn't work anymore either.
SkyLight has a bunch of SLSTransaction*
methods that may work:
SLSTransactionAddWindowToSpace
SLSTransactionAddWindowToSpaceAndRemoveFromSpaces
SLSTransactionBindSurface
SLSTransactionClearMenuBarSystemOverrideAlphas
SLSTransactionClearWindowLockedBounds
SLSTransactionClearWindowSystemLevel
SLSTransactionCommit
SLSTransactionCommitCoalescing
SLSTransactionCreate
SLSTransactionDeferWindowMoveEvents
SLSTransactionDestroySpace
SLSTransactionGetTypeID
SLSTransactionHideSpace
SLSTransactionMoveWindowWithGroup
SLSTransactionMoveWindowsToManagedSpace
SLSTransactionOrderSurface
SLSTransactionOrderWindow
SLSTransactionOrderWindowGroup
SLSTransactionOverrideAppSleepNotifications
SLSTransactionPostBroadcastNotification
SLSTransactionPostNotificationToConnection
SLSTransactionRemoveWindowFromSpace
SLSTransactionRemoveWindowFromSpaces
SLSTransactionResetSpaceMenuBar
SLSTransactionResetWindow
SLSTransactionResetWindowSubLevel
SLSTransactionSetBackdropChameleonContribution
SLSTransactionSetChameleonUpdatesEnabled
SLSTransactionSetClientAdvisory
SLSTransactionSetEventCapture
SLSTransactionSetManagedDisplayCurrentSpace
SLSTransactionSetManagedDisplayIsAnimating
SLSTransactionSetMenuBarBounds
SLSTransactionSetMenuBarSystemOverrideAlpha
SLSTransactionSetSafeApertureOverride
SLSTransactionSetSafeApertureSpaceReveal
SLSTransactionSetSpaceAbsoluteLevel
SLSTransactionSetSpaceAlpha
SLSTransactionSetSpaceOrderingWeight
SLSTransactionSetSpaceReveal
SLSTransactionSetSpaceShape
SLSTransactionSetSpaceTransform
SLSTransactionSetSurfaceBounds
SLSTransactionSetSurfaceOpacity
SLSTransactionSetSurfaceResolution
SLSTransactionSetWindowAlpha
SLSTransactionSetWindowBrightness
SLSTransactionSetWindowGlobalClipShape
SLSTransactionSetWindowLevel
SLSTransactionSetWindowLockedBounds
SLSTransactionSetWindowOpaqueShape
SLSTransactionSetWindowProperty
SLSTransactionSetWindowReleasesBackingOnOrderOut
SLSTransactionSetWindowResolution
SLSTransactionSetWindowShape
SLSTransactionSetWindowSubLevel
SLSTransactionSetWindowSystemAlpha
SLSTransactionSetWindowSystemLevel
SLSTransactionSetWindowTransform
SLSTransactionSetWindowWarp
SLSTransactionShowSpace
SLSTransactionSpaceTileMoveToSpaceAtIndex
SLSTransactionSystemStatusBarRegisterReplicantWindow
SLSTransactionSystemStatusBarRegisterSortedWindow
SLSTransactionSystemStatusBarSetDropPriority
SLSTransactionSystemStatusBarSetItemPrivacyIndicator
SLSTransactionSystemStatusBarSetSelectedContentFrame
SLSTransactionSystemStatusBarUnregisterWindow
SLSTransactionUpdateRegion
SLSTransactionWillSwitchSpaces
attempt at the headers, all but SLSTransactionCreate
segfault for me:
typedef int SLSConnectionID;
typedef size_t SLSSpaceID;
typedef NSObject SLSTransaction;
SLSTransaction* SLSTransactionCreate(SLSConnectionID cid);
int SLSTransactionCommit(SLSConnectionID cid, SLSTransaction* txID);
void SLSTransactionAddWindowToSpace(SLSConnectionID cid, CGWindowID window, SLSSpaceID space);
void SLSTransactionRemoveWindowFromSpace(SLSConnectionID cid, CGWindowID window, SLSSpaceID space);
void SLSTransactionRemoveWindowFromSpaces(SLSConnectionID cid, CGWindowID window, CFArrayRef spaces);
https://bugs.chromium.org/p/chromium/issues/detail?id=74812 mentions CGSSpaceAddWindowsAndRemoveFromSpaces
which could also work
If I would have to guess, Apple has been working on the library and created a regression along the way. I would find it surprising that they would drop some support between minor versions (even for internal stuff). I will keep digging if any information surfaces.
the chromium page also mentioned CGSMoveWorkspaceWindowList
, couldn't find it exported from CoreGraphics but found SLSMoveWorkspaceWindowList
exported from SkyLight.
it's working for moving a window to the primary space (1). unsure how to move to other spaces.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <CoreGraphics/CoreGraphics.h>
typedef int CGSConnection;
typedef int CGSWindow;
typedef int CGSWorkspaceID;
extern OSStatus SLSMoveWorkspaceWindowList(const CGSConnection connection,
CGSWindow *wids,
int count,
CGSWorkspaceID toWorkspace);
extern CGSConnection _CGSDefaultConnection(void);
int main(int argc, char **argv) {
CGSConnection con = _CGSDefaultConnection();
CGSWindow wid = 12130;
CGSWindow wids[] = {wid};
CGSWorkspaceID spaceId = 4;
printf("%i\n",SLSMoveWorkspaceWindowList(con, wids, 1, spaceId));
return 0;
}
compiled with: clang -framework AppKit -framework SkyLight -framework CoreGraphics -iframework /System/Library/PrivateFrameworks/ source.m
I got this working in my project using SLSMoveWindowsToManagedSpace
from SkyLight.
The prototype:
// ...
extern void SLSMoveWindowsToManagedSpace(int cid, CFArrayRef windows, uint64 sid);
The usage:
public func moveWindows(_ windows: [Window]) {
SLSMoveWindowsToManagedSpace(Space.connectionID, windows.map(\.identifier) as CFArray, identifier)
}
nice, the aliased CGSMoveWindowsToManagedSpace
is also working
Awesome, thanks for digging in! We will need to make this conditional 12.0 upwards.
Thanks everyone for helping! 🙌 This should now be fixed in master (build 115).
did you test moveWindows
on big sur? CGSMoveWindowsToManagedSpace
should be working.
found usages from 2018 and 2019:
https://github.com/kinglouie/CoreGraphicsExtension/blob/da8d4e53849099edb69dd78377d4e9140ddd9d77/Sources/CoreGraphicsExtension/NSWindow%2BCGE.m https://github.com/fsestini/macos-corelibs/blob/53345c4b3f05b7a51026b3e166ceeb9ed3cddc7a/src/MacSdk/Framework/CoreGraphics/Space.hs https://github.com/p10q/wmctrl/blob/290fd635fedcadedc2194fbd204f4a98fa2d5fef/common/accessibility/display.mm https://github.com/saforem2/chunkwm/blob/7d078f6f44fd23040e1ba5b9650a9e0df3fe526f/src/common/accessibility/display.mm
@kaatt I did not! I wanted to be more on the safe side. If someone can confirm, we can adjust the documentation and conditional. 👍
commit from 2017: https://github.com/saforem2/chunkwm/commit/48b268a509349750dd5e99a4ae419a5963a1b872
must be working since high sierra at least
Can confirm, it works with 12.2 again. Thank you very much! ❤️
@kaatt Cool, thanks! Adjusted the conditional and documentation to 10.13+.
I changed
mainWindow.spaces().forEach((space) => {
space.removeWindows([mainWindow]);
});
activeSpace.addWindows([mainWindow]);
to
activeSpace.moveWindows([mainWindow]);
But it doesn't seem to work?
The overall goal is to create a hotkey (tilde, dropdown) window effect. Does anyone have a working example of this for the newer macOS versions?
@NightMachinary Hey! Sorry to hear, could you share a bit more of you script? What is activeSpace
and mainWindow
?
@NightMachinary Hey! Sorry to hear, could you share a bit more of you script? What is
activeSpace
andmainWindow
?
My .phoenix.js
pretty much has this single function, but it's a bit messy. I myself only altered the script a bit; it was written by someone else.
The relevant function is moveAppToActiveSpace
at line 381. activeSpace
is the current space. mainWindow
is the main window of the app that the hotkey is supposed to show on top of the current app.
It was working previously (it still mostly works except that the windows don't move to the current space), but after upgrading macOS, it regressed.
@NightMachinary Just to confirm, you have upgraded to Phoenix 3.0.0? The script is indeed a bit messy, so it’s hard to follow what might be the issue, but I would suggest you debug it line-by-line to see where does the expectations break.
@kasper Yes, I'm on Version 3.0.0 (127)
. I'll do some debugging when I have the time.
@kasper the CGSMoveWindowsToManagedSpace doesn't seem to move fullscreen windows. The CGSAddWindowsToSpaces API did move them.
How did you work around this limitation? It's a blocker for my project (https://github.com/lwouis/alt-tab-macos), and I'm curious how you guys dealt with CGSAddWindowsToSpaces being broken from macOS 12.2 onward.
@lwouis I might have missed this limitation. I guess that is considered reordering Spaces and doesn’t work because of that?
I'm not sure why it doesn't work with fullscreen windows but it doesn't, which means it's not a 1-to-1 replacement of the other API
Yep, I wish Apple would support Spaces APIs officially.
Hey,
Is it possible that the current macOS update broke Phoenix? Suddenly my window management doesn't work anymore; I have a set of scripts which moves windows to different spaces when attaching / detaching a monitor.
Best regards, CK