Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active October 12, 2023 17:54
Show Gist options
  • Save bellbind/0cbd295130e4e1dc4459925e81048cf1 to your computer and use it in GitHub Desktop.
Save bellbind/0cbd295130e4e1dc4459925e81048cf1 to your computer and use it in GitHub Desktop.
[macos][swift4]Switch and Display DisplayMode command and StatusBar Menu app
#include <stdio.h>
#include <stdlib.h>
#include <CoreGraphics/CoreGraphics.h>
// https://github.com/robbertkl/ResolutionMenu/blob/master/Resolution%20Menu/DisplayModeMenuItem.m
// CoreGraphics DisplayMode struct used in private APIs
typedef struct {
uint32_t modeNumber;
uint32_t flags;
uint32_t width;
uint32_t height;
uint32_t depth;
uint8_t unknown[170];
uint16_t freq;
uint8_t more_unknown[16];
float density;
} CGSDisplayMode;
// CoreGraphics private APIs with support for scaled (retina) display modes
void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int* modeNum);
void CGSConfigureDisplayMode(
CGDisplayConfigRef config, CGDirectDisplayID display, int modeNum);
void CGSGetNumberOfDisplayModes(CGDirectDisplayID display, int* nModes);
void CGSGetDisplayModeDescriptionOfLength(
CGDirectDisplayID display, int idx, CGSDisplayMode* mode, int length);
#!/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))
}
#!/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()
Display the source blob
Display the rendered blob
Raw
<svg xmlns="http://www.w3.org/2000/svg" viewVox="0 0 300 300">
<text x="50%" y="60%" text-anchor="middle" dominant-baseline="central" font-size="300">&#x1F5A5;</text>
</svg>
<?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>
#!/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/
@bellbind
Copy link
Author

bellbind commented Nov 12, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment