Controlling OSX windows
Asked Answered
F

5

12

I'm trying to control windows of a foreign OSX applications from my application. I'd like to 1. move the windows on the screen 2. resize the windows on the screen 3. change the currently active window of the application 4. get the currently active window.

(And I'd like to do this either through ObjC/C/C++ apis).

What are the API calls that I should be looking for, considering that I have the CGWindowIDs of the windows that I want to control? That is, I'd expect to find functions with signatures of something like: MoveWindow(CGWindowID winId, int x, int y), ResizeWindow(CGWindowID winId, int width, int height), Activatewindow(CGWindowID winId), CGWindowID GetCurrentlyActivatedWindow().

For 3, I'm already using SetFrontProcess to pull a process to be up front, but this doesn't let me select the specific window of a process if it has multiple.

Flop answered 13/11, 2009 at 17:49 Comment(0)
G
12

One way of doing this is indeed to use the accessibility APIs.

An application I'm developing is doing just this to get the front window, the document path of the front window and many other attributes.

The way I'm doing this is through AppleScript. It can be clumsy at times, but it seems to be fairly reliable. I use AppScript to send AppleScript from within my Cocoa app. It's thread safe and more stable than the alternatives - either Scripting Bridge or NSAppleScript.

The difficult bit will be identifying a window using it's window ID in AppleScript - AppleScript doesn't seem to have a window ID property that matches up to CGWindowID. However, you can get any window you want using AppleScript.

  1. Move frontmost window to 100, 100

    tell application "System Events"
     set theprocess to the first process whose frontmost is true
     set thewindow to the value of attribute "AXFocusedWindow" of theprocess
        set position of thewindow to {100, 100}
    end tell
    
  2. Resize frontmost window to 200, 300

    tell application "System Events"
      set theprocess to the first process whose frontmost is true
      set thewindow to the value of attribute "AXFocusedWindow" of theprocess
         set size of thewindow to {200, 300}
    end tell
    
  3. Change current window of the frontmost application

    tell application "System Events"
      set theprocess to the first process whose frontmost is true
      set thewindow to the value of attribute "AXFocusedWindow" of theprocess
         set size of thewindow to {200, 300}
         windows of theprocess
         -- Code to get ID of window you want to activate
         tell window 2 of theprocess -- assuming it's window 2
               perform action "AXRaise"
         end tell
    end tell
    
  4. Window that's active

    tell application "System Events"
     set theprocess to the first process whose frontmost is true
     set thewindow to the value of attribute "AXFocusedWindow" of theprocess
    end tell
    

There's an application available for AppScript called ASTranslate that will turn this AppleScript into the Objective C code that calls the relevant commands in AppScript.

For more information on how to get the size and bounds of windows (these are read only as far as I'm aware) see the Son of Grab sample application.

Ga answered 14/11, 2009 at 3:31 Comment(3)
These code examples helped me in several ways, since I want to achieve the same as Sami. :) Thanks a lot!Cannabin
No problems. It was a pleasure to help - this community has given me so many great answers to my questions it was nice to be able to offer something back.Ga
Is there a method to just focus the window, without accessibilty? My uses case is, my app has focus, it lists all running apps, selecting an app in that list will focus it. Currently if I use activateWithOptions: it is not unminimizing the minimized windows, so if all windows minmized it does nothing. So I'm trying to find another way to focus (which uniminimizes)Ethbin
B
2

Based on your comment, you can do this with the OS X accessibility API, but I believe "access for assistive devices" must be turned on in the user's Accessibility preferences.

There's a third-party shareware app whose name escapes me at the moment that lets you move any window around (and I think resize it) with keyboard commands.

Batts answered 13/11, 2009 at 18:18 Comment(1)
Yep, MercuryMover was it. :-)Batts
O
1

Accessibility needs to be enabled in System Preferences for this to work. It's applescript, but could be used in objective-c with the scripting bridge.

-- Moves safari window by deltaposition
tell application "System Events"
    tell application "Safari"
        set win to first window
        set b to bounds of win
        set deltaposition to {50, 0}
        set bounds of first window to {(item 1 of b) + (item 1 of deltaposition), (item 2 of b) + (item 2 of deltaposition), (item 3 of b) + (item 1 of deltaposition), (item 4 of b) + (item 2 of deltaposition)}
    end tell
end tell
Omnifarious answered 14/11, 2009 at 3:44 Comment(0)
C
1

Use CGWindowListCopyWindowInfo to grab the kCGWindowOwnerPID of each window. Then you can use "distributed objects" if you want access to ObjectiveC stuff, or Mach RPC for other stuff. All of this is documented at http://developer.apple.com

There are many good reasons for wanting to message other applications - for instance when developing novel user interfaces that are neither mice nor keyboards.

Caltrop answered 26/8, 2010 at 5:39 Comment(1)
Hi bgri may you please provide some example code. I have used CGWindowListCopyWindowInfo to get all the kCGWindowOwnerPIDs but I cant figure out how to focus windows by it.Ethbin
R
-1

I think you should explain why you want to do this. The only utilities I know of that move all other apps' windows are Spaces and Expose, which are both supplied by Apple. If you want to take over an entire screen, there's public API for that but moving another apps' windows sounds suspicious.

Ravel answered 13/11, 2009 at 17:57 Comment(2)
I'm wanting to automate a 3rd party application. To do this, I need to be able to get the current state of the application and send it mouse/keyboard events. ResizeWindow will help me with getting the current state through screen scraping, and ActivateWindow is needed to be able to reliably send mouse/keyboard events to the application. GetCurrentlyActivatedWindow would be used to bring back the original active window, so have the automation work without intefering with the user as much as possible. MoveWindow would be just a 'nice to have' thing for developing this.Flop
Ok, that being the case you should look into the Accessibility/Scripting API. Any Cocoa app picks up some degree of scriptability, which may be as much as you need.Ravel

© 2022 - 2024 — McMap. All rights reserved.