Last active
October 12, 2023 17:54
-
-
Save bellbind/0cbd295130e4e1dc4459925e81048cf1 to your computer and use it in GitHub Desktop.
[macos][swift4]Switch and Display DisplayMode command and StatusBar Menu app
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env swift -import-objc-header cg-hidden.h | |
// $ swiftc -import-objc-header cg-hidden.h DisplayMode.swift | |
import Foundation | |
import CoreFoundation | |
import CoreGraphics | |
let display = CGMainDisplayID() | |
let count = UnsafeMutablePointer<Int32>.allocate(capacity: 1) | |
defer {count.deallocate()} | |
CGSGetNumberOfDisplayModes(display, count); | |
let argv = CommandLine.arguments | |
if argv.count == 2 { | |
guard let modeNum = Int32(argv[1]) else { | |
fputs("mode num should be integer but: \(argv[1])\n", stderr) | |
exit(1) | |
} | |
if modeNum < 0 || count.pointee <= modeNum { | |
fputs("mode num should be in 0-\(count.pointee - 1) but: \(argv[1])\n", stderr) | |
exit(1) | |
} | |
let config = UnsafeMutablePointer<CGDisplayConfigRef?>.allocate(capacity: 1) | |
defer {config.deallocate()} | |
CGBeginDisplayConfiguration(config) | |
CGSConfigureDisplayMode(config.pointee, display, modeNum) | |
CGCompleteDisplayConfiguration(config.pointee, CGConfigureOption.permanently) | |
} | |
let current = UnsafeMutablePointer<Int32>.allocate(capacity: 1) | |
defer {current.deallocate()} | |
CGSGetCurrentDisplayMode(display, current); | |
let dmode = UnsafeMutablePointer<CGSDisplayMode>.allocate(capacity: 1) | |
defer {dmode.deallocate()} | |
let size = Int32(MemoryLayout<CGSDisplayMode>.size) | |
for i in 0 ..< count.pointee { | |
CGSGetDisplayModeDescriptionOfLength(display, i, dmode, size) | |
let mark = i == current.pointee ? "(current)" : "" | |
print(String(format: "mode %2d: %5d x %-5d \(mark)", | |
i, dmode.pointee.width, dmode.pointee.height)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env swift -import-objc-header cg-hidden.h | |
// DisplayMode switcher on StatrusBar (for swift4) | |
// $ swiftc -import-objc-header cg-hidden.h DisplayModeSwitcher.swift | |
import Cocoa | |
let monoText = NSFont.monospacedDigitSystemFont(ofSize: NSFont.systemFontSize, weight: .regular) | |
let attrsT: [NSAttributedString.Key: Any] = [.font: monoText] | |
let attrsS: [NSAttributedString.Key: Any] = [.font: monoText, .expansion: 0.8] | |
func resolution(_ id: String, _ w: String, _ h: String, _ mark: String) -> NSAttributedString { | |
let parts = [(String(repeating: " ", count: 3 - id.count), attrsS), ("\(id): ", attrsT), | |
(String(repeating: " ", count: 5 - w.count), attrsS), ("\(w) x \(h)", attrsT), | |
(String(repeating: " ", count: 5 - h.count), attrsS), (mark, attrsT)] | |
let title = NSMutableAttributedString() | |
for (text, attrs) in parts {title.append(NSAttributedString(string: text, attributes: attrs))} | |
return title | |
} | |
class Main: NSObject, NSMenuDelegate { | |
func menuWillOpen(_ menu: NSMenu) { | |
menu.removeAllItems() | |
let display = CGMainDisplayID() | |
let count = UnsafeMutablePointer<Int32>.allocate(capacity: 1) | |
defer {count.deallocate()} | |
CGSGetNumberOfDisplayModes(display, count) | |
let current = UnsafeMutablePointer<Int32>.allocate(capacity: 1) | |
defer {current.deallocate()} | |
CGSGetCurrentDisplayMode(display, current) | |
let dmode = UnsafeMutablePointer<CGSDisplayMode>.allocate(capacity: 1) | |
defer {dmode.deallocate()} | |
let size = Int32(MemoryLayout<CGSDisplayMode>.size) | |
let items = (0 ..< count.pointee).map {(i) -> NSMenuItem in | |
CGSGetDisplayModeDescriptionOfLength(display, i, dmode, size) | |
let mark = i == current.pointee ? "\u{2713}" : "" | |
let mi = NSMenuItem() | |
mi.attributedTitle = resolution( | |
"\(i)", "\(dmode.pointee.width)", "\(dmode.pointee.height)", "\(mark)") | |
mi.target = self | |
mi.action = #selector(self.switchMode(_:)) | |
mi.tag = Int(i) | |
return mi | |
} | |
for item in items {menu.addItem(item)} | |
menu.addItem(NSMenuItem.separator()) | |
let terminate = #selector(NSApplication.shared.terminate) | |
menu.addItem(withTitle: "Quit", action: terminate, keyEquivalent: "") | |
} | |
@objc func switchMode(_ sender: NSMenuItem) { | |
let display = CGMainDisplayID() | |
let config = UnsafeMutablePointer<CGDisplayConfigRef?>.allocate(capacity: 1) | |
defer {config.deallocate()} | |
CGBeginDisplayConfiguration(config) | |
CGSConfigureDisplayMode(config.pointee, display, Int32(sender.tag)) | |
CGCompleteDisplayConfiguration(config.pointee, CGConfigureOption.permanently) | |
} | |
} | |
let main = Main() | |
NSApplication.loadApplication() | |
let menu = NSMenu() | |
menu.delegate = main | |
let statusItem = NSStatusBar.system.statusItem(withLength: -1) | |
statusItem.button?.title = "\u{1F5A5}" | |
//statusItem.button?.title = "\u{1F4BB}" | |
statusItem.menu = menu | |
NSApplication.shared.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>CFBundleInfoDictionaryVersion</key> | |
<string>6.0</string> | |
<key>CFBundlePackageType</key> | |
<string>APPL</string> | |
<key>CFBundleDevelopmentRegion</key> | |
<string>en</string> | |
<key>CFBundleExecutable</key> | |
<string>%name%</string> | |
<key>CFBundleIdentifier</key> | |
<string>%id%</string> | |
<key>CFBundleIconFile</key> | |
<string>%name%.icns</string> | |
<key>CFBundleName</key> | |
<string>%name%</string> | |
<key>CFBundleShortVersionString</key> | |
<string>1.0</string> | |
<key>CFBundleVersion</key> | |
<string>1</string> | |
<key>LSUIElement</key> | |
<true/> | |
</dict> | |
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
if [ "$(which convert)" == "" ] ; then | |
echo "[Abort] Please install imagemagick" | |
exit 1 | |
fi | |
namespace=bellbind | |
name=DisplayModeSwitcher | |
svg=icon.svg | |
id="$namespace.$name" | |
#NOTE: Prepare your certificate in "Keychain Access" | |
#cert=bellbind | |
# build bin | |
cd $(dirname $0) | |
swiftopts="-import-objc-header cg-hidden.h" | |
swiftc $swiftopts "$name.swift" | |
# build icns from svg | |
iconset="$name.iconset" | |
png="$svg.png" | |
opts="-fuzz 15% -transparent white" | |
mkdir -p "$iconset" | |
qlmanage -t -s 1024 -o . "$svg" | |
convert "$png" $opts -resize 16x16 "$iconset/icon_16x16.png" | |
convert "$png" $opts -resize 32x32 "$iconset/icon_16x16@2x.png" | |
convert "$png" $opts -resize 32x32 "$iconset/icon_32x32.png" | |
convert "$png" $opts -resize 64x64 "$iconset/icon_32x32@2x.png" | |
convert "$png" $opts -resize 128x128 "$iconset/icon_128x128.png" | |
convert "$png" $opts -resize 256x256 "$iconset/icon_128x128@2x.png" | |
convert "$png" $opts -resize 256x256 "$iconset/icon_256x256.png" | |
convert "$png" $opts -resize 512x512 "$iconset/icon_256x256@2x.png" | |
convert "$png" $opts -resize 512x512 "$iconset/icon_512x512.png" | |
convert "$png" $opts -resize 1024x1024 "$iconset/icon_512x512@2x.png" | |
iconutil -c icns "$iconset" | |
# build app bundle | |
app="$name.app" | |
mkdir -p "$app/Contents/MacOS" | |
mkdir -p "$app/Contents/Resources" | |
sed -e "s/%name%/$name/g;s/%id%/$id/g" Info.plist.xml > "$app/Contents/Info.plist" | |
cp "$name" "$app/Contents/MacOS" | |
cp "$name.icns" "$app/Contents/Resources" | |
# cleanup | |
rm -r "$name.iconset" | |
rm "$name" "$name.icns" "$png" | |
# codesign | |
if [ "$cert" ] ; then | |
codesign -s "$cert" "$app" | |
fi | |
# make dmg | |
mkdir -p dmg/ | |
cp -a "$app" dmg/ | |
( cd dmg ; ln -s /Applications/ ) | |
hdiutil create -srcfolder dmg -volname "$name" "$name.dmg" | |
rm -rf dmg/ |
Author
bellbind
commented
Nov 12, 2018
•
- C commandline with hidden API: https://gist.github.com/bellbind/27220148ea752335259dfce63f43bd5d
- StatusBar app with standard API: https://gist.github.com/bellbind/b69f6ef133f0ad5266ff40be2001aecc
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment