I am trying to get window id of of every window.
set r to {}
tell application "System Events"
repeat with t in windows of processes
set sid to id of t
set end of r to {title:title of t, id:sid}
end repeat
end tell
r
The above code returns
error "System Events got an error: Can’t get id of item 1 of every window of every process." number -1728 from id of item 1 of every window of every process
How to get the window id of every window?
I wrote a little Objective-C program for you that gets the window owners, window names and window ids:
////////////////////////////////////////////////////////////////////////////////
// windowlist.m
// Mark Setchell
//
// Get list of windows with their WindowOwnerNames, WindowNames and WindowNumbers
//
// Compile with:
// clang windowlist.m -o windowlist -framework coregraphics -framework cocoa
//
// Run with:
// ./windowlist
//
// You can then run "screencapture" to capture that window:
//
// screencapture -l<windowid> -x someFile.[png|jpg|tif]
////////////////////////////////////////////////////////////////////////////////
#include <Cocoa/Cocoa.h>
#include <CoreGraphics/CGWindow.h>
int main(int argc, char **argv)
{
NSArray *windows = (NSArray *)CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements|kCGWindowListOptionOnScreenOnly,kCGNullWindowID);
for(NSDictionary *window in windows){
int WindowNum = [[window objectForKey:(NSString *)kCGWindowNumber] intValue];
NSString* OwnerName = [window objectForKey:(NSString *)kCGWindowOwnerName];
NSString* WindowName= [window objectForKey:(NSString *)kCGWindowName];
printf("%s:%s:%d\n",[OwnerName UTF8String],[WindowName UTF8String],WindowNum);
}
}
Output
./windowlist
Preview:(null):300
Safari:(null):48
Terminal:(null):231
VirtualBox:(null):212
Mail:(null):150
Dropbox:(null):181
Finder:(null):118
Notification Center:(null):83
Google Drive:(null):73
Copy:(null):68
InkServer:(null):49
iTerm:(null):44
Google Drive::69
Copy::66
Dropbox::63
Creative Cloud::57
Spotlight::41
SystemUIServer::33
SystemUIServer:(null):36
SystemUIServer::31
Window Server:Menubar:3
Dock:Dock:23
iTerm:2. bash:190
iTerm:1. bash:336
This is not possible with this code.
In the processes array of System Events the property id of a window is not required to be available in AppleScript, that's the reason why you get the error.
If an application has an AppleScript dictionary and the windows element is provided then all windows have an id property, but not all applications support AppleScript and the non-document based applications don't provide the windows element by default.
Related
I'm trying to make a simple macOS Cocoa application using NSStatusItem to create a clickable icon on the system status bar. However, when I launch my application, I get this warning and the icon doesn't show up:
2020-03-03 14:43:11.564 Mocha_bug_example[936:39572] CGSGetActiveMenuBarDrawingStyle((CGSConnectionID)[NSApp contextID], &sCachedMenuBarDrawingStyle) returned error 268435459 on line 46 in NSStatusBarMenuBarDrawingStyle _NSStatusBarGetCachedMenuBarDrawingStyle(void)
Here's a minimal reproducible example for my application:
#import <AppKit/AppKit.h>
NSStatusItem* statusItem;
int main (int argc, char* argv[]) {
statusItem = [NSStatusBar.systemStatusBar statusItemWithLength: -1];
statusItem.button.title = #"foobar";
statusItem.visible = YES;
[NSApplication.sharedApplication run];
return 0;
}
I compiled and ran the example like this:
MacBook-Air-5:Mocha ericreed$ clang -o Mocha_bug_example -framework AppKit -fobjc-arc Mocha_bug_example.m
MacBook-Air-5:Mocha ericreed$ ./Mocha_bug_example
2020-03-03 14:43:11.564 Mocha_bug_example[936:39572] CGSGetActiveMenuBarDrawingStyle((CGSConnectionID)[NSApp contextID], &sCachedMenuBarDrawingStyle) returned error 268435459 on line 46 in NSStatusBarMenuBarDrawingStyle _NSStatusBarGetCachedMenuBarDrawingStyle(void)
[Application hung until I pressed Ctrl+C]
^C
MacBook-Air-5:Mocha ericreed$
Note: disabling automatic reference counting and adding [statusItem release]; after calling run as this similar question suggested made no visible difference.
This is how to add status bar item to command line app mac osx cocoa
Adapting apodidae's answer to Swift. Just put this in the main.swift file:
let app = NSApplication()
let statusItem = NSStatusBar.system.statusItem(withLength: -1)
statusItem.button!.title = "Hello, world!"
app.run()
I don't understand the finer details of the NSReleasePool as apodidae included, but it works for me without that.
#import <Cocoa/Cocoa.h>
int main(){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSApplication *application = [NSApplication sharedApplication];
NSStatusItem* statusItem;
statusItem = [NSStatusBar.systemStatusBar statusItemWithLength: -1];
statusItem.button.title = #"foobar";
statusItem.visible = YES;
[application run];
[pool drain];
return 0;
}
Save file with the name 'statusBar_SO.m'
Compile from Terminal:
clang statusBar_SO.m -framework Cocoa -o statusBar && ./statusBar
This is not the kind of thing you can do in main().
Except for extrememly unusual situations, you should never modify the main() that comes with the application template, and it must call NSApplicationMain():
int main(int argc, char *argv[])
{
// start the application
return NSApplicationMain(argc, (const char **) argv);
}
The Cocoa framework doesn't get initialized until you call NSApplicationMain() and is generally unusable until then.
This kind of setup should be done in applicationWillFinishLaunching or applicationDidFinishLaunching.
Update
The original poster is not using Xcode and is willing to brave the wilderness alone. ;)
This also implies that their application bundle will not have a main NIB file that would normally create and connect the application delegate object, main menu, and so forth.
There are intrepid individuals who have braved this territory and you can read about it in Creating a Cocoa application without NIB files.
Is it possible to get a AXUIElementRef of an open application knowing its PID?
My project is opening a file/folder. Then I get its PID and I want to be able to get access to this open window, so that is why I want AXUIElementRef of it.
extern AXUIElementRef AXUIElementCreateApplication ( pid_t pid);
Creates and returns the top-level accessibility object for the application with the specified process ID.
AXUIElementCreateApplication
Here is my solution as below in Swift 5
let applicationRef = AXUIElementCreateApplication(pid)
var uiElementsValue: AnyObject?
AXUIElementCopyAttributeValue(applicationRef, kAXWindowsAttribute as CFString, &uiElementsValue)
guard let appElements = uiElementsValue as? [AXUIElement] else { return }
'appElements' are your opening windows.
I am building a mac application, that at a certain time, needs to switch the currently logged in user to a different, preset one. Essentially a modified login window.
Is there a way to do this using cocoa?
-- Ari
Edit: Is there a way to not require the user to input their password?
Before I say my solution, I want to say that #jrodatus's answer is excellent, it just is for a slightly different use case.
I came up with this little applescript:
set theUser to "user"
set theUser to do shell script "/usr/bin/id -u " & theUser
set password to "pswd"
do shell script "/System/Library/CoreServices/Menu\\ Extras/User.menu/Contents/Resources/CGSession -switchToUserID " & theUser
repeat
try
tell application "System Events"
repeat until visible of process "SecurityAgent" is false
set visible of process "SecurityAgent" to false
end repeat
keystroke password
keystroke return
end tell
exit repeat
on error
tell application "System Events"
end tell
end try
end repeat
This simply triggers the login screen, with -switchToUserID set to the username's user id. Then when at least one window of SecurityAgent (The login screen) is visible, simulate the keystroke of the password, then enter the result is when run the login window opens, with the password typed in. Also, this has no delays.
As explained in answer to a similar question here, there is a command-line tool called "CGSession" hidden in the System folder that should do what you need. To run a command-line tool inside a Cocoa application, look into NSTask.
To switch users directly, find out the unix user ID of your preset user by running "id -u theUserName" and then use the output as the argument for executing:
/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -switchToUserID theUserIDNumber
Or to simply get to the login window (without logging out), run:
/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend
Here is a quick Obj-C category for NSWorkspace.
NSWorkspace-SwitchUser.h:
#import <Cocoa/Cocoa.h>
#interface NSWorkspace (SwitchUser)
-(BOOL)switchToUser:(NSString *)userName;
#end
NSWorkspace-SwitchUser.m:
#import "NSWorkspace-SwitchUser.h"
#import <sys/types.h>
#import <pwd.h>
#import <stdlib.h>
#import <unistd.h>
#import <stdio.h>
#implementation NSWorkspace (SwitchUser)
-(BOOL)switchToUser:(NSString *)userName {
struct passwd *pwd = malloc(sizeof(struct passwd));
if (!pwd) {
NSLog(#"Couldn't allocate struct passwd for getpwnam_r.");
return FALSE;
}
size_t buf_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
char *buffer = malloc(buf_len);
if (!buffer) {
NSLog(#"Couldn't allocate buffer for getpwnam_r.");
return FALSE;
}
getpwnam_r([userName UTF8String], pwd, buffer, buf_len, &pwd);
if (!pwd) {
NSLog(#"getpwnam_r failed to find the requested user.");
return FALSE;
}
uid_t userID = pwd->pw_uid;
free(pwd);
free(buffer);
// Run CGSession with the -switchToUserID argument
NSTask *cgsTask = [NSTask launchedTaskWithLaunchPath:#"/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession"
arguments:[NSArray arrayWithObjects:#"-switchToUserID",[NSString stringWithFormat:#"%u",userID],nil]];
// Wait till the task completes.
// Should be able to use -[NSTask waitUntilExit] instead, but it wasn't working for me :-P
while ([cgsTask isRunning]) {
usleep(100000);
}
return ([cgsTask terminationStatus] == 0);
}
#end
Edit: If you need to switch users without requiring the user to enter their password, there doesn't seem to be any way to do that without AppleScript, which IMO is unsafe in every sense of the word. But you might glean what you need here and here.
I need to get the mouse to click on a spot on the screen, to be specific a flash object in safari....
Id tried to do this with applescript but it didnt work. Then I found this script on the internet.
// File:
// click.m
//
// Compile with:
// gcc -o click click.m -framework ApplicationServices -framework Foundation
//
// Usage:
// ./click -x pixels -y pixels
// At the given coordinates it will click and release.
#import <Foundation/Foundation.h>
#import <ApplicationServices/ApplicationServices.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSUserDefaults *args = [NSUserDefaults standardUserDefaults];
// grabs command line arguments -x and -y
//
int x = [args integerForKey:#"x"];
int y = [args integerForKey:#"y"];
// The data structure CGPoint represents a point in a two-dimensional
// coordinate system. Here, X and Y distance from upper left, in pixels.
//
CGPoint pt;
pt.x = x;
pt.y = y;
// This is where the magic happens. See CGRemoteOperation.h for details.
//
// CGPostMouseEvent( CGPoint mouseCursorPosition,
// boolean_t updateMouseCursorPosition,
// CGButtonCount buttonCount,
// boolean_t mouseButtonDown, ... )
//
// So, we feed coordinates to CGPostMouseEvent, put the mouse there,
// then click and release.
//
CGPostMouseEvent( pt, 1, 1, 1 );
CGPostMouseEvent( pt, 1, 1, 0 );
[pool release];
return 0;
}
I have only scripted in applescript so I didnt quite understood it
but when I activate it it clicks on the top left
but here is my question, what should I chance in the script to make it click other places than in the top corner
more info about the script on this website: http://hints.macworld.com/article.php?story=2008051406323031
This is stuff that you learn in most introductory programming courses. A complete answer would be very long, so I just tell you a few cornerstones:
The program that you downloaded is not a script
It's objective-C-sourcecode
You need to learn how to work with the Terminal application (the command line).
You need to learn how to invoke commands on the terminal (e.g. gcc)
You have to understand the meaning of the word compile. In this case it's something the author wanted you to do at the command line.
Second step:
// starts a comment in Objective C
gcc ... is the command that should be executed on the command line to compile the program
./click is what you do to invoke the program (after you compiled it :-) )
gcc -o click click.m -framework ApplicationServices -framework Foundation
means:
gcc: Gnu C Compiler
-o click: The program should be named click
click.m: This should be the name of the source code (the file that you called 'script')
hope this helps...
I'm kinda new to Xcode and even programming.
From Xcode, in my code, how do I show the console and clear the screen?
I know I could do it with the Xcode preferences, but I would like to do it programmatically.
This works for me - leave out the last activate part if you wish Xcode to stay on top of your app:
bool ClearXCodeDebuggerConsole()
{
NSString *const scriptText = #"\
tell application \"System Events\"\n\
set currentapp to the name of the current application\n\
end tell\n\
tell application \"Xcode\" to activate\n\
tell application \"System Events\"\n\
keystroke \"r\" using {command down, control down, option down}\n\
end tell\n\
tell application currentapp to activate\n\
return true";
NSAppleScript *script = [[[NSAppleScript alloc] initWithSource:scriptText] autorelease];
[scriptText release];
NSDictionary *dictError = nil;
NSAppleEventDescriptor *result = [script executeAndReturnError:&dictError];
if (!result) return false;
if ([result booleanValue] != YES) return false;
return true;
}
You can display the console window by pressing Shift + Command + R. You can clear the console window by pressing Control + Option + Command + R. Both options are available from the Run menu.