Determine OS X keyboard layout ("input source") in the terminal/a script? - macos

I would like to determine the OS X keyboard layout (or "input source" as OS X calls it) from the terminal so that I can show it in places like the tmux status bar.
So I want to know if the current layout is "U.S." or "Swedish - Pro" for example.
Googling turns up nothing for me. Is this possible?

Note: #MarkSetchell deserves credit for coming up with the fundamental approach - where to [start to] look and what tools to use.
After further investigation and back and forth in the comments I thought I'd summarize the solution (as of OS X 10.9.1):
do shell script "defaults read ~/Library/Preferences/com.apple.HIToolbox.plist \\
AppleSelectedInputSources | \\
egrep -w 'KeyboardLayout Name' | sed -E 's/^.+ = \"?([^\"]+)\"?;$/\\1/'"
Note how \ is escaped as \\ for the benefit of AppleScript, which ensures that just \ reaches the shell. If you want to execute the same command directly from the shell (as one line), it would be:
defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources | egrep -w 'KeyboardLayout Name' |sed -E 's/^.+ = \"?([^\"]+)\"?;$/\1/'
The currently selected keyboard layout is stored in the user-level file ~/Library/Preferences/com.apple.HIToolbox.plist, top-level key AppleSelectedInputSources, subkey KeyboardLayout Name.
defaults read ensures that the current settings are read (sadly, as of OSX 10.9, the otherwise superior /usr/libexec/PlistBuddy sees only a cached version, which may be out of sync).
Since defaults read cannot return an individual key's value, the value of interest must be extracted via egrep and sed - one caveat there is that defaults read conditionally uses double quotes around key names and string values, depending on whether they are a single word (without punctuation) or not.
Update:
Turns out that AppleScript itself can parse property lists, but it's a bit like pulling teeth.
Also, incredibly, the potentially-not-fully-current-values problem also affects AppleScript's parsing.
Below is an AppleScript handler that gets the current keyboard layout; it uses a do shell script-based workaround to ensure that the plist file is current, but otherwise uses AppleScript's property-list features, via the Property List Suite of application System Events.
Note: Obviously, the above shell-based approach is much shorter in this case, but the code below demonstrates general techniques for working with property lists.
# Example call.
set activeKbdLayout to my getActiveKeyboardLayout() # ->, e.g., "U.S."
on getActiveKeyboardLayout()
# Surprisingly, using POSIX-style paths (even with '~') works with
# the `property list file` type.
set plistPath to "~/Library/Preferences/com.apple.HIToolbox.plist"
# !! First, ensure that the plist cache is flushed and that the
# !! *.plist file contains the current value; simply executing
# !! `default read` against the file - even with a dummy
# !! key - does that.
try
do shell script "defaults read " & plistPath & " dummy"
end try
tell application "System Events"
repeat with pli in property list items of ¬
property list item "AppleSelectedInputSources" of ¬
property list file plistPath
# Look for (first) entry with key "KeyboardLayout Name" and return
# its value.
# Note: Not all entries may have a 'KeyboardLayout Name' key,
# so we must ignore errors.
try
return value of property list item "KeyboardLayout Name" of pli
end try
end repeat
end tell
end getActiveKeyboardLayout

Recently I had written a small console utility (https://github.com/myshov/xkbswitch-macosx) on Objective-C to do this. It's a lot faster than a script based solutions. It can to get the current input layout but also it can to set the given input layout.
To get a current layout:
$xkbswitch -ge
> US
To set a given layout:
$xkbswith -se Russian

I am not sure of this answer, but it may be worth checking out. If you look in file:
/Library/Preferences/com.apple.HIToolbox.plist
there is a variable called
AppleCurrentKeyboardLayoutSourceID
and mine is set to "British" and I am in Britain...
You can read the file in a script with:
defaults read /Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources
sample output below:
(
{
InputSourceKind = "Keyboard Layout";
"KeyboardLayout ID" = 2;
"KeyboardLayout Name" = British;
}
)
So, I guess your question can be simply answered using this:
#!/bin/bash
defaults read /Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources | grep -sq Swedish
[[ $? -eq 0 ]] && echo Swedish

This question led to the creation of the keyboardSwitcher CLI Tool:
https://github.com/Lutzifer/keyboardSwitcher
Though similar to the already mentioned https://github.com/myshov/xkbswitch-macosx this has additional features, e.g. the list of Layouts is not hardcoded and thus can also support third party layouts (e.g. Logitech) and supports installation via homebrew.

Figured out how to do it with AppleScript, assuming you have the menu bar input menu.
Run this in a terminal:
osascript -e 'tell application "System Events" to tell process "SystemUIServer" to get the value of the first menu bar item of menu bar 1 whose description is "text input"'
Works fine even if you only show the input menu as flag icons, without the input source name.
Mavericks will probably prompt you to allow access, the first time. In earlier versions of OS X I suspect you'll need to turn on support for assistive devices in your accessibility preferences.

I was searching for an answer to an issue I was having with the keyboard layout that lead me to this post. I found the solution for my problem here.
Resolved Issues
You might experience difficulty logging into your account because the keyboard layout may change unexpectedly at the
Login window. (40821875)
Workaround: Log in to your account, launch Terminal, and execute the
following command:
sudo rm -rf /var/db/securityagent/Library/Preferences/com.apple.HIToolbox.plist
This is an Apple official release note for Mojave

Related

How to enable/disable “Show as run destination” for all simulators

Is it possible to toggle the “Show as run destination” flag for multiple iOS simulators instead of changing it one by one in the "Device and Simulators" window?
Is there a command line with that purpose?
I decided to find it by myself using fswatch. BTW, it's really useful for this kind of situations. By monitoring the changes of file while toggling the "Show as run destination" flag, I found out that Xcode was changing the ~/Library/Preferences/com.apple.dt.Xcode.plist file 🙌
After some analysis, I noticed the key that I needed to change to achieve what I had in mind. The key is DVTIgnoredDevices and it holds an array of simulators. So, each simulator UUID in that list will be ignored in Xcode.
Now, I can change the DVTIgnoredDevices key using defaults command line tool specifying the needed value type:
-array Allows the user to specify an array as the value for the given preference key:
defaults write somedomain preferenceKey -array element1 element2 element3
The specified array overwrites the value of the key if the key was present at the time of the write. If the key was not present, it is created with the new value.
Example:
defaults write com.apple.dt.Xcode DVTIgnoredDevices '(
"80E16DBC-2FE5-48AC-8A44-1F5DEFA00EA7",
"B8C4D5FF-8F1A-4895-BD16-CCAFECD71098"
)'
After setting the DVTIgnoredDevices key, you need to clean the DerivedData folder and restart Xcode. To clean the DerivedData folder, please see this answer or just run the shortcut shift+alt+cmd+k (that's what I usually do).
Tested on Xcode Version 9.4 (9F1027a).
UPDATE:
I usually like to have just a couple of Simulators in the list, so I decided to do a script using instruments -s devices and add all the current Simulators to the DVTIgnoredDevices key. Then I chose which simulator(s) will be shown 😊
Xcode-hide-all-iPhone-simulators.sh
simulatorsIdentifiers=$(instruments -s devices |
grep -o "iPhone .* (.*) \[.*\]" | #only iPhone
grep -o "\[.*\]" | #only UUID
sed "s/^\[\(.*\)\]$/\1/" | #remove square brackets
sed 's/^/"/;$!s/$/"/;$s/$/"/' | #add quotes
sed '$!s/$/,/' #add comma to separate each element
)
arrayOfSimulatorsIdentifiers=($(echo "$simulatorsIdentifiers" | tr ',' '\n'))
# Add simulators to DVTIgnoredDevices
echo "${#arrayOfSimulatorsIdentifiers[#]}"
for index in "${!arrayOfSimulatorsIdentifiers[#]}"
do
echo "$index Adding: ${arrayOfSimulatorsIdentifiers[index]}"
done
defaults write com.apple.dt.Xcode DVTIgnoredDevices -array ${arrayOfSimulatorsIdentifiers[#]}
Gist file

How can I find files by content in Mac OS X?

I want to find files at a given location whose content matches a given string. For example, there are a lot of files inside the desktop folder (or anywhere), like *.pdf, *.rtf, *.doc, *.txt, *.html and so on.
The user will be prompted to enter a string thistext and select the location /Users/UserName/Desktop. I want to get a list of the files from this location whose content contains thistext.
I found a command utility mdfind, but it returns the files whose name contains thistext as well. I don't want these files in the result list; I only want files whose content is thistext. I've used grep, but it's not working properly for me. Is there a way to customize grep or mdfind command to work for me?
Or if there is any AppleScript script available for performing such task?
I think there are some syntax errors in the above answer.
I just tested this in AppleScript, and it works for me in Yosemite 10.10.5:
set textToSearchFor to "YourTextHere"
set searchDir to "~/Documents/Test/"
set cmdStr to "mdfind 'kMDItemTextContent == \"*" & textToSearchFor & "*\"cd' -onlyin " & searchDir
set lstFiles to (do shell script cmdStr)
log lstFiles
Result:
(*/Users/UserName/Documents/Test/PDF_Log.txt*)
You can specify a query that only examines each file's text content, like so:
mdfind -onlyin ~/Desktop 'kMDItemTextContent == *thistext* cdw'
The cdw at the end of the query string means the comparison should ignore case, diacritics, and width (which is mostly relevant for text with Asian characters).
Also, if you're doing this from an app, you shouldn't invoke the mdfind command as a subprocess. You should use the NSMetadataQuery class to do it within your app.

Embed a bash shell script in an AppleScriptObjC application with Xcode

I have attempted to follow the instructions on this post but I am falling short of understanding how some of the posters instructions work.
I want to be able to package the app with a prewritten bash script and then execute it, but don't follow from Step 4 onwards.
Post writes:
4. Also in your AppleScriptObjC script, add the following where appropriate:
property pathToResources : "NSString" -- works if added before script command
5. Where appropriate, also add the following in your AppleScriptObjC script:
set yourScript to pathToResources & "/yourScriptFile.sh"
-- gives the complete unix path
-- if needed, you can convert this to the Apple style path:
set yourScriptPath to (((yourScript as text) as POSIX file) as alias)`
6. As an aside, you could then open the file for read using
tell application "Finder"
open yourScriptPath
end tell
Questions:
Where do I add the line:
property pathToResources : "NSString"
Do I add which of the following, and where?
set yourScript to pathToResources & "/yourScriptFile.sh"
OR
set yourScriptPath to (((yourScript as text) as POSIX file) as alias)
How is it possible to execute the script itself? The mention As an aside, you could then open the file for read using only covers the Apple style path, it does not cover using the aforementioned style.
Can anyone shed a bit more light on this for me, or post a static copy of a AppDelegate.applescript file that shows how the original poster required the base code to be used? I have tried his method and looked across the internet for the past 3 weeks to no avail. I don't want to have to convert all my code for specific tools from bash scripts into AppleScript, as this would take a lot of work.
I only need to know how to reference to the script file (for example myBashScript.sh) in my app, which would reside in the application and be included by Xcode at time of compilation.
I think you should use the command path to resource <specifiedResource>.
See Standard Additions, path to resource.
You could set it by set myVariableName to path to resource "myBashScript.sh" or just use the command instead of your property so it points always to the right place (a user could move your app while running... lol).
ADDITION:
I did it that way in my AppleScript-Application:
on run_scriptfile(this_scriptfile)
try
set the script_file to path to resource this_scriptfile
return (run script script_file)
end try
return false
end run_scriptfile
Whenever I want to run a script that is bundled within my app I do this:
if my run_scriptfile("TestScript.scpt") is false then error number -128
run_scriptfile(this_scriptfile) returns true when everything worked.
I ended up bringing all the information together and now have a solution.
This takes into consideration the following facts:
firstScript = variable name that points to a script called scriptNumberOne.sh
scriptNumberOne.sh = the script that I have embedded into my application to run
ButtonHandlerRunScript_ = the name of the Received Action in Xcode
pathToResources = variable that points to the internal Resources folder of my application, regardless of it's current location
Using this information, below is a copy of a vanilla AppDelegate.applescript in my AppleScriptObjC Xcode project:
script AppDelegate
property parent : class "NSObject"
property pathToResources : "NSString"
on applicationWillFinishLaunching_(aNotification)
set pathToResources to (current application's class "NSBundle"'s mainBundle()'s resourcePath()) as string
end applicationWillFinishLaunching_
on ButtonHandlerRunScript_(sender)
set firstScript to pathToResources & "/scriptNumberOne.sh"
do shell script firstScript
end ButtonHandlerRunScript_
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return current application's NSTerminateNow
end applicationShouldTerminate_
end script

How can I make org-protocol work on Openbox?

I tried the instructions - I am using Firefox on Lubuntu (Openbox). But I get the error
"Firefox doesn't know how to open this address, because the protocol (org-protocol) isn't associated with any program".
How should I fix this?
The following steps for setting up org-protocol work with Ubuntu 16.04 (Xenial Xerus) and presumably later versions. Org-mode is assumed to have already been set-up (and installed using apt-get install org-mode or via the ELPA repository).
Set-up
Add .desktop file
Create and save a file called org-protocol.desktop to ~/.local/share/applications containing:
[Desktop Entry]
Name=org-protocol
Exec=emacsclient %u
Type=Application
Terminal=false
Categories=System;
MimeType=x-scheme-handler/org-protocol;
Then run:
$ update-desktop-database ~/.local/share/applications/
This step makes Firefox aware that "org-protocol" is a valid scheme-handler or protocol (by updating ~/.local/share/applications/mimeinfo.cache), and causes Firefox to prompt for a program to use when opening these kinds of links.
Add config settings to ~/.emacs.d/init.el (or ~/.emacs) file
Have the following settings in your Emacs configuration file:
(server-start)
(require 'org-protocol)
Also add some template definitions to the configuration file, for example:
(setq org-protocol-default-template-key "l")
(setq org-capture-templates
'(("t" "Todo" entry (file+headline "/path/to/notes.org" "Tasks")
"* TODO %?\n %i\n %a")
("l" "Link" entry (file+olp "/path/to/notes.org" "Web Links")
"* %a\n %?\n %i")
("j" "Journal" entry (file+datetree "/path/to/journal.org")
"* %?\nEntered on %U\n %i\n %a")))
Now run Emacs.
Create your notes.org file
Assuming you use the capture templates defined in step 2, you will need to prepare a notes.org file at the location you specified in step 2. You must create this file -- if it is not created along with the headlines specified in step 2, org-mode will just give a warning when you try to capture web-pages. So, given the capture templates from step 2, notes.org should contain the following:
* Tasks
* Web Links
Add bookmarklet(s) to Firefox
Save bookmark to toolbar containing something like the following as the location:
javascript:location.href='org-protocol://capture?template=l&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)+'&body='+encodeURIComponent(window.getSelection())
If you are using an older version of org-mode, you may need to use the following instead:
javascript:location.href='org-protocol://capture://l/'+encodeURIComponent(location.href)+'/'+encodeURIComponent(document.title)+'/'+encodeURIComponent(window.getSelection())
Notice the 'l' (lowercase L) in the above URL -- this is what chooses the capture template (automatically) -- it is the key one would normally have to press when capturing with org-mode via C-c c.
When you click on this bookmarklet, Firefox will ask what program to use to handle the "org-protocol" protocol. You can simply choose the default program that appears ("org-protocol").
Using it
(Optionally) select some text on a webpage you're viewing in Firefox. When you click on the bookmarklet, the link and selected text will be placed in the Emacs capture buffer. Go to Emacs, modify the capture buffer as desired, and press C-c C-c to save it.
Add protocol handler
Create file ~/.local/share/applications/org-protocol.desktop containing:
[Desktop Entry]
Name=org-protocol
Exec=emacsclient %u
Type=Application
Terminal=false
Categories=System;
MimeType=x-scheme-handler/org-protocol;
Note: Each line's key must be capitalized exactly as displayed, or it will be an invalid .desktop file.
Then update ~/.local/share/applications/mimeinfo.cache by running:
On GNOME:
update-desktop-database ~/.local/share/applications/
On KDE:
kbuildsycoca4
Configure Emacs
Init file
Add to your Emacs init file:
(server-start)
(require 'org-protocol)
Capture template
You'll probably want to add a capture template something like this:
("w" "Web site"
entry
(file+olp "/path/to/inbox.org" "Web")
"* %c :website:\n%U %?%:initial")
Note: Using %:initial instead of %i seems to handle multi-line content better.
This will result in a capture like this:
\* [[http://orgmode.org/worg/org-contrib/org-protocol.html][org-protocol.el – Intercept calls from emacsclient to trigger custom actions]] :website:
[2015-09-29 Tue 11:09] About org-protocol.el
org-protocol.el is based on code and ideas from org-annotation-helper.el and org-browser-url.el.
Configure Firefox
Expose protocol-handler
On some versions of Firefox, it may be necessary to add this setting. You may skip this step and come back to it if you get an error saying that Firefox doesn't know how to handle org-protocol links.
Open about:config and create a new boolean value named network.protocol-handler.expose.org-protocol and set it to true.
Note: If you do skip this step, and you do encounter the error, Firefox may replace all open tabs in the window with the error message, making it difficult or impossible to recover those tabs. It's best to use a new window with a throwaway tab to test this setup until you know it's working.
Make bookmarklet
Make a bookmarklet with the location:
javascript:location.href='org-protocol://capture://w/'+encodeURIComponent(location.href)+'/'+encodeURIComponent(document.title)+'/'+encodeURIComponent(window.getSelection())
Note: The w in the URL chooses the corresponding capture template. You can leave it out if you want to be prompted for the template.
When you click on this bookmarklet for the first time, Firefox will ask what program to use to handle the org-protocol protocol. If you are using Ubuntu 12.04 (Precise Pangolin), you must add the /usr/bin/emacsclient program, and choose it. With Ubuntu 12.10 (Quantal Quetzal) or later, you can simply choose the default program that appears (org-protocol).
You can select text in the page when you capture and it will be copied into the template, or you can just capture the page title and URL.
Tridactyl
If you're using Tridactyl, you can map key sequences something like this:
bind cc js location.href='org-protocol://capture://w/'+encodeURIComponent(content.location.href)+'/'+encodeURIComponent(content.document.title)+'/'+encodeURIComponent(content.document.getSelection())
You might also want to add one for the `store-link` sub-protocol, like:
bind cl js location.href='org-protocol://store-link://'+encodeURIComponent(content.location.href)+'/'+encodeURIComponent(content.document.title)
Capture script
You may want to use this script to capture input from a terminal, either as an argument or piped in:
#!/bin/bash
if [[ $# ]]
then
data="$#"
else
data=$(cat)
fi
if [[ -z $data ]]
then
exit 1
fi
encoded=$(python -c "import sys, urllib; print urllib.quote(' '.join(sys.argv[1:]), safe='')" "${data[#]}")
# "link" and "title" are not used, but seem to be necessary to get
# $encoded to be captured
emacsclient "org-protocol://capture://link/title/$encoded"
Then you can capture input from the shell like this:
tail /var/log/syslog | org-capture
org-capture "I can capture from a terminal!"
These instructions are more up-to-date than the ones in Mark's answer.

Global preferences for AppleScript app

Is it possible to save some kind of settings for an app create in AppleScript?
The settings should be loaded at the beginning of the script and be saved at the end of the script.
Example:
if loadSetting("timesRun") then
set timesRun to loadSetting("timesRun")
else
set timesRun to 0
end if
set timesRun to timesRun + 1
display dialog timesRun
saveSetting("timesRun", timesRun)
Where the dialog would show 1 the first time running the script, 2 the second time...
And the functions loadSetting and saveSetting would be the functions i need.
Script properties are persistent, though the saved value is overwritten by the value specified in the script whenever you re-save the script. Run:
property |count| : 0
display alert "Count is " & |count|
set |count| to |count| + 1
a few times, re-save it then run it a few more.
If you want to use the user defaults system, you can use do shell script "defaults ..." commands or (if using Applescript Studio) default entry "propertyName" of user defaults. In Applescript Studio, you bind values to user defaults.
This is also working well (check the first comment to the hint):
http://hints.macworld.com/article.php?story=20050402194557539
It uses the "defaults" system and you get your preferences in the ~/Library/Preferences
Applescript supports natively reading and writing plists through System Events:
use application "System Events" # Avoids tell blocks, note: 10.9 only
property _myPlist : "~/Library/Preferences/com.plistname.plist
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
set plistItemValue to plistItemValue + 1
set value of property list item "plistItem" of contents of property list file _myPlist to plistItemValue
The only problem with this is that it can't create the plists so if the existence of the plist is not certain you need to wrap it on a try.
try
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
on error -1728 # file not found error
do shell script "defaults write com.plistname.plist plistItem 0"
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
end try

Resources