Activate a window using its Window ID

后端 未结 2 463
一向
一向 2021-02-07 08:30

How would I programmatically activate i.e move-to-front-and-focus a window on macOS (not belonging to my app) given its Window ID. My app would run with user grante

2条回答
  •  生来不讨喜
    2021-02-07 09:26

    For anyone looking for an Objective C solution:

    #import 
    #import 
    #import 
    #import 
    #import 
    
    bool activate_window_of_id(unsigned long wid) {
      bool success = false;
      const CGWindowLevel kScreensaverWindowLevel = CGWindowLevelForKey(kCGScreenSaverWindowLevelKey);
      CFArrayRef windowArray = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
      CFIndex windowCount = 0;
      if ((windowCount = CFArrayGetCount(windowArray))) {
        for (CFIndex i = 0; i < windowCount; i++) {
          NSDictionary *windowInfoDictionary = (__bridge NSDictionary *)((CFDictionaryRef)CFArrayGetValueAtIndex(windowArray, i));
          NSNumber *ownerPID = (NSNumber *)(windowInfoDictionary[(id)kCGWindowOwnerPID]);
          NSNumber *level = (NSNumber *)(windowInfoDictionary[(id)kCGWindowLayer]);
          if (level.integerValue < kScreensaverWindowLevel) {
            NSNumber *windowID = windowInfoDictionary[(id)kCGWindowNumber];
            if (wid == windowID.integerValue) {
              CFIndex appCount = [[[NSWorkspace sharedWorkspace] runningApplications] count];
              for (CFIndex j = 0; j < appCount; j++) {
                if (ownerPID.integerValue == [[[[NSWorkspace sharedWorkspace] runningApplications] objectAtIndex:j] processIdentifier]) {
                  NSRunningApplication *appWithPID = [[[NSWorkspace sharedWorkspace] runningApplications] objectAtIndex:j];
                  [appWithPID activateWithOptions:NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps];
                  char buf[PROC_PIDPATHINFO_MAXSIZE];
                  proc_pidpath(ownerPID.integerValue, buf, sizeof(buf));
                  NSString *buffer = [NSString stringWithUTF8String:buf];
                  unsigned long location = [buffer rangeOfString:@".app/Contents/MacOS/" options:NSBackwardsSearch].location;
                  NSString *path = (location != NSNotFound) ? [buffer substringWithRange:NSMakeRange(0, location)] : buffer;
                  NSString *app = [@" of application \\\"" stringByAppendingString:[path lastPathComponent]];
                  NSString *index = [@"set index of window id " stringByAppendingString:[windowID stringValue]];
                  NSString *execScript = [[index stringByAppendingString:app] stringByAppendingString:@"\\\" to 1"];
                  char *pointer = NULL;
                  size_t buffer_size = 0;
                  NSMutableArray *array = [[NSMutableArray alloc] init];
                  FILE *file = popen([[[@"osascript -e \"" stringByAppendingString:execScript] stringByAppendingString:@"\" 2>&1"] UTF8String], "r");
                  while (getline(&pointer, &buffer_size, file) != -1)
                    [array addObject:[NSString stringWithUTF8String:pointer]];
                  char *error = (char *)[[array componentsJoinedByString:@""] UTF8String];
                  if (strlen(error) > 0 && error[strlen(error) - 1] == '\n')
                    error[strlen(error) - 1] = '\0';
                  if ([[NSString stringWithUTF8String:error] isEqualToString:@""])
                    success = true;
                  [array release];
                  free(pointer);
                  pclose(file);
                  break;
                }
              }
            }
          }
        }
      }
      CFRelease(windowArray);
      return success;
    }
    

    Note, unlike Daniel's answer, this will not just bring the specified application's windows to the front, it will also make sure the specific window whose id matches the one specified will be the topmost out of that app's collection of windows. It will return true on success, and false on failure. I noticed it brings to front for some apps but not for others. I'm not sure why. The code it is based on does not work as advertised for its original purpose. Although, it did help me a lot to get working all the stuff I needed to answer this question. The code my answer is based on can be found here. Ignore the original usage.

提交回复
热议问题