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.
Related
Is there a way to programmatically launch the "Force Quit Applications" app that can be launched from the 'Apple menu' or by pressing Command-Option-Esc.
I haven't been able to find out whether it's a separate app or perhaps something that can be invoked by a command line parameter to Activity Monitor.
I've tried the simulating the keystrokes but it doesn't seem to work:
CGEventFlags flags = kCGEventFlagMaskAlternate | kCGEventFlagMaskCommand;
CGKeyCode virtualKey = kVK_Escape;
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef KbdEvent = CGEventCreateKeyboardEvent(source, virtualKey, YES);
CGEventSetFlags(KbdEvent, (CGEventFlags)flags);
CGEventTapLocation location = kCGHIDEventTap;
CGEventPost(location, KbdEvent);
CFRelease(KbdEvent);
CFRelease(source);
I also noticed that CGEvents cannot trigger the "Force Quit Applications" window, perhaps it works only on the lower IOHID level (like the Xcode Simulator).
The only way to invoke the window I have found is to use System Events:
system("osascript -l JavaScript -e \"Application('System Events').processes['Finder'].menuBars[0].menus['Apple'].menuItems['Force Quit…'].click()\"");
You can check to see if the window is open with the following:
#include <Carbon/Carbon.h>
int IsForceQuitOpen() {
int found = 0;
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFIndex numWindows = CFArrayGetCount(windowList);
for (int i = 0; i < (int)numWindows; i++) {
CFDictionaryRef info = (CFDictionaryRef)CFArrayGetValueAtIndex(windowList, i);
CFStringRef appName = (CFStringRef)CFDictionaryGetValue(info, kCGWindowOwnerName);
if (CFEqual(appName, CFSTR("loginwindow"))) {
found = 1;
}
}
return found;
}
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.
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 would like to call a IBAction within a cocoa app from applescript:
I want to call:
- (IBAction)reverse:(id)pId;
{
direction = -1;
}
with a line in an external applescript file like:
tell application "theapp"
reverse
end tell
Any Ideas?
Thanks in Advance
Use NSAppleScript.
NSAppleScript *as = [[NSAppleScript alloc] initWithSource:#"tell application \"theapp\"\nreverse\nend tell"];
NSDictionary *err = nil;
NSAppleEventDescriptor *desc = [as executeAndReturnError:&err];
NSLog(#"Error: %#\nData: %#", err, desc.data);
[as release];
There is also a good answer about Scripting Bridge here
I know, that I can use Apple Event Object Model for moving and resizing windows of Cocoa applications. But what can I use for Carbon applications?
Peter was right, you can access to bounds of any window using the following AppleScript:
tell application "System Events"
set allProcesses to application processes
repeat with i from 1 to count allProcesses
tell process i
repeat with x from 1 to (count windows)
position of window x
size of window x
end repeat
end tell
end repeat
end tell
You can also use the Accessibility API. This is how I think Optimal Layout is doing it.
First you have make sure your app has permission to use it.
BOOL checkForAccessibility()
{
NSDictionary *options = #{(__bridge id) kAXTrustedCheckOptionPrompt : #YES};
return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef) options);
}
Next, use the NSWorkspace::RunningApplications to get the PID of the app whose window you want to manipulate.
NSArray<NSRunningApplication *> *runningApps =[[NSWorkspace sharedWorkspace] runningApplications];
for( NSRunningApplication *app in runningApps )
{
if( [app bundleIdentifier] != nil && [[app bundleIdentifier] compare:#"IdentifierOfAppYouWantToFindHere"] == 0 )
{
PID = [app processIdentifier];
}
}
Then use the PID to get access to the main window reference using the Accessibility API.
AXUIElementRef app = AXUIElementCreateApplication( PID );
AXUIElementRef win;
AXError error = AXUIElementCopyAttributeValue( app, kAXMainWindowAttribute, ( CFTypeRef* )&win );
while( error != kAXErrorSuccess ) // wait for it... wait for it.... YaY found me a window! waiting while program loads.
error = AXUIElementCopyAttributeValue( app, kAXMainWindowAttribute, ( CFTypeRef* )&win );
Now you can set the size and position using something like this:
CGSize windowSize;
CGPoint windowPosition;
windowSize.width = width;
windowSize.height = height;
windowPosition.x = x;
windowPosition.y = y;
AXValueRef temp = AXValueCreate( kAXValueCGSizeType, &windowSize );
AXUIElementSetAttributeValue( win, kAXSizeAttribute, temp );
temp = AXValueCreate( kAXValueCGPointType, &windowPosition );
AXUIElementSetAttributeValue( win, kAXPositionAttribute, temp );
CFRelease( temp );
CFRelease( win );
Same thing. You can use Apple Events on any scriptable application, and Apple Events and scriptability are a lot older than Carbon.