Skip to content

Instantly share code, notes, and snippets.

@qbilius
Created January 19, 2024 07:06
Show Gist options
  • Select an option

  • Save qbilius/5e6496fd00e6f12a8637e712ec969a11 to your computer and use it in GitHub Desktop.

Select an option

Save qbilius/5e6496fd00e6f12a8637e712ec969a11 to your computer and use it in GitHub Desktop.
Color indicator of an input source in MacOS
// Color indicator of an input source in MacOS
// Shows a dot at the top right corner that changes color depending on the current input source.
// By default, it is set to show a green dot for the US keyboard layout and a red one for anything else.
// You can show or hide it by pressing Cmd+Opt+Ctrl+Shift.
// Why would one need such an indicator?
// In order to have more screen space, I keep my menu bar hidden and only show up when I move mouse to the top of the screen.
// Thus, I cannot see which input source is currently selected and often I find myself typing in non-US symbols
// when in fact I want to use US layout, and vice versa.
// This indicator removes this guesswork by providing me with a quick and non-intrusive way to determine the current layout.
// Usage:
// Dependencies:
// - If using `clang` to compile: `xcode-select --install`
// - If using `gcc` to compile: `brew install gcc`
// Compile:
// clang -framework Cocoa -o lang-dot lang-dot.m
// or
// gcc -framework Cocoa -o lang-dot lang-dot.m
// Run: ./lang-dot
// Automatically start upon reboot:
// crontab -e
// @reboot /path/to/lang-dot
#import <Cocoa/Cocoa.h>
NSString *targetLayout = @"com.apple.keylayout.US";
NSString *domain = @"com.apple.HIToolbox";
NSString *key = @"AppleCurrentKeyboardLayoutInputSourceID";
static BOOL isWindowVisible = YES;
void checkAndUpdate(NSWindow *window) {
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:domain];
id keyboardLayout = [userDefaults objectForKey:key];
NSColor *backgroundColor = [keyboardLayout isEqualToString:targetLayout] ? [NSColor greenColor] : [NSColor redColor];
// Must run asynchronously as otherwise the color doesn't update
dispatch_async(dispatch_get_main_queue(), ^{
[window setBackgroundColor:backgroundColor];
});
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSApplication *application = [NSApplication sharedApplication];
NSRect mainScreenFrame = [NSScreen mainScreen].frame;
// Place it in the top right corner
NSRect frame = NSMakeRect(mainScreenFrame.size.width - 3,
mainScreenFrame.size.height - 3,
3,
3);
NSWindow *window = [[NSWindow alloc] initWithContentRect:frame
styleMask:NSWindowStyleMaskBorderless
backing:NSBackingStoreBuffered
defer:NO];
// Place the window above most other windows
[window setLevel:NSFloatingWindowLevel + 1];
[window setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
[window makeKeyAndOrderFront:nil]; // Bring the window to the front
// Detect Cmd+Opt+Ctrl+Shift even when the app is not focused
[NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskFlagsChanged handler:^(NSEvent *event) {
unsigned int flags = [event modifierFlags];
bool cmdPressed = (flags & NSEventModifierFlagCommand) != 0;
bool optPressed = (flags & NSEventModifierFlagOption) != 0;
bool ctrlPressed = (flags & NSEventModifierFlagControl) != 0;
bool shiftPressed = (flags & NSEventModifierFlagShift) != 0;
if (cmdPressed && optPressed && ctrlPressed && shiftPressed) {
isWindowVisible = !isWindowVisible;
if (isWindowVisible) {
[window orderFront:nil];
} else {
[window orderOut:nil];
}
}
}];
// Use NSRunLoop to check and update every second
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:[NSBlockOperation blockOperationWithBlock:^{
checkAndUpdate(window);
}]
selector:@selector(main)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[application run];
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment