Changeset 91 for trunk/appswitch/main.c


Ignore:
Timestamp:
02/10/03 20:08:11 (18 years ago)
Author:
Nicholas Riley
Message:

appswitch 1.0b1 code changes

Location:
trunk/appswitch
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/appswitch

    • Property svn:ignore set to
      build
      *~
      .DS_Store
  • trunk/appswitch/main.c

    r85 r91  
    1919
    2020#include <unistd.h>
     21#include <sys/ioctl.h>
    2122#include "CPS.h"
    2223
    2324const char *APP_NAME;
    2425
    25 #define VERSION "1.0d1"
     26#define VERSION "1.0b1"
    2627
    2728struct {
     
    3233    char *path;
    3334    enum {
    34         MATCH_UNKNOWN, MATCH_CREATOR, MATCH_BUNDLE_ID, MATCH_NAME, MATCH_PID, MATCH_PATH
     35        MATCH_UNKNOWN, MATCH_FRONT, MATCH_CREATOR, MATCH_BUNDLE_ID, MATCH_NAME, MATCH_PID, MATCH_PATH, MATCH_ALL
    3536    } matchType;
     37    enum {
     38        APP_NONE, APP_SWITCH, APP_SHOW, APP_HIDE, APP_KILL, APP_LIST, APP_PRINT_PID
     39    } appAction;
     40    enum {
     41        ACTION_NONE, ACTION_SHOW_ALL, ACTION_HIDE_OTHERS
     42    } action;
     43    enum {
     44        FINAL_NONE, FINAL_SWITCH
     45    } finalAction;
    3646} OPTS =
    3747{
    38     kLSUnknownCreator, NULL, NULL, -1, NULL, MATCH_UNKNOWN
     48    kLSUnknownCreator, NULL, NULL, -1, NULL, MATCH_UNKNOWN, APP_NONE, ACTION_NONE, FINAL_NONE
    3949};
    4050
     
    5565
    5666void usage() {
    57     fprintf(stderr, "usage: %s [-c creator] [-i bundleID] [-a name] [-p pid] [path]\n"
     67    fprintf(stderr, "usage: %s [-sShHkFlP] [-c creator] [-i bundleID] [-a name] [-p pid] [path]\n"
     68            "  -s            show application, bring windows to front (do not switch)\n"
     69            "  -S            show all applications\n"
     70            "  -h            hide application\n"
     71            "  -H            hide other applications\n"
     72            "  -k            kill application\n"
     73            "  -l            list applications\n"
     74            "  -P            print application process ID\n"
     75            "  -F            bring current application's windows to front\n"
    5876            "  -c creator    match application by four-character creator code ('ToyS')\n"
    5977            "  -i bundle ID  match application by bundle identifier (com.apple.scripteditor)\n"
    6078            "  -p pid        match application by process identifier [slower]\n"
    61             "  -a name       match application by name\n", APP_NAME);
     79            "  -a name       match application by name\n"
     80            , APP_NAME);
    6281    fprintf(stderr, "appswitch "VERSION" (c) 2003 Nicholas Riley <http://web.sabi.net/nriley/software/>.\n"
    6382            "Please send bugs, suggestions, etc. to <appswitch@sabi.net>.\n");
     
    113132    if (argc == 1) usage();
    114133
    115     const char *opts = "c:i:p:a:";
    116    
    117     ch = getopt(argc, argv, opts);
    118 
    119     switch (ch) {
    120         case 'p':
    121             if (sscanf(optarg, "%d", &OPTS.pid) != 1 || OPTS.pid < 0)
    122                 errexit("invalid process identifier (argument of -p)");
    123             OPTS.matchType = MATCH_PID;
    124             break;
    125         case 'c':
    126             if (strlen(optarg) != 4) errexit("creator (argument of -c) must be four characters long");
    127             OPTS.creator = *(OSTypePtr)optarg;
    128             OPTS.matchType = MATCH_CREATOR;
    129             break;
    130         case 'i':
    131             OPTS.bundleID = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());
    132             OPTS.matchType = MATCH_BUNDLE_ID;
    133             break;
    134         case 'a':
    135             OPTS.name = strdup(optarg);
    136             OPTS.matchType = MATCH_NAME;
    137             break;
    138         case -1:
    139             break;
    140         default: usage();
    141     }
    142 
    143     if (getopt(argc, argv, opts) != -1)
    144         errexit("choose only one of -c, -i, -u, -p, -a options");
     134    const char *opts = "c:i:p:a:sShHklPF";
     135
     136    while ( (ch = getopt(argc, argv, opts)) != -1) {
     137        switch (ch) {
     138            case 'p':
     139                if (OPTS.matchType != MATCH_UNKNOWN) errexit("choose only one of -c, -i, -p, -a options");
     140                if (sscanf(optarg, "%d", &OPTS.pid) != 1 || OPTS.pid < 0)
     141                    errexit("invalid process identifier (argument of -p)");
     142                OPTS.matchType = MATCH_PID;
     143                break;
     144            case 'c':
     145                if (OPTS.matchType != MATCH_UNKNOWN) errexit("choose only one of -c, -i, -p, -a options");
     146                if (strlen(optarg) != 4) errexit("creator (argument of -c) must be four characters long");
     147                OPTS.creator = *(OSTypePtr)optarg;
     148                OPTS.matchType = MATCH_CREATOR;
     149                break;
     150            case 'i':
     151                if (OPTS.matchType != MATCH_UNKNOWN) errexit("choose only one of -c, -i, -p, -a options");
     152                OPTS.bundleID = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());
     153                OPTS.matchType = MATCH_BUNDLE_ID;
     154                break;
     155            case 'a':
     156                if (OPTS.matchType != MATCH_UNKNOWN) errexit("choose only one of -c, -i, -p, -a options");
     157                OPTS.name = strdup(optarg);
     158                OPTS.matchType = MATCH_NAME;
     159                break;
     160            case 's':
     161                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     162                OPTS.appAction = APP_SHOW;
     163                break;
     164            case 'h':
     165                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     166                OPTS.appAction = APP_HIDE;
     167                break;
     168            case 'k':
     169                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     170                OPTS.appAction = APP_KILL;
     171                break;
     172            case 'l':
     173                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     174                OPTS.appAction = APP_LIST;
     175                break;
     176            case 'P':
     177                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     178                OPTS.appAction = APP_PRINT_PID;
     179                break;
     180            case 'S':
     181                if (OPTS.action != ACTION_NONE) errexit("choose -S, -H or neither option");
     182                OPTS.action = ACTION_SHOW_ALL;
     183                break;
     184            case 'H':
     185                if (OPTS.action != ACTION_NONE) errexit("choose -S, -H or neither option");
     186                OPTS.action = ACTION_HIDE_OTHERS;
     187                break;
     188            case 'F':
     189                if (OPTS.finalAction != FINAL_NONE) errexit("choose only one -F option");
     190                OPTS.finalAction = FINAL_SWITCH;
     191                break;
     192            default: usage();
     193        }
     194    }
    145195
    146196    argc -= optind;
     
    150200
    151201    if (OPTS.matchType == MATCH_UNKNOWN) {
    152         if (argc != 1) usage();
    153         OPTS.path = argv[0];
    154         OPTS.matchType = MATCH_PATH;
    155     }
    156 }
    157 
    158 int main (int argc, char * const argv[]) {
    159     OSStatus err;
    160 
    161     APP_NAME = argv[0];
    162     getargs(argc, argv);
    163    
     202        if (argc == 0) {
     203            if (OPTS.appAction == APP_LIST) {
     204                OPTS.matchType = MATCH_ALL;
     205            } else if (OPTS.action != ACTION_NONE || OPTS.finalAction != FINAL_NONE) {
     206                OPTS.matchType = MATCH_FRONT;
     207            } else usage();
     208        } else if (argc == 1) {
     209            OPTS.path = argv[0];
     210            OPTS.matchType = MATCH_PATH;
     211        } else usage();
     212    }
     213
     214    if (OPTS.matchType != MATCH_FRONT && OPTS.appAction == APP_NONE)
     215        OPTS.appAction = APP_SWITCH;
     216
     217}
     218
     219CPSProcessSerNum frontApplication() {
     220    CPSProcessSerNum psn;
     221    OSStatus err = CPSGetFrontProcess(&psn);
     222    if (err != noErr) osstatusexit(err, "can't get frontmost process");
     223#if DEBUG
     224    fprintf(stderr, "front application PSN %ld.%ld\n", psn.hi, psn.lo);
     225#endif
     226    return psn;
     227}
     228
     229CPSProcessSerNum matchApplication(CPSProcessInfoRec *info) {
    164230    long pathMaxLength = pathconf("/", _PC_PATH_MAX);
    165231    long nameMaxLength = pathconf("/", _PC_NAME_MAX);
     
    170236    if (path == NULL || name == NULL) errexit("can't allocate memory for path or filename buffer");
    171237
    172     // need to establish connection with window server
    173     InitCursor();
    174    
     238    if (OPTS.matchType == MATCH_FRONT) return frontApplication();
     239
     240    OSStatus err;
    175241    CPSProcessSerNum psn = {
    176242        kNoProcess, kNoProcess
    177243    };
    178     CPSProcessInfoRec info;
    179244    int len;
     245    char *format = NULL;
     246    if (OPTS.appAction == APP_LIST) {
     247        int termwidth = 80;
     248        struct winsize ws;
     249        char *banner = "       PSN   PID TYPE CREA NAME                ";
     250                     // 12345678.0 12345 1234 1234 12345678901234567890
     251        printf("%s PATH\n", banner);
     252        if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) != -1 ||
     253             ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) != -1 ||
     254             ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) != -1) ||
     255            ws.ws_col != 0) termwidth = ws.ws_col;
     256        int pathlen = termwidth - strlen(banner) - 1;
     257        asprintf(&format, "%%8ld.%%ld %%5ld %%c%%c%%c%%c %%c%%c%%c%%c %%-20.20s %%-%d.%ds\n", pathlen, pathlen);
     258    }
     259   
    180260    while ( (err = CPSGetNextProcess(&psn)) == noErr) {
    181         err = CPSGetProcessInfo(&psn, &info, path, pathMaxLength, &len, name, nameMaxLength);
     261        err = CPSGetProcessInfo(&psn, info, path, pathMaxLength, &len, name, nameMaxLength);
    182262        if (err != noErr) osstatusexit(err, "can't get information for process PSN %ld.%ld", psn.hi, psn.lo);
    183263
     
    185265        fprintf(stderr, "%ld.%ld: %s : %s\n", psn.hi, psn.lo, name, path);
    186266#endif
    187        
     267
    188268        switch (OPTS.matchType) {
    189             case MATCH_UNKNOWN:
    190                 break;
    191             case MATCH_CREATOR: if (OPTS.creator != info.ExecFileCreator) continue;
     269            case MATCH_ALL:
     270                break;
     271            case MATCH_CREATOR: if (OPTS.creator != info->ExecFileCreator) continue;
    192272                break;
    193273            case MATCH_NAME: if (strcmp(name, OPTS.name) != 0) continue;
    194274                break;
    195             case MATCH_PID: if (OPTS.pid != info.UnixPID) continue;
     275            case MATCH_PID: if (OPTS.pid != info->UnixPID) continue;
    196276                break;
    197277            case MATCH_PATH: if (strcmp(path, OPTS.path) != 0) continue;
    198278                break;
    199279            case MATCH_BUNDLE_ID:
    200             {
    201                 CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), false);
    202                 if (url == NULL) errexit("can't get bundle location for process '%s' (PSN %ld.%ld, pid %d)", name, psn.hi, psn.lo, info.UnixPID);
    203                 CFBundleRef bundle = CFBundleCreate(NULL, url);
    204                 if (bundle != NULL) {
    205                     CFStringRef bundleID = CFBundleGetIdentifier(bundle);
     280               {
     281                   CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), false);
     282                   if (url == NULL) errexit("can't get bundle location for process '%s' (PSN %ld.%ld, pid %ld)", name, psn.hi, psn.lo, info->UnixPID);
     283                   CFBundleRef bundle = CFBundleCreate(NULL, url);
     284                   if (bundle != NULL) {
     285                       CFStringRef bundleID = CFBundleGetIdentifier(bundle);
    206286#if DEBUG
    207                     CFShow(bundleID);
     287                       CFShow(bundleID);
    208288#endif
    209                     if (bundleID != NULL && CFStringCompare(OPTS.bundleID, bundleID, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
    210                         break;
    211                 }
    212                 CFRelease(url);
    213                 continue;
    214             }
     289                       if (bundleID != NULL && CFStringCompare(OPTS.bundleID, bundleID, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
     290                           break;
     291                   }
     292                   CFRelease(url);
     293                   continue;
     294               }
    215295            default:
    216                 errexit("unknown match type");
     296                errexit("internal error: invalid match type");
    217297        }
    218         err = CPSSetFrontProcess(&psn);
    219         if (err != noErr) osstatusexit(err, "can't set front process");
    220         exit(0);
     298        if (OPTS.appAction == APP_LIST) {
     299            char *type = (char *)&(info->ExecFileType), *crea = (char *)&(info->ExecFileCreator);
     300            printf(format, psn.hi, psn.lo, info->UnixPID,
     301                   type[0], type[1], type[2], type[3],
     302                   crea[0], crea[1], crea[2], crea[3],
     303                   name, path);
     304            continue;
     305        }
     306        return psn;
    221307    }
    222308    if (err != procNotFound) osstatusexit(err, "can't get next process");
    223309
     310    if (OPTS.appAction == APP_LIST) return frontApplication();
     311
    224312    errexit("can't find matching process");
    225     return 0;
    226 }
     313    return psn;
     314}
     315
     316int main (int argc, char * const argv[]) {
     317    OSStatus err = noErr;
     318
     319    APP_NAME = argv[0];
     320    getargs(argc, argv);
     321
     322    // need to establish connection with window server
     323    InitCursor();
     324
     325    CPSProcessInfoRec info;
     326    CPSProcessSerNum psn = matchApplication(&info);
     327
     328    const char *verb;
     329    switch (OPTS.appAction) {
     330        case APP_NONE: break;
     331        case APP_LIST: break; // already handled in matchApplication
     332        case APP_SWITCH: err = CPSSetFrontProcess(&psn); verb = "set front"; break;
     333        case APP_SHOW: err = CPSPostShowReq(&psn); verb = "show"; break;
     334        case APP_HIDE: err = CPSPostHideReq(&psn); verb = "hide"; break;
     335        case APP_KILL: err = CPSPostKillRequest(&psn, kNilOptions); verb = "kill"; break;
     336        case APP_PRINT_PID:
     337            if (info.UnixPID <= 0) errexit("can't get process ID");
     338            printf("%lu\n", info.UnixPID); // pid_t is signed, but this field isn't
     339            break;
     340        default:
     341            errexit("internal error: invalid application action");
     342    }
     343    if (err != noErr) osstatusexit(err, "can't %s process", verb);
     344
     345    switch (OPTS.action) {
     346        case ACTION_NONE: break;
     347        case ACTION_SHOW_ALL: err = CPSPostShowAllReq(&psn); verb = "show all"; break;
     348        case ACTION_HIDE_OTHERS: err = CPSPostHideMostReq(&psn); verb = "hide other"; break;
     349        default:
     350            errexit("internal error: invalid action");
     351    }
     352    if (err != noErr) osstatusexit(err, "can't %s processes", verb);
     353
     354    switch (OPTS.finalAction) {
     355        case FINAL_NONE: break;
     356        case FINAL_SWITCH:
     357            psn = frontApplication();
     358#if DEBUG
     359            fprintf(stderr, "posting show request for %ld.%ld\n", psn.hi, psn.lo);
     360#endif
     361            if (OPTS.action != ACTION_NONE) usleep(750000); // XXX
     362            err = CPSPostShowReq(&psn) || CPSSetFrontProcess(&psn);
     363            verb = "bring current application's windows to the front";
     364            break;
     365        default:
     366            errexit("internal error: invalid final action");   
     367    }
     368    if (err != noErr) osstatusexit(err, "can't %s", verb);
     369
     370    exit(0);
     371}
Note: See TracChangeset for help on using the changeset viewer.