├── LICENSE ├── README.md ├── audio.py ├── blink ├── dbrightness ├── display-brightness.c ├── kbrightness ├── kbrightness.swift └── keyboard-brightness.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nick Sweeting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Archived: Use https://github.com/EthanRDoesMC/KBPulse for Macs released after 2016 2 | 3 | --- 4 | 5 | # Control Mac Keyboard Brightness: kbrightness & dbrightness 6 | Programmatically flash the keyboard lights and control display brightness on Macs. You can flash them to the beat of music, or use it for alerts and notifications. 7 | 8 | This was inspired by [@tcr's repo](https://github.com/tcr/macbook-brightness). This is also an alternative to the old iSpazz iTunes plugin, which no longer works. 9 | I had trouble finding any other easy-to-use binary for controlling keyboard brightness, so I made one. 10 | 11 | 12 | ![Flashing keyboard gif](https://i.imgur.com/AS6tTre.gif) 13 | ![Flashing display gif](https://i.imgur.com/cRFsoDM.gif) 14 | 15 | ## Usage 16 | 17 | - `blink` is a shortcut to flash the keyboard lights [n] times for [t] seconds each time 18 | - `kbrightness` manages the keyboard backlight brightness 19 | - `dbrightness` manages the display backlight brightness 20 | - `python3 audio.py` flash the keyboard based on the audio input from your mic, makes it flash to the beat of music 21 | 22 | Use blink in your shell scripts to alert you when things have succeeded or failed. 23 | e.g. `wget https://example.com/large-file.mp4 && blink 2` or `./tests.py || blink 3 1` 24 | 25 | ```bash 26 | git clone https://github.com/pirate/mac-keyboard-brightness 27 | cd mac-keyboard-brightness/ 28 | 29 | ./kbrightness # gets current keyboard brightness 30 | # 0.286447 31 | ./kbrightness 0.85 # sets keyboard brightness to 85% 32 | 33 | ./dbrightness # gets current display brightness 34 | # 0.938477 35 | ./dbrightness 0.42 # sets display brightness to 42% 36 | ======= 37 | 38 | ./blink # flash the keyboard lights once (good for subtle alerts, e.g. git pull && blink 2) 39 | ./blink 2 # flash the keyboard lights twice 40 | ./blink 10 0.1 # flash the keyboard lights 10 times, for 0.1 seconds each time 41 | ./blink 1000 0.01 # turn your keyboard into a disco strobe 42 | 43 | ======= 44 | 45 | # Flash your keyboard to the beat of the music! (uses mic input) 46 | brew install python3 pyaudio portaudio 47 | pip3 install --upgrade pyaudio audioop 48 | python3 audio.py 49 | ``` 50 | You should be able to download the repo and use the binaries without needing to recompile anything (tested on macOS Sierra, High Sierra, and Mojave). 51 | 52 | ## Why? 53 | 54 | It's fun. Here are some ideas: 55 | 56 | - make a bitbar menubar app to control keyboard brightness 57 | - make your keyboard lights flash for security alerts using [Security Growler](https://github.com/pirate/security-growler) 58 | - make your keyboard flash right before your display is about to sleep 59 | - make your keyboard flash on incoming email 60 | - make your keyboard flash to the beat of music 61 | - make your keyboard flash when your boss's iPhone comes within bluetooth range 62 | 63 | ## Advanced 64 | 65 | If you want to write more advanced programs to update the brightness at higher frequencies 66 | (e.g. to make your keyboard flash to music), you can use the C functions directly. 67 | 68 | - `setDisplayBrightness`, `getDisplayBrightness` 69 | - `setKeyboardBrightness`, `getKeyboardBrightness` 70 | - `getLightSensors`: get ambient light sensor values, see [@tcr's original repo](https://github.com/tcr/macbook-brightness/blob/master/displaybrightness.c#L54) 71 | 72 | Compile each file individually with: 73 | 74 | ```bash 75 | gcc -std=c99 -o kbrightness keyboard-brightness.c -framework IOKit -framework ApplicationServices 76 | # OR 77 | gcc -std=c99 -o dbrightness display-brightness.c -framework IOKit -framework ApplicationServices 78 | ``` 79 | 80 | ## Links 81 | 82 | - https://github.com/maxmouchet/LightKit control keyboard and screen brightness via Swift 83 | - https://github.com/tcr/macbook-brightness (the core brightness code is copied from @tcr's, but separated into two cli utils) 84 | - http://stackoverflow.com/questions/3239749/programmatically-change-mac-display-brightness 85 | - https://web.archive.org/web/20110828210316/http://mattdanger.net:80/2008/12/adjust-mac-os-x-display-brightness-from-the-terminal/ 86 | - http://osxbook.com/book/bonus/chapter10/light/ 87 | - https://github.com/samnung/maclight/blob/master/lights_handle.cpp 88 | - http://www.keindesign.de/stefan/Web/Sites/iWeb/Site/iSpazz.html 89 | - https://github.com/bhoeting/DiscoKeyboard 90 | -------------------------------------------------------------------------------- /audio.py: -------------------------------------------------------------------------------- 1 | try: 2 | from subprocess import run 3 | except ImportError: 4 | print('You must run this program with python3 not python2:\n' 5 | ' brew install python3') 6 | raise 7 | 8 | try: 9 | import pyaudio 10 | import audioop 11 | except ImportError: 12 | print('Missing pyaudio, run:\n' 13 | ' pip3 install --upgrade pyaudio audioop') 14 | raise 15 | 16 | 17 | CHUNK = 1024 18 | FORMAT = pyaudio.paInt16 19 | CHANNELS = 1 20 | RATE = 44100 21 | POLL_SPEED = 0.001 22 | 23 | 24 | def get_mic(p): 25 | stream = p.open(format=FORMAT, 26 | channels=CHANNELS, 27 | rate=RATE, 28 | input=True, 29 | frames_per_buffer=CHUNK) 30 | return stream 31 | 32 | 33 | def runloop(stream): 34 | while True: 35 | data = stream.read(CHUNK) 36 | rms = audioop.rms(data, 2) # here's where you calculate the volume 37 | level = rms / 20000 38 | run(['./kbrightness', str(level)]) 39 | 40 | 41 | if __name__ == '__main__': 42 | print('[+] Starting...') 43 | p = pyaudio.PyAudio() 44 | stream = get_mic(p) 45 | try: 46 | runloop(stream) 47 | except (KeyboardInterrupt, Exception): 48 | stream.stop_stream() 49 | stream.close() 50 | p.terminate() 51 | print('[X] Stopped.') 52 | -------------------------------------------------------------------------------- /blink: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Nick Sweeting 2017 3 | # MIT License 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | cd "$DIR" 7 | 8 | if [[ "$1" == "-h" || "$1" == "--help" || "$1" == "help" ]]; then 9 | echo "Flash the keyboard [1] times with a [0.15]s delay, between [0] and [1] brightness levels" 10 | echo "Usage:" 11 | echo " blink [times] [duration] [low_brightness] [high_brightness]" 12 | exit 0 13 | fi 14 | 15 | kbrightness="$DIR/kbrightness" # full path to the kbrightness binary 16 | 17 | flashes=${1:-'1'} # how many flashes 18 | duration=${2:-'0.15'} # duration of each flash in seconds 19 | low_level=${3:-'0'} # lowest brightness level, 0.0 -> 1.0 20 | high_level=${4:-'1'} # highest brightness level, 0.0 -> 1.0 21 | before=$("$kbrightness") # get the current brightness level 22 | 23 | for i in $(seq 1 $flashes); do 24 | "$kbrightness" $low_level 25 | sleep $duration 26 | "$kbrightness" $high_level 27 | sleep $duration 28 | done 29 | 30 | # set keyboard back to existing brightness level before blink ran 31 | "$kbrightness" $before 32 | -------------------------------------------------------------------------------- /dbrightness: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pirate/mac-keyboard-brightness/22f1f56cbf0149805226886389f831d4bb99d2dc/dbrightness -------------------------------------------------------------------------------- /display-brightness.c: -------------------------------------------------------------------------------- 1 | /* 2 | Set Mac Display Backlight Brightness 3 | 4 | Usage: 5 | gcc -std=c99 -o dbrightness display-brightness.c -framework IOKit -framework ApplicationServices 6 | ./dbrightness 0.8 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | // According to: https://stackoverflow.com/questions/20025868/cgdisplayioserviceport-is-deprecated-in-os-x-10-9-how-to-replace 13 | // ...CGDisplayIOServicePort is deprecated. However, the discussion from: https://github.com/glfw/glfw/blob/e0a6772e5e4c672179fc69a90bcda3369792ed1f/src/cocoa_monitor.m 14 | // ...suggests that GLFW's cocoa_monitor offers the implementation we need. Eun's solution appears to work well. It is pasted below. 15 | 16 | // Returns the io_service_t corresponding to a CG display ID, or 0 on failure. 17 | // The io_service_t should be released with IOObjectRelease when not needed. 18 | // 19 | static io_service_t IOServicePortFromCGDisplayID(CGDirectDisplayID displayID) 20 | { 21 | io_iterator_t iter; 22 | io_service_t serv, servicePort = 0; 23 | 24 | CFMutableDictionaryRef matching = IOServiceMatching("IODisplayConnect"); 25 | 26 | // releases matching for us 27 | kern_return_t err = IOServiceGetMatchingServices(kIOMasterPortDefault, 28 | matching, 29 | &iter); 30 | if ( err ) 31 | return 0; 32 | 33 | while ( (serv = IOIteratorNext(iter)) != 0 ) 34 | { 35 | CFDictionaryRef displayInfo; 36 | CFNumberRef vendorIDRef; 37 | CFNumberRef productIDRef; 38 | CFNumberRef serialNumberRef; 39 | 40 | displayInfo = IODisplayCreateInfoDictionary( serv, kIODisplayOnlyPreferredName ); 41 | 42 | Boolean success; 43 | success = CFDictionaryGetValueIfPresent( displayInfo, CFSTR(kDisplayVendorID), (const void**) & vendorIDRef ); 44 | success &= CFDictionaryGetValueIfPresent( displayInfo, CFSTR(kDisplayProductID), (const void**) & productIDRef ); 45 | 46 | if ( !success ) 47 | { 48 | CFRelease(displayInfo); 49 | continue; 50 | } 51 | 52 | SInt32 vendorID; 53 | CFNumberGetValue( vendorIDRef, kCFNumberSInt32Type, &vendorID ); 54 | SInt32 productID; 55 | CFNumberGetValue( productIDRef, kCFNumberSInt32Type, &productID ); 56 | 57 | // If a serial number is found, use it. 58 | // Otherwise serial number will be nil (= 0) which will match with the output of 'CGDisplaySerialNumber' 59 | SInt32 serialNumber = 0; 60 | if ( CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplaySerialNumber), (const void**) & serialNumberRef) ) 61 | { 62 | CFNumberGetValue( serialNumberRef, kCFNumberSInt32Type, &serialNumber ); 63 | } 64 | 65 | // If the vendor and product id along with the serial don't match 66 | // then we are not looking at the correct monitor. 67 | // NOTE: The serial number is important in cases where two monitors 68 | // are the exact same. 69 | if( CGDisplayVendorNumber(displayID) != vendorID || 70 | CGDisplayModelNumber(displayID) != productID || 71 | CGDisplaySerialNumber(displayID) != serialNumber ) 72 | { 73 | CFRelease(displayInfo); 74 | continue; 75 | } 76 | 77 | servicePort = serv; 78 | CFRelease(displayInfo); 79 | break; 80 | } 81 | 82 | IOObjectRelease(iter); 83 | return servicePort; 84 | } 85 | 86 | float getDisplayBrightness(void) { 87 | CGDisplayErr dErr; 88 | io_service_t service; 89 | CGDirectDisplayID targetDisplay; 90 | 91 | CFStringRef key = CFSTR(kIODisplayBrightnessKey); 92 | float brightness = HUGE_VALF; 93 | 94 | targetDisplay = CGMainDisplayID(); 95 | service = IOServicePortFromCGDisplayID(targetDisplay); 96 | 97 | dErr = IODisplayGetFloatParameter(service, kNilOptions, key, &brightness); 98 | 99 | return brightness; 100 | } 101 | 102 | void setDisplayBrightness(float brightness) { 103 | CGDisplayErr dErr; 104 | io_service_t service; 105 | CGDirectDisplayID targetDisplay; 106 | CFStringRef key = CFSTR(kIODisplayBrightnessKey); 107 | 108 | targetDisplay = CGMainDisplayID(); 109 | service = IOServicePortFromCGDisplayID(targetDisplay); 110 | 111 | dErr = IODisplaySetFloatParameter(service, kNilOptions, key, brightness); 112 | } 113 | 114 | 115 | int main(int argc, char **argv) { 116 | // ./dbrightness 0.523 117 | float brightness; 118 | if (argc > 1 && sscanf(argv[1], "%f", &brightness) == 1) { 119 | setDisplayBrightness(brightness); 120 | } else { 121 | printf("%f", getDisplayBrightness()); 122 | } 123 | exit(0); 124 | } 125 | -------------------------------------------------------------------------------- /kbrightness: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pirate/mac-keyboard-brightness/22f1f56cbf0149805226886389f831d4bb99d2dc/kbrightness -------------------------------------------------------------------------------- /kbrightness.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Backlight { 4 | private var isOn = false 5 | private var isFlashing = false 6 | private var numberOfToggles = 0 7 | private var isFlashingOnce = false 8 | private var connect: mach_port_t = 0 9 | private var timer: Timer = Timer() 10 | 11 | static var sharedBacklight = Backlight() 12 | static let FastFlashingInterval = 0.02 13 | static let MediumFlashingInterval = 0.06 14 | static let SlowFlashingInterval = 0.2 15 | static let MinBrightness:UInt64 = 0x0 16 | static var MaxBrightness:UInt64 = 0xfff 17 | 18 | 19 | 20 | init() { 21 | // Get the AppleLMUController (thing that accesses the light hardware) 22 | let serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, 23 | IOServiceMatching("AppleLMUController")) 24 | assert(serviceObject != 0, "Failed to get service object") 25 | 26 | // Open the AppleLMUController 27 | let status = IOServiceOpen(serviceObject, mach_task_self_, 0, &connect) 28 | assert(status == KERN_SUCCESS, "Failed to open IO service") 29 | 30 | // Start with the backlight off 31 | on(); 32 | } 33 | 34 | 35 | 36 | func startFlashing(target: AnyObject, interval: Float64, selector: Selector) { 37 | self.timer = Timer.scheduledTimer( 38 | timeInterval: interval, target: target, selector: selector, userInfo: nil, repeats: true) 39 | 40 | // We need to add the timer to the mainRunLoop so it doesn't stop flashing when the menu is accessed 41 | RunLoop.main.add(self.timer, forMode: RunLoopMode.commonModes) 42 | self.isFlashing = true 43 | } 44 | 45 | func stopFlashing() { 46 | self.isFlashing = false 47 | self.timer.invalidate() 48 | } 49 | 50 | func toggle() { 51 | if self.isOn { 52 | self.off(); 53 | } else { 54 | self.on(); 55 | } 56 | 57 | self.numberOfToggles += 1 58 | if self.numberOfToggles >= 3 && isFlashingOnce { 59 | self.timer.invalidate() 60 | isFlashingOnce = false 61 | } 62 | } 63 | 64 | func on() { 65 | set(brightness: Backlight.MaxBrightness) 66 | isOn = true 67 | } 68 | 69 | func off() { 70 | set(brightness: Backlight.MinBrightness) 71 | isOn = false 72 | } 73 | 74 | func set(brightness: UInt64) { 75 | var output: UInt64 = 0 76 | var outputCount: UInt32 = 1 77 | let setBrightnessMethodId:UInt32 = 2 78 | let input: [UInt64] = [0, brightness] 79 | 80 | let status = IOConnectCallMethod(connect, setBrightnessMethodId, input, UInt32(input.count), 81 | nil, 0, &output, &outputCount, nil, nil) 82 | 83 | assert(status == KERN_SUCCESS, "Failed to set brightness; status: \(status)") 84 | } 85 | 86 | func printe(vale: Int32) { 87 | Backlight.MaxBrightness = UInt64(vale * 16) 88 | } 89 | 90 | } 91 | 92 | -------------------------------------------------------------------------------- /keyboard-brightness.c: -------------------------------------------------------------------------------- 1 | /* 2 | Set Mac Keyboard Backlight Brightness 3 | 4 | Usage: 5 | gcc -std=c99 -o kbrightness keyboard-brightness.c -framework IOKit -framework ApplicationServices 6 | ./kbrightness 0.8 7 | */ 8 | 9 | enum { 10 | kGetSensorReadingID = 0, // getSensorReading(int *, int *) 11 | kGetLEDBrightnessID = 1, // getLEDBrightness(int, int *) 12 | kSetLEDBrightnessID = 2, // setLEDBrightness(int, int, int *) 13 | kSetLEDFadeID = 3, // setLEDFade(int, int, int, int *) 14 | }; 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | static io_connect_t dataPort = 0; 21 | 22 | io_connect_t getDataPort(void) { 23 | kern_return_t kr; 24 | io_service_t serviceObject; 25 | 26 | if (dataPort) return dataPort; 27 | 28 | // Look up a registered IOService object whose class is AppleLMUController 29 | serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController")); 30 | 31 | if (!serviceObject) { 32 | printf("Failed to connect to AppleLMUController\n"); 33 | return 0; 34 | } 35 | 36 | // Create a connection to the IOService object 37 | kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &dataPort); 38 | IOObjectRelease(serviceObject); 39 | 40 | if (kr != KERN_SUCCESS) { 41 | printf("Failed to open IoService object\n"); 42 | return 0; 43 | } 44 | return dataPort; 45 | } 46 | 47 | float getKeyboardBrightness(void) { 48 | float f; 49 | kern_return_t kr; 50 | 51 | uint64_t inputCount = 1; 52 | uint64_t inputValues[1] = {0}; 53 | 54 | uint32_t outputCount = 1; 55 | uint64_t outputValues[1]; 56 | 57 | uint32_t out_brightness; 58 | 59 | kr = IOConnectCallScalarMethod( 60 | getDataPort(), 61 | kGetLEDBrightnessID, 62 | inputValues, 63 | inputCount, 64 | outputValues, 65 | &outputCount 66 | ); 67 | 68 | out_brightness = outputValues[0]; 69 | 70 | if (kr != KERN_SUCCESS) { 71 | printf("getKeyboardBrightness() error\n"); 72 | return 0; 73 | } 74 | 75 | f = out_brightness; 76 | f /= 0xfff; 77 | return (float)f; 78 | } 79 | 80 | void setKeyboardBrightness(float in) { 81 | kern_return_t kr; 82 | 83 | uint64_t inputCount = 2; 84 | uint64_t inputValues[2]; 85 | uint64_t in_unknown = 0; 86 | uint64_t in_brightness = in * 0xfff; 87 | 88 | inputValues[0] = in_unknown; 89 | inputValues[1] = in_brightness; 90 | 91 | uint32_t outputCount = 1; 92 | uint64_t outputValues[1]; 93 | 94 | uint32_t out_brightness; 95 | 96 | kr = IOConnectCallScalarMethod( 97 | getDataPort(), 98 | kSetLEDBrightnessID, 99 | inputValues, 100 | inputCount, 101 | outputValues, 102 | &outputCount 103 | ); 104 | 105 | out_brightness = outputValues[0]; 106 | 107 | if (kr != KERN_SUCCESS) { 108 | printf("setKeyboardBrightness() error\n"); 109 | return; 110 | } 111 | } 112 | 113 | 114 | int main(int argc, char **argv) { 115 | // ./kbrightness 0.523 116 | float brightness; 117 | if (argc > 1 && sscanf(argv[1], "%f", &brightness) == 1) { 118 | setKeyboardBrightness(brightness); 119 | } else { 120 | printf("%f", getKeyboardBrightness()); 121 | } 122 | exit(0); 123 | } 124 | --------------------------------------------------------------------------------