Posts Tagged Carbon

Accessing and modifying window properties


In the previous two posts, we looked at how to retrieve a window that had focus and how to open a system preference pane. To recap, check out the previous two posts here and here. In this post, we will look how to retrieve certain properties of the window that we retrieve using the Accessibility API. We will look at how to modify the window’s position and size so that the window will occupy the left half of the screen.
Pre-Requisites

Ensure the "Enable access for assistive devices" is checked

  1. Know how to retrieve the focused window. To recap, check out the previous post.
  2. Should have checked “Enable access for assitive devices” in the Universal Access preference pane

Implementation

First, we will look at how to retrieve the window’s properties. In this example, we will look at how to retrieve the window’s position and size. To make it easier to modify these properties, we create a few variables to temporarily store the properties.

    AXUIElementRef _systemWideElement;
    AXUIElementRef _focusedApp;
    CFTypeRef _focusedWindow;
    CFTypeRef _position;
    CFTypeRef _size;

I have assumed that you have already initialized the _systemWideElement. If not, you can use the following line to do so.

    _systemWideElement = AXUIElementCreateSystemWide();

Then, we are going to use this AXUiElement to first retrieve the app that focus. Then we will retrieve the window that has focus. After which, we can get its properties by using the following method:

    extern AXError AXUIElementCopyAttributeValue (
        AXUIElementRef element,
        CFStringRef attribute,
        CFTypeRef *value);

Parameters:

  1. element – It is the system wide element
  2. attribute – For this example, the attribute will be size(NSAccessibilitySizeAttribute) and position(NSAccessibilityPositionAttribute). There are a lot more attributes that you can get. Go here for other properties.
  3. value. This is where the value of the attribute will be copied to.

Lets look at how this method can be used.

    //Get the app that has the focus
    AXUIElementCopyAttributeValue(_systemWideElement,
                                 (CFStringRef)kAXFocusedApplicationAttribute,
                                 (CFTypeRef*)&_focusedApp);

    //Get the window that has the focus
    if(AXUIElementCopyAttributeValue((AXUIElementRef)_focusedApp,
                                    (CFStringRef)NSAccessibilityFocusedWindowAttribute,
                                    (CFTypeRef*)&_focusedWindow) == kAXErrorSuccess) {

	if(CFGetTypeID(_focusedWindow) == AXUIElementGetTypeID()) {
           //Get the Window's Current Position
	   if(AXUIElementCopyAttributeValue((AXUIElementRef)_focusedWindow,
                                           (CFStringRef)NSAccessibilityPositionAttribute,
                                           (CFTypeRef*)&_position) != kAXErrorSuccess) {
	       NSLog(@"Can't Retrieve Window Position");
	   }
          //Get the Window's Current Size
          if(AXUIElementCopyAttributeValue((AXUIElementRef)_focusedWindow,
                                           (CFStringRef)NSAccessibilitySizeAttribute,
                                           (CFTypeRef*)&_size) != kAXErrorSuccess) {
		NSLog(@"Can't Retrieve Window Size");
          }
	}
    }else {
	NSLog(@"Problem with App");
    }

Now that we have retrieved the position and size of the window, we will look at how to modify these properties. In the following example, we are trying to move the window to the (0,0) coordinate and fill left half of the screen. To set a property, we will use the following method,


    extern AXError AXUIElementSetAttributeValue (
        AXUIElementRef element,
        CFStringRef attribute,
        CFTypeRef value);

The parameters are the same as the AXUIElementCopyAttributeValue method.

    NSPoint thePoint;
    thePoint.x = 0;
    thePoint.y = 0;
    //convert the NSPoint to CFTypeRef
    _position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&thePoint));

    if(AXUIElementSetAttributeValue((AXUIElementRef)_focusedWindow,
                                    (CFStringRef)NSAccessibilityPositionAttribute,
                                    (CFTypeRef*)_position) != kAXErrorSuccess){
	NSLog(@"Position cannot be changed");
    }

    NSSize theSize;
    NSSize _fullScreenSize = [[[NSScreen mainScreen] frame] size];
    if(AXValueGetType(_size) == kAXValueCGSizeType) {
	theSize.width = ((_fullScreenSize.width)/2);
	theSize.height = _fullScreenSize.height;
       // convert from NSSize to CFTypeRef
       _size = (CFTypeRef)(AXValueCreate(kAXValueCGSizeType, (const void *)&theSize));
    }

    if(AXUIElementSetAttributeValue((AXUIElementRef)_focusedWindow,
         (CFStringRef)NSAccessibilitySizeAttribute,
         (CFTypeRef*)_size) != kAXErrorSuccess){
         NSLog(@"Size cannot be modified");
    }

There we go! That should do it.

Tags: , , , , , , , ,

Retrieving the window that has focus


In this post, we are going to look at how to retrieve the window that has focus, that does not necessarily belong to your app. For this to be possible, you MUST have enabled access for assistive devices in the “Universal Access” Preference Pane.

Ensure the "Enable access for assistive devices" is checked

Firstly, we need to check if the preference has been set. To do this we have to use one of the Accessibility APIs.

    Boolean AXAPIEnabled (void);

This method returns true if the access for assistive devices have been enabled. False, otherwise. If access has not been enabled, we will inform the user to enable it and quit the app(for now. Next post we will look at how to open the preference pane).

Secondly, we need to create a system wide element that can listen to focused accessibility object regardless of which application is currently active.

    AXUIElementRef AXUIElementCreateSystemWide (void);

We have to use this element to retrieve information about the focused application and then focused window. We cannot get the focused window directly. Therefore, we have to first get the application that is in focus and the get the window that is in focus. Note: We can only get copies of the above mentioned information. To do this, we have to use the following method.

    AXError AXUIElementCopyAttributeValue (
        AXUIElementRef element,
        CFStringRef attribute,
        CFTypeRef *value);

The allowed attributes can be found here. ‘Value’ refers to the value associated with the specified attribute. Thats it folks. Now we can look at the actual implementation.

Implementation

    AXUIElementRef _systemWideElement;
    if(!AXAPIEnabled()){
        //exit
    }
    AXUIElementRef _focusedApp;
    CFTypeRef _focusedWindow;
    _systemWideElement = AXUIElementCreateSystemWide();
    AXUIElementCopyAttributeValue(_systemWideElement,
                         (CFStringRef)kAXFocusedApplicationAttribute,(CFTypeRef*)&_focusedApp);
    AXUIElementCopyAttributeValue((AXUIElementRef)_focusedApp,
                         (CFStringRef)NSAccessibilityFocusedWindowAttribute,(CFTypeRef*)&_focusedWindow)

That is all for now! Good luck. Comments are welcome.

Tags: , , , , , , ,

Adding a HotKey Listener


In this post, we will look at how to register your hot key listeners with the OS. It is not rocket science, really. All you have to do is implement one method and call two methods. First, we will look at what exactly are HotKeys. Then the three methods need to implement a HotKey and finally, the implementation itself.

What are HotKeys?
HotKeys are combinations of key presses that user uses to take advantage of a feature. For example, the Spotlight in Mac OS X has a default HotKey of Cmd+Space. Similarly, different applications use different HotKeys to simply the usage of the application.

Methods Required

OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent,void *userData);

The above method is the one you would have to implement to handle the hot keys. The function name is not a issue, you can be creative with it.

InstallApplicationEventHandler( handler, numTypes, list, userData, outHandlerRef );

The above method will install the event handler with the OS. When the user presses the appropriate keys, the OS will invoke the handler with the userData and the EventRef.

extern OSStatus RegisterEventHotKey(
     UInt32            inHotKeyCode,
     UInt32            inHotKeyModifiers,
     EventHotKeyID     inHotKeyID,
     EventTargetRef    inTarget,
     OptionBits        inOptions,
     EventHotKeyRef *  outRef);

This is the method that actually registers for the HotKeys. The points below will give you some idea of how to use this method.

  1. inHotKeyModifiers: This parameter takes the key codes for the command(cmdKey),alt/option(optionKey),crtl(controlKey) and shift(shiftKey) keys. The variables in the brackets are the key codes that you would have to use. You can mix and the match the key codes. For example, you can say cmdKey+shiftKey or controlKey+cmdKey, etc…
  2. inHotKeyCode: This parameter takes the virtual key codes of the keyboard. The virtual key codes can be found here . For example, if you want to use cmdKey+m as your HotKey. You would register the cmdKey in the previous parameter and the char ‘m’ in the this parameter.
  3. inHotKeyID: This parameter will be used to differentiate one HotKey from another. The EventHotKeyID will contain a signature and a id. The signature is to ensure the HotKey is registered with your app and the id will identify which hot key was invoked.
  4. inTarget: This is generally GetEventDispatcherTarget();
  5. outRef: The EventHotKeyRef is required for the unregistering the HotKey later on. Be sure to keep track of one EventHotKeyRef for each HotKey. This will help you when you are trying to modify just one HotKey.

Implementation
First we are going to implement the method that handles the HotKey events.

OSStatus MyHotKeyHander(EventHandlerCallRef nextHandler,EventRef theEvent,void *userData){
	//Do something once the key is pressed
	EventHotKeyID hotKeyID;
	GetEventParameter(theEvent,kEventParamDirectObject,typeEventHotKeyID,
                                        NULL,sizeof(hotKeyID),NULL,&hotKeyID);
	int temphotKeyId = hotKeyID.id; //Get the id, so we can know which HotKey we are handling.
        switch(temphotKeyId){
             //Now you know which HotKey. Do something...
       }
}

Then we are going to install a ApplicationEventHandler.

     EventTypeSpec _eventType;
     _eventType.eventClass = kEventClassKeyboard;
     _eventType.eventKind = kEventHotKeyPressed;
     InstallApplicationEventHandler(&MyHotKeyHander,1,_eventType,self,NULL);
     //Here i am returning 'self' as the user data. So when the MyHotKeyHandler
     // is called, it will contain self as the data. You can
     // declare any object you want and that will end up as the userData in
     // MyHotKeyHandler.

Now the final step to register the HotKeys.

	OSStatus error;
	EventHotKeyID hotKeyID;
	EventHotKeyRef hotKeyRef;

	hotKeyID.signature='hk';
	hotKeyID.id	= 1;

	error = RegisterEventHotKey(cmdKey+optionKey, 123 , hotKeyID,
                                 GetEventDispatcherTarget(), 0, &hotKeyRef);
                                 // cmd+option+(left arrow)

	if(error){
		//handle error
	}

If you want to unregister the HotKey, all you would have to do is call the UnregisterEventHotKey method with the EventHotKeyRef.

        OSStatus error;
	EventHotKeyRef hotKeyRef = // The HotKeyRef for the HotKey you want to unregister;
	error = UnregisterEventHotKey(hotKeyRef);
	if(error){
		//handle error
	}

Thats it folks! Good luck! Comments are always welcome.

Tags: , , ,