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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<?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