How can I move/resize windows programmatically from another application? - macos

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.

Related

How to invoke "Force Quit Applications" on a Mac

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;
}

How to access finder sidebar shared content in cocoa

I want to access the shared content of left sidebar of finder in mac, so that i can get the system list connected to the same network. I can access favorite content, but could not get succeeded to access.
I am using this code to access favorite content of finder.
UInt32 seed;
LSSharedFileListRef sflRef = LSSharedFileListCreate(NULL,
kLSSharedFileListFavoriteItems,
NULL);
CFArrayRef items = LSSharedFileListCopySnapshot( sflRef, &seed );
for( size_t i = 0; i < CFArrayGetCount(items); i++ )
{
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(items, i);
if( !item )
continue;
CFURLRef outURL = NULL;
LSSharedFileListItemResolve( item, kLSSharedFileListNoUserInteraction, (CFURLRef*) &outURL, NULL );
if( !outURL )
continue;
//The actual path string of the item
CFStringRef itemPath = CFURLCopyFileSystemPath(outURL,kCFURLPOSIXPathStyle);
// TODO: Do whatever you want to do with your path here!!!!
CFRelease(outURL);
CFRelease(itemPath);
}
CFRelease(items);
CFRelease(sflRef);
Since i want to access systems available in shared network i change the key according to the key in the header file
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.fram‌​ework/Headers/LSSharedFileList.h
But i get nothing for shared content.
Can anyone help me for accessing this.
Thanks for your time to help me in advance.
You can use kLSSharedFileListRecentServerItems to get Recently connected network volumes.

How to detect a specific System Tray Popup/Tooltip

I need to detect a specific Windows System Tray Tooltip/Popup (USB Device Not Recognized). I don't seem to be having much luck polling with FindWindow. Is there a hook or something that will show me each one that pops up?
I have managed it doing the following:
HWND HMyTooltip = NULL, HNew = FindWindow( "tooltips_class32", NULL );
// Cycle through all visible tooltip windows looking for the one we want
while (HNew && !HMyTooltip)
{
if (IsWindowVisible(HNew))
{
HMyTooltip = HNew;
// If you want to find a particular tooltip, check the text (Note: GetWindowText doesn't work)
SendMessage( HMyTooltip, WM_GETTEXT, ARRAYSIZE(Title), (LPARAM)Title );
if (_strnicmp( Title, "USB Device Not Recognised", 22 ) != 0)
HMyTooltip = NULL;
}
HNew = GetWindow( HNew, GW_HWNDNEXT );
}

How to get users mail addresses on OS X Lion?

I was getting the users mail addresses from Libraray/Preferences/com.apple.mail.plist. They are not there any more in Lion :P Do I have to use scripting bridge? Any hints? Thanks
I would get them right out of Address Book. That should work regardless of what email app is being used.
// Find 'me' card in address book.
ABPerson* meCard = [[ABAddressBook sharedAddressBook] me];
if( meCard == nil ) {
NSLog( #"Could not find me!" );
return;
}
// Get my email addresses.
ABMultiValue* anEmailList = [meCard valueForProperty:kABEmailProperty];
if( anEmailList == nil ) {
NSLog( #"I have no email!" );
return;
}
// Output them.
for( NSUInteger index = 0; index < [anEmailList count]; index++ ) {
NSString* aLabel = [anEmailList labelAtIndex:index];
NSString* aValue = [anEmailList valueAtIndex:index];
NSLog( #"%#: %#", aLabel, aValue );
}
Mail in Lion stores the equivalent in ~/Library/Mail/V2/MailData/Accounts.plist. Note however that you are assuming that the user uses the Apple Mail program, unless that's what you really need you may want to have alternative methods for getting the address. You may for instance depending on how the system has been set up use the CSIdentity APIs, such as CSIdentityGetEmailAddress().
Apple script will get the job done.

X11/Xlib: Window always on top

A window should stay on top of all other windows. Is this somehow possible with plain x11/xlib? Googling for "Always on top" and "x11" / "xlib" didn't return anything useful.
I'd avoid toolkits like GTK+, if somehow possible.
I'm using Ubuntu with gnome desktop. In the window menu, there's an option "Always On Top". Is this provided by the X server or the window manager? If the second is the case, is there a general function that can be called for nearly any wm? Or how to do this in an "X11-generic" way?
Edit: I implemented fizzer's answer, now having following code:
XSelectInput(this->display, this->window,
ButtonPressMask |
StructureNotifyMask |
ExposureMask |
KeyPressMask |
PropertyChangeMask |
VisibilityChangeMask );
// ...
// In a loop:
if (XPending(this->display) >= 0)
{
XNextEvent(this->display, &ev);
switch(ev.type) {
// ...
case VisibilityNotify:
XRaiseWindow(this->display, this->window);
XFlush(this->display);
break;
// ...
}
}
But the eventhandling and raising nearly never gets executed even my mask is correct?!
#define _NET_WM_STATE_REMOVE 0 // remove/unset property
#define _NET_WM_STATE_ADD 1 // add/set property
#define _NET_WM_STATE_TOGGLE 2 // toggle property
Bool MakeAlwaysOnTop(Display* display, Window root, Window mywin)
{
Atom wmStateAbove = XInternAtom( display, "_NET_WM_STATE_ABOVE", 1 );
if( wmStateAbove != None ) {
printf( "_NET_WM_STATE_ABOVE has atom of %ld\n", (long)wmStateAbove );
} else {
printf( "ERROR: cannot find atom for _NET_WM_STATE_ABOVE !\n" );
return False;
}
Atom wmNetWmState = XInternAtom( display, "_NET_WM_STATE", 1 );
if( wmNetWmState != None ) {
printf( "_NET_WM_STATE has atom of %ld\n", (long)wmNetWmState );
} else {
printf( "ERROR: cannot find atom for _NET_WM_STATE !\n" );
return False;
}
// set window always on top hint
if( wmStateAbove != None )
{
XClientMessageEvent xclient;
memset( &xclient, 0, sizeof (xclient) );
//
//window = the respective client window
//message_type = _NET_WM_STATE
//format = 32
//data.l[0] = the action, as listed below
//data.l[1] = first property to alter
//data.l[2] = second property to alter
//data.l[3] = source indication (0-unk,1-normal app,2-pager)
//other data.l[] elements = 0
//
xclient.type = ClientMessage;
xclient.window = mywin; // GDK_WINDOW_XID(window);
xclient.message_type = wmNetWmState; //gdk_x11_get_xatom_by_name_for_display( display, "_NET_WM_STATE" );
xclient.format = 32;
xclient.data.l[0] = _NET_WM_STATE_ADD; // add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
xclient.data.l[1] = wmStateAbove; //gdk_x11_atom_to_xatom_for_display (display, state1);
xclient.data.l[2] = 0; //gdk_x11_atom_to_xatom_for_display (display, state2);
xclient.data.l[3] = 0;
xclient.data.l[4] = 0;
//gdk_wmspec_change_state( FALSE, window,
// gdk_atom_intern_static_string ("_NET_WM_STATE_BELOW"),
// GDK_NONE );
XSendEvent( display,
//mywin - wrong, not app window, send to root window!
root, // <-- DefaultRootWindow( display )
False,
SubstructureRedirectMask | SubstructureNotifyMask,
(XEvent *)&xclient );
XFlush(display);
return True;
}
return False;
}
You don't want to use XRaiseWindow() to try to stay on top. Some window managers will ignore it entirely. For those that don't, consider what happens if more than one app tries to do this. Boom! That's why the window manager is in charge of stacking windows, not the app.
The way you do this is to use the protocols defined in the Extended Window Manager Hints (EWMH), see: http://www.freedesktop.org/wiki/Specifications/wm-spec
Specifically here you want _NET_WM_STATE_ABOVE which is how the "Always on Top" menu item works.
If you aren't using a toolkit you'll want to get used to scavenging in toolkit source code to figure out how to do things. In this case you could look at the function gdk_window_set_keep_above() in GTK+'s X11 backend. That will show how to use the _NET_WM_STATE_ABOVE hint.
I wrote something like this in Xlib many years ago. It's a few lines of code. When your window is partially obscured you get a VisibilityNotify event, then call XRaiseWindow. Watch out for the case where two of your 'always on top' windows overlap.
Use Actual Title Buttons (http://www.actualtools.com/titlebuttons/) for example. It allows to stay any windows always on top , roll up, make transparency and etc..

Resources