Introducing Mac::Glue

Introducing Mac::Glue
by Simon Cozens, Chris Nandor |

Some Examples

In a couple of weeks, we'll be presenting a "Mac::Glue Hacks" article in the spirit of the O'Reilly hacks books series, with several simple Mac::Glue-based application scripting tricks to whet your appetite and explore what Mac::Glue can do. But to get you started, here's a couple we found particularly useful.

First, iTunes allows you to give a rating to your favorite songs, on the scale of zero to five stars. Actually, internally, this is stored in the iTunes database as a number between 0 and 100. Simon keeps iTunes playing randomly over his extensive music collection, and every time an interesting track comes up, he runs this script:



my $itunes = Mac::Glue->new("iTunes");

exit unless $itunes->prop("player state")->get eq "playing";



my $rating = $itunes->prop("current track")->prop("rating");

$rating->set(to => ($rating->get + 20))

  if $rating->get < 81;

As well as getting properties from Mac::Glue, we can also set them back with the set method.

One more complex example is the happening script Chris uses to publish details of what's going on at his computer. As well as simply reporting the current foremost application, it dispatches based on that application to report more information. For instance, if Safari has the focus, it reports what web page is being looked at; if it's the Terminal, what program is currently being run. It also contacts iTunes to see what song is playing, and if there's nothing playing on a local iTunes, asks likely other computers on the network if they're playing anything.

Once happening has discovered what's going on, it checks to see if the iChat status is set to "Available," and if so, resets itself it to report this status. Let's break down happening and see how it accomplishes each of these tasks.

First, to work out the name of the currently focused application:



my $system = get_app('System Events') or return;

$app    ||= $system->prop(name => item => 1,

    application_process => whose(frontmost => equals => 1)

);



$app->get;

get_app is just a utility function that memorizes the process of calling Mac::Glue->new($app_name); since loading up the glue file is quite expensive, keeping around application glue objects is a big speed-saving approach.

The next incantation shows you how natural Mac::Glue programming can look, but also how much you need to know about how the Apple environment works. We're asking the System Events library to tell us about the application process that matches a certain condition. Mac::Glue exports the whose function to create conditions.

The important thing about this is the fact that we use $app ||= .... The construction that we saved in $app does not give us "the name of the front-most application at this moment," but it represents the whole concept of "the name of the front-most application." At any time in the future, we can call get on it, and it will find out and return the name of the front-most application at that time, even if it has changed since the last time you called get.

Now that we know what the front-most application is, we can look it up in a hash that contains subroutines returning information specific to that application. For instance, here's the entry for Safari:



Safari => sub { my ($glue) = @_;

                my $obj = $glue->prop(url => document => 1 => window => 1);

                my $url = $obj->get;

                return URI->new($url)->host if $url;

This returns the host part of the URL in the first document in the first window. For ircle, an IRC client, this code will get the channel and server name for the current connection:



ircle       => sub { sprintf("%s:%s",

               $_[0]->prop('currentchannel')->get,

               $_[0]->prop(servername => connection =>

                   $_[0]->prop('currentconnection')->get

               )->get

              )

            },

A decent default action is to return the window title:



default     => sub { my($glue) = @_;

                     my $obj = $objs{$glue->{APPNAME}} ||=

                               $glue->prop(name => window => 1);

                     $obj->get;

                   },

As before, we cache the concept of "the name of the current window" and only create it when we don't have one already.

Now let's look at the "Now playing in iTunes" part:



$state  ||= $itunes->prop('player state');

return unless $state->get eq "playing";



$track  ||= $itunes->prop('current track');

%props    = map { $_ => $track->prop($_) } qw(name artist)

            unless keys %props;



my %info;

for my $prop (keys %props) {

    $info{$prop} = $props{$prop}->get;

}

This first checks to see if iTunes is playing, and returns unless it is. Next, we look for the current track, and get handles to the name and artist properties of that track, as in our previous iTunes example.

Finally, when we've set up all the handles we need, we call get to turn them into real data. This populates %info with the name and artist of the currently playing track.

Now that we have the current application name, the extra information, and the current track, we can publish them as the iChat status, with this subroutine:



use Mac::Apps::Launch qw(IsRunning);



sub ichat {

    my($output) = @_;



    my $ichat = get_app('iChat') or return;

    return unless IsRunning($ichat->{ID});



    $status  ||= $ichat->prop('status');

    return unless $status->get eq 'available';



    $message ||= $ichat->prop('status message');

    $message->set(to => $output);

}

First, we have the IsRunning subroutine from Mac::AppleEvents::Simple, which takes the old-style four-character ID of the application we want to ask about. The ID slot of the glue object will tell us this ID, and so we can immediately give up setting the iChat status if iChat isn't even running. Then we use set as before to change the status to whatever we want.

Finally, we mentioned that happening can also ask other hosts what's playing on their iTunes as well. This is because, if "Remote Apple Events" is turned on in the Sharing preferences, Macs support passing these Apple events between machines. Of course, this often requires authentication, so when it first contacts a host to send an Event, happening will pop-up a login box to ask for credentials -- this is all handled internally by the operating system. Here's the code that happening actually uses:



my $found = 0;

if (IsRunning($itunes->{ID})) {

    $itunes->ADDRESS;

    $found = 1 if $state->get eq 'playing';

}



unless ($found) {

    for my $host (@hosts) {

        next unless $hosts{$host} + 60 < time();

        $itunes->ADDRESS(eppc => iTunes => $host);

        $found = 1, last if $state->get eq 'playing';

        $hosts{$host} = time();

    }

}

The first paragraph checks to see if iTunes is running locally. If so, we're done. If not, we're going to have to ask the hosts specified in the @hosts array about it. The first and last lines inside the for loop simple ensure that hosts are only tried every minute at most. The second line in there is the interesting one, though:



$itunes->ADDRESS(eppc => iTunes => $host);

This changes the iTunes glue handle from being a local one to being one that contacts the "iTunes" application on host $host over EPPC, the remote Apple events transport.

Because $state is the player status of $itunes, it will now return the correct status even though $itunes now refers to an application on a different computer! Similarly, all the handles we have to the artist and name of the current track will correctly refer to $itunes, no matter which iTunes instance that means.

We hope you'll join us next time for more Mac::Glue tips and tricks, as we look at real-life applications of scripting Mac applications in Perl.

Prev  [1] [2] 

Close    To Top
  • Prev Article-Programming:
  • Next Article-Programming:
  • Now: Tutorial for Web and Software Design > Programming > Perl > Programming Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction