at_yasu's blog

ロード的なことを

叩いてSpacesの画面が切り替わる

タイトル通り叩いてSpacesの画面を切り替わるツールを作ろうとして止まってます。Obj-CからAppleScriptを呼び出そうとしてるのですが、上手くサブルーチンが呼び出せない・・・無理なのかなぁ・・・

以下はObj-Cのソースと、AppleScriptのソース


myApplication.h

//
//  myApplication.h
//  touchLeopard
//
//  Created by 安井 惇 on 08/02/04.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import "smsutils.h"

#define myApplicationTouchLeopardName @"touchLeopard" // AppleScriptのファイル名

typedef enum {
    myAppNone = -1,
    myAppUp = 0,
    myAppDown,
    myAppRight,
    myAppLeft
} myApplicationArrowType;

@interface myApplication : NSWindow {
    sms_data_t dataSave;
    int x_stable;
    int y_stable;
    NSTimer* th;
    NSAppleScript* myAppScript;
    
    IBOutlet NSButton *arrowUpButton;
    IBOutlet NSButton *arrowDownButton;
    IBOutlet NSButton *arrowLeftButton;
    IBOutlet NSButton *arrowRightButton;
}

-(IBOutlet) checkButton:(id)sender;
-(IBOutlet) arrowKeyDown:(id)sender;

- (NSAppleScript*) getAppleScriptWithFileName:(NSString*)name;
@end


myApplication.m

//
//  myApplication.m
//  touchLeopard
//
//  Created by 安井 惇 on 08/02/04.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//
// ex, http://developer.apple.com/jp/technotes/tn2006/tn2084.html
//     フェード・イン、フェードアウトアニメーション(2):setNeedsDisplay in
//         http://homepage.mac.com/mkino2/backnumber/2001_01.html
#import "myApplication.h"

static sms_data_t SMSDATAT_ZERO () {
    static sms_data_t d;
    d.x = 0;
    d.y = 0;
    d.z = 0;
    return d;
}

@implementation myApplication

- (id)initWithContentRect:(NSRect)contentRect
                styleMask:(NSUInteger)windowStyle
                  backing:(NSBackingStoreType)bufferingType
                    defer:(BOOL)deferCreation
{
    self = [super initWithContentRect:contentRect
                            styleMask:windowStyle
                              backing:bufferingType
                                defer:deferCreation];
    if (self != nil) {
        th = nil;
        dataSave = SMSDATAT_ZERO();
        x_stable = 0;
        y_stable = 0;
    }
    
    return self;
}

- (void) dealloc
{
    [super dealloc];
}


// AppleScriptを得るメソッド
- (NSAppleScript*) getAppleScriptWithFileName:(NSString*)name
{
    NSAppleScript *script = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:name
                                                     ofType:@"scpt"
                                                inDirectory:@"Scripts"];
    if (path != nil) {
        NSURL *url = [NSURL fileURLWithPath:path];
        if (url != nil) {
            NSDictionary* error = [NSDictionary dictionary];
            script = [[NSAppleScript alloc] initWithContentsOfURL:url
                                                            error:&error];
            if (script == nil) {
                NSLog(@"%@",error);
            }
        }
    } else {
        NSLog(@"getAppleScriptWithFileName: error file is nto found %@.scpt",name);
    }
    
    NSLog(@"getAppleScriptWithFileName:return: %@", script);
    
    return script;
}

// AppleScriptを実行させる
- (void) runningAppleScriptArrowParam:(myApplicationArrowType) type
{
    
    NSAppleScript *script = [self getAppleScriptWithFileName:myApplicationTouchLeopardName];

    if (script == nil)
        return;
    
    // 第1パラメータを作成する
    NSString *param = nil;
    switch(type) {
        case myAppUp:    param = @"Up";    break;
        case myAppDown:  param = @"Down";  break;
        case myAppRight: param = @"Right"; break;
        case myAppLeft:  param = @"Left";  break;
        default: return ;
    }
    
    NSLog(@"runningAppleScriptArrowParam:param =>  %@", param);

    NSAppleEventDescriptor* firstParameter = [NSAppleEventDescriptor descriptorWithString:param];
    
    // パラメータのリストを作成し値を設定する
    NSAppleEventDescriptor* parameters = [NSAppleEventDescriptor listDescriptor];
    [parameters insertDescriptor:firstParameter atIndex:1];
    
    // AppleEventターゲットを作成する
    ProcessSerialNumber psn = {0, kCurrentProcess};
    NSAppleEventDescriptor* target = [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber
                                                                                    bytes:&psn
                                                                                   length:sizeof(ProcessSerialNumber)];
    
    // スクリプトの呼び出しメソッド名を使用してNSAppleEventDescriptorを作成する、
    // これはスクリプトステートメント「on show_message(user_message)」に使用する
    // ルーチン名は小文字にする必要がある。
    NSAppleEventDescriptor* handler = [NSAppleEventDescriptor descriptorWithString:[@"switchscreen" lowercaseString]];
    
    // AppleScriptサブルーチンのイベントを作成する、
    // メソッド名とパラメータリストを設定する
    // ここら辺の引数は適当に近い。-1708エラーはここが原因だと思われる
    // eventClassの参考
    //    http://developer.apple.com/documentation/Carbon/Reference/Apple_Event_Manager/Reference/reference.html#//apple_ref/doc/c_ref/AEEventClass
    // eventIdの参考
    //    http://developer.apple.com/documentation/Carbon/Reference/Apple_Event_Manager/Reference/reference.html#//apple_ref/doc/c_ref/AEEventID
    // forKeywordの参考
    //    http://developer.apple.com/documentation/Carbon/Reference/Apple_Event_Manager/Reference/reference.html#//apple_ref/doc/constant_group/Keyword_Parameter_Constants
    NSAppleEventDescriptor* event = [NSAppleEventDescriptor appleEventWithEventClass:kAECoreSuite
                                                                             eventID:kAEGetData
                                                                    targetDescriptor:target
                                                                            returnID:kAutoGenerateReturnID
                                                                       transactionID:kAnyTransactionID];
    [event setParamDescriptor:handler
                   forKeyword:keySelectProc];
    [event setParamDescriptor:parameters forKeyword:keyDirectObject];

    // AppleScriptを起動
    NSDictionary* errors = [NSDictionary dictionary];
    if (![script executeAppleEvent:event
                             error:&errors]) {
        NSLog(@"Error: %@",errors);
    }
    [script release];
}

/* ======================================================================== */

// open a device and get sms data.
+ (sms_data_t) getSMSData
{
    sms_data_t data = SMSDATAT_ZERO();
    sms_t sms;

    if (smsOpen(&sms)) {
        NSLog(@"SMS Open Error");
        return data;
    }
    
    /* Get the data from SMS Device. */
    if (!smsGetData(&sms, &data)) {
        data.x *= sms.unit;
        data.y *= sms.unit;
        data.z *= sms.unit;
    }
    
    /* close to device */
    smsClose(&sms);
    
    return data;
}

// 検知器から、移動幅を取り出し、スクリプトを動かす
- (void) runningSMS
{
    // open a device and get data
    sms_data_t d = [self->isa getSMSData];
    
    // 補正処理
    if (d.x < 0) d.x += 256;
    if (d.y < 0) d.y += 256;
    
    if (abs(d.x) < 20) x_stable++;
    if (abs(d.y) < 20) y_stable++;

    // 判別処理
    // 横揺れの幅と、縦揺れの幅を比べて判断
    myApplicationArrowType type = myAppNone;

    if (abs(d.x) > abs(d.y)) {

        if (abs(d.x) > 30 && x_stable > 30) {
            x_stable = 0;
        
            // d.x が 0より小さい時は左、大きい時は右
            type = (d.x <= 0) ? myAppLeft : myAppRight;
        }
        
    }
    else
    {
        if (abs(d.y) > 30 && y_stable > 30) {
            y_stable = 0;

            // d.x が 0より小さい時は下、大きい時は上
            type = (d.y <= 0) ? myAppDown : myAppUp;
        }
    }
    
    [self runningAppleScriptArrowParam:type];
}

-(IBOutlet) checkButton:(id)sender
{
    if ([(NSButton*)sender state] == NSOnState) {
        th = [NSTimer scheduledTimerWithTimeInterval:0.05
                                              target:self
                                            selector:@selector(runningSMS)
                                            userInfo:nil
                                             repeats:YES];       
    } else {
        [th invalidate];
    }
}

-(IBOutlet) arrowKeyDown:(id)sender
{
    myApplicationArrowType type = myAppNone;
    if (sender == arrowUpButton) {
        type = myAppUp;
    }
    else if (sender == arrowDownButton) {
        type = myAppDown;
    }
    else if (sender == arrowLeftButton) {
        type = myAppLeft;
    }
    else if (sender == arrowRightButton) {
        type = myAppRight;
    }
    
    NSLog(@"arrowKeyDown => %d", type);
    
    [self runningAppleScriptArrowParam:type];
}

@end


以下がAppleScript

-- touchLeopard.applescript
-- touchLeopard

--  Created by ?? ? on 08/02/04.
--  Copyright 2008 __MyCompanyName__. All rights reserved.
-- $Id: touchLeopard.applescript 64 2008-02-04 15:52:54Z yasui $
--
-- ex, http://idlysphere.blog66.fc2.com/?tag=Spaces

on gui_scripting_check()
	tell application "System Events" to set gui_scripting_enabled to UI elements enabled
	if not gui_scripting_enabled then
		tell application "System Preferences"
			activate
			set current pane to pane "com.apple.preference.universalaccess"
			display dialog "This application utilizes the built-in Graphic User Interface Scripting architecture of Mac OS X which is currently disabled." & return & return & "You can activate GUI Scripting by selecting the checkbox \"Enable access for assistive devices\" in the Universal Access preference pane." with icon 2 buttons {"OK"} default button 1
		end tell
	end if
	return gui_scripting_enabled
end gui_scripting_check

on get_spaces_numbers_key_modifiers()
	tell application "System Events" to tell spaces preferences of expose preferences to return (get key modifiers of (get properties of numbers key modifiers))
end get_spaces_numbers_key_modifiers

on list_to_string(l, d)
	tell (a reference to my text item delimiters)
		set {o, contents} to {contents, d}
		set {l, contents} to {"" & l, o}
	end tell
	return l as Unicode text
end list_to_string

on switch_to_cntkey()
	if not my gui_scripting_check() then return
	set key_modifiers to my get_spaces_numbers_key_modifiers()
	tell application "System Events"
		set key_modifier_list to {}
		if key_modifiers contains command then set end of key_modifier_list to "command down"
		if key_modifiers contains control then set end of key_modifier_list to "control down"
		if key_modifiers contains option then set end of key_modifier_list to "option down"
		if key_modifiers contains shift then set end of key_modifier_list to "shift down"
		set key_modifier_list to my list_to_string(key_modifier_list, ", ")
		if key_modifier_list ≠ "" then set key_modifier_list to " using {" & key_modifier_list & "}"
	end tell
	return key_modifier_list
end switch_to_cntkey



(*
    arg arrow type
        kUpArrow      => Up
        kDownArrow  => Down
        kLeftArrow    => Left
        kRightArrow  => Right
*)
on switchScreen(arrow)
	set k to my switch_to_cntkey()
	run script ("tell application \"System Events\" to keystroke k" & arrow & "Arrow using " & k)
	log ("tell application \"System Events\" to keystroke k" & arrow & "Arrow using " & k)
end switchScreen

on idle
end idle

on open
	log "tell application \"System Events\" to keystroke k" & arrow & "Arrow using (command down)"
end open