All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sikuli.natives.MacUtil Maven / Gradle / Ivy

/*
 * Copyright 2010-2014, Sikuli.org, sikulix.com
 * Released under the MIT License.
 *
 * modified RaiMan 2013
 */
package org.sikuli.natives;

import org.sikuli.script.App;
import org.sikuli.script.RunTime;
import org.sikuli.script.Runner;

import javax.swing.*;
import java.awt.*;
import java.util.Map;

public class MacUtil implements OSUtil {

  private static boolean _askedToEnableAX = false;
  private String usedFeature;
  private static RunTime runTime = null;
  
	@Override
  public void checkLibAvailability() {
    runTime = RunTime.get();
    RunTime.loadLibrary("MacUtil");
  }

  /*
  tell application "System Events"
    set found to "NotFound"
    try
      set found to first item of (processes whose name is "#APP#")
      set found to first item of (processes whose unix id is equal to #PID#)
    end try
    found
  end tell
  if not found is equal to "NotFound" then
    set windowName to ""
    try
    set windowName to name of first window of application "#APP#"
    end try
    set found to {name of found, «class idux» of found, windowName}
  end if
  found
  */
  static String cmd = "tell application \"System Events\"\n"
          + "set found to \"NotFound\"\n"
          + "try\n"
          + "#LINE#\n"
          + "end try\n"
          + "end tell\n"
          + "if not found is equal to \"NotFound\" then\n"
          + "set windowName to \"\"\n"
          + "try\n"
          + "set windowName to name of first window of application (name of found)\n"
          + "end try\n"
          + "set found to {name of found, «class idux» of found, windowName}\n"
          + "end if\n" +
            "found\n";
  static String cmdLineApp = "set found to first item of (processes whose name is \"#APP#\")";
  static String cmdLinePID = "set found to first item of (processes whose unix id is equal to #PID#)";

  @Override
  public App.AppEntry getApp(int appPID, String appName) {
    App.AppEntry app = null;
    String name = "";
    String theCmd = "";
    int pid = -1;
    Object filter;
    if (appPID < 0) {
      filter = appName;
    } else {
      filter = appPID;
    }
    if (filter instanceof String) {
      name = (String) filter;
      theCmd = cmd.replace("#LINE#", cmdLineApp);
      theCmd = theCmd.replaceAll("#APP#", name);
    } else if (filter instanceof Integer) {
      pid = (Integer) filter;
      theCmd = cmd.replace("#LINE#", cmdLinePID);
      theCmd = theCmd.replaceAll("#PID#", "" + pid);
    } else {
      return app;
    }
    int retVal = Runner.runas(theCmd, true);
    String result = RunTime.get().getLastCommandResult();
    if (retVal > -1) {
      if (!result.contains("NotFound")) {
        String[] parts = result.split(",");
        String title = parts[2];
        if (parts.length > 3) {
          for (int i = 3; i < parts.length; i++) {
            title += "," + parts[i];
          }
        }
        app = new App.AppEntry(parts[0].trim(), parts[1].trim(), title.trim(), "", "");
      }
    }
    return app;
  }

  @Override
  public int isRunning(App.AppEntry app) {
    if (app.pid > 0) {
      return 1;
    }
    if (app.name.isEmpty()) {
      return -1;
    }
    if (getWindow(app.name, 0) != null) {
      return 1;
    }
    App.AppEntry ae = getApp(app.pid, app.name);
    if (ae != null && ae.pid > 0) {
      return 1;
    }
    return 0;
  }

  @Override
  public int open(String appName) {
    if (_openApp(appName)) {
      return 0;
    }
    return -1;
  }

  @Override
  public int open(App.AppEntry app) {
    String appName = app.execName.startsWith(app.name) ? app.name : app.execName;
    int retval = 0;
    if (runTime.osVersion.startsWith("10.10.")) {
      if (Runner.runas(String.format("tell app \"%s\" to activate", appName), true) != 0) {
        retval = -1;
      }
    } else {
      retval = open(appName);
    }
		if (retval == 0) {
				retval = getPID(appName);
		}
		return retval;
  }

  @Override
  public int switchto(String appName) {
    return open(appName);
  }

  @Override
  public int switchto(int pid, int num) {
    return -1;
  }

  // ignore winNum on Mac
  @Override
  public int switchto(String appName, int winNum) {
    return open(appName);
  }

  @Override
  public int switchto(App.AppEntry app, int num) {
    String appName = app.execName.startsWith(app.name) ? app.name : app.execName;
    int retval = 0;
    if (runTime.osVersion.startsWith("10.10.")) {
      if (Runner.runas(String.format("tell app \"%s\" to activate", appName), true) < 0) {
        retval = -1;
      }
    } else {
      retval = open(appName);
    }
		if (retval == 0) {
				retval = getPID(appName);
		}
    return retval;
  }

  @Override
  public int close(String appName) {
    try {
      String cmd[] = {"sh", "-c",
        "ps aux |  grep \"" + appName + "\" | awk '{print $2}' | xargs kill"};
      Process p = Runtime.getRuntime().exec(cmd);
      p.waitFor();
      return p.exitValue();
    } catch (Exception e) {
      return -1;
    }
  }

  @Override
  public int close(int pid) {
    try {
      String cmd[] = {"sh", "-c", "kill " + pid};
      Process p = Runtime.getRuntime().exec(cmd);
      p.waitFor();
      return p.exitValue();
    } catch (Exception e) {
      return -1;
    }
  }

  @Override
  public int close(App.AppEntry app) {
    if (app.pid > -1) {
      return close(app.pid);
    }
    String appName = app.execName.startsWith(app.name) ? app.name : app.execName;
    return close(appName);
  }

  private void checkAxEnabled(String name) {
    if (!System.getProperty("os.name").toLowerCase().startsWith("mac")) {
      return;
    }
    if (Integer.parseInt(System.getProperty("os.version").replace(".", "")) > 108 && !isAxEnabled()) {
      if (name == null) {
        JOptionPane.showMessageDialog(null,
                "This app uses Sikuli feature " + usedFeature + ", which needs\n"
                + "access to the Mac's assistive device support.\n"
                + "You have to explicitly allow this in the System Preferences.\n"
                + "(System Preferences -> Security & Privacy -> Privacy)\n"
                + "Currently we cannot do this for you.\n\n"
                + "Be prepared to get some crash after clicking ok.\n"
                + "Please check the System Preferences and come back.",
                "SikuliX on Mac Mavericks Special", JOptionPane.PLAIN_MESSAGE);
        System.out.println("[error] MacUtil: on Mavericks: no access to assistive device support");
      }
      usedFeature = name;
      return;
    }
    if (!isAxEnabled()) {
      if (_askedToEnableAX) {
        return;
      }
      int ret = JOptionPane.showConfirmDialog(null,
              "You need to enable Accessibility API to use the function \""
              + name + "\".\n"
              + "Should I open te System Preferences for you?",
              "Accessibility API not enabled",
              JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE);
      if (ret == JOptionPane.YES_OPTION) {
        openAxSetting();
        JOptionPane.showMessageDialog(null,
                "Check \"Enable access for assistant devices\""
                + "in the System Preferences\n and then close this dialog.",
                "Enable Accessibility API", JOptionPane.INFORMATION_MESSAGE);
      }
      _askedToEnableAX = true;
    }
  }

//Mac Mavericks: delete app entry from list - in terminal on one line
//sudo sqlite3 /Library/Application\ Support/com.apple.TCC/Tcc.db
//'delete from access where client like "%part of app name%"'

  @Override
  public Rectangle getWindow(String appName, int winNum) {
    checkAxEnabled("getWindow");
    int pid = getPID(appName);
    return getWindow(pid, winNum);
  }

  @Override
  public Rectangle getWindow(String appName) {
    return getWindow(appName, 0);
  }

  @Override
  public Rectangle getWindow(int pid) {
    return getWindow(pid, 0);
  }

  @Override
  public Rectangle getWindow(int pid, int winNum) {
    Rectangle rect = getRegion(pid, winNum);
    checkAxEnabled(null);
    return rect;
  }

  @Override
  public Rectangle getFocusedWindow() {
    checkAxEnabled("getFocusedWindow");
    Rectangle rect = getFocusedRegion();
    checkAxEnabled(null);
    return rect;
  }

  @Override
  public native void bringWindowToFront(Window win, boolean ignoreMouse);

  public static native boolean _openApp(String appName);

  public static native int getPID(String appName);

  public static native Rectangle getRegion(int pid, int winNum);

  public static native Rectangle getFocusedRegion();

  public static native boolean isAxEnabled();

  public static native void openAxSetting();

  @Override
  public Map getApps(String name) {
    return null;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy