├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── images ├── Sbar.svg ├── Sbar_icon.svg ├── bar_item.svg ├── default.png └── example.png ├── makefile ├── plugins ├── battery.sh ├── clock.sh ├── front_app.sh ├── space.sh └── volume.sh ├── sketchybarrc └── src ├── alias.c ├── alias.h ├── animation.c ├── animation.h ├── app_windows.c ├── app_windows.h ├── background.c ├── background.h ├── bar.c ├── bar.h ├── bar_item.c ├── bar_item.h ├── bar_manager.c ├── bar_manager.h ├── color.c ├── color.h ├── custom_events.c ├── custom_events.h ├── display.c ├── display.h ├── event.c ├── event.h ├── font.c ├── font.h ├── graph.c ├── graph.h ├── group.c ├── group.h ├── hotload.c ├── hotload.h ├── image.c ├── image.h ├── mach.c ├── mach.h ├── media.h ├── media.m ├── message.c ├── message.h ├── misc ├── defines.h ├── env_vars.h ├── extern.h ├── help.h └── helpers.h ├── mouse.c ├── mouse.h ├── popup.c ├── popup.h ├── power.c ├── power.h ├── shadow.c ├── shadow.h ├── sketchybar.c ├── slider.c ├── slider.h ├── text.c ├── text.h ├── volume.c ├── volume.h ├── wifi.h ├── wifi.m ├── window.c ├── window.h ├── workspace.h └── workspace.m /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: felixkratz 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | bin 3 | todo.md 4 | notifications.md 5 | .ccls-cache 6 | .DS_Store 7 | .ccls 8 | .cache 9 | .cmake 10 | CMakeCache.txt 11 | CMakeFiles/ 12 | cmake_install.cmake 13 | compile_commands.json 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | Install 7 | 8 | Documentation 9 | 10 | Setups 11 | 12 | Plugins 13 |

14 | 15 |

16 | 17 | 18 | 19 | 20 |

21 | 22 |

23 | This bar project aims to create a highly flexible, customizable, fast and powerful status bar replacement for people that like playing with 24 | shell scripts. 25 |

26 | 27 | ![](images/example.png) 28 |

29 | More Setups 30 |

31 | 32 | 33 | 34 | ## Features 35 | * Full *configurability* at any time 36 | * Dynamic *animation* system 37 | * Powerful *scripting* and *event* system 38 | * Optimized to be *fast* and *efficient* 39 | * Interactive *mouse* support 40 | * Support for displaying macOS menu bar apps (*aliases*) 41 | * Can draw arbitrary *graphs* 42 | * On-demand *popup* menus 43 | 44 | The main design principle of this project is that *all* elements of the bar can 45 | be added, removed and freely changed at any point in time. Thus, the 46 | configuration of the bar is not *static*, rather it is possible to adapt the 47 | appearance of the bar completely dynamically with the help of a powerful 48 | event-driven scripting system at any point in time using the highly 49 | configurable basic building blocks SketchyBar offers. 50 | 51 | ## Getting Started 52 | Refer to the installation guide in the [documentation](https://felixkratz.github.io/SketchyBar/setup) to get the program set up. 53 | Once this is sorted you can start to become familiar with the syntax of sketchybar by going through the default [*sketchybarrc*](https://github.com/FelixKratz/SketchyBar/blob/master/sketchybarrc) file and the default [*plugin scripts*](https://github.com/FelixKratz/SketchyBar/blob/master/plugins), 54 | which are located in `~/.config/sketchybar/` and look like this: 55 | 56 | ![](images/default.png) 57 | 58 | All commands and options are explained in detail in the relevant sections 59 | of the configuration [documentation](https://felixkratz.github.io/SketchyBar/config/bar). You can try the commands directly from 60 | the commandline to see which affect they have and how they alter the bar. Once you have become familiar with the syntax you can 61 | look for a config to start from [here](https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top) or start from scratch and customize 62 | everything to your liking. 63 | 64 | You might also enjoy looking at the [Tips & Tricks](https://felixkratz.github.io/SketchyBar/config/tricks) section 65 | for some further tips on your journey. If you are searching for functional items you might want to check the 66 | [plugins](https://github.com/FelixKratz/SketchyBar/discussions/12?sort=top) section if someone has already created what you are looking for. 67 | 68 | Should you encounter things not working as you expect them to, please *do not* hesitate to open an [issue](https://github.com/FelixKratz/SketchyBar/issues), as 69 | this is either a bug or a documentation problem and relevant in any case. 70 | 71 | ## Documentation 72 | For the full documentation of all commands and properties please refer to the [website](https://felixkratz.github.io/SketchyBar/config/bar). 73 | 74 | If questions remain, feel free to consult the [Q&A](https://github.com/FelixKratz/SketchyBar/discussions/categories/q-a) section. 75 | 76 | ## Supporting 77 | *You* can support this project is many ways: 78 | - By *creating* issues and pull-requests if you encounter problems 79 | - By *sharing* your [plugins](https://github.com/FelixKratz/SketchyBar/discussions/12) and [setups](https://github.com/FelixKratz/SketchyBar/discussions/47) 80 | - By *starring* the project on GitHub 81 | - If this project has value to you, consider quantifying it and *donating* to a charity of your choice. If you want to let me know about your donation, you 82 | can contact me via [email](mailto:felix.kratz@tu-dortmund.de?Subject=Donation). 83 | - If you want to support me directly, you can do so via [ko-fi](https://ko-fi.com/felixkratz) 84 | 85 | ## Credits 86 | This project was forked from *[spacebar](https://github.com/cmacrae/spacebar)* and completely reimagined and rewritten.
87 | The original idea is based on the status bar that was included in *[yabai](https://github.com/koekeishiya/yabai)* before getting removed. 88 | 89 | 90 | ## Related Projects 91 | - [SbarLua](https://github.com/FelixKratz/SbarLua): A Lua API for SketchyBar 92 | - [sketchybar-app-font](https://github.com/kvndrsslr/sketchybar-app-font): A symbol font for SketchyBar 93 | - [SketchyBarHelper](https://github.com/FelixKratz/SketchyBarHelper): A header for C/C++ to directly communicate with SketchyBar 94 | 95 | ## Some animation examples 96 | 97 | https://user-images.githubusercontent.com/22680421/211198711-45318f04-e96f-4aa1-a0ba-c7f30f050902.mp4 98 | 99 | 100 | -------------------------------------------------------------------------------- /images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FelixKratz/SketchyBar/a8691a3fca676557bc6d8c69167e61df7462bbfe/images/default.png -------------------------------------------------------------------------------- /images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FelixKratz/SketchyBar/a8691a3fca676557bc6d8c69167e61df7462bbfe/images/example.png -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -std=c99 -Wall -Ofast -ffast-math -fvisibility=hidden -fno-common 2 | 3 | LIBS = -framework Carbon \ 4 | -framework AppKit \ 5 | -framework CoreAudio \ 6 | -framework CoreWLAN \ 7 | -framework CoreVideo \ 8 | -framework IOKit \ 9 | -F/System/Library/PrivateFrameworks \ 10 | -framework SkyLight \ 11 | -framework DisplayServices \ 12 | -framework MediaRemote 13 | 14 | ODIR = bin 15 | SRC = src 16 | 17 | _OBJ = alias.o background.o bar_item.o custom_events.o event.o graph.o \ 18 | image.o mouse.o shadow.o font.o text.o message.o mouse.o bar.o color.o \ 19 | window.o bar_manager.o display.o group.o mach.o popup.o \ 20 | animation.o workspace.om volume.o slider.o power.o wifi.om media.om \ 21 | hotload.o app_windows.o 22 | 23 | OBJ = $(patsubst %, $(ODIR)/%, $(_OBJ)) 24 | 25 | .PHONY: all clean arm x86 profile leak universal 26 | 27 | all: clean universal 28 | 29 | leak: CFLAGS=-std=c99 -Wall -g 30 | leak: clean arm64 31 | leak: 32 | /usr/libexec/PlistBuddy -c "Add :com.apple.security.get-task-allow bool true" bin/tmp.entitlements 33 | codesign -s - --entitlements bin/tmp.entitlements -f ./bin/sketchybar 34 | leaks -atExit -- ./bin/sketchybar 35 | 36 | x86: CFLAGS+=-target x86_64-apple-macos10.13 37 | x86: $(ODIR)/sketchybar 38 | 39 | arm64: CFLAGS+=-target arm64-apple-macos11 40 | arm64: $(ODIR)/sketchybar 41 | 42 | universal: 43 | $(MAKE) x86 44 | mv $(ODIR)/sketchybar $(ODIR)/sketchybar_x86 45 | rm -rf $(ODIR)/*.o* 46 | $(MAKE) arm64 47 | mv $(ODIR)/sketchybar $(ODIR)/sketchybar_arm64 48 | lipo -create -output $(ODIR)/sketchybar $(ODIR)/sketchybar_x86 $(ODIR)/sketchybar_arm64 49 | 50 | debug: CFLAGS=-std=c99 -Wall -g 51 | debug: arm64 52 | 53 | asan: CFLAGS=-std=c99 -Wall -g -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer 54 | asan: clean arm64 55 | ./bin/sketchybar 56 | 57 | $(ODIR)/sketchybar: $(SRC)/sketchybar.c $(OBJ) | $(ODIR) 58 | $(CC) $(CFLAGS) $^ -o $@ $(LIBS) 59 | 60 | $(ODIR)/%.o: $(SRC)/%.c $(SRC)/%.h | $(ODIR) 61 | $(CC) -c -o $@ $< $(CFLAGS) 62 | 63 | $(ODIR)/%.om: $(SRC)/%.m $(SRC)/%.h | $(ODIR) 64 | $(CC) -c -o $@ $< $(CFLAGS) 65 | 66 | $(ODIR): 67 | mkdir $(ODIR) 68 | 69 | clean: 70 | rm -rf $(ODIR) 71 | -------------------------------------------------------------------------------- /plugins/battery.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PERCENTAGE="$(pmset -g batt | grep -Eo "\d+%" | cut -d% -f1)" 4 | CHARGING="$(pmset -g batt | grep 'AC Power')" 5 | 6 | if [ "$PERCENTAGE" = "" ]; then 7 | exit 0 8 | fi 9 | 10 | case "${PERCENTAGE}" in 11 | 9[0-9]|100) ICON="" 12 | ;; 13 | [6-8][0-9]) ICON="" 14 | ;; 15 | [3-5][0-9]) ICON="" 16 | ;; 17 | [1-2][0-9]) ICON="" 18 | ;; 19 | *) ICON="" 20 | esac 21 | 22 | if [[ "$CHARGING" != "" ]]; then 23 | ICON="" 24 | fi 25 | 26 | # The item invoking this script (name $NAME) will get its icon and label 27 | # updated with the current battery status 28 | sketchybar --set "$NAME" icon="$ICON" label="${PERCENTAGE}%" 29 | -------------------------------------------------------------------------------- /plugins/clock.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # The $NAME variable is passed from sketchybar and holds the name of 4 | # the item invoking this script: 5 | # https://felixkratz.github.io/SketchyBar/config/events#events-and-scripting 6 | 7 | sketchybar --set "$NAME" label="$(date '+%d/%m %H:%M')" 8 | 9 | -------------------------------------------------------------------------------- /plugins/front_app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Some events send additional information specific to the event in the $INFO 4 | # variable. E.g. the front_app_switched event sends the name of the newly 5 | # focused application in the $INFO variable: 6 | # https://felixkratz.github.io/SketchyBar/config/events#events-and-scripting 7 | 8 | if [ "$SENDER" = "front_app_switched" ]; then 9 | sketchybar --set "$NAME" label="$INFO" 10 | fi 11 | -------------------------------------------------------------------------------- /plugins/space.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # The $SELECTED variable is available for space components and indicates if 4 | # the space invoking this script (with name: $NAME) is currently selected: 5 | # https://felixkratz.github.io/SketchyBar/config/components#space----associate-mission-control-spaces-with-an-item 6 | 7 | sketchybar --set "$NAME" background.drawing="$SELECTED" 8 | -------------------------------------------------------------------------------- /plugins/volume.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # The volume_change event supplies a $INFO variable in which the current volume 4 | # percentage is passed to the script. 5 | 6 | if [ "$SENDER" = "volume_change" ]; then 7 | VOLUME="$INFO" 8 | 9 | case "$VOLUME" in 10 | [6-9][0-9]|100) ICON="󰕾" 11 | ;; 12 | [3-5][0-9]) ICON="󰖀" 13 | ;; 14 | [1-9]|[1-2][0-9]) ICON="󰕿" 15 | ;; 16 | *) ICON="󰖁" 17 | esac 18 | 19 | sketchybar --set "$NAME" icon="$ICON" label="$VOLUME%" 20 | fi 21 | -------------------------------------------------------------------------------- /sketchybarrc: -------------------------------------------------------------------------------- 1 | # This is a demo config to showcase some of the most important commands. 2 | # It is meant to be changed and configured, as it is intentionally kept sparse. 3 | # For a (much) more advanced configuration example see my dotfiles: 4 | # https://github.com/FelixKratz/dotfiles 5 | 6 | PLUGIN_DIR="$CONFIG_DIR/plugins" 7 | 8 | ##### Bar Appearance ##### 9 | # Configuring the general appearance of the bar. 10 | # These are only some of the options available. For all options see: 11 | # https://felixkratz.github.io/SketchyBar/config/bar 12 | # If you are looking for other colors, see the color picker: 13 | # https://felixkratz.github.io/SketchyBar/config/tricks#color-picker 14 | 15 | sketchybar --bar position=top height=40 blur_radius=30 color=0x40000000 16 | 17 | ##### Changing Defaults ##### 18 | # We now change some default values, which are applied to all further items. 19 | # For a full list of all available item properties see: 20 | # https://felixkratz.github.io/SketchyBar/config/items 21 | 22 | default=( 23 | padding_left=5 24 | padding_right=5 25 | icon.font="Hack Nerd Font:Bold:17.0" 26 | label.font="Hack Nerd Font:Bold:14.0" 27 | icon.color=0xffffffff 28 | label.color=0xffffffff 29 | icon.padding_left=4 30 | icon.padding_right=4 31 | label.padding_left=4 32 | label.padding_right=4 33 | ) 34 | sketchybar --default "${default[@]}" 35 | 36 | ##### Adding Mission Control Space Indicators ##### 37 | # Let's add some mission control spaces: 38 | # https://felixkratz.github.io/SketchyBar/config/components#space----associate-mission-control-spaces-with-an-item 39 | # to indicate active and available mission control spaces. 40 | 41 | SPACE_ICONS=("1" "2" "3" "4" "5" "6" "7" "8" "9" "10") 42 | for i in "${!SPACE_ICONS[@]}" 43 | do 44 | sid="$(($i+1))" 45 | space=( 46 | space="$sid" 47 | icon="${SPACE_ICONS[i]}" 48 | icon.padding_left=7 49 | icon.padding_right=7 50 | background.color=0x40ffffff 51 | background.corner_radius=5 52 | background.height=25 53 | label.drawing=off 54 | script="$PLUGIN_DIR/space.sh" 55 | click_script="yabai -m space --focus $sid" 56 | ) 57 | sketchybar --add space space."$sid" left --set space."$sid" "${space[@]}" 58 | done 59 | 60 | ##### Adding Left Items ##### 61 | # We add some regular items to the left side of the bar, where 62 | # only the properties deviating from the current defaults need to be set 63 | 64 | sketchybar --add item chevron left \ 65 | --set chevron icon= label.drawing=off \ 66 | --add item front_app left \ 67 | --set front_app icon.drawing=off script="$PLUGIN_DIR/front_app.sh" \ 68 | --subscribe front_app front_app_switched 69 | 70 | ##### Adding Right Items ##### 71 | # In the same way as the left items we can add items to the right side. 72 | # Additional position (e.g. center) are available, see: 73 | # https://felixkratz.github.io/SketchyBar/config/items#adding-items-to-sketchybar 74 | 75 | # Some items refresh on a fixed cycle, e.g. the clock runs its script once 76 | # every 10s. Other items respond to events they subscribe to, e.g. the 77 | # volume.sh script is only executed once an actual change in system audio 78 | # volume is registered. More info about the event system can be found here: 79 | # https://felixkratz.github.io/SketchyBar/config/events 80 | 81 | sketchybar --add item clock right \ 82 | --set clock update_freq=10 icon= script="$PLUGIN_DIR/clock.sh" \ 83 | --add item volume right \ 84 | --set volume script="$PLUGIN_DIR/volume.sh" \ 85 | --subscribe volume volume_change \ 86 | --add item battery right \ 87 | --set battery update_freq=120 script="$PLUGIN_DIR/battery.sh" \ 88 | --subscribe battery system_woke power_source_change 89 | 90 | ##### Force all scripts to run the first time (never do this in a script) ##### 91 | sketchybar --update 92 | -------------------------------------------------------------------------------- /src/alias.c: -------------------------------------------------------------------------------- 1 | #include "alias.h" 2 | 3 | void print_all_menu_items(FILE* rsp) { 4 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 5 | if (__builtin_available(macOS 11.0, *)) { 6 | if (!CGRequestScreenCaptureAccess()) { 7 | respond(rsp, "[!] Query (default_menu_items): Screen Recording " 8 | "Permissions not given. Restart SketchyBar after granting " 9 | "permissions.\n"); 10 | return; 11 | } 12 | } 13 | 14 | #endif 15 | CFArrayRef window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, 16 | kCGNullWindowID ); 17 | int window_count = CFArrayGetCount(window_list); 18 | 19 | float x_pos[window_count]; 20 | char* owner[window_count]; 21 | char* name[window_count]; 22 | memset(owner, 0, sizeof(owner)); 23 | memset(name, 0, sizeof(name)); 24 | 25 | int item_count = 0; 26 | for (int i = 0; i < window_count; ++i) { 27 | x_pos[i] = -9999.f; 28 | CFDictionaryRef dictionary = CFArrayGetValueAtIndex(window_list, i); 29 | if (!dictionary) continue; 30 | 31 | CFStringRef owner_ref = CFDictionaryGetValue(dictionary, 32 | kCGWindowOwnerName); 33 | 34 | CFNumberRef owner_pid_ref = CFDictionaryGetValue(dictionary, 35 | kCGWindowOwnerPID); 36 | 37 | CFStringRef name_ref = CFDictionaryGetValue(dictionary, kCGWindowName); 38 | CFNumberRef layer_ref = CFDictionaryGetValue(dictionary, kCGWindowLayer); 39 | CFDictionaryRef bounds_ref = CFDictionaryGetValue(dictionary, 40 | kCGWindowBounds); 41 | 42 | if (!name_ref || !owner_ref || !owner_pid_ref || !layer_ref || !bounds_ref) 43 | continue; 44 | 45 | long long int layer = 0; 46 | CFNumberGetValue(layer_ref, CFNumberGetType(layer_ref), &layer); 47 | uint64_t owner_pid = 0; 48 | CFNumberGetValue(owner_pid_ref, 49 | CFNumberGetType(owner_pid_ref), 50 | &owner_pid ); 51 | 52 | if (layer != MENUBAR_LAYER) continue; 53 | CGRect bounds = CGRectNull; 54 | if (!CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) continue; 55 | owner[item_count] = cfstring_copy(owner_ref); 56 | name[item_count] = cfstring_copy(name_ref); 57 | x_pos[item_count++] = bounds.origin.x; 58 | } 59 | 60 | if (item_count > 0) { 61 | fprintf(rsp, "[\n"); 62 | int counter = 0; 63 | for (int i = 0; i < item_count; i++) { 64 | float current_pos = x_pos[0]; 65 | uint32_t current_pos_id = 0; 66 | for (int j = 0; j < window_count; j++) { 67 | if (!name[j] || !owner[j]) continue; 68 | if (x_pos[j] > current_pos) { 69 | current_pos = x_pos[j]; 70 | current_pos_id = j; 71 | } 72 | } 73 | 74 | if (!name[current_pos_id] || !owner[current_pos_id]) continue; 75 | if (strcmp(name[current_pos_id], "") != 0) { 76 | if (counter++ > 0) { 77 | fprintf(rsp, ", \n"); 78 | } 79 | fprintf(rsp, "\t\"%s,%s\"", owner[current_pos_id], 80 | name[current_pos_id] ); 81 | } 82 | x_pos[current_pos_id] = -9999.f; 83 | } 84 | fprintf(rsp, "\n]\n"); 85 | for (int i = 0; i < window_count; i++) { 86 | if (owner[i]) free(owner[i]); 87 | if (name[i]) free(name[i]); 88 | } 89 | } 90 | CFRelease(window_list); 91 | } 92 | 93 | void alias_get_permission(struct alias* alias) { 94 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 95 | if (__builtin_available(macOS 11.0, *)) { 96 | alias->permission = CGRequestScreenCaptureAccess(); 97 | } 98 | #else 99 | if(__builtin_available(macos 10.15, *)) { 100 | CGImageRef img = CGWindowListCreateImage(CGRectMake(0, 0, 1, 1), 101 | kCGWindowListOptionOnScreenOnly, 102 | kCGNullWindowID, 103 | kCGWindowImageDefault ); 104 | 105 | CFRelease(img); 106 | } 107 | #endif 108 | } 109 | 110 | void alias_init(struct alias* alias) { 111 | alias->name = NULL; 112 | alias->owner = NULL; 113 | alias->color_override = false; 114 | color_init(&alias->color, 0xffff0000); 115 | alias->update_frequency = 1; 116 | alias->counter = 0; 117 | 118 | window_init(&alias->window); 119 | image_init(&alias->image); 120 | } 121 | 122 | static void alias_find_window(struct alias* alias) { 123 | CFArrayRef window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, 124 | kCGNullWindowID ); 125 | int window_count = CFArrayGetCount(window_list); 126 | 127 | for (int i = 0; i < window_count; ++i) { 128 | CFDictionaryRef dictionary = CFArrayGetValueAtIndex(window_list, i); 129 | if (!dictionary) continue; 130 | 131 | CFStringRef owner_ref = CFDictionaryGetValue(dictionary, 132 | kCGWindowOwnerName); 133 | 134 | CFNumberRef owner_pid_ref = CFDictionaryGetValue(dictionary, 135 | kCGWindowOwnerPID); 136 | 137 | CFStringRef name_ref = CFDictionaryGetValue(dictionary, kCGWindowName); 138 | if (!name_ref) continue; 139 | if (!owner_ref) continue; 140 | char* owner = cfstring_copy(owner_ref); 141 | char* name = cfstring_copy(name_ref); 142 | 143 | if (!(alias->owner && strcmp(alias->owner, owner) == 0 144 | && ((alias->name && strcmp(alias->name, name) == 0) 145 | || (!alias->name && strcmp(name, "") != 0) ))) { 146 | free(owner); 147 | free(name); 148 | continue; 149 | } 150 | free(owner); 151 | free(name); 152 | 153 | CFNumberRef layer_ref = CFDictionaryGetValue(dictionary, kCGWindowLayer); 154 | if (!layer_ref) continue; 155 | 156 | uint64_t layer = 0; 157 | CFNumberGetValue(layer_ref, CFNumberGetType(layer_ref), &layer); 158 | if (layer != MENUBAR_LAYER) continue; 159 | 160 | CFNumberGetValue(owner_pid_ref, 161 | CFNumberGetType(owner_pid_ref), 162 | &alias->pid ); 163 | 164 | CFNumberRef window_id_ref = CFDictionaryGetValue(dictionary, 165 | kCGWindowNumber); 166 | 167 | if (!window_id_ref) continue; 168 | CFDictionaryRef bounds_ref = CFDictionaryGetValue(dictionary, kCGWindowBounds); 169 | if (!bounds_ref) continue; 170 | 171 | CGRect bounds; 172 | CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds); 173 | 174 | uint64_t wid; 175 | CFNumberGetValue(window_id_ref, 176 | CFNumberGetType(window_id_ref), 177 | &wid ); 178 | 179 | alias->window.id = (uint32_t)wid; 180 | alias->window.frame.size = bounds.size; 181 | alias->window.origin = bounds.origin; 182 | 183 | CFRelease(window_list); 184 | return; 185 | } 186 | alias->window.id = 0; 187 | CFRelease(window_list); 188 | } 189 | 190 | static bool alias_update_image(struct alias* alias, bool forced) { 191 | if (alias->window.id == 0) alias_find_window(alias); 192 | if (alias->window.id == 0) return false; 193 | 194 | bool disabled = false; 195 | CGImageRef image_ref = window_capture(&alias->window, &disabled); 196 | 197 | if (!image_ref) { 198 | if (!disabled) { 199 | alias->window.id = 0; 200 | image_destroy(&alias->image); 201 | } 202 | return false; 203 | } 204 | 205 | return image_set_image(&alias->image, 206 | image_ref, 207 | alias->window.frame, 208 | forced ); 209 | } 210 | 211 | void alias_setup(struct alias* alias, char* owner, char* name) { 212 | alias->name = name; 213 | alias->owner = owner; 214 | alias_get_permission(alias); 215 | alias_update_image(alias, true); 216 | } 217 | 218 | uint32_t alias_get_length(struct alias* alias) { 219 | if (alias->image.image_ref) return alias->image.bounds.size.width; 220 | return 0; 221 | } 222 | 223 | uint32_t alias_get_height(struct alias* alias) { 224 | if (alias->image.image_ref) return alias->image.bounds.size.height; 225 | return 0; 226 | } 227 | 228 | bool alias_update(struct alias* alias, bool forced) { 229 | if (alias->update_frequency == 0) return false; 230 | 231 | alias->counter++; 232 | if (forced || alias->counter >= alias->update_frequency) { 233 | alias->counter = 0; 234 | if (alias_update_image(alias, forced)) { 235 | return true; 236 | } 237 | } 238 | return false; 239 | } 240 | 241 | void alias_draw(struct alias* alias, CGContextRef context) { 242 | if (alias->color_override) { 243 | CGContextSaveGState(context); 244 | image_draw(&alias->image, context); 245 | CGContextClipToMask(context, alias->image.bounds, alias->image.image_ref); 246 | CGContextSetRGBFillColor(context, 247 | alias->color.r, 248 | alias->color.g, 249 | alias->color.b, 250 | alias->color.a ); 251 | 252 | CGContextFillRect(context, alias->image.bounds); 253 | CGContextRestoreGState(context); 254 | } 255 | else { 256 | image_draw(&alias->image, context); 257 | } 258 | } 259 | 260 | void alias_destroy(struct alias* alias) { 261 | image_destroy(&alias->image); 262 | if (alias->name) free(alias->name); 263 | if (alias->owner) free(alias->owner); 264 | alias->name = NULL; 265 | alias->owner = NULL; 266 | } 267 | 268 | void alias_calculate_bounds(struct alias* alias, uint32_t x, uint32_t y) { 269 | image_calculate_bounds(&alias->image, x, y); 270 | } 271 | 272 | bool alias_parse_sub_domain(struct alias* alias, FILE* rsp, struct token property, char* message) { 273 | struct key_value_pair key_value_pair = get_key_value_pair(property.text,'.'); 274 | if (key_value_pair.key && key_value_pair.value) { 275 | struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) }; 276 | struct token entry = { key_value_pair.value, strlen(key_value_pair.value)}; 277 | if (token_equals(subdom, SUB_DOMAIN_SHADOW)) 278 | return shadow_parse_sub_domain(&alias->image.shadow, 279 | rsp, 280 | entry, 281 | message ); 282 | else if (token_equals(subdom, SUB_DOMAIN_COLOR)) { 283 | bool changed = !alias->color_override; 284 | alias->color_override = true; 285 | return color_parse_sub_domain(&alias->color, rsp, entry, message) 286 | || changed; 287 | } 288 | else { 289 | respond(rsp, "[!] Alias: Invalid subdomain '%s'\n", subdom.text); 290 | } 291 | } 292 | else if (token_equals(property, PROPERTY_COLOR)) { 293 | color_set_hex(&alias->color, token_to_uint32t(get_token(&message))); 294 | alias->color_override = true; 295 | return true; 296 | } else if (token_equals(property, PROPERTY_SCALE)) { 297 | return image_set_scale(&alias->image, token_to_float(get_token(&message))); 298 | } else if (token_equals(property, PROPERTY_UPDATE_FREQ)) { 299 | alias->update_frequency = token_to_uint32t(get_token(&message)); 300 | return false; 301 | } else { 302 | respond(rsp, "[!] Alias: Invalid property '%s' \n", property.text); 303 | } 304 | return false; 305 | } 306 | -------------------------------------------------------------------------------- /src/alias.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "misc/helpers.h" 4 | #include "window.h" 5 | #include "image.h" 6 | 7 | #define MENUBAR_LAYER 0x19 8 | 9 | struct alias { 10 | bool permission; 11 | uint32_t update_frequency; 12 | uint32_t counter; 13 | 14 | char* name; 15 | char* owner; 16 | 17 | pid_t pid; 18 | 19 | struct window window; 20 | 21 | bool color_override; 22 | struct color color; 23 | struct image image; 24 | }; 25 | 26 | void alias_init(struct alias* alias); 27 | void alias_setup(struct alias* alias, char* owner, char* name); 28 | uint32_t alias_get_length(struct alias* alias); 29 | uint32_t alias_get_height(struct alias* alias); 30 | 31 | void alias_calculate_bounds(struct alias* alias, uint32_t x, uint32_t y); 32 | void alias_draw(struct alias* alias, CGContextRef context); 33 | bool alias_update(struct alias* alias, bool forced); 34 | void alias_destroy(struct alias* alias); 35 | 36 | void print_all_menu_items(FILE* rsp); 37 | 38 | bool alias_parse_sub_domain(struct alias* alias, FILE* rsp, struct token property, char* message); 39 | -------------------------------------------------------------------------------- /src/animation.c: -------------------------------------------------------------------------------- 1 | #include "animation.h" 2 | #include "event.h" 3 | 4 | static CVReturn animation_frame_callback(CVDisplayLinkRef display_link, const CVTimeStamp* now, const CVTimeStamp* output_time, CVOptionFlags flags, CVOptionFlags* flags_out, void* context) { 5 | struct event event = { (void*)output_time->hostTime, ANIMATOR_REFRESH }; 6 | event_post(&event); 7 | return kCVReturnSuccess; 8 | } 9 | 10 | struct animation* animation_create() { 11 | struct animation* animation = malloc(sizeof(struct animation)); 12 | memset(animation, 0, sizeof(struct animation)); 13 | 14 | return animation; 15 | } 16 | 17 | static void animation_destroy(struct animation* animation) { 18 | if (animation) free(animation); 19 | } 20 | 21 | static void animation_lock(struct animation* animation) { 22 | animation->locked = true; 23 | } 24 | 25 | void animation_setup(struct animation* animation, void* target, animator_function* update_function, int initial_value, int final_value, uint32_t duration, char interp_function) { 26 | // The animation duration is represented as a frame count equivalent on a 27 | // 60Hz display. E.g. 120frames = 2 seconds 28 | animation->duration = (double)duration / 60.0; 29 | animation->initial_value = initial_value; 30 | animation->final_value = final_value; 31 | animation->update_function = update_function; 32 | animation->target = target; 33 | animation->separate_bytes = false; 34 | animation->as_float = false; 35 | 36 | if (interp_function == INTERP_FUNCTION_TANH) { 37 | animation->interp_function = &function_tanh; 38 | } else if (interp_function == INTERP_FUNCTION_SIN) { 39 | animation->interp_function = &function_sin; 40 | } else if (interp_function == INTERP_FUNCTION_QUADRATIC) { 41 | animation->interp_function = &function_square; 42 | } else if (interp_function == INTERP_FUNCTION_EXP) { 43 | animation->interp_function = &function_exp; 44 | } else if (interp_function == INTERP_FUNCTION_CIRC) { 45 | animation->interp_function = &function_circ; 46 | } else { 47 | animation->interp_function = &function_linear; 48 | } 49 | } 50 | 51 | static bool animation_update(struct animation* animation, uint64_t time, uint64_t clock) { 52 | if (!animation->target 53 | || !animation->update_function 54 | || animation->waiting ) { 55 | return false; 56 | } 57 | 58 | if (!animation->initial_time) animation->initial_time = time; 59 | double t = animation->duration > 0 60 | ? ((double)(time - animation->initial_time) 61 | / (double)(animation->duration * clock)) 62 | : 1.0; 63 | 64 | bool final_frame = t >= 1.0; 65 | if (t < 0.0) t = 0.0; 66 | if (t > 1.0) t = 1.0; 67 | 68 | double slider = final_frame ? 1.0 : animation->interp_function(t); 69 | 70 | int value; 71 | if (animation->separate_bytes) { 72 | for (int i = 0; i < 4; i++) { 73 | unsigned char byte_i = *((unsigned char*)&animation->initial_value + i); 74 | unsigned char byte_f = *((unsigned char*)&animation->final_value + i); 75 | 76 | unsigned char byte_val = (1. - slider) * byte_i + slider * byte_f; 77 | *((unsigned char*)&value + i) = byte_val; 78 | } 79 | } else if (animation->as_float) { 80 | *((float*)&value) = (1. - slider) * *(float*)&animation->initial_value 81 | + slider * *(float*)&animation->final_value; 82 | 83 | } else { 84 | value = (1. - slider) * animation->initial_value 85 | + slider * animation->final_value; 86 | } 87 | 88 | bool needs_update; 89 | if (animation->as_float) { 90 | needs_update = 91 | ((bool (*)(void*, float))animation->update_function)(animation->target, 92 | *((float*)&value) ); 93 | } else { 94 | needs_update = animation->update_function(animation->target, value); 95 | } 96 | 97 | bool found_item = false; 98 | for (int i = 0; i < g_bar_manager.bar_item_count; i++) { 99 | if (needs_update 100 | && (animation->target >= (void*)g_bar_manager.bar_items[i]) 101 | && (animation->target < ((void*)g_bar_manager.bar_items[i] 102 | + sizeof(struct bar_item) ))) { 103 | 104 | bar_item_needs_update(g_bar_manager.bar_items[i]); 105 | found_item = true; 106 | } 107 | } 108 | 109 | if (!found_item && needs_update) g_bar_manager.bar_needs_update = true; 110 | 111 | animation->finished = final_frame; 112 | if (animation->finished && animation->next) { 113 | animation->next->previous = NULL; 114 | animation->next->waiting = false; 115 | animation->next = NULL; 116 | } 117 | return needs_update; 118 | } 119 | 120 | void animator_init(struct animator* animator) { 121 | animator->animations = NULL; 122 | animator->animation_count = 0; 123 | animator->interp_function = 0; 124 | animator->duration = 0; 125 | animator->display_link = NULL; 126 | 127 | animator_renew_display_link(animator); 128 | } 129 | 130 | void animator_renew_display_link(struct animator* animator) { 131 | animator_destroy_display_link(animator); 132 | CVDisplayLinkCreateWithActiveCGDisplays(&animator->display_link); 133 | 134 | CVDisplayLinkSetOutputCallback(animator->display_link, 135 | animation_frame_callback, 136 | animator ); 137 | 138 | animator->clock = CVGetHostClockFrequency(); 139 | CVDisplayLinkStart(animator->display_link); 140 | } 141 | 142 | void animator_destroy_display_link(struct animator* animator) { 143 | if (animator->display_link) { 144 | CVDisplayLinkStop(animator->display_link); 145 | CVDisplayLinkRelease(animator->display_link); 146 | animator->display_link = NULL; 147 | } 148 | } 149 | 150 | void animator_lock(struct animator* animator) { 151 | for (int i = 0; i < animator->animation_count; i++) { 152 | animation_lock(animator->animations[i]); 153 | } 154 | } 155 | 156 | static void animator_calculate_offset_for_animation(struct animator* animator, struct animation* animation) { 157 | if (animator->animation_count < 1) return; 158 | 159 | struct animation* previous = NULL; 160 | for (int i = animator->animation_count - 1; i >= 0; i--) { 161 | struct animation* current = animator->animations[i]; 162 | if (current->target == animation->target 163 | && current->update_function == animation->update_function) { 164 | previous = current; 165 | break; 166 | } 167 | } 168 | 169 | if (previous) { 170 | animation->initial_value = previous->final_value; 171 | previous->next = animation; 172 | animation->previous = previous; 173 | animation->waiting = true; 174 | } 175 | } 176 | 177 | void animator_add(struct animator* animator, struct animation* animation) { 178 | animator_calculate_offset_for_animation(animator, animation); 179 | animator->animations = realloc(animator->animations, 180 | sizeof(struct animation*) 181 | * ++animator->animation_count); 182 | animator->animations[animator->animation_count - 1] = animation; 183 | 184 | if (!animator->display_link) animator_renew_display_link(animator); 185 | } 186 | 187 | static void animator_remove(struct animator* animator, struct animation* animation) { 188 | if (animator->animation_count == 1) { 189 | free(animator->animations); 190 | animator->animations = NULL; 191 | animator->animation_count = 0; 192 | } else { 193 | struct animation* tmp[animator->animation_count - 1]; 194 | int count = 0; 195 | for (int i = 0; i < animator->animation_count; i++) { 196 | if (animator->animations[i] == animation) continue; 197 | tmp[count++] = animator->animations[i]; 198 | } 199 | animator->animation_count--; 200 | animator->animations = realloc(animator->animations, 201 | sizeof(struct animation*) 202 | *animator->animation_count); 203 | 204 | memcpy(animator->animations, 205 | tmp, 206 | sizeof(struct animation*)*animator->animation_count); 207 | } 208 | 209 | if (animation->previous) animation->previous->next = NULL; 210 | if (animation->next) animation->next->previous = NULL; 211 | 212 | animation_destroy(animation); 213 | } 214 | 215 | void animator_cancel_locked(struct animator* animator, void* target, animator_function* function) { 216 | struct animation* remove[animator->animation_count]; 217 | memset(remove, 0, animator->animation_count); 218 | uint32_t remove_count = 0; 219 | 220 | for (int i = 0; i < animator->animation_count; i++) { 221 | struct animation* animation = animator->animations[i]; 222 | if (animation->locked 223 | && animation->target == target 224 | && animation->update_function == function) { 225 | remove[remove_count++] = animation; 226 | } 227 | } 228 | 229 | for (uint32_t i = 0; i < remove_count; i++) { 230 | animator_remove(animator, remove[i]); 231 | } 232 | } 233 | 234 | bool animator_cancel(struct animator* animator, void* target, animator_function* function) { 235 | bool needs_update = false; 236 | 237 | struct animation* remove[animator->animation_count]; 238 | memset(remove, 0, animator->animation_count); 239 | uint32_t remove_count = 0; 240 | 241 | for (int i = 0; i < animator->animation_count; i++) { 242 | struct animation* animation = animator->animations[i]; 243 | if (animation->target == target 244 | && animation->update_function == function) { 245 | needs_update |= function(animation->target, animation->final_value); 246 | remove[remove_count++] = animation; 247 | } 248 | } 249 | 250 | for (uint32_t i = 0; i < remove_count; i++) { 251 | animator_remove(animator, remove[i]); 252 | } 253 | 254 | return needs_update; 255 | } 256 | 257 | bool animator_update(struct animator* animator, uint64_t time) { 258 | bool needs_refresh = false; 259 | struct animation* remove[animator->animation_count]; 260 | memset(remove, 0, animator->animation_count); 261 | uint32_t remove_count = 0; 262 | 263 | for (uint32_t i = 0; i < animator->animation_count; i++) { 264 | needs_refresh |= animation_update(animator->animations[i], 265 | time, 266 | animator->clock ); 267 | 268 | if (animator->animations[i]->finished) { 269 | remove[remove_count++] = animator->animations[i]; 270 | } 271 | } 272 | 273 | for (uint32_t i = 0; i < remove_count; i++) { 274 | animator_remove(animator, remove[i]); 275 | } 276 | 277 | if (animator->animation_count == 0) animator_destroy_display_link(animator); 278 | return needs_refresh; 279 | } 280 | 281 | void animator_destroy(struct animator* animator) { 282 | if (animator->animation_count > 0) { 283 | if (animator->display_link) 284 | CVDisplayLinkStop(animator->display_link); 285 | CVDisplayLinkRelease(animator->display_link); 286 | animator->display_link = NULL; 287 | 288 | for (int i = 0; i < animator->animation_count; i++) { 289 | animation_destroy(animator->animations[i]); 290 | } 291 | } 292 | 293 | if (animator->animations) free(animator->animations); 294 | } 295 | -------------------------------------------------------------------------------- /src/animation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "misc/helpers.h" 4 | 5 | extern struct bar_manager g_bar_manager; 6 | 7 | #define ANIMATE(f, o, p, t) \ 8 | {\ 9 | if (g_bar_manager.animator.duration > 0) { \ 10 | animator_cancel_locked(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \ 11 | struct animation* animation = animation_create(); \ 12 | animation_setup(animation, \ 13 | (void*)o, \ 14 | (bool (*)(void*, int))&f, \ 15 | p, \ 16 | t, \ 17 | g_bar_manager.animator.duration, \ 18 | g_bar_manager.animator.interp_function ); \ 19 | animator_add(&g_bar_manager.animator, animation); \ 20 | } else { \ 21 | needs_refresh = animator_cancel(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \ 22 | needs_refresh |= f(o, t); \ 23 | } \ 24 | } 25 | 26 | #define ANIMATE_FLOAT(f, o, p, t) \ 27 | {\ 28 | if (g_bar_manager.animator.duration > 0) { \ 29 | animator_cancel_locked(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \ 30 | struct animation* animation = animation_create(); \ 31 | float initial_value = p; \ 32 | float final_value = t; \ 33 | animation_setup(animation, \ 34 | (void*)o, \ 35 | (bool (*)(void*, int))&f, \ 36 | *(int*)&initial_value, \ 37 | *(int*)&final_value, \ 38 | g_bar_manager.animator.duration, \ 39 | g_bar_manager.animator.interp_function ); \ 40 | animation->as_float = true; \ 41 | animator_add(&g_bar_manager.animator, animation); \ 42 | } else { \ 43 | needs_refresh = animator_cancel(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \ 44 | needs_refresh |= f(o, t); \ 45 | } \ 46 | } 47 | 48 | #define ANIMATE_BYTES(f, o, p, t) \ 49 | {\ 50 | if (g_bar_manager.animator.duration > 0) { \ 51 | animator_cancel_locked(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \ 52 | struct animation* animation = animation_create(); \ 53 | animation_setup(animation, \ 54 | (void*)o, \ 55 | (bool (*)(void*, int))&f, \ 56 | p, \ 57 | t, \ 58 | g_bar_manager.animator.duration, \ 59 | g_bar_manager.animator.interp_function ); \ 60 | animation->separate_bytes = true; \ 61 | animator_add(&g_bar_manager.animator, animation); \ 62 | } else { \ 63 | needs_refresh = animator_cancel(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \ 64 | needs_refresh |= f(o, t); \ 65 | } \ 66 | } 67 | 68 | #define ANIMATOR_FUNCTION(name) bool name(void* target, int value); 69 | typedef ANIMATOR_FUNCTION(animator_function); 70 | 71 | #define ANIMATION_FUNCTION(name) double name(double x); 72 | typedef ANIMATION_FUNCTION(animation_function); 73 | 74 | #define INTERP_FUNCTION_LINEAR 'l' 75 | #define INTERP_FUNCTION_QUADRATIC 'q' 76 | #define INTERP_FUNCTION_SIN 's' 77 | #define INTERP_FUNCTION_TANH 't' 78 | #define INTERP_FUNCTION_CIRC 'c' 79 | #define INTERP_FUNCTION_BOUNCE 'b' 80 | #define INTERP_FUNCTION_EXP 'e' 81 | #define INTERP_FUNCTION_OVERSHOOT 'o' 82 | 83 | 84 | struct animation { 85 | bool separate_bytes; 86 | bool as_float; 87 | bool locked; 88 | bool finished; 89 | bool waiting; 90 | 91 | uint64_t initial_time; 92 | double duration; 93 | 94 | int initial_value; 95 | int final_value; 96 | animation_function* interp_function; 97 | 98 | void* target; 99 | animator_function* update_function; 100 | 101 | struct animation* next; 102 | struct animation* previous; 103 | }; 104 | 105 | struct animation* animation_create(); 106 | void animation_setup(struct animation* animation, void* target, animator_function* update_function, int initial_value, int final_value, uint32_t duration, char interp_function); 107 | 108 | struct animator { 109 | CVDisplayLinkRef display_link; 110 | 111 | double clock; 112 | uint32_t interp_function; 113 | uint32_t duration; 114 | struct animation** animations; 115 | uint32_t animation_count; 116 | }; 117 | 118 | void animator_init(struct animator* animator); 119 | void animator_add(struct animator* animator, struct animation* animation); 120 | 121 | bool animator_cancel(struct animator* animator, void* target, animator_function* function); 122 | void animator_cancel_locked(struct animator* animator, void* target, animator_function* function); 123 | 124 | bool animator_update(struct animator* animator, uint64_t time); 125 | void animator_lock(struct animator* animator); 126 | void animator_destroy(struct animator* animator); 127 | 128 | void animator_renew_display_link(struct animator* animator); 129 | void animator_destroy_display_link(struct animator* animator); 130 | -------------------------------------------------------------------------------- /src/app_windows.c: -------------------------------------------------------------------------------- 1 | #include "app_windows.h" 2 | #include "workspace.h" 3 | #include "misc/helpers.h" 4 | #include "event.h" 5 | 6 | extern pid_t g_pid; 7 | struct app_windows g_windows = { 0 }; 8 | struct app_windows g_hidden_windows = { 0 }; 9 | bool g_space_window_events = false; 10 | 11 | static bool iterator_window_suitable(CFTypeRef iterator) { 12 | uint64_t tags = SLSWindowIteratorGetTags(iterator); 13 | uint64_t attributes = SLSWindowIteratorGetAttributes(iterator); 14 | uint32_t parent_wid = SLSWindowIteratorGetParentID(iterator); 15 | if (((parent_wid == 0) 16 | && ((attributes & 0x2) 17 | || (tags & 0x400000000000000)) 18 | && (((tags & 0x1)) 19 | || ((tags & 0x2) 20 | && (tags & 0x80000000))))) { 21 | return true; 22 | } 23 | return false; 24 | } 25 | 26 | void app_window_clear(struct app_window* window) { 27 | memset(window, 0, sizeof(struct app_window)); 28 | } 29 | 30 | void app_windows_add(struct app_windows* windows, struct app_window* window) { 31 | for (int i = 0; i < windows->num_windows; i++) { 32 | if (!windows->windows[i].wid) { 33 | windows->windows[i] = *window; 34 | return; 35 | } 36 | } 37 | 38 | windows->windows = realloc(windows->windows, 39 | sizeof(struct app_window) 40 | * ++windows->num_windows); 41 | 42 | windows->windows[windows->num_windows - 1] = *window; 43 | } 44 | 45 | void app_windows_clear_space(struct app_windows* windows, uint64_t sid) { 46 | for (int i = 0; i < windows->num_windows; i++) { 47 | if (windows->windows[i].sid == sid) app_window_clear(windows->windows + i); 48 | } 49 | } 50 | 51 | void app_windows_register_notifications() { 52 | uint32_t window_count = 0; 53 | uint32_t wid_list[g_windows.num_windows + g_hidden_windows.num_windows]; 54 | for (int i = 0; i < g_windows.num_windows; i++) { 55 | if (g_windows.windows[i].wid) 56 | wid_list[window_count++] = g_windows.windows[i].wid; 57 | } 58 | 59 | for (int i = 0; i < g_hidden_windows.num_windows; i++) { 60 | if (g_hidden_windows.windows[i].wid) 61 | wid_list[window_count++] = g_hidden_windows.windows[i].wid; 62 | } 63 | SLSRequestNotificationsForWindows(g_connection, wid_list, window_count); 64 | } 65 | 66 | bool app_windows_find(struct app_windows* windows, struct app_window* window) { 67 | for (int i = 0; i < windows->num_windows; i++) { 68 | if (windows->windows[i].wid == window->wid 69 | && windows->windows[i].sid == window->sid) { 70 | return true; 71 | } 72 | } 73 | return false; 74 | } 75 | 76 | struct app_window* app_windows_find_by_wid(struct app_windows* windows, uint32_t wid) { 77 | for (int i = 0; i < windows->num_windows; i++) { 78 | if (windows->windows[i].wid == wid) return &windows->windows[i]; 79 | } 80 | return NULL; 81 | } 82 | 83 | static bool app_window_suitable(struct app_window* window) { 84 | CFArrayRef target_ref = cfarray_of_cfnumbers(&window->wid, 85 | sizeof(uint32_t), 86 | 1, 87 | kCFNumberSInt32Type); 88 | 89 | if (!target_ref) return false; 90 | 91 | bool suitable = false; 92 | CFTypeRef query = SLSWindowQueryWindows(g_connection, target_ref, 0x0); 93 | if (query) { 94 | CFTypeRef iterator = SLSWindowQueryResultCopyWindows(query); 95 | if (iterator && SLSWindowIteratorGetCount(iterator) > 0) { 96 | if (SLSWindowIteratorAdvance(iterator)) { 97 | if (iterator_window_suitable(iterator)) suitable = true; 98 | } 99 | } 100 | if (iterator) CFRelease(iterator); 101 | CFRelease(query); 102 | } 103 | CFRelease(target_ref); 104 | return suitable; 105 | } 106 | 107 | static void app_windows_post_event_for_space(struct app_windows* windows, uint64_t sid) { 108 | int index = mission_control_index(sid); 109 | pid_t pid_list[windows->num_windows]; 110 | uint32_t pid_count[windows->num_windows]; 111 | char* pid_name[windows->num_windows]; 112 | 113 | memset(&pid_list, 0, sizeof(pid_t)*windows->num_windows); 114 | memset(&pid_count, 0, sizeof(uint32_t)*windows->num_windows); 115 | memset(&pid_name, 0, sizeof(char*)*windows->num_windows); 116 | 117 | uint32_t length = 64; 118 | for (int i = 0; i < windows->num_windows; i++) { 119 | for (int j = 0; j < windows->num_windows; j++) { 120 | if ((!pid_list[j] || pid_list[j] == windows->windows[i].pid) 121 | && windows->windows[i].sid == sid) { 122 | pid_list[j] = windows->windows[i].pid; 123 | pid_count[j]++; 124 | if (!pid_name[j]) { 125 | pid_name[j] = workspace_copy_app_name_for_pid(pid_list[j]); 126 | length += pid_name[j] ? (strlen(pid_name[j]) + 16) : 0; 127 | } 128 | break; 129 | } 130 | } 131 | } 132 | 133 | for (int i = 0; i < windows->num_windows; i++) { 134 | for (int j = i + 1; j < windows->num_windows; j++) { 135 | if (pid_name[i] 136 | && pid_name[j] 137 | && strcmp(pid_name[i], pid_name[j]) == 0) { 138 | free(pid_name[j]); 139 | pid_name[j] = NULL; 140 | 141 | pid_count[i] += pid_count[j]; 142 | } 143 | } 144 | } 145 | 146 | char payload[length]; 147 | memset(payload, 0, length); 148 | snprintf(payload, length, "{\n" 149 | "\t\"space\": %d,\n" 150 | "\t\"apps\": {\n", 151 | index ); 152 | 153 | char* cursor = payload + strlen(payload); 154 | bool first = true; 155 | for (int i = 0; i < windows->num_windows; i++) { 156 | if (!pid_list[i]) break; 157 | if (!pid_name[i]) continue; 158 | if (!first) { 159 | snprintf(cursor, length - (cursor - payload), ",\n"); 160 | cursor = payload + strlen(payload); 161 | } else first = false; 162 | snprintf(cursor, 163 | length - (cursor - payload), 164 | "\t\t\"%s\": %d", 165 | pid_name[i], 166 | pid_count[i] ); 167 | 168 | free(pid_name[i]); 169 | cursor = payload + strlen(payload); 170 | } 171 | 172 | snprintf(cursor, length - (cursor - payload), "\n\t}\n}\n"); 173 | struct event event = { payload, SPACE_WINDOWS_CHANGED }; 174 | event_post(&event); 175 | } 176 | 177 | static void app_windows_update_space(struct app_windows* windows, uint64_t sid, bool silent) { 178 | app_windows_clear_space(windows, sid); 179 | CFArrayRef space_list_ref = cfarray_of_cfnumbers(&sid, 180 | sizeof(uint64_t), 181 | 1, 182 | kCFNumberSInt64Type); 183 | 184 | uint64_t set_tags = 1; 185 | uint64_t clear_tags = 0; 186 | CFArrayRef window_list = SLSCopyWindowsWithOptionsAndTags(g_connection, 187 | 0, 188 | space_list_ref, 189 | 0x2, 190 | &set_tags, 191 | &clear_tags ); 192 | 193 | if (window_list) { 194 | uint32_t window_count = CFArrayGetCount(window_list); 195 | if (window_count > 0) { 196 | CFTypeRef query = SLSWindowQueryWindows(g_connection, window_list, 0x0); 197 | if (query) { 198 | CFTypeRef iterator = SLSWindowQueryResultCopyWindows(query); 199 | if (iterator) { 200 | while(SLSWindowIteratorAdvance(iterator)) { 201 | if (iterator_window_suitable(iterator)) { 202 | uint32_t wid = SLSWindowIteratorGetWindowID(iterator); 203 | int wid_cid = 0; 204 | SLSGetWindowOwner(g_connection, wid, &wid_cid); 205 | 206 | pid_t pid = 0; 207 | SLSConnectionGetPID(wid_cid, &pid); 208 | struct app_window window = {.wid = wid, .sid = sid, .pid = pid}; 209 | app_windows_add(windows, &window); 210 | } 211 | } 212 | CFRelease(iterator); 213 | } 214 | CFRelease(query); 215 | } 216 | } 217 | CFRelease(window_list); 218 | } 219 | 220 | CFRelease(space_list_ref); 221 | if (!silent) app_windows_post_event_for_space(windows, sid); 222 | app_windows_register_notifications(); 223 | } 224 | 225 | struct window_spawn_data { 226 | uint64_t sid; 227 | uint32_t wid; 228 | }; 229 | 230 | static void window_spawn_handler(uint32_t event, struct window_spawn_data* data, size_t _, int cid) { 231 | uint32_t wid = data->wid; 232 | uint64_t sid = data->sid; 233 | 234 | if (!wid || !sid) return; 235 | 236 | struct app_window window = { .wid = wid, .sid = sid, .pid = 0 }; 237 | 238 | if (event == 1325 && app_window_suitable(&window)) { 239 | app_windows_update_space(&g_windows, sid, false); 240 | } else if (event == 1326 && app_windows_find(&g_windows, &window)) { 241 | app_windows_update_space(&g_windows, sid, false); 242 | struct app_window* window = app_windows_find_by_wid(&g_hidden_windows, 243 | wid ); 244 | if (window) app_window_clear(window); 245 | } 246 | } 247 | 248 | static void window_hide_handler(uint32_t event, uint32_t* window_id, size_t _, int cid) { 249 | uint32_t wid = *window_id; 250 | 251 | if (event == 816) { 252 | struct app_window* window = app_windows_find_by_wid(&g_windows, wid); 253 | if (window) { 254 | if (!app_windows_find(&g_hidden_windows, window)) { 255 | app_windows_add(&g_hidden_windows, window); 256 | } 257 | app_windows_update_space(&g_windows, window->sid, false); 258 | } 259 | } else if (event == 815) { 260 | struct app_window* window = app_windows_find_by_wid(&g_hidden_windows, wid); 261 | if (window) { 262 | app_windows_update_space(&g_windows, window->sid, false); 263 | app_window_clear(window); 264 | app_windows_register_notifications(); 265 | } 266 | } 267 | } 268 | 269 | static void update_all_spaces(struct app_windows* windows, bool silent) { 270 | uint32_t display_count = 0; 271 | uint32_t* displays = display_active_display_list(&display_count); 272 | for (int i = 0; i < display_count; i++) { 273 | int space_count = 0; 274 | uint64_t* spaces = display_space_list(displays[i], &space_count); 275 | for (int j = 0; j < space_count; j++) { 276 | app_windows_update_space(windows, spaces[j], silent); 277 | } 278 | if (spaces) free(spaces); 279 | } 280 | if (displays) free(displays); 281 | } 282 | 283 | static void space_handler(uint32_t event, void* data, size_t data_length, void* context) { 284 | update_all_spaces(&g_windows, event == 1401); 285 | } 286 | 287 | void forced_space_windows_event() { 288 | if (g_space_window_events) update_all_spaces(&g_windows, false); 289 | } 290 | 291 | void begin_receiving_space_window_events() { 292 | if (!g_space_window_events) { 293 | SLSRegisterNotifyProc(window_spawn_handler, 294 | 1325, 295 | (void*)(intptr_t)g_connection); 296 | 297 | SLSRegisterNotifyProc(window_spawn_handler, 298 | 1326, 299 | (void*)(intptr_t)g_connection); 300 | 301 | SLSRegisterNotifyProc(window_hide_handler, 302 | 815, 303 | (void*)(intptr_t)g_connection); 304 | 305 | SLSRegisterNotifyProc(window_hide_handler, 306 | 816, 307 | (void*)(intptr_t)g_connection); 308 | 309 | SLSRegisterNotifyProc((void*)space_handler, 1401, NULL); 310 | if (__builtin_available(macOS 13.0, *)) { 311 | SLSRegisterNotifyProc((void*)space_handler, 1327, NULL); 312 | SLSRegisterNotifyProc((void*)space_handler, 1328, NULL); 313 | } 314 | 315 | g_space_window_events = true; 316 | update_all_spaces(&g_windows, true); 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/app_windows.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "event.h" 3 | 4 | struct app_window { 5 | uint32_t wid; 6 | uint64_t sid; 7 | pid_t pid; 8 | }; 9 | 10 | struct app_windows { 11 | struct app_window* windows; 12 | uint32_t num_windows; 13 | }; 14 | 15 | void begin_receiving_space_window_events(); 16 | void forced_space_windows_event(); 17 | -------------------------------------------------------------------------------- /src/background.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "image.h" 3 | 4 | struct background { 5 | bool enabled; 6 | float clip; 7 | bool overrides_height; 8 | 9 | int padding_left; 10 | int padding_right; 11 | int y_offset; 12 | uint32_t border_width; 13 | uint32_t corner_radius; 14 | 15 | CGRect bounds; 16 | struct image image; 17 | struct shadow shadow; 18 | struct color color; 19 | struct color border_color; 20 | 21 | struct background** clips; 22 | uint32_t num_clips; 23 | }; 24 | 25 | struct bar; 26 | 27 | void background_init(struct background* background); 28 | void background_calculate_bounds(struct background* background, uint32_t x, uint32_t y, uint32_t width, uint32_t height); 29 | 30 | bool background_set_enabled(struct background* background, bool enabled); 31 | bool background_set_color(struct background* background, uint32_t color); 32 | bool background_set_height(struct background* background, uint32_t height); 33 | bool background_set_padding_left(struct background* background, uint32_t pad); 34 | bool background_set_padding_right(struct background* background, uint32_t pad); 35 | 36 | void background_draw(struct background* background, CGContextRef context); 37 | 38 | struct background* background_get_clip(struct background* background, uint32_t adid); 39 | void background_clip_bar(struct background* background, int offset, struct bar* bar); 40 | bool background_clip_needs_update(struct background* background, struct bar* bar); 41 | bool background_clips_bar(struct background* background); 42 | 43 | void background_clear_pointers(struct background* background); 44 | void background_destroy(struct background* background); 45 | 46 | void background_serialize(struct background* background, char* indent, FILE* rsp, bool detailed); 47 | bool background_parse_sub_domain(struct background* background, FILE* rsp, struct token property, char* message); 48 | -------------------------------------------------------------------------------- /src/bar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bar_item.h" 3 | #include "misc/helpers.h" 4 | #include "window.h" 5 | 6 | struct bar { 7 | bool shown; 8 | bool hidden; 9 | bool mouse_over; 10 | 11 | uint32_t sid; 12 | uint32_t dsid; 13 | uint32_t did; 14 | uint32_t adid; 15 | 16 | struct window window; 17 | }; 18 | 19 | struct bar *bar_create(uint32_t did); 20 | void bar_close_window(struct bar* bar); 21 | void bar_destroy(struct bar* bar); 22 | void bar_set_hidden(struct bar* bar, bool hidden); 23 | void bar_calculate_bounds(struct bar* bar); 24 | void bar_resize(struct bar* bar); 25 | void bar_draw(struct bar* bar, bool forced, bool threaded); 26 | void bar_order_item_windows(struct bar* bar); 27 | 28 | bool bar_draws_item(struct bar* bar, struct bar_item* bar_item); 29 | 30 | void bar_change_space(struct bar* bar, uint64_t dsid); 31 | 32 | void context_set_font_smoothing(CGContextRef context, bool smoothing); 33 | void join_render_threads(); 34 | -------------------------------------------------------------------------------- /src/bar_item.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "alias.h" 4 | #include "custom_events.h" 5 | #include "graph.h" 6 | #include "group.h" 7 | #include "misc/env_vars.h" 8 | #include "misc/helpers.h" 9 | #include "popup.h" 10 | #include "text.h" 11 | #include "slider.h" 12 | 13 | #define BAR_ITEM 'i' 14 | #define BAR_COMPONENT_GRAPH 'g' 15 | #define BAR_COMPONENT_SPACE 's' 16 | #define BAR_COMPONENT_ALIAS 'a' 17 | #define BAR_COMPONENT_GROUP 'b' 18 | #define BAR_COMPONENT_SLIDER 't' 19 | #define BAR_PLUGIN 'p' 20 | 21 | struct bar_item { 22 | char type; 23 | char* name; 24 | 25 | // Update Modifiers 26 | uint32_t counter; 27 | bool needs_update; 28 | bool updates; 29 | bool updates_only_when_shown; 30 | bool lazy; 31 | bool selected; 32 | bool mouse_over; 33 | bool ignore_association; 34 | bool overrides_association; 35 | 36 | // Drawing Modifiers 37 | bool drawing; 38 | bool shadow; 39 | bool has_const_width; 40 | bool scroll_texts; 41 | char align; 42 | uint32_t custom_width; 43 | uint32_t blur_radius; 44 | 45 | // These are 32bit masks where the ith bit represents the ith screen/display/bar association 46 | bool associated_to_active_display; 47 | uint32_t associated_bar; 48 | uint32_t associated_display; 49 | uint32_t associated_space; 50 | uint32_t update_frequency; 51 | 52 | char* script; 53 | char* click_script; 54 | struct signal_args signal_args; 55 | 56 | // The position in the bar: l,r,c 57 | char position; 58 | int y_offset; 59 | 60 | // Background 61 | struct background background; 62 | 63 | // Icon properties 64 | struct text icon; 65 | 66 | // Label properties 67 | struct text label; 68 | 69 | // Graph Data 70 | bool has_graph; 71 | struct graph graph; 72 | 73 | // Alias Data 74 | bool has_alias; 75 | struct alias alias; 76 | 77 | // Slider Properties 78 | bool has_slider; 79 | struct slider slider; 80 | 81 | // Group Properties 82 | struct group* group; 83 | 84 | // Update Events 85 | uint64_t update_mask; 86 | 87 | // Windows 88 | uint32_t num_windows; 89 | struct window** windows; 90 | 91 | // Popup 92 | struct popup popup; 93 | struct bar_item* parent; 94 | 95 | // Mach 96 | mach_port_t event_port; 97 | }; 98 | 99 | struct bar_item* bar_item_create(); 100 | void bar_item_inherit_from_item(struct bar_item* bar_item, struct bar_item* ancestor); 101 | void bar_item_init(struct bar_item* bar_item, struct bar_item* default_item); 102 | void bar_item_serialize(struct bar_item* bar_item, FILE* rsp); 103 | void bar_item_destroy(struct bar_item* bar_item, bool free_memory); 104 | 105 | bool bar_item_is_shown(struct bar_item* bar_item); 106 | void bar_item_needs_update(struct bar_item* bar_item); 107 | bool bar_item_update(struct bar_item* bar_item, char* sender, bool forced, struct env_vars* env_vars); 108 | 109 | void bar_item_on_click(struct bar_item* bar_item, uint32_t type, uint32_t mouse_button_code, uint32_t modifier, CGPoint point); 110 | void bar_item_on_scroll(struct bar_item* bar_item, int scroll_delta, uint32_t modifier); 111 | void bar_item_on_drag(struct bar_item* bar_item, CGPoint point); 112 | void bar_item_mouse_entered(struct bar_item* bar_item); 113 | void bar_item_mouse_exited(struct bar_item* bar_item); 114 | void bar_item_cancel_drag(struct bar_item* bar_item); 115 | 116 | void bar_item_append_associated_space(struct bar_item* bar_item, uint32_t bit); 117 | void bar_item_append_associated_display(struct bar_item* bar_item, uint32_t bit); 118 | void bar_item_append_associated_bar(struct bar_item* bar_item, uint32_t adid); 119 | void bar_item_remove_associated_bar(struct bar_item* bar_item, uint32_t adid); 120 | void bar_item_reset_associated_bar(struct bar_item* bar_item); 121 | 122 | bool bar_item_set_name(struct bar_item* bar_item, char* name); 123 | bool bar_item_set_type(struct bar_item* bar_item, char* type); 124 | bool bar_item_set_position(struct bar_item* bar_item, char* position); 125 | bool bar_item_set_media_cover(struct bar_item* bar_item, struct image* image); 126 | 127 | uint32_t bar_item_get_length(struct bar_item* bar_item, bool ignore_override); 128 | uint32_t bar_item_get_height(struct bar_item* bar_item); 129 | 130 | struct window* bar_item_get_window(struct bar_item* bar_item, uint32_t adid); 131 | void bar_item_remove_window(struct bar_item* bar_item, uint32_t adid); 132 | 133 | CGPoint bar_item_calculate_shadow_offsets(struct bar_item* bar_item); 134 | uint32_t bar_item_calculate_bounds(struct bar_item* bar_item, uint32_t bar_height, uint32_t x, uint32_t y); 135 | void* draw_item_proc(void* context); 136 | void bar_item_draw(struct bar_item* bar_item, CGContextRef context); 137 | bool bar_item_clip_needs_update_for_bar(struct bar_item* bar_item, struct bar* bar); 138 | void bar_item_clip_bar(struct bar_item* bar_item, int offset, struct bar* bar); 139 | bool bar_item_clips_bar(struct bar_item* bar_item); 140 | 141 | void bar_item_change_space(struct bar_item* bar_item, uint64_t dsid, uint32_t adid); 142 | 143 | void bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE* rsp); 144 | void bar_item_parse_subscribe_message(struct bar_item* bar_item, char* message, FILE* rsp); 145 | -------------------------------------------------------------------------------- /src/bar_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bar.h" 3 | #include "bar_item.h" 4 | #include "animation.h" 5 | 6 | #define CLOCK_CALLBACK(name) void name(CFRunLoopTimerRef timer, void *context) 7 | typedef CLOCK_CALLBACK(clock_callback); 8 | 9 | #define DISPLAY_MAIN_PATTERN 0 10 | #define DISPLAY_ALL_PATTERN UINT32_MAX 11 | 12 | #define TOPMOST_LEVEL_WINDOW 'w' 13 | #define TOPMOST_LEVEL_ALL 'a' 14 | 15 | struct bar_manager { 16 | CFRunLoopTimerRef clock; 17 | 18 | bool frozen; 19 | bool sleeps; 20 | bool shadow; 21 | bool topmost; 22 | bool sticky; 23 | bool font_smoothing; 24 | bool any_bar_hidden; 25 | bool needs_ordering; 26 | bool might_need_clipping; 27 | bool bar_needs_update; 28 | bool bar_needs_resize; 29 | bool show_in_fullscreen; 30 | 31 | uint32_t displays; 32 | char position; 33 | 34 | int margin; 35 | uint32_t blur_radius; 36 | uint32_t notch_width; 37 | uint32_t notch_offset; 38 | uint32_t notch_display_height; 39 | uint32_t active_adid; 40 | uint32_t window_level; 41 | 42 | struct bar** bars; 43 | uint32_t bar_count; 44 | uint32_t active_displays; 45 | 46 | struct bar_item** bar_items; 47 | struct bar_item default_item; 48 | uint32_t bar_item_count; 49 | 50 | struct background background; 51 | struct custom_events custom_events; 52 | 53 | struct animator animator; 54 | struct image current_artwork; 55 | }; 56 | 57 | void bar_manager_init(struct bar_manager* bar_manager); 58 | void bar_manager_begin(struct bar_manager* bar_manager); 59 | void bar_manager_reset(struct bar_manager* bar_manager); 60 | 61 | struct bar_item* bar_manager_create_item(struct bar_manager* bar_manager); 62 | void bar_manager_remove_item(struct bar_manager* bar_manager, struct bar_item* bar_item); 63 | void bar_manager_move_item(struct bar_manager* bar_manager, struct bar_item* item, struct bar_item* reference, bool before); 64 | void bar_manager_handle_notification(struct bar_manager* bar_manager, struct notification* notification); 65 | 66 | void bar_manager_animator_refresh(struct bar_manager* bar_manager, uint64_t time); 67 | void bar_manager_update(struct bar_manager* bar_manager, bool forced); 68 | void bar_manager_update_space_components(struct bar_manager* bar_manager, bool forced); 69 | bool bar_manager_set_margin(struct bar_manager* bar_manager, int margin); 70 | bool bar_manager_set_y_offset(struct bar_manager* bar_manager, int y_offset); 71 | bool bar_manager_set_bar_height(struct bar_manager* bar_manager, int height); 72 | bool bar_manager_set_background_blur(struct bar_manager* bar_manager, uint32_t radius); 73 | bool bar_manager_set_position(struct bar_manager* bar_manager, char pos); 74 | bool bar_manager_set_spaces(struct bar_manager* bar_manager, bool value); 75 | bool bar_manager_set_spaces_for_all_displays(struct bar_manager* bar_manager, bool value); 76 | bool bar_manager_set_displays(struct bar_manager* bar_manager, uint32_t displays); 77 | bool bar_manager_set_hidden(struct bar_manager* bar_manager, uint32_t sid, bool hidden); 78 | bool bar_manager_set_topmost(struct bar_manager* bar_manager, char level, bool topmost); 79 | bool bar_manager_set_sticky(struct bar_manager *bar_manager, bool sticky); 80 | bool bar_manager_set_shadow(struct bar_manager* bar_manager, bool shadow); 81 | bool bar_manager_set_font_smoothing(struct bar_manager* bar_manager, bool smoothing); 82 | bool bar_manager_set_show_in_fullscreen(struct bar_manager* bar_manager, bool show_in_fullscreen); 83 | bool bar_manager_set_notch_width(struct bar_manager* bar_manager, uint32_t width); 84 | bool bar_manager_set_notch_offset(struct bar_manager* bar_manager, uint32_t offset); 85 | bool bar_manager_set_notch_display_height(struct bar_manager* bar_manager, uint32_t offset); 86 | void bar_manager_sort(struct bar_manager* bar_manager, struct bar_item** ordering, uint32_t count); 87 | 88 | struct bar_item* bar_manager_get_item_by_point(struct bar_manager* bar_manager, CGPoint point, struct window** window_out); 89 | struct bar* bar_manager_get_bar_by_point(struct bar_manager* bar_manager, CGPoint point); 90 | struct popup* bar_manager_get_popup_by_point(struct bar_manager* bar_manager, CGPoint point); 91 | struct bar_item* bar_manager_get_item_by_wid(struct bar_manager* bar_manager, uint32_t wid, struct window** window_out); 92 | struct popup* bar_manager_get_popup_by_wid(struct bar_manager* bar_manager, uint32_t wid); 93 | struct bar* bar_manager_get_bar_by_wid(struct bar_manager* bar_manager, uint32_t wid); 94 | int bar_manager_get_item_index_for_name(struct bar_manager* bar_manager, char* name); 95 | uint32_t bar_manager_length_for_bar_side(struct bar_manager* bar_manager, struct bar* bar, char side); 96 | bool bar_manager_mouse_over_any_popup(struct bar_manager* bar_manager); 97 | bool bar_manager_mouse_over_any_bar(struct bar_manager* bar_manager); 98 | 99 | void bar_manager_freeze(struct bar_manager* bar_manager); 100 | void bar_manager_unfreeze(struct bar_manager* bar_manager); 101 | 102 | void bar_manager_display_changed(struct bar_manager* bar_manager); 103 | void bar_manager_display_resized(struct bar_manager* bar_manager, uint32_t did); 104 | void bar_manager_display_moved(struct bar_manager* bar_manager, uint32_t did); 105 | void bar_manager_display_removed(struct bar_manager* bar_manager, uint32_t did); 106 | void bar_manager_display_added(struct bar_manager* bar_manager, uint32_t did); 107 | void bar_manager_refresh(struct bar_manager* bar_manager, bool forced, bool threaded); 108 | void bar_manager_resize(struct bar_manager* bar_manager); 109 | 110 | void bar_manager_poll_active_display(struct bar_manager* bar_manager); 111 | void bar_manager_handle_mouse_entered_global(struct bar_manager* bar_manager); 112 | void bar_manager_handle_mouse_exited_global(struct bar_manager* bar_manager); 113 | void bar_manager_handle_mouse_scrolled_global(struct bar_manager* bar_manager, int scroll_delta, uint32_t did, uint32_t modifier); 114 | void bar_manager_handle_mouse_entered(struct bar_manager* bar_manager, struct bar_item* bar_item); 115 | void bar_manager_handle_mouse_exited(struct bar_manager* bar_manager, struct bar_item* bar_item); 116 | void bar_manager_handle_front_app_switch(struct bar_manager* bar_manager, char* info); 117 | void bar_manager_handle_space_change(struct bar_manager* bar_manager, bool forced); 118 | void bar_manager_handle_display_change(struct bar_manager* bar_manager); 119 | void bar_manager_handle_system_woke(struct bar_manager* bar_manager); 120 | void bar_manager_handle_system_will_sleep(struct bar_manager* bar_manager); 121 | void bar_manager_handle_volume_change(struct bar_manager* bar_manager, float volume); 122 | void bar_manager_handle_wifi_change(struct bar_manager* bar_manager, char* ssid); 123 | void bar_manager_handle_brightness_change(struct bar_manager* bar_manager, float brightness); 124 | void bar_manager_handle_power_source_change(struct bar_manager* bar_manager, char* state); 125 | void bar_manager_handle_media_change(struct bar_manager* bar_manager, char* info); 126 | void bar_manager_handle_media_cover_change(struct bar_manager* bar_manager, CGImageRef image); 127 | void bar_manager_handle_space_windows_change(struct bar_manager* bar_manager, char* info); 128 | void bar_manager_custom_events_trigger(struct bar_manager* bar_manager, char* name, struct env_vars* env_vars); 129 | 130 | void bar_manager_destroy(struct bar_manager* bar_manager); 131 | 132 | void bar_manager_serialize(struct bar_manager* bar_manager, FILE* rsp); 133 | -------------------------------------------------------------------------------- /src/color.c: -------------------------------------------------------------------------------- 1 | #include "color.h" 2 | #include "bar_manager.h" 3 | #include "animation.h" 4 | 5 | static bool color_update_hex(struct color* color) { 6 | uint32_t prev = color->hex; 7 | color->hex = (((uint32_t)(color->a * 255.f)) << 24) 8 | + (((uint32_t)(color->r * 255.f)) << 16) 9 | + (((uint32_t)(color->g * 255.f)) << 8) 10 | + (((uint32_t)(color->b * 255.f)) << 0); 11 | 12 | return prev != color->hex; 13 | } 14 | 15 | void color_init(struct color* color, uint32_t hex) { 16 | color_set_hex(color, hex); 17 | } 18 | 19 | bool color_set_hex(struct color* color, uint32_t hex) { 20 | color->a = clamp(((hex >> 24) & 0xff) / 255.f, 0.f, 1.f); 21 | color->r = clamp(((hex >> 16) & 0xff) / 255.f, 0.f, 1.f); 22 | color->g = clamp(((hex >> 8) & 0xff) / 255.f, 0.f, 1.f); 23 | color->b = clamp(((hex >> 0) & 0xff) / 255.f, 0.f, 1.f); 24 | return color_update_hex(color); 25 | } 26 | 27 | bool color_set_alpha(struct color* color, float alpha) { 28 | color->a = clamp(alpha, 0.f, 1.f); 29 | return color_update_hex(color); 30 | } 31 | 32 | bool color_set_r(struct color* color, float red) { 33 | color->r = clamp(red, 0.f, 1.f); 34 | return color_update_hex(color); 35 | } 36 | 37 | bool color_set_g(struct color* color, float green) { 38 | color->g = clamp(green, 0.f, 1.f); 39 | return color_update_hex(color); 40 | } 41 | 42 | bool color_set_b(struct color* color, float blue) { 43 | color->b = clamp(blue, 0.f, 1.f); 44 | return color_update_hex(color); 45 | } 46 | 47 | bool color_parse_sub_domain(struct color* color, FILE* rsp, struct token property, char* message) { 48 | bool needs_refresh = false; 49 | 50 | if (token_equals(property, PROPERTY_COLOR_HEX)) { 51 | ANIMATE_BYTES(color_set_hex, 52 | color, 53 | color->hex, 54 | token_to_int(get_token(&message))); 55 | } 56 | else if (token_equals(property, PROPERTY_COLOR_ALPHA)) { 57 | ANIMATE_FLOAT(color_set_alpha, 58 | color, 59 | color->a, 60 | token_to_float(get_token(&message))); 61 | } 62 | else if (token_equals(property, PROPERTY_COLOR_RED)) { 63 | ANIMATE_FLOAT(color_set_r, 64 | color, 65 | color->r, 66 | token_to_float(get_token(&message))); 67 | } 68 | else if (token_equals(property, PROPERTY_COLOR_GREEN)) { 69 | ANIMATE_FLOAT(color_set_g, 70 | color, 71 | color->g, 72 | token_to_float(get_token(&message))); 73 | } 74 | else if (token_equals(property, PROPERTY_COLOR_BLUE)) { 75 | ANIMATE_FLOAT(color_set_b, 76 | color, 77 | color->b, 78 | token_to_float(get_token(&message))); 79 | } else { 80 | respond(rsp, "[?] Color: Invalid property '%s'\n", property); 81 | } 82 | 83 | return needs_refresh; 84 | } 85 | -------------------------------------------------------------------------------- /src/color.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "misc/helpers.h" 5 | 6 | struct color { 7 | float r; 8 | float g; 9 | float b; 10 | float a; 11 | uint32_t hex; 12 | }; 13 | 14 | static struct color g_transparent = { 0 }; 15 | 16 | void color_init(struct color* color, uint32_t hex); 17 | bool color_set_hex(struct color* color, uint32_t hex); 18 | bool color_set_alpha(struct color* color, float alpha); 19 | bool color_set_r(struct color* color, float red); 20 | bool color_set_g(struct color* color, float green); 21 | bool color_set_b(struct color* color, float blue); 22 | 23 | bool color_parse_sub_domain(struct color* color, FILE* rsp, struct token property, char* message); 24 | -------------------------------------------------------------------------------- /src/custom_events.c: -------------------------------------------------------------------------------- 1 | #include "custom_events.h" 2 | 3 | static struct custom_event* custom_event_create(void) { 4 | return malloc(sizeof(struct custom_event)); 5 | } 6 | 7 | void custom_event_init(struct custom_event* custom_event, char* name, char* notification) { 8 | custom_event->name = name; 9 | custom_event->notification = notification; 10 | } 11 | 12 | void custom_event_destroy(struct custom_event* custom_event) { 13 | if (custom_event->name) free(custom_event->name); 14 | if (custom_event->notification) free(custom_event->notification); 15 | free(custom_event); 16 | } 17 | 18 | void custom_events_init(struct custom_events* custom_events) { 19 | custom_events->count = 0; 20 | custom_events->events = NULL; 21 | 22 | // System Events 23 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_FRONT_APP_SWITCHED), NULL); 24 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SPACE_CHANGE), NULL); 25 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_DISPLAY_CHANGE), NULL); 26 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SYSTEM_WOKE), NULL); 27 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_ENTERED), NULL); 28 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_EXITED), NULL); 29 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_CLICKED), NULL); 30 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_SCROLLED), NULL); 31 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SYSTEM_WILL_SLEEP), NULL); 32 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_ENTERED_GLOBAL), NULL); 33 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_EXITED_GLOBAL), NULL); 34 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_SCROLLED_GLOBAL), NULL); 35 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_VOLUME_CHANGE), NULL); 36 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_BRIGHTNESS_CHANGE), NULL); 37 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_POWER_SOURCE_CHANGE), NULL); 38 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_WIFI_CHANGE), NULL); 39 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MEDIA_CHANGE), NULL); 40 | custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE), NULL); 41 | } 42 | 43 | void custom_events_append(struct custom_events* custom_events, char* name, char* notification) { 44 | if (custom_events_get_flag_for_name(custom_events, name) > 0) { 45 | if (name) free(name); 46 | if (notification) free(notification); 47 | return; 48 | } 49 | custom_events->count++; 50 | custom_events->events = (struct custom_event**) realloc( 51 | custom_events->events, 52 | sizeof(struct custom_event*) * custom_events->count); 53 | 54 | custom_events->events[custom_events->count - 1] = custom_event_create(); 55 | custom_events->events[custom_events->count - 1]->name = name; 56 | custom_events->events[custom_events->count - 1]->notification = notification; 57 | if (notification) 58 | workspace_create_custom_observer(&g_workspace_context, notification); 59 | } 60 | 61 | uint64_t custom_events_get_flag_for_name(struct custom_events* custom_events, char* name) { 62 | for (int i = 0; i < custom_events->count; i++) { 63 | if (strcmp(name, custom_events->events[i]->name) == 0) { 64 | return 1ULL << i; 65 | } 66 | } 67 | return 0; 68 | } 69 | 70 | char* custom_events_get_name_for_notification(struct custom_events* custom_events, char* notification) { 71 | for (int i = 0; i < custom_events->count; i++) { 72 | if (!custom_events->events[i]->notification) continue; 73 | if (strcmp(notification, custom_events->events[i]->notification) == 0) { 74 | return custom_events->events[i]->name; 75 | } 76 | } 77 | return NULL; 78 | } 79 | 80 | void custom_events_destroy(struct custom_events* custom_events) { 81 | for (int i = 0; i < custom_events->count; i++) { 82 | custom_event_destroy(custom_events->events[i]); 83 | } 84 | free(custom_events->events); 85 | } 86 | 87 | void custom_events_serialize(struct custom_events* custom_events, FILE* rsp) { 88 | fprintf(rsp, "{\n"); 89 | for (int i = 0; i < custom_events->count; i++) { 90 | fprintf(rsp, "\t\"%s\": {\n" 91 | "\t\t\"bit\": %llu,\n" 92 | "\t\t\"notification\": \"%s\"\n", 93 | custom_events->events[i]->name, 94 | 1ULL << i, 95 | custom_events->events[i]->notification); 96 | if (i < custom_events->count - 1) fprintf(rsp, "\t},\n"); 97 | } 98 | fprintf(rsp, "\t}\n}\n"); 99 | } 100 | -------------------------------------------------------------------------------- /src/custom_events.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "misc/helpers.h" 3 | 4 | #define UPDATE_FRONT_APP_SWITCHED 1ULL 5 | #define UPDATE_SPACE_CHANGE (1ULL << 1) 6 | #define UPDATE_DISPLAY_CHANGE (1ULL << 2) 7 | #define UPDATE_SYSTEM_WOKE (1ULL << 3) 8 | #define UPDATE_MOUSE_ENTERED (1ULL << 4) 9 | #define UPDATE_MOUSE_EXITED (1ULL << 5) 10 | #define UPDATE_MOUSE_CLICKED (1ULL << 6) 11 | #define UPDATE_MOUSE_SCROLLED (1ULL << 7) 12 | #define UPDATE_SYSTEM_WILL_SLEEP (1ULL << 8) 13 | #define UPDATE_ENTERED_GLOBAL (1ULL << 9) 14 | #define UPDATE_EXITED_GLOBAL (1ULL << 10) 15 | #define UPDATE_SCROLLED_GLOBAL (1ULL << 11) 16 | #define UPDATE_VOLUME_CHANGE (1ULL << 12) 17 | #define UPDATE_BRIGHTNESS_CHANGE (1ULL << 13) 18 | #define UPDATE_POWER_SOURCE_CHANGE (1ULL << 14) 19 | #define UPDATE_WIFI_CHANGE (1ULL << 15) 20 | #define UPDATE_MEDIA_CHANGE (1ULL << 16) 21 | #define UPDATE_SPACE_WINDOWS_CHANGE (1ULL << 17) 22 | 23 | extern void* g_workspace_context; 24 | extern void workspace_create_custom_observer(void** context, char* name); 25 | 26 | struct custom_event { 27 | char* name; 28 | char* notification; 29 | }; 30 | 31 | void custom_event_init(struct custom_event* custom_event, char* name, char* notification); 32 | 33 | struct custom_events { 34 | uint32_t count; 35 | struct custom_event** events; 36 | }; 37 | 38 | void custom_events_init(struct custom_events* custom_events); 39 | void custom_events_append(struct custom_events* custom_events, char* name, char* notification); 40 | uint64_t custom_events_get_flag_for_name(struct custom_events* custom_events, char* name); 41 | char* custom_events_get_name_for_notification(struct custom_events* custom_events, char* notification); 42 | void custom_events_destroy(struct custom_events* custom_events); 43 | 44 | void custom_events_serialize(struct custom_events* custom_events, FILE* rsp); 45 | -------------------------------------------------------------------------------- /src/display.c: -------------------------------------------------------------------------------- 1 | #include "display.h" 2 | #include "misc/helpers.h" 3 | 4 | extern int workspace_display_notch_height(uint32_t did); 5 | extern int g_connection; 6 | extern int g_space_management_mode; 7 | extern bool g_brightness_events; 8 | 9 | 10 | float g_last_brightness = -1.f; 11 | static void brightness_handler(void* notification_center, uint32_t did, void* name, const void* sender, CFDictionaryRef info) { 12 | float b = 0; 13 | float* brightness = &b; 14 | DisplayServicesGetBrightness(did, brightness); 15 | if (g_last_brightness < *brightness - 1e-2 16 | || g_last_brightness > *brightness + 1e-2) { 17 | g_last_brightness = *brightness; 18 | struct event event = { (void*) brightness, BRIGHTNESS_CHANGED }; 19 | event_post(&event); 20 | } 21 | } 22 | 23 | static DISPLAY_EVENT_HANDLER(display_handler) { 24 | if (flags & kCGDisplayAddFlag) { 25 | struct event event = { (void *)(intptr_t) did, DISPLAY_ADDED }; 26 | event_post(&event); 27 | 28 | if (g_brightness_events && DisplayServicesCanChangeBrightness(did)) 29 | DisplayServicesRegisterForBrightnessChangeNotifications(did, did, (void*)brightness_handler); 30 | } else if (flags & kCGDisplayRemoveFlag) { 31 | struct event event = { (void *)(intptr_t) did, DISPLAY_REMOVED }; 32 | event_post(&event); 33 | 34 | if (g_brightness_events && DisplayServicesCanChangeBrightness(did)) 35 | DisplayServicesUnregisterForBrightnessChangeNotifications(did, did); 36 | } else if (flags & kCGDisplayMovedFlag) { 37 | struct event event = { (void *)(intptr_t) did, DISPLAY_MOVED }; 38 | event_post(&event); 39 | } else if (flags & kCGDisplayDesktopShapeChangedFlag) { 40 | struct event event = { (void *)(intptr_t) did, DISPLAY_RESIZED }; 41 | event_post(&event); 42 | } 43 | } 44 | 45 | CFStringRef display_uuid(uint32_t did) { 46 | CFUUIDRef uuid_ref = CGDisplayCreateUUIDFromDisplayID(did); 47 | if (!uuid_ref) return NULL; 48 | 49 | CFStringRef uuid_str = CFUUIDCreateString(NULL, uuid_ref); 50 | CFRelease(uuid_ref); 51 | 52 | return uuid_str; 53 | } 54 | 55 | CGRect display_bounds(uint32_t did) { 56 | return CGDisplayBounds(did); 57 | } 58 | 59 | uint64_t display_space_id(uint32_t did) { 60 | CFStringRef uuid = display_uuid(did); 61 | if (!uuid) return 0; 62 | 63 | uint64_t sid = SLSManagedDisplayGetCurrentSpace(g_connection, uuid); 64 | CFRelease(uuid); 65 | return sid; 66 | } 67 | 68 | uint64_t *display_space_list(uint32_t did, int *count) { 69 | CFStringRef uuid = display_uuid(did); 70 | if (!uuid) return NULL; 71 | 72 | CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection); 73 | if (!display_spaces_ref) return NULL; 74 | 75 | uint64_t *space_list = NULL; 76 | int display_spaces_count = CFArrayGetCount(display_spaces_ref); 77 | 78 | for (int i = 0; i < display_spaces_count; ++i) { 79 | CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i); 80 | CFStringRef identifier = CFDictionaryGetValue(display_ref, 81 | CFSTR("Display Identifier")); 82 | 83 | if (!CFEqual(uuid, identifier)) continue; 84 | 85 | CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR("Spaces")); 86 | int spaces_count = CFArrayGetCount(spaces_ref); 87 | 88 | space_list = malloc(sizeof(uint64_t) * spaces_count); 89 | *count = spaces_count; 90 | 91 | for (int j = 0; j < spaces_count; ++j) { 92 | CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j); 93 | CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR("id64")); 94 | CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &space_list[j]); 95 | } 96 | } 97 | CFRelease(display_spaces_ref); 98 | CFRelease(uuid); 99 | 100 | return space_list; 101 | } 102 | 103 | int display_arrangement(uint32_t did) { 104 | if (display_active_display_count() == 1) { 105 | uint32_t result = 0; 106 | uint32_t count = 0; 107 | CGGetActiveDisplayList(1, &result, &count); 108 | if (did == result && count == 1) return 1; 109 | else return 0; 110 | } 111 | 112 | CFStringRef uuid = display_uuid(did); 113 | if (!uuid) return 0; 114 | 115 | CFArrayRef displays = SLSCopyManagedDisplays(g_connection); 116 | if (!displays) { 117 | CFRelease(uuid); 118 | return 0; 119 | } 120 | 121 | int result = 0; 122 | int displays_count = CFArrayGetCount(displays); 123 | 124 | for (int i = 0; i < displays_count; ++i) { 125 | if (CFEqual(CFArrayGetValueAtIndex(displays, i), uuid)) { 126 | result = i + 1; 127 | break; 128 | } 129 | } 130 | CFRelease(displays); 131 | CFRelease(uuid); 132 | return result; 133 | } 134 | 135 | uint32_t display_main_display_id(void) { 136 | return CGMainDisplayID(); 137 | } 138 | 139 | static CFStringRef display_active_display_uuid(void) { 140 | if (g_space_management_mode != 1) { 141 | CGEventRef event = CGEventCreate(NULL); 142 | if (!event) return NULL; 143 | CGPoint cursor = CGEventGetLocation(event); 144 | uint32_t count = 0; 145 | uint32_t* dids = display_active_display_list(&count); 146 | uint32_t mouse_did = 0; 147 | for (uint32_t i = 0; i < count; i++) { 148 | if (CGRectContainsPoint(CGDisplayBounds(dids[i]), cursor)) { 149 | mouse_did = dids[i]; 150 | break; 151 | } 152 | } 153 | if (dids) free(dids); 154 | CFRelease(event); 155 | 156 | return display_uuid(mouse_did); 157 | } else return SLSCopyActiveMenuBarDisplayIdentifier(g_connection); 158 | } 159 | 160 | uint32_t display_active_display_id(void) { 161 | if (display_active_display_count() == 1) { 162 | uint32_t did = 0; 163 | uint32_t count = 0; 164 | CGGetActiveDisplayList(1, &did, &count); 165 | if (count == 1) return did; 166 | else { 167 | printf("ERROR (id): No active display detected!\n"); 168 | return 0; 169 | } 170 | } 171 | 172 | CFStringRef uuid = display_active_display_uuid(); 173 | if (!uuid) return 0; 174 | CFUUIDRef uuid_ref = CFUUIDCreateFromString(NULL, uuid); 175 | uint32_t result = CGDisplayGetDisplayIDFromUUID(uuid_ref); 176 | CFRelease(uuid_ref); 177 | CFRelease(uuid); 178 | return result; 179 | } 180 | 181 | CFStringRef display_arrangement_display_uuid(int arrangement) { 182 | CFStringRef result = NULL; 183 | CFArrayRef displays = SLSCopyManagedDisplays(g_connection); 184 | 185 | int displays_count = CFArrayGetCount(displays); 186 | for (int i = 0; i < displays_count; ++i) { 187 | if ((i+1) != arrangement) continue; 188 | result = CFRetain(CFArrayGetValueAtIndex(displays, i)); 189 | break; 190 | } 191 | 192 | CFRelease(displays); 193 | return result; 194 | } 195 | 196 | uint32_t display_arrangement_display_id(int arrangement) { 197 | uint32_t result = 0; 198 | CFArrayRef displays = SLSCopyManagedDisplays(g_connection); 199 | 200 | int displays_count = CFArrayGetCount(displays); 201 | for (int i = 0; i < displays_count; ++i) { 202 | if ((i+1) != arrangement) continue; 203 | CFUUIDRef uuid_ref = CFUUIDCreateFromString(NULL, 204 | CFArrayGetValueAtIndex(displays, i)); 205 | result = CGDisplayGetDisplayIDFromUUID(uuid_ref); 206 | CFRelease(uuid_ref); 207 | break; 208 | } 209 | 210 | CFRelease(displays); 211 | return result; 212 | } 213 | 214 | uint32_t display_active_display_adid(void) { 215 | if (display_active_display_count() == 1) return 1; 216 | 217 | CFStringRef uuid = display_active_display_uuid(); 218 | if (!uuid) return 0; 219 | CFArrayRef displays = SLSCopyManagedDisplays(g_connection); 220 | if (!displays) { 221 | CFRelease(uuid); 222 | return 0; 223 | } 224 | 225 | int result = 0; 226 | int displays_count = CFArrayGetCount(displays); 227 | 228 | for (int i = 0; i < displays_count; ++i) { 229 | if (CFEqual(CFArrayGetValueAtIndex(displays, i), uuid)) { 230 | result = i + 1; 231 | break; 232 | } 233 | } 234 | CFRelease(displays); 235 | CFRelease(uuid); 236 | return result; 237 | } 238 | 239 | bool display_menu_bar_visible(void) { 240 | int status = 0; 241 | SLSGetMenuBarAutohideEnabled(g_connection, &status); 242 | return !status; 243 | } 244 | 245 | CGRect display_menu_bar_rect(uint32_t did) { 246 | CGRect bounds = {}; 247 | 248 | #ifdef __x86_64__ 249 | SLSGetRevealedMenuBarBounds(&bounds, g_connection, display_space_id(did)); 250 | #elif __arm64__ 251 | int notch_height = workspace_display_notch_height(did); 252 | if (notch_height) { 253 | bounds.size.height = notch_height + 6; 254 | } else { 255 | bounds.size.height = 24; 256 | } 257 | 258 | bounds.size.width = CGDisplayPixelsWide(did); 259 | #endif 260 | 261 | return bounds; 262 | } 263 | 264 | uint32_t display_active_display_count(void) { 265 | uint32_t count; 266 | CGGetActiveDisplayList(0, NULL, &count); 267 | return count; 268 | } 269 | 270 | uint32_t *display_active_display_list(uint32_t *count) { 271 | int display_count = display_active_display_count(); 272 | uint32_t *result = malloc(sizeof(uint32_t) * display_count); 273 | CGGetActiveDisplayList(display_count, result, count); 274 | return result; 275 | } 276 | 277 | bool display_begin() { 278 | return CGDisplayRegisterReconfigurationCallback(display_handler, NULL) 279 | == kCGErrorSuccess; 280 | } 281 | 282 | bool display_end() { 283 | return CGDisplayRemoveReconfigurationCallback(display_handler, NULL) 284 | == kCGErrorSuccess; 285 | } 286 | 287 | void forced_brightness_event() { 288 | g_last_brightness = -1.f; 289 | brightness_handler(NULL, display_active_display_id(), NULL, NULL, NULL); 290 | } 291 | 292 | void begin_receiving_brightness_events() { 293 | if (g_brightness_events) return; 294 | g_brightness_events = true; 295 | uint32_t count; 296 | uint32_t* result = display_active_display_list(&count); 297 | for (int i = 0; i < count; i++) { 298 | uint32_t did = *(result + i); 299 | if (DisplayServicesCanChangeBrightness(did)) { 300 | DisplayServicesRegisterForBrightnessChangeNotifications(did, did, (void*)brightness_handler); 301 | } 302 | } 303 | } 304 | 305 | void display_serialize(FILE* rsp) { 306 | uint32_t count = 0; 307 | uint32_t* display_ids = display_active_display_list(&count); 308 | if (!display_ids) return; 309 | 310 | fprintf(rsp, "[\n"); 311 | for (int i = 0; i < count; i++) { 312 | fprintf(rsp, "\t{\n"); 313 | 314 | uint32_t did = display_arrangement_display_id(i + 1); 315 | CFStringRef uuid_ref = display_uuid(did); 316 | CGRect frame = CGDisplayBounds(did); 317 | char* uuid = NULL; 318 | if (uuid_ref) { 319 | uuid = cfstring_copy(uuid_ref); 320 | CFRelease(uuid_ref); 321 | } 322 | 323 | fprintf(rsp, "\t\t\"arrangement-id\":%d,\n", display_arrangement(did)); 324 | fprintf(rsp, "\t\t\"DirectDisplayID\":%d,\n", did); 325 | fprintf(rsp, "\t\t\"UUID\":\"%s\",\n", uuid ? uuid : ""); 326 | fprintf(rsp, "\t\t\"frame\":{\n\t\t\"x\":%.4f,\n\t\t\"y\":%.4f,\n\t\t\"w\":%.4f,\n\t\t\"h\":%.4f\n\t\t}\n", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); 327 | 328 | if (i == count - 1) 329 | fprintf(rsp, "\t}\n"); 330 | else 331 | fprintf(rsp, "\t},\n"); 332 | 333 | if (uuid) free(uuid); 334 | } 335 | fprintf(rsp, "]\n"); 336 | free(display_ids); 337 | } 338 | -------------------------------------------------------------------------------- /src/display.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "event.h" 3 | #include "misc/helpers.h" 4 | 5 | #define DISPLAY_EVENT_HANDLER(name) void name(uint32_t did, CGDisplayChangeSummaryFlags flags, void *context) 6 | typedef DISPLAY_EVENT_HANDLER(display_callback); 7 | 8 | uint32_t display_main_display_id(void); 9 | uint32_t display_active_display_id(void); 10 | uint32_t display_active_display_adid(void); 11 | uint32_t display_arrangement_display_id(int arrangement); 12 | bool display_menu_bar_visible(void); 13 | CGRect display_menu_bar_rect(uint32_t did); 14 | uint32_t display_active_display_count(void); 15 | uint32_t* display_active_display_list(uint32_t* count); 16 | bool display_begin(void); 17 | bool display_end(void); 18 | 19 | CFStringRef display_uuid(uint32_t did); 20 | CGRect display_bounds(uint32_t did); 21 | uint64_t display_space_id(uint32_t did); 22 | uint64_t* display_space_list(uint32_t did, int* count); 23 | int display_arrangement(uint32_t did); 24 | 25 | void forced_brightness_event(); 26 | void begin_receiving_brightness_events(); 27 | 28 | void display_serialize(FILE* rsp); 29 | -------------------------------------------------------------------------------- /src/event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bar_manager.h" 3 | #include "message.h" 4 | 5 | enum event_type { 6 | EVENT_TYPE_UNKNOWN, 7 | APPLICATION_FRONT_SWITCHED, 8 | SPACE_CHANGED, 9 | DISPLAY_ADDED, 10 | DISPLAY_REMOVED, 11 | DISPLAY_MOVED, 12 | DISPLAY_RESIZED, 13 | DISPLAY_CHANGED, 14 | MENU_BAR_HIDDEN_CHANGED, 15 | SYSTEM_WOKE, 16 | SYSTEM_WILL_SLEEP, 17 | SHELL_REFRESH, 18 | ANIMATOR_REFRESH, 19 | MACH_MESSAGE, 20 | MOUSE_UP, 21 | MOUSE_DRAGGED, 22 | MOUSE_ENTERED, 23 | MOUSE_EXITED, 24 | MOUSE_SCROLLED, 25 | VOLUME_CHANGED, 26 | WIFI_CHANGED, 27 | BRIGHTNESS_CHANGED, 28 | POWER_SOURCE_CHANGED, 29 | MEDIA_CHANGED, 30 | COVER_CHANGED, 31 | SPACE_WINDOWS_CHANGED, 32 | DISTRIBUTED_NOTIFICATION, 33 | HOTLOAD, 34 | 35 | INIT_MUTEX, 36 | EVENT_TYPE_COUNT 37 | }; 38 | 39 | struct event { 40 | void* context; 41 | enum event_type type; 42 | }; 43 | 44 | void event_post(struct event *event); 45 | -------------------------------------------------------------------------------- /src/font.c: -------------------------------------------------------------------------------- 1 | #include "font.h" 2 | #include "animation.h" 3 | #include "bar_manager.h" 4 | 5 | struct feature_mapping { 6 | char opentype_tag[5]; 7 | int truetype_feature; 8 | int truetype_selector; 9 | }; 10 | 11 | // Non-exhaustive list of OpenType tags and corresponding TrueType features and selectors 12 | struct feature_mapping feature_mappings[] = { 13 | {"liga", kLigaturesType, kCommonLigaturesOnSelector}, 14 | {"dlig", kLigaturesType, kRareLigaturesOnSelector}, 15 | 16 | {"tnum", kNumberSpacingType, kMonospacedNumbersSelector}, 17 | {"pnum", kNumberSpacingType, kProportionalNumbersSelector}, 18 | 19 | {"smcp", kLowerCaseType, kLowerCaseSmallCapsSelector}, 20 | {"c2sc", kUpperCaseType, kUpperCaseSmallCapsSelector}, 21 | 22 | {"onum", kNumberCaseType, kLowerCaseNumbersSelector}, 23 | {"lnum", kNumberCaseType, kUpperCaseNumbersSelector}, 24 | 25 | {"afrc", kFractionsType, kVerticalFractionsSelector}, 26 | {"frac", kFractionsType, kDiagonalFractionsSelector}, 27 | 28 | {"subs", kVerticalPositionType, kInferiorsSelector}, 29 | {"sups", kVerticalPositionType, kSuperiorsSelector}, 30 | 31 | {"zero", kTypographicExtrasType, kSlashedZeroOnSelector}, 32 | 33 | {"swsh", kContextualAlternatesType, kSwashAlternatesOnSelector}, 34 | {"cswh", kContextualAlternatesType, kContextualSwashAlternatesOnSelector}, 35 | 36 | {"calt", kContextualAlternatesType, kContextualAlternatesOnSelector}, 37 | 38 | // kStylisticAlternativesType = 35 39 | {"salt", 35, 2}, 40 | {"ss01", 35, 2}, {"ss02", 35, 4}, {"ss03", 35, 6}, {"ss04", 35, 8}, 41 | {"ss05", 35, 10}, {"ss06", 35, 12}, {"ss07", 35, 14}, {"ss08", 35, 16}, 42 | {"ss09", 35, 18}, {"ss10", 35, 20}, {"ss11", 35, 22}, {"ss12", 35, 24}, 43 | {"ss13", 35, 26}, {"ss14", 35, 28}, {"ss15", 35, 30}, {"ss16", 35, 32}, 44 | {"ss17", 35, 34}, {"ss18", 35, 36}, {"ss19", 35, 38}, {"ss20", 35, 40}, 45 | 46 | {"", 0, 0} 47 | }; 48 | 49 | void get_truetype_feature(const char* opentype_tag, int* truetype_feature, int* truetype_selector) { 50 | for (int i = 0; feature_mappings[i].opentype_tag[0] != '\0'; ++i) { 51 | if (strcmp(feature_mappings[i].opentype_tag, opentype_tag) == 0) { 52 | *truetype_feature = feature_mappings[i].truetype_feature; 53 | *truetype_selector = feature_mappings[i].truetype_selector; 54 | return; 55 | } 56 | } 57 | } 58 | 59 | void font_register(char* font_path) { 60 | CFStringRef url_string = CFStringCreateWithCString(kCFAllocatorDefault, 61 | font_path, 62 | kCFStringEncodingUTF8); 63 | if (url_string) { 64 | CFURLRef url_ref = CFURLCreateWithString(kCFAllocatorDefault, 65 | url_string, 66 | NULL ); 67 | if (url_ref) { 68 | CTFontManagerRegisterFontsForURL(url_ref, 69 | kCTFontManagerScopeProcess, 70 | NULL ); 71 | CFRelease(url_ref); 72 | } 73 | CFRelease(url_string); 74 | } 75 | free(font_path); 76 | } 77 | 78 | void font_create_ctfont(struct font* font) { 79 | 80 | CFStringRef family_ref = CFStringCreateWithCString(NULL, 81 | font->family, 82 | kCFStringEncodingUTF8); 83 | 84 | CFStringRef style_ref = CFStringCreateWithCString(NULL, 85 | font->style, 86 | kCFStringEncodingUTF8); 87 | 88 | CFNumberRef size_ref = CFNumberCreate(NULL, 89 | kCFNumberFloat32Type, 90 | &font->size ); 91 | 92 | const void *keys[] = { kCTFontFamilyNameAttribute, 93 | kCTFontStyleNameAttribute, 94 | kCTFontSizeAttribute }; 95 | 96 | const void *values[] = { family_ref, style_ref, size_ref }; 97 | CFDictionaryRef attr = CFDictionaryCreate(NULL, 98 | keys, 99 | values, 100 | array_count(keys), 101 | &kCFTypeDictionaryKeyCallBacks, 102 | &kCFTypeDictionaryValueCallBacks); 103 | 104 | CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attr); 105 | 106 | if (font->ct_font) CFRelease(font->ct_font); 107 | 108 | if (font->features) { 109 | char* features_copy = string_copy(font->features); 110 | char* feature = strtok(features_copy, ","); 111 | 112 | while (feature) { 113 | int feature_name = 0; 114 | int feature_selector = 0; 115 | 116 | bool valid_feature = false; 117 | 118 | if (sscanf(feature, "%d:%d", &feature_name, &feature_selector) == 2) { 119 | valid_feature = true; 120 | } else if (strlen(feature) == 4) { 121 | get_truetype_feature(feature, &feature_name, &feature_selector); 122 | 123 | if (feature_name != 0) { 124 | valid_feature = true; 125 | } 126 | } 127 | 128 | if (valid_feature) { 129 | CFNumberRef name = CFNumberCreate(NULL, kCFNumberIntType, &feature_name); 130 | CFNumberRef value = CFNumberCreate(NULL, kCFNumberIntType, &feature_selector); 131 | 132 | CTFontDescriptorRef new_descriptor = CTFontDescriptorCreateCopyWithFeature(descriptor, name, value); 133 | CFRelease(descriptor); 134 | descriptor = new_descriptor; 135 | 136 | CFRelease(name); 137 | CFRelease(value); 138 | } 139 | 140 | feature = strtok(NULL, ","); 141 | } 142 | 143 | free(features_copy); 144 | } 145 | 146 | font->ct_font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL); 147 | 148 | CFRelease(descriptor); 149 | CFRelease(attr); 150 | CFRelease(size_ref); 151 | CFRelease(style_ref); 152 | CFRelease(family_ref); 153 | } 154 | 155 | void font_init(struct font* font) { 156 | font->size = 14.f; 157 | font->style = string_copy("Bold"); 158 | font->family = string_copy("Hack Nerd Font"); 159 | font_create_ctfont(font); 160 | } 161 | 162 | bool font_set_style(struct font* font, char* style, bool forced) { 163 | if (!style) return false; 164 | if (!forced && font->style && string_equals(font->style, style)) { 165 | free(style); 166 | return false; 167 | } 168 | if (font->style && style != font->style) free(font->style); 169 | font->style = style; 170 | font->font_changed = true; 171 | 172 | return true; 173 | } 174 | 175 | bool font_set_family(struct font* font, char* family, bool forced) { 176 | if (!family) return false; 177 | if (!forced && font->family && string_equals(font->family, family)) { 178 | free(family); 179 | return false; 180 | } 181 | if (font->family) free(font->family); 182 | font->family = family; 183 | font->font_changed = true; 184 | 185 | return true; 186 | } 187 | 188 | bool font_set_size(struct font* font, float size) { 189 | if (font->size == size) return false; 190 | 191 | font->size = size; 192 | font->font_changed = true; 193 | 194 | return true; 195 | } 196 | 197 | bool font_set_features(struct font* font, char* features) { 198 | if (!features) return false; 199 | if (font->features && string_equals(font->features, features)) { 200 | free(features); 201 | return false; 202 | } 203 | if (font->features) free(font->features); 204 | font->features = features; 205 | font->font_changed = true; 206 | 207 | return true; 208 | } 209 | 210 | bool font_set(struct font* font, char* font_string, bool forced) { 211 | if (!font_string) return false; 212 | 213 | float size = 10.0f; 214 | char font_properties[2][255] = { {}, {} }; 215 | sscanf(font_string, 216 | "%254[^:]:%254[^:]:%f", 217 | font_properties[0], 218 | font_properties[1], 219 | &size ); 220 | 221 | free(font_string); 222 | 223 | bool change = font_set_family(font, string_copy(font_properties[0]), forced); 224 | change |= font_set_style(font, string_copy(font_properties[1]), forced); 225 | change |= font_set_size(font, size); 226 | 227 | return change; 228 | } 229 | 230 | void font_clear_pointers(struct font* font) { 231 | font->ct_font = NULL; 232 | font->family = NULL; 233 | font->style = NULL; 234 | font->features = NULL; 235 | } 236 | 237 | void font_destroy(struct font* font) { 238 | if (font->style) free(font->style); 239 | if (font->family) free(font->family); 240 | if (font->features) free(font->features); 241 | if (font->ct_font) CFRelease(font->ct_font); 242 | font_clear_pointers(font); 243 | } 244 | 245 | bool font_parse_sub_domain(struct font* font, FILE* rsp, struct token property, char* message) { 246 | bool needs_refresh = false; 247 | if (token_equals(property, PROPERTY_FONT_SIZE)) { 248 | struct token token = get_token(&message); 249 | ANIMATE_FLOAT(font_set_size, 250 | font, 251 | font->size, 252 | token_to_float(token)); 253 | } else if (token_equals(property, PROPERTY_FONT_FAMILY)) { 254 | struct token token = get_token(&message); 255 | needs_refresh = font_set_family(font, token_to_string(token), false); 256 | } else if (token_equals(property, PROPERTY_FONT_STYLE)) { 257 | struct token token = get_token(&message); 258 | needs_refresh = font_set_style(font, token_to_string(token), false); 259 | } else if (token_equals(property, PROPERTY_FONT_FEATURES)) { 260 | struct token token = get_token(&message); 261 | needs_refresh = font_set_features(font, token_to_string(token)); 262 | } else { 263 | respond(rsp, "[!] Text: Invalid property '%s'\n", property.text); 264 | } 265 | 266 | return needs_refresh; 267 | } 268 | -------------------------------------------------------------------------------- /src/font.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "misc/helpers.h" 4 | 5 | struct font { 6 | CTFontRef ct_font; 7 | 8 | bool font_changed; 9 | float size; 10 | char* family; 11 | char* style; 12 | char* features; 13 | }; 14 | 15 | void font_register(char* font_path); 16 | 17 | void font_init(struct font* font); 18 | void font_destroy(struct font* font); 19 | bool font_set(struct font* font, char* font_string, bool forced); 20 | bool font_set_size(struct font* font, float size); 21 | bool font_set_family(struct font* font, char* family, bool forced); 22 | bool font_set_style(struct font* font, char* style, bool forced); 23 | void font_create_ctfont(struct font* font); 24 | void font_clear_pointers(struct font* font); 25 | 26 | bool font_parse_sub_domain(struct font* font, FILE* rsp, struct token property, char* message); 27 | -------------------------------------------------------------------------------- /src/graph.c: -------------------------------------------------------------------------------- 1 | #include "graph.h" 2 | 3 | void graph_init(struct graph* graph) { 4 | graph->width = 0; 5 | graph->cursor = 0; 6 | 7 | graph->line_width = 0.5; 8 | graph->fill = true; 9 | graph->overrides_fill_color = false; 10 | graph->enabled = true; 11 | 12 | color_init(&graph->line_color, 0xffcccccc); 13 | color_init(&graph->fill_color, 0xffcccccc); 14 | } 15 | 16 | void graph_setup(struct graph* graph, uint32_t width) { 17 | graph->width = width; 18 | graph->y = malloc(sizeof(float) * width); 19 | memset(graph->y, 0, sizeof(float) * width); 20 | } 21 | 22 | float graph_get_y(struct graph* graph, uint32_t i) { 23 | if (!graph->enabled) return 0.f; 24 | return graph->y[ (graph->cursor + i)%graph->width ]; 25 | } 26 | 27 | void graph_push_back(struct graph* graph, float y) { 28 | if (!graph->enabled) return; 29 | graph->y[graph->cursor] = y; 30 | 31 | ++graph->cursor; 32 | graph->cursor %= graph->width; 33 | } 34 | 35 | uint32_t graph_get_length(struct graph* graph) { 36 | if (graph->enabled) return graph->width; 37 | return 0; 38 | } 39 | 40 | void graph_calculate_bounds(struct graph* graph, uint32_t x, uint32_t y, uint32_t height) { 41 | graph->bounds.size.height = height; 42 | graph->bounds.origin.x = x; 43 | graph->bounds.origin.y = y - graph->bounds.size.height / 2 44 | + graph->line_width; 45 | } 46 | 47 | void graph_draw(struct graph* graph, CGContextRef context) { 48 | uint32_t x = graph->bounds.origin.x + (graph->rtl ? graph->width : 0); 49 | uint32_t y = graph->bounds.origin.y; 50 | uint32_t height = graph->bounds.size.height; 51 | 52 | uint32_t sample_width = 1; 53 | bool fill = graph->fill; 54 | CGContextSaveGState(context); 55 | CGContextSetRGBStrokeColor(context, 56 | graph->line_color.r, 57 | graph->line_color.g, 58 | graph->line_color.b, 59 | graph->line_color.a ); 60 | 61 | if (graph->overrides_fill_color) 62 | CGContextSetRGBFillColor(context, 63 | graph->fill_color.r, 64 | graph->fill_color.g, 65 | graph->fill_color.b, 66 | graph->fill_color.a ); 67 | else 68 | CGContextSetRGBFillColor(context, 69 | graph->line_color.r, 70 | graph->line_color.g, 71 | graph->line_color.b, 72 | 0.2 * graph->line_color.a); 73 | 74 | CGContextSetLineWidth(context, graph->line_width); 75 | CGMutablePathRef p = CGPathCreateMutable(); 76 | uint32_t start_x = x; 77 | if (graph->rtl) { 78 | CGPathMoveToPoint(p, 79 | NULL, 80 | x, 81 | y + graph_get_y(graph, graph->width - 1) * height); 82 | 83 | for (int i = graph->width - 1; i > 0; --i, x -= sample_width) { 84 | CGPathAddLineToPoint(p, NULL, x, y + graph_get_y(graph, i) * height); 85 | } 86 | } 87 | else { 88 | CGPathMoveToPoint(p, NULL, x, y + graph_get_y(graph, 0) * height); 89 | for (int i = graph->width - 1; i > 0; --i, x += sample_width) { 90 | CGPathAddLineToPoint(p, NULL, x, y + graph_get_y(graph, i) * height); 91 | } 92 | } 93 | CGContextAddPath(context, p); 94 | CGContextStrokePath(context); 95 | if (fill) { 96 | if (graph->rtl) { 97 | CGPathAddLineToPoint(p, NULL, x + sample_width, y); 98 | } 99 | else { 100 | CGPathAddLineToPoint(p, NULL, x - sample_width, y); 101 | } 102 | CGPathAddLineToPoint(p, NULL, start_x, y); 103 | CGPathCloseSubpath(p); 104 | CGContextAddPath(context, p); 105 | CGContextFillPath(context); 106 | } 107 | CGPathRelease(p); 108 | CGContextRestoreGState(context); 109 | } 110 | 111 | void graph_serialize(struct graph* graph, char* indent, FILE* rsp) { 112 | fprintf(rsp, "%s\"color\": \"0x%x\",\n" 113 | "%s\"fill_color\": \"0x%x\",\n" 114 | "%s\"line_width\": \"%f\",\n" 115 | "%s\"data\": [\n", 116 | indent, graph->line_color.hex, 117 | indent, graph->fill_color.hex, 118 | indent, graph->line_width, indent); 119 | int counter = 0; 120 | for (int i = 0; i < graph->width; i++) { 121 | if (counter++ > 0) fprintf(rsp, ",\n"); 122 | fprintf(rsp, "%s\t\"%f\"", indent, graph->y[i]); 123 | } 124 | fprintf(rsp, "\n%s]", indent); 125 | } 126 | 127 | void graph_destroy(struct graph* graph) { 128 | if (!graph->enabled) return; 129 | if (graph->y) free(graph->y); 130 | graph->y = NULL; 131 | } 132 | 133 | bool graph_parse_sub_domain(struct graph* graph, FILE* rsp, struct token property, char* message) { 134 | if (token_equals(property, PROPERTY_COLOR)) { 135 | return color_set_hex(&graph->line_color, 136 | token_to_uint32t(get_token(&message))); 137 | } else if (token_equals(property, PROPERTY_FILL_COLOR)) { 138 | graph->overrides_fill_color = true; 139 | return color_set_hex(&graph->fill_color, 140 | token_to_uint32t(get_token(&message))); 141 | } else if (token_equals(property, PROPERTY_LINE_WIDTH)) { 142 | graph->line_width = token_to_float(get_token(&message)); 143 | return true; 144 | } 145 | else { 146 | struct key_value_pair key_value_pair = get_key_value_pair(property.text, 147 | '.' ); 148 | if (key_value_pair.key && key_value_pair.value) { 149 | struct token subdom = {key_value_pair.key,strlen(key_value_pair.key)}; 150 | struct token entry = {key_value_pair.value,strlen(key_value_pair.value)}; 151 | if (token_equals(subdom, SUB_DOMAIN_COLOR)) { 152 | return color_parse_sub_domain(&graph->line_color, rsp, entry, message); 153 | } 154 | else if (token_equals(subdom, SUB_DOMAIN_FILL_COLOR)) { 155 | return color_parse_sub_domain(&graph->fill_color, rsp, entry, message); 156 | } 157 | else { 158 | respond(rsp, "[!] Graph: Invalid subdomain '%s'\n", subdom.text); 159 | } 160 | } 161 | else { 162 | respond(rsp, "[!] Graph: Invalid property '%s'\n", property.text); 163 | } 164 | } 165 | return false; 166 | } 167 | -------------------------------------------------------------------------------- /src/graph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "misc/helpers.h" 3 | #include "color.h" 4 | 5 | struct graph { 6 | bool rtl; 7 | bool fill; 8 | bool enabled; 9 | bool overrides_fill_color; 10 | 11 | float* y; 12 | uint32_t width; 13 | uint32_t cursor; 14 | float line_width; 15 | 16 | CGRect bounds; 17 | struct color line_color; 18 | struct color fill_color; 19 | }; 20 | 21 | void graph_init(struct graph* graph); 22 | void graph_setup(struct graph* graph, uint32_t width); 23 | void graph_push_back(struct graph* graph, float y); 24 | float graph_get_y(struct graph* graph, uint32_t i); 25 | uint32_t graph_get_length(struct graph* graph); 26 | 27 | void graph_calculate_bounds(struct graph* graph, uint32_t x, uint32_t y, uint32_t height); 28 | void graph_draw(struct graph* graph, CGContextRef context); 29 | void graph_destroy(struct graph* graph); 30 | 31 | void graph_serialize(struct graph* graph, char* indent, FILE* rsp); 32 | bool graph_parse_sub_domain(struct graph* graph, FILE* rsp, struct token property, char* message); 33 | -------------------------------------------------------------------------------- /src/group.c: -------------------------------------------------------------------------------- 1 | #include "group.h" 2 | #include "bar.h" 3 | 4 | static struct bar_item* group_get_first_member(struct group* group, struct bar* bar) { 5 | if (group->num_members == 1) return NULL; 6 | 7 | int min = INT32_MAX; 8 | struct bar_item* first_item = NULL; 9 | 10 | for (int i = 1; i < group->num_members; i++) { 11 | struct bar_item* member = group->members[i]; 12 | if (bar_draws_item(bar, member)) { 13 | struct window* window = bar_item_get_window(member, bar->adid); 14 | if (window->origin.x < min) { 15 | min = window->origin.x; 16 | first_item = member; 17 | } 18 | } 19 | } 20 | 21 | return first_item; 22 | } 23 | 24 | static struct bar_item* group_get_last_member(struct group* group, struct bar* bar) { 25 | if (group->num_members == 1) return NULL; 26 | 27 | int max = INT32_MIN; 28 | struct bar_item* last_item = NULL; 29 | 30 | for (int i = 1; i < group->num_members; i++) { 31 | struct bar_item* member = group->members[i]; 32 | if (bar_draws_item(bar, member)) { 33 | struct window* window = bar_item_get_window(member, bar->adid); 34 | if (window->origin.x + window->frame.size.width > max) { 35 | max = window->origin.x + window->frame.size.width; 36 | last_item = member; 37 | } 38 | } 39 | } 40 | 41 | return last_item; 42 | } 43 | 44 | struct group* group_create() { 45 | struct group* group = malloc(sizeof(struct group)); 46 | memset(group, 0, sizeof(struct group)); 47 | return group; 48 | } 49 | 50 | void group_init(struct group* group) { 51 | group->num_members = 0; 52 | group->members = NULL; 53 | } 54 | 55 | bool group_is_item_member(struct group* group, struct bar_item* item) { 56 | for (uint32_t i = 0; i < group->num_members; i++) { 57 | if (group->members[i] == item) return true; 58 | } 59 | return false; 60 | } 61 | 62 | void group_add_member(struct group* group, struct bar_item* item) { 63 | if (group_is_item_member(group, item)) return; 64 | if (item->group && item->group->members && item->group->members[0] == item) { 65 | for (int i = 1; i < item->group->num_members; i++) { 66 | group_add_member(group, item->group->members[i]); 67 | } 68 | } else { 69 | group->num_members++; 70 | group->members = realloc(group->members, 71 | sizeof(struct bar_item*)*group->num_members); 72 | group->members[group->num_members - 1] = item; 73 | item->group = group; 74 | } 75 | } 76 | 77 | uint32_t group_get_length(struct group* group, struct bar* bar) { 78 | int len = group->last_window->origin.x 79 | + group->last_window->frame.size.width 80 | + group->last_item->background.padding_right 81 | + group->first_item->background.padding_left 82 | - group->first_window->origin.x; 83 | 84 | return max(len, 0); 85 | } 86 | 87 | void group_remove_member(struct group* group, struct bar_item* bar_item) { 88 | if (group->num_members <= 0) return; 89 | struct bar_item* tmp[group->num_members - 1]; 90 | int count = 0; 91 | for (int i = 0; i < group->num_members; i++) { 92 | if (group->members[i] == bar_item) continue; 93 | tmp[count++] = group->members[i]; 94 | } 95 | group->num_members--; 96 | group->members = realloc(group->members, 97 | sizeof(struct bar_item*)*group->num_members); 98 | memcpy(group->members, tmp, sizeof(struct bar_item*)*group->num_members); 99 | } 100 | 101 | void group_destroy(struct group* group) { 102 | for (int i = 0; i < group->num_members; i++) { 103 | group->members[i]->group = NULL; 104 | } 105 | if (group->members) free(group->members); 106 | free(group); 107 | } 108 | 109 | void group_calculate_bounds(struct group* group, struct bar* bar, uint32_t y) { 110 | group->first_item = group_get_first_member(group, bar); 111 | group->first_window = bar_item_get_window(group->first_item, bar->adid); 112 | 113 | group->last_item = group_get_last_member(group, bar); 114 | group->last_window = bar_item_get_window(group->last_item, bar->adid); 115 | 116 | if (!group->first_window || !group->last_window) { 117 | group->bounds.origin = g_nirvana; 118 | return; 119 | } 120 | 121 | uint32_t group_length = group_get_length(group, bar); 122 | CGPoint shadow_offsets = bar_item_calculate_shadow_offsets(group->members[0]); 123 | 124 | 125 | group->bounds = (CGRect){{group->first_window->origin.x 126 | - group->first_item->background.padding_left, 127 | group->first_window->origin.y}, 128 | {group_length 129 | + shadow_offsets.x 130 | + shadow_offsets.y, 131 | group->first_window->frame.size.height}}; 132 | 133 | background_calculate_bounds(&group->members[0]->background, 134 | max(shadow_offsets.x, 0), 135 | y + group->members[0]->y_offset, 136 | group_get_length(group, bar), 137 | group->members[0]->background.bounds.size.height); 138 | } 139 | 140 | void group_serialize(struct group* group, char* indent, FILE* rsp) { 141 | int counter = 0; 142 | for (int i = 1; i < group->num_members; i++) { 143 | if (!group->members[i]) continue; 144 | if (counter++ > 0) fprintf(rsp, ",\n"); 145 | fprintf(rsp, "%s\"%s\"", indent, group->members[i]->name); 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /src/group.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bar_item.h" 3 | 4 | struct bar; 5 | 6 | struct group { 7 | CGRect bounds; 8 | 9 | struct window* first_window; 10 | struct window* last_window; 11 | 12 | struct bar_item* first_item; 13 | struct bar_item* last_item; 14 | 15 | uint32_t num_members; 16 | struct bar_item** members; 17 | }; 18 | 19 | struct group* group_create(); 20 | void group_init(struct group* group); 21 | void group_set_name(struct group* group, char* _name); 22 | void group_add_member(struct group* group, struct bar_item* item); 23 | void group_remove_member(struct group* group, struct bar_item* bar_item); 24 | uint32_t group_get_length(struct group* group, struct bar* bar); 25 | 26 | void group_calculate_bounds(struct group* group, struct bar* bar, uint32_t y); 27 | void group_destroy(struct group* group); 28 | 29 | void group_serialize(struct group* group, char* indent, FILE* rsp); 30 | -------------------------------------------------------------------------------- /src/hotload.c: -------------------------------------------------------------------------------- 1 | #include "bar_manager.h" 2 | #include "event.h" 3 | #include 4 | #include 5 | 6 | extern char g_config_file[4096]; 7 | extern char g_name[256]; 8 | bool g_hotload = false; 9 | int64_t g_last_hotload = 0; 10 | 11 | void hotload_set_state(int state) { 12 | g_hotload = state; 13 | } 14 | 15 | int hotload_get_state() { 16 | return g_hotload; 17 | } 18 | 19 | bool set_config_file_path(char* file) { 20 | char* path = realpath(file, NULL); 21 | if (path) { 22 | snprintf(g_config_file, sizeof(g_config_file), "%s", path); 23 | free(path); 24 | return true; 25 | } 26 | return false; 27 | } 28 | 29 | static bool get_config_file(char *restrict filename, char *restrict buffer, int buffer_size) { 30 | char *xdg_home = getenv("XDG_CONFIG_HOME"); 31 | if (xdg_home && *xdg_home) { 32 | snprintf(buffer, buffer_size, "%s/%s/%s", xdg_home, g_name, filename); 33 | if (file_exists(buffer)) return true; 34 | } 35 | 36 | char *home = getenv("HOME"); 37 | if (!home) return false; 38 | 39 | snprintf(buffer, buffer_size, "%s/.config/%s/%s", home, g_name, filename); 40 | if (file_exists(buffer)) return true; 41 | 42 | snprintf(buffer, buffer_size, "%s/.%s", home, filename); 43 | return file_exists(buffer); 44 | } 45 | 46 | void exec_config_file() { 47 | if (!*g_config_file 48 | && !get_config_file("sketchybarrc", g_config_file, sizeof(g_config_file))) { 49 | printf("could not locate config file..\n"); 50 | return; 51 | } 52 | 53 | if (!file_exists(g_config_file)) { 54 | printf("file '%s' does not exist..\n", g_config_file); 55 | return; 56 | } 57 | 58 | setenv("CONFIG_DIR", dirname(g_config_file), 1); 59 | chdir(dirname(g_config_file)); 60 | 61 | if (!ensure_executable_permission(g_config_file)) { 62 | printf("could not set the executable permission bit for '%s'\n", g_config_file); 63 | return; 64 | } 65 | 66 | if (!fork_exec(g_config_file, NULL)) { 67 | printf("failed to execute file '%s'\n", g_config_file); 68 | return; 69 | } 70 | } 71 | 72 | static void handler(ConstFSEventStreamRef stream, void* context, size_t count, void* paths, const FSEventStreamEventFlags* flags, const FSEventStreamEventId* ids) { 73 | if (g_hotload && count > 0) { 74 | // Limit the hotload rate to avoid locking up the system on a hotload loop 75 | int64_t time = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX); 76 | if (time - g_last_hotload > (1ULL << 30)) { 77 | g_last_hotload = time; 78 | struct event event = { NULL, HOTLOAD }; 79 | event_post(&event); 80 | } 81 | } 82 | } 83 | 84 | int begin_receiving_config_change_events() { 85 | char* file = dirname(g_config_file); 86 | CFStringRef file_ref = CFStringCreateWithCString( 87 | kCFAllocatorDefault, file, kCFStringEncodingUTF8); 88 | 89 | CFArrayRef paths = CFArrayCreate(NULL, 90 | (const void**)&file_ref, 91 | 1, 92 | &kCFTypeArrayCallBacks); 93 | 94 | FSEventStreamRef stream = FSEventStreamCreate( 95 | kCFAllocatorDefault, 96 | handler, 97 | NULL, 98 | paths, 99 | kFSEventStreamEventIdSinceNow, 100 | 0.5, 101 | kFSEventStreamCreateFlagNoDefer 102 | | kFSEventStreamCreateFlagFileEvents); 103 | 104 | CFRelease(file_ref); 105 | CFRelease(paths); 106 | 107 | FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), 108 | kCFRunLoopDefaultMode); 109 | 110 | FSEventStreamStart(stream); 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /src/hotload.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define HOTLOAD_STATE_ENABLED true 4 | #define HOTLOAD_STATE_DISABLED false 5 | 6 | void exec_config_file(); 7 | void begin_receiving_config_change_events(); 8 | void hotload_set_state(int state); 9 | int hotload_get_state(); 10 | bool set_config_file_path(char* file); 11 | -------------------------------------------------------------------------------- /src/image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "shadow.h" 3 | #include "misc/defines.h" 4 | 5 | extern CGImageRef workspace_icon_for_app(char* app); 6 | 7 | struct image { 8 | bool enabled; 9 | 10 | float scale; 11 | CGSize size; 12 | CGRect bounds; 13 | 14 | char* path; 15 | 16 | CGImageRef image_ref; 17 | CFDataRef data_ref; 18 | 19 | struct shadow shadow; 20 | 21 | struct color border_color; 22 | float border_width; 23 | uint32_t corner_radius; 24 | 25 | int padding_left; 26 | int padding_right; 27 | int y_offset; 28 | 29 | struct image* link; 30 | }; 31 | 32 | void image_init(struct image* image); 33 | bool image_set_enabled(struct image* image, bool enabled); 34 | void image_copy(struct image* image, CGImageRef source); 35 | bool image_set_image(struct image* image, CGImageRef new_image_ref, CGRect bounds, bool forced); 36 | bool image_load(struct image* image, char* path, FILE* rsp); 37 | bool image_set_scale(struct image* image, float scale); 38 | 39 | CGSize image_get_size(struct image* image); 40 | void image_calculate_bounds(struct image* image, uint32_t x, uint32_t y); 41 | void image_draw(struct image* image, CGContextRef context); 42 | void image_clear_pointers(struct image* image); 43 | void image_destroy(struct image* image); 44 | 45 | void image_serialize(struct image* image, char* indent, FILE* rsp); 46 | bool image_parse_sub_domain(struct image* image, FILE* rsp, struct token property, char* message); 47 | -------------------------------------------------------------------------------- /src/mach.c: -------------------------------------------------------------------------------- 1 | #include "mach.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | mach_port_t mach_get_bs_port(char* bs_name) { 8 | mach_port_name_t task = mach_task_self(); 9 | 10 | mach_port_t bs_port; 11 | if (task_get_special_port(task, 12 | TASK_BOOTSTRAP_PORT, 13 | &bs_port ) != KERN_SUCCESS) { 14 | return 0; 15 | } 16 | 17 | mach_port_t port; 18 | if (bootstrap_look_up(bs_port, 19 | bs_name, 20 | &port ) != KERN_SUCCESS) { 21 | return 0; 22 | } 23 | 24 | return port; 25 | } 26 | 27 | void mach_receive_message(mach_port_t port, struct mach_buffer* buffer, bool timeout) { 28 | *buffer = (struct mach_buffer) { 0 }; 29 | mach_msg_return_t msg_return; 30 | if (timeout) 31 | msg_return = mach_msg(&buffer->message.header, 32 | MACH_RCV_MSG | MACH_RCV_TIMEOUT, 33 | 0, 34 | sizeof(struct mach_buffer), 35 | port, 36 | 100, 37 | MACH_PORT_NULL ); 38 | else 39 | msg_return = mach_msg(&buffer->message.header, 40 | MACH_RCV_MSG, 41 | 0, 42 | sizeof(struct mach_buffer), 43 | port, 44 | MACH_MSG_TIMEOUT_NONE, 45 | MACH_PORT_NULL ); 46 | 47 | if (msg_return != MACH_MSG_SUCCESS) { 48 | buffer->message.descriptor.address = NULL; 49 | } 50 | } 51 | 52 | char* mach_send_message(mach_port_t port, char* message, uint32_t len, bool await_response) { 53 | if (!message || !port) return NULL; 54 | 55 | mach_port_t response_port; 56 | mach_port_name_t task = mach_task_self(); 57 | if (await_response) { 58 | if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, 59 | &response_port ) != KERN_SUCCESS) { 60 | return NULL; 61 | } 62 | 63 | if (mach_port_insert_right(task, response_port, 64 | response_port, 65 | MACH_MSG_TYPE_MAKE_SEND)!= KERN_SUCCESS) { 66 | return NULL; 67 | } 68 | } 69 | 70 | struct mach_message msg = { 0 }; 71 | msg.header.msgh_remote_port = port; 72 | if (await_response) { 73 | msg.header.msgh_local_port = response_port; 74 | msg.header.msgh_id = response_port; 75 | msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 76 | MACH_MSG_TYPE_MAKE_SEND, 77 | 0, 78 | MACH_MSGH_BITS_COMPLEX ); 79 | } else { 80 | msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND 81 | & MACH_MSGH_BITS_REMOTE_MASK, 82 | 0, 83 | 0, 84 | MACH_MSGH_BITS_COMPLEX ); 85 | } 86 | 87 | msg.header.msgh_size = sizeof(struct mach_message); 88 | 89 | msg.msgh_descriptor_count = 1; 90 | msg.descriptor.address = message; 91 | msg.descriptor.size = len * sizeof(char); 92 | msg.descriptor.copy = MACH_MSG_VIRTUAL_COPY; 93 | msg.descriptor.deallocate = false; 94 | msg.descriptor.type = MACH_MSG_OOL_DESCRIPTOR; 95 | 96 | mach_msg(&msg.header, 97 | MACH_SEND_MSG, 98 | sizeof(struct mach_message), 99 | 0, 100 | MACH_PORT_NULL, 101 | MACH_MSG_TIMEOUT_NONE, 102 | MACH_PORT_NULL ); 103 | 104 | if (await_response) { 105 | struct mach_buffer buffer = { 0 }; 106 | mach_receive_message(response_port, &buffer, true); 107 | char* rsp = NULL; 108 | if (buffer.message.descriptor.address) { 109 | rsp = malloc(strlen(buffer.message.descriptor.address) + 1); 110 | memcpy(rsp, buffer.message.descriptor.address, 111 | strlen(buffer.message.descriptor.address) + 1); 112 | } else { 113 | rsp = malloc(1); 114 | *rsp = '\0'; 115 | } 116 | 117 | mach_msg_destroy(&buffer.message.header); 118 | mach_port_mod_refs(task, response_port, MACH_PORT_RIGHT_RECEIVE, -1); 119 | mach_port_deallocate(task, response_port); 120 | 121 | return rsp; 122 | } 123 | 124 | return NULL; 125 | } 126 | 127 | void mach_message_callback(CFMachPortRef port, void* message, CFIndex size, void* context) { 128 | struct mach_server* mach_server = context; 129 | struct mach_buffer buffer; 130 | buffer.message = *(struct mach_message*)message; 131 | mach_server->handler(&buffer); 132 | mach_msg_destroy(&buffer.message.header); 133 | } 134 | 135 | #pragma clang diagnostic push 136 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 137 | extern char g_name[256]; 138 | bool mach_server_begin(struct mach_server* mach_server, mach_handler handler) { 139 | mach_server->task = mach_task_self(); 140 | 141 | if (mach_port_allocate(mach_server->task, 142 | MACH_PORT_RIGHT_RECEIVE, 143 | &mach_server->port ) != KERN_SUCCESS) { 144 | return false; 145 | } 146 | 147 | struct mach_port_limits limits = {}; 148 | limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE; 149 | 150 | if (mach_port_set_attributes(mach_server->task, 151 | mach_server->port, 152 | MACH_PORT_LIMITS_INFO, 153 | (mach_port_info_t)&limits, 154 | MACH_PORT_LIMITS_INFO_COUNT) != KERN_SUCCESS) { 155 | return false; 156 | } 157 | 158 | if (mach_port_insert_right(mach_server->task, 159 | mach_server->port, 160 | mach_server->port, 161 | MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { 162 | return false; 163 | } 164 | 165 | if (task_get_special_port(mach_server->task, 166 | TASK_BOOTSTRAP_PORT, 167 | &mach_server->bs_port) != KERN_SUCCESS) { 168 | return false; 169 | } 170 | 171 | char bs_name[256]; 172 | snprintf(bs_name, 256, MACH_BS_NAME_FMT, g_name); 173 | 174 | if (bootstrap_register(mach_server->bs_port, 175 | bs_name, 176 | mach_server->port ) != KERN_SUCCESS) { 177 | return false; 178 | } 179 | 180 | mach_server->handler = handler; 181 | mach_server->is_running = true; 182 | 183 | CFMachPortContext context = {0, (void*)mach_server}; 184 | 185 | CFMachPortRef cf_mach_port = CFMachPortCreateWithPort(NULL, 186 | mach_server->port, 187 | mach_message_callback, 188 | &context, 189 | false ); 190 | 191 | CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(NULL, 192 | cf_mach_port, 193 | 0 ); 194 | 195 | CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode); 196 | CFRelease(source); 197 | CFRelease(cf_mach_port); 198 | return true; 199 | } 200 | #pragma clang diagnostic pop 201 | -------------------------------------------------------------------------------- /src/mach.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define MACH_BS_NAME_FMT "git.felix.%s" 10 | 11 | struct mach_message { 12 | mach_msg_header_t header; 13 | mach_msg_size_t msgh_descriptor_count; 14 | mach_msg_ool_descriptor_t descriptor; 15 | }; 16 | 17 | struct mach_buffer { 18 | struct mach_message message; 19 | mach_msg_trailer_t trailer; 20 | }; 21 | 22 | #define MACH_HANDLER(name) void name(struct mach_buffer* message) 23 | typedef MACH_HANDLER(mach_handler); 24 | 25 | struct mach_server { 26 | bool is_running; 27 | mach_port_name_t task; 28 | mach_port_t port; 29 | mach_port_t bs_port; 30 | 31 | mach_handler* handler; 32 | }; 33 | 34 | bool mach_server_begin(struct mach_server* mach_server, mach_handler handler); 35 | char* mach_send_message(mach_port_t port, char* message, uint32_t len, bool await_response); 36 | mach_port_t mach_get_bs_port(char* bs_name); 37 | -------------------------------------------------------------------------------- /src/media.h: -------------------------------------------------------------------------------- 1 | void initialize_media_events(); 2 | void begin_receiving_media_events(); 3 | void forced_media_change_event(); 4 | -------------------------------------------------------------------------------- /src/media.m: -------------------------------------------------------------------------------- 1 | #include "event.h" 2 | #include 3 | 4 | extern void MRMediaRemoteRegisterForNowPlayingNotifications(dispatch_queue_t queue); 5 | extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t queue, void (^block)(NSDictionary* dict)); 6 | extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, void (^block)(BOOL playing)); 7 | extern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int null, dispatch_queue_t queue, void (^block)(CFStringRef name)); 8 | 9 | extern NSString* kMRMediaRemoteNowPlayingApplicationIsPlayingDidChangeNotification; 10 | extern NSString* kMRMediaRemoteNowPlayingInfoDidChangeNotification; 11 | extern NSString* kMRMediaRemoteNowPlayingApplicationDidChangeNotification; 12 | 13 | extern NSString* kMRMediaRemoteNowPlayingInfoAlbum; 14 | extern NSString* kMRMediaRemoteNowPlayingInfoArtist; 15 | extern NSString* kMRMediaRemoteNowPlayingInfoTitle; 16 | extern NSString* kMRMediaRemoteNowPlayingInfoArtworkMIMEType; 17 | extern NSString* kMRMediaRemoteNowPlayingInfoArtworkData; 18 | extern NSString* kMRMediaRemoteNowPlayingApplicationDisplayNameUserInfoKey; 19 | 20 | @interface media_context : NSObject {} 21 | @property const char* app; 22 | @property const char* artist; 23 | @property const char* title; 24 | @property const char* album; 25 | @property CGImageRef artwork; 26 | @property BOOL playing; 27 | - (id)init; 28 | @end 29 | 30 | @implementation media_context 31 | - (id)init { 32 | if ((self = [super init])) { 33 | [NSNotificationCenter.defaultCenter addObserver:self 34 | selector:@selector(media_change:) 35 | name:kMRMediaRemoteNowPlayingInfoDidChangeNotification 36 | object:nil]; 37 | 38 | [NSNotificationCenter.defaultCenter addObserver:self 39 | selector:@selector(playing_change:) 40 | name:kMRMediaRemoteNowPlayingApplicationIsPlayingDidChangeNotification 41 | object:nil]; 42 | 43 | [NSNotificationCenter.defaultCenter addObserver:self 44 | selector:@selector(media_change:) 45 | name:kMRMediaRemoteNowPlayingApplicationDidChangeNotification 46 | object:nil]; 47 | self.app = NULL; 48 | self.artist = NULL; 49 | self.title = NULL; 50 | self.album = NULL; 51 | } 52 | 53 | return self; 54 | } 55 | 56 | char* g_media_info = NULL; 57 | bool g_media_events = false; 58 | - (void) update { 59 | @autoreleasepool { 60 | if (self.app && self.artist && self.title && self.album) { 61 | char* escaped_artist = escape_string((char*)self.artist); 62 | char* escaped_title = escape_string((char*)self.title); 63 | char* escaped_album = escape_string((char*)self.album); 64 | 65 | uint32_t info_len = strlen(self.app) 66 | + strlen(escaped_artist) 67 | + strlen(escaped_title) 68 | + strlen(escaped_album) + 256; 69 | 70 | char info[info_len]; 71 | snprintf(info, info_len, "{\n" 72 | "\t\"state\": \"%s\",\n" 73 | "\t\"title\": \"%s\",\n" 74 | "\t\"album\": \"%s\",\n" 75 | "\t\"artist\": \"%s\",\n" 76 | "\t\"app\": \"%s\"\n}", 77 | self.playing ? "playing" : "paused", 78 | escaped_title, 79 | escaped_album, 80 | escaped_artist, 81 | self.app ); 82 | 83 | free(escaped_artist); 84 | free(escaped_title); 85 | free(escaped_album); 86 | 87 | if (self.artwork) { 88 | struct event cover_event = { self.artwork, COVER_CHANGED }; 89 | event_post(&cover_event); 90 | } 91 | 92 | if (!g_media_info || strcmp(info, g_media_info) != 0) { 93 | g_media_info = realloc(g_media_info, info_len); 94 | memcpy(g_media_info, info, info_len); 95 | 96 | char payload_info[info_len]; 97 | memcpy(payload_info, info, info_len); 98 | 99 | struct event event = { payload_info, MEDIA_CHANGED }; 100 | event_post(&event); 101 | } 102 | } 103 | } 104 | } 105 | 106 | - (void)playing_change:(NSNotification *)notification { 107 | if (!g_media_events) return; 108 | MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_get_main_queue(), ^(BOOL playing) { 109 | self.playing = playing; 110 | [self media_change:notification]; 111 | }); 112 | } 113 | 114 | - (void)media_change:(NSNotification *)notification { 115 | if (!g_media_events) return; 116 | MRMediaRemoteGetNowPlayingApplicationDisplayName(0, dispatch_get_main_queue(), ^(CFStringRef name_nr) { 117 | if (!name_nr) return; 118 | 119 | CFStringRef name = CFStringCreateCopy(CFAllocatorGetDefault(), name_nr); 120 | @autoreleasepool { 121 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(NSDictionary* dict) { 122 | @autoreleasepool { 123 | if (dict && name) { 124 | NSString* app = (NSString*)name; 125 | NSString* artist = [dict objectForKey:kMRMediaRemoteNowPlayingInfoArtist]; 126 | NSString* title = [dict objectForKey:kMRMediaRemoteNowPlayingInfoTitle]; 127 | NSString* album = [dict objectForKey:kMRMediaRemoteNowPlayingInfoAlbum]; 128 | if (artist && title && album && name) { 129 | self.app = (char*)[app UTF8String]; 130 | self.artist = (char*)[artist UTF8String]; 131 | self.title = (char*)[title UTF8String]; 132 | self.album = (char*)[album UTF8String]; 133 | 134 | NSString* mime_type = [dict objectForKey:kMRMediaRemoteNowPlayingInfoArtworkMIMEType]; 135 | NSData* ns_data = [dict objectForKey:kMRMediaRemoteNowPlayingInfoArtworkData]; 136 | CGImageRef image = NULL; 137 | if (mime_type && ns_data) { 138 | CFDataRef data = CFDataCreate(NULL, 139 | [ns_data bytes], 140 | [ns_data length]); 141 | 142 | CGDataProviderRef provider 143 | = CGDataProviderCreateWithCFData(data); 144 | 145 | if (provider) { 146 | CGImageSourceRef source 147 | = CGImageSourceCreateWithDataProvider(provider, NULL); 148 | if (source) { 149 | image = CGImageSourceCreateImageAtIndex(source, 0, NULL); 150 | CFRelease(source); 151 | } 152 | CFRelease(provider); 153 | } 154 | CFRelease(data); 155 | } 156 | self.artwork = image; 157 | [self update]; 158 | } 159 | } 160 | } 161 | 162 | CFRelease(name); 163 | }); 164 | } 165 | }); 166 | } 167 | @end 168 | 169 | media_context* g_media_context = NULL; 170 | void initialize_media_events() { 171 | MRMediaRemoteRegisterForNowPlayingNotifications(dispatch_get_main_queue()); 172 | 173 | g_media_context = [media_context alloc]; 174 | [g_media_context init]; 175 | } 176 | 177 | void begin_receiving_media_events() { 178 | g_media_events = true; 179 | } 180 | 181 | void forced_media_change_event() { 182 | if (g_media_info) free(g_media_info); 183 | g_media_info = NULL; 184 | [g_media_context playing_change:NULL]; 185 | } 186 | -------------------------------------------------------------------------------- /src/message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "alias.h" 5 | #include "background.h" 6 | #include "bar_item.h" 7 | #include "bar_manager.h" 8 | #include "display.h" 9 | #include "group.h" 10 | #include "slider.h" 11 | #include "mach.h" 12 | #include "event.h" 13 | #include "misc/helpers.h" 14 | #include "misc/defines.h" 15 | 16 | 17 | MACH_HANDLER(mach_message_handler); 18 | void handle_message_mach(struct mach_buffer* buffer); 19 | -------------------------------------------------------------------------------- /src/misc/defines.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DOMAIN_ADD "--add" 4 | #define COMMAND_ADD_ITEM "item" 5 | #define COMMAND_ADD_COMPONENT "component" 6 | #define COMMAND_ADD_EVENT "event" 7 | 8 | #define DOMAIN_UPDATE "--update" 9 | 10 | #define DOMAIN_PUSH "--push" 11 | 12 | #define DOMAIN_TRIGGER "--trigger" 13 | 14 | #define DOMAIN_DEFAULT "--default" 15 | #define COMMAND_DEFAULT_RESET "reset" 16 | 17 | #define DOMAIN_CLONE "--clone" 18 | 19 | #define DOMAIN_RENAME "--rename" 20 | 21 | #define DOMAIN_REORDER "--reorder" 22 | 23 | #define DOMAIN_REMOVE "--remove" 24 | 25 | #define DOMAIN_MOVE "--move" 26 | 27 | #define DOMAIN_SET "--set" 28 | 29 | #define DOMAIN_ANIMATE "--animate" 30 | 31 | #define DOMAIN_EXIT "--exit" 32 | 33 | #define DOMAIN_HOTLOAD "--hotload" 34 | #define DOMAIN_RELOAD "--reload" 35 | #define DOMAIN_ADD_FONT "--load-font" 36 | 37 | #define SUB_DOMAIN_ICON "icon" 38 | #define SUB_DOMAIN_LABEL "label" 39 | #define SUB_DOMAIN_BACKGROUND "background" 40 | #define SUB_DOMAIN_GRAPH "graph" 41 | #define SUB_DOMAIN_ALIAS "alias" 42 | #define SUB_DOMAIN_POPUP "popup" 43 | #define SUB_DOMAIN_SHADOW "shadow" 44 | #define SUB_DOMAIN_IMAGE "image" 45 | #define SUB_DOMAIN_KNOB "knob" 46 | #define SUB_DOMAIN_SLIDER "slider" 47 | #define SUB_DOMAIN_FONT "font" 48 | #define SUB_DOMAIN_COLOR "color" 49 | #define SUB_DOMAIN_BORDER_COLOR "border_color" 50 | #define SUB_DOMAIN_HIGHLIGHT_COLOR "highlight_color" 51 | #define SUB_DOMAIN_FILL_COLOR "fill_color" 52 | 53 | #define PROPERTY_FONT "font" 54 | #define PROPERTY_COLOR "color" 55 | #define PROPERTY_HIGHLIGHT "highlight" 56 | #define PROPERTY_HIGHLIGHT_COLOR "highlight_color" 57 | #define PROPERTY_PADDING_LEFT "padding_left" 58 | #define PROPERTY_PADDING_RIGHT "padding_right" 59 | #define PROPERTY_HEIGHT "height" 60 | #define PROPERTY_BORDER_COLOR "border_color" 61 | #define PROPERTY_BORDER_WIDTH "border_width" 62 | #define PROPERTY_CORNER_RADIUS "corner_radius" 63 | #define PROPERTY_FILL_COLOR "fill_color" 64 | #define PROPERTY_LINE_WIDTH "line_width" 65 | #define PROPERTY_BLUR_RADIUS "blur_radius" 66 | #define PROPERTY_DRAWING "drawing" 67 | #define PROPERTY_CLIP "clip" 68 | #define PROPERTY_DISTANCE "distance" 69 | #define PROPERTY_ANGLE "angle" 70 | #define PROPERTY_SCALE "scale" 71 | #define PROPERTY_STRING "string" 72 | #define PROPERTY_SCROLL_TEXTS "scroll_texts" 73 | #define PROPERTY_SCROLL_DURATION "scroll_duration" 74 | 75 | #define PROPERTY_COLOR_HEX "hex" 76 | #define PROPERTY_COLOR_ALPHA "alpha" 77 | #define PROPERTY_COLOR_RED "red" 78 | #define PROPERTY_COLOR_GREEN "green" 79 | #define PROPERTY_COLOR_BLUE "blue" 80 | 81 | #define PROPERTY_FONT_FAMILY "family" 82 | #define PROPERTY_FONT_STYLE "style" 83 | #define PROPERTY_FONT_SIZE "size" 84 | #define PROPERTY_FONT_FEATURES "features" 85 | 86 | #define PROPERTY_UPDATES "updates" 87 | #define PROPERTY_POSITION "position" 88 | #define PROPERTY_ASSOCIATED_DISPLAY "associated_display" 89 | #define PROPERTY_ASSOCIATED_SPACE "associated_space" 90 | #define PROPERTY_UPDATE_FREQ "update_freq" 91 | #define PROPERTY_SCRIPT "script" 92 | #define PROPERTY_CLICK_SCRIPT "click_script" 93 | #define PROPERTY_ICON "icon" 94 | #define PROPERTY_YOFFSET "y_offset" 95 | #define PROPERTY_WIDTH "width" 96 | #define PROPERTY_LABEL "label" 97 | #define PROPERTY_CACHE_SCRIPTS "cache_scripts" 98 | #define PROPERTY_LAZY "lazy" 99 | #define PROPERTY_IGNORE_ASSOCIATION "ignore_association" 100 | #define PROPERTY_EVENT_PORT "mach_helper" 101 | #define PROPERTY_PERCENTAGE "percentage" 102 | #define PROPERTY_MAX_CHARS "max_chars" 103 | 104 | #define DOMAIN_BAR "--bar" 105 | #define PROPERTY_POSITION "position" 106 | #define PROPERTY_MARGIN "margin" 107 | #define PROPERTY_DISPLAY "display" 108 | #define PROPERTY_SPACE "space" 109 | #define PROPERTY_TOPMOST "topmost" 110 | #define PROPERTY_STICKY "sticky" 111 | #define PROPERTY_SHOW_IN_FULLSCREEN "show_in_fullscreen" 112 | #define PROPERTY_HIDDEN "hidden" 113 | #define PROPERTY_FONT_SMOOTHING "font_smoothing" 114 | #define PROPERTY_SHADOW "shadow" 115 | #define PROPERTY_ALIGN "align" 116 | #define PROPERTY_NOTCH_WIDTH "notch_width" 117 | #define PROPERTY_NOTCH_OFFSET "notch_offset" 118 | #define PROPERTY_NOTCH_DISPLAY_HEIGHT "notch_display_height" 119 | #define PROPERTY_HORIZONTAL "horizontal" 120 | 121 | #define DOMAIN_SUBSCRIBE "--subscribe" 122 | #define COMMAND_SUBSCRIBE_FRONT_APP_SWITCHED "front_app_switched" 123 | #define COMMAND_SUBSCRIBE_SPACE_CHANGE "space_change" 124 | #define COMMAND_SUBSCRIBE_DISPLAY_CHANGE "display_change" 125 | #define COMMAND_SUBSCRIBE_SYSTEM_WOKE "system_woke" 126 | #define COMMAND_SUBSCRIBE_SYSTEM_WILL_SLEEP "system_will_sleep" 127 | #define COMMAND_SUBSCRIBE_VOLUME_CHANGE "volume_change" 128 | #define COMMAND_SUBSCRIBE_WIFI_CHANGE "wifi_change" 129 | #define COMMAND_SUBSCRIBE_BRIGHTNESS_CHANGE "brightness_change" 130 | #define COMMAND_SUBSCRIBE_POWER_SOURCE_CHANGE "power_source_change" 131 | #define COMMAND_SUBSCRIBE_MEDIA_CHANGE "media_change" 132 | #define COMMAND_SUBSCRIBE_MOUSE_ENTERED "mouse.entered" 133 | #define COMMAND_SUBSCRIBE_MOUSE_EXITED "mouse.exited" 134 | #define COMMAND_SUBSCRIBE_MOUSE_CLICKED "mouse.clicked" 135 | #define COMMAND_SUBSCRIBE_MOUSE_SCROLLED "mouse.scrolled" 136 | #define COMMAND_SUBSCRIBE_MOUSE_ENTERED_GLOBAL "mouse.entered.global" 137 | #define COMMAND_SUBSCRIBE_MOUSE_EXITED_GLOBAL "mouse.exited.global" 138 | #define COMMAND_SUBSCRIBE_MOUSE_SCROLLED_GLOBAL "mouse.scrolled.global" 139 | #define COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE "space_windows_change" 140 | 141 | #define DOMAIN_QUERY "--query" 142 | #define COMMAND_QUERY_DEFAULT_ITEMS "default_menu_items" 143 | #define COMMAND_QUERY_ITEM "item" 144 | #define COMMAND_QUERY_DEFAULTS "defaults" 145 | #define COMMAND_QUERY_BAR "bar" 146 | #define COMMAND_QUERY_EVENTS "events" 147 | #define COMMAND_QUERY_DISPLAYS "displays" 148 | 149 | #define ARGUMENT_COMMON_VAL_ON "on" 150 | #define ARGUMENT_COMMON_VAL_NOT_OFF "!off" 151 | #define ARGUMENT_COMMON_VAL_TRUE "true" 152 | #define ARGUMENT_COMMON_VAL_NOT_FALSE "!false" 153 | #define ARGUMENT_COMMON_VAL_ONE "1" 154 | #define ARGUMENT_COMMON_VAL_NOT_ZERO "!0" 155 | #define ARGUMENT_COMMON_VAL_YES "yes" 156 | #define ARGUMENT_COMMON_VAL_NOT_NO "!no" 157 | #define ARGUMENT_COMMON_VAL_OFF "off" 158 | #define ARGUMENT_COMMON_VAL_NOT_ON "!on" 159 | #define ARGUMENT_COMMON_VAL_FALSE "false" 160 | #define ARGUMENT_COMMON_VAL_NOT_TRUE "!true" 161 | #define ARGUMENT_COMMON_VAL_ZERO "0" 162 | #define ARGUMENT_COMMON_VAL_NOT_ONE "!1" 163 | #define ARGUMENT_COMMON_VAL_NO "no" 164 | #define ARGUMENT_COMMON_VAL_NOT_YES "!yes" 165 | #define ARGUMENT_COMMON_VAL_TOGGLE "toggle" 166 | #define ARGUMENT_COMMON_VAL_BEFORE "before" 167 | #define ARGUMENT_COMMON_VAL_AFTER "after" 168 | 169 | #define ARGUMENT_DISPLAY_MAIN "main" 170 | #define ARGUMENT_DISPLAY_ALL "all" 171 | 172 | #define ARGUMENT_UPDATES_WHEN_SHOWN "when_shown" 173 | #define ARGUMENT_DYNAMIC "dynamic" 174 | 175 | #define ARGUMENT_WINDOW "window" 176 | 177 | #define POSITION_TOP 't' 178 | #define POSITION_BOTTOM 'b' 179 | #define POSITION_LEFT 'l' 180 | #define POSITION_RIGHT 'r' 181 | #define POSITION_CENTER 'c' 182 | #define POSITION_POPUP 'p' 183 | #define POSITION_CENTER_LEFT 'q' 184 | #define POSITION_CENTER_RIGHT 'e' 185 | 186 | #define TYPE_GRAPH "graph" 187 | #define TYPE_SPACE "space" 188 | #define TYPE_ALIAS "alias" 189 | #define TYPE_GROUP "bracket" 190 | #define TYPE_SLIDER "slider" 191 | #define TYPE_ITEM "item" 192 | 193 | #define REGEX_DELIMITER '/' 194 | -------------------------------------------------------------------------------- /src/misc/env_vars.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | struct key_value_pair { 7 | char* key; 8 | char* value; 9 | }; 10 | 11 | struct env_vars { 12 | uint32_t count; 13 | struct key_value_pair** vars; 14 | }; 15 | 16 | static inline void env_vars_init(struct env_vars* env_vars) { 17 | env_vars->vars = NULL; 18 | env_vars->count = 0; 19 | } 20 | 21 | static inline void env_vars_unset(struct env_vars* env_vars, char* key) { 22 | struct key_value_pair* key_value_pair = NULL; 23 | for (int i = 0; i < env_vars->count; i++) { 24 | if (strcmp(env_vars->vars[i]->key, key) == 0) 25 | key_value_pair = env_vars->vars[i]; 26 | } 27 | 28 | if (key_value_pair == NULL) return; 29 | 30 | if (env_vars->count == 1) { 31 | free(env_vars->vars); 32 | env_vars->vars = NULL; 33 | env_vars->count = 0; 34 | } else { 35 | struct key_value_pair* tmp[env_vars->count - 1]; 36 | int count = 0; 37 | for (int i = 0; i < env_vars->count; i++) { 38 | if (env_vars->vars[i] == key_value_pair) continue; 39 | tmp[count++] = env_vars->vars[i]; 40 | } 41 | env_vars->count--; 42 | env_vars->vars = realloc(env_vars->vars, 43 | sizeof(struct key_value_pair*)*env_vars->count); 44 | 45 | memcpy(env_vars->vars, 46 | tmp, 47 | sizeof(struct key_value_pair*)*env_vars->count); 48 | } 49 | 50 | if (key_value_pair->key) free(key_value_pair->key); 51 | if (key_value_pair->value) free(key_value_pair->value); 52 | free(key_value_pair); 53 | } 54 | 55 | static inline void env_vars_set(struct env_vars* env_vars, char* key, char* value) { 56 | env_vars_unset(env_vars, key); 57 | 58 | env_vars->count++; 59 | env_vars->vars = realloc(env_vars->vars, 60 | env_vars->count * sizeof(struct key_value_pair*)); 61 | 62 | env_vars->vars[env_vars->count - 1] = malloc(sizeof(struct key_value_pair)); 63 | env_vars->vars[env_vars->count - 1]->key = key; 64 | env_vars->vars[env_vars->count - 1]->value = value; 65 | } 66 | 67 | static inline char* env_vars_get_value_for_key(struct env_vars* env_vars, char* key) { 68 | for (int i = 0; i < env_vars->count; i++) { 69 | if (strcmp(env_vars->vars[i]->key, key) == 0) 70 | return env_vars->vars[i]->value; 71 | } 72 | return NULL; 73 | } 74 | 75 | static inline char* env_vars_copy_serialized_representation(struct env_vars* env_vars, uint32_t* len) { 76 | uint32_t length = 0; 77 | for (int i = 0; i < env_vars->count; i++) { 78 | length += env_vars->vars[i]->key 79 | ? strlen(env_vars->vars[i]->key) + 1 80 | : 1; 81 | 82 | length += env_vars->vars[i]->value 83 | ? strlen(env_vars->vars[i]->value) + 1 84 | : 1; 85 | } 86 | 87 | uint32_t caret = 0; 88 | char* seri = (char*)malloc(++length); 89 | for (int i = 0; i < env_vars->count; i++) { 90 | if (env_vars->vars[i]->key) { 91 | uint32_t len = strlen(env_vars->vars[i]->key) + 1; 92 | memcpy(seri + caret, 93 | env_vars->vars[i]->key, 94 | len ); 95 | 96 | caret += len; 97 | } else { 98 | seri[caret++] = '\0'; 99 | } 100 | 101 | if (env_vars->vars[i]->value) { 102 | uint32_t len = strlen(env_vars->vars[i]->value) + 1; 103 | memcpy(seri + caret, 104 | env_vars->vars[i]->value, 105 | len ); 106 | 107 | caret += len; 108 | } else { 109 | seri[caret++] = '\0'; 110 | } 111 | } 112 | seri[caret++] = '\0'; 113 | assert(caret == length); 114 | *len = length; 115 | return seri; 116 | } 117 | 118 | static inline void env_vars_destroy(struct env_vars* env_vars) { 119 | for (int i = 0; i < env_vars->count; i++) { 120 | if (env_vars->vars[i]->key) free(env_vars->vars[i]->key); 121 | if (env_vars->vars[i]->value) free(env_vars->vars[i]->value); 122 | free(env_vars->vars[i]); 123 | } 124 | free(env_vars->vars); 125 | } 126 | -------------------------------------------------------------------------------- /src/misc/extern.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern CGError DisplayServicesRegisterForBrightnessChangeNotifications(uint32_t did, uint32_t passthrough, void* callback); 4 | extern CGError DisplayServicesRegisterForAmbientLightCompensationNotifications(uint32_t did, uint32_t passthrough, void* callback); 5 | 6 | extern CGError DisplayServicesUnregisterForBrightnessChangeNotifications(uint32_t did, uint32_t passthrough); 7 | extern CGError DisplayServicesUnregisterForAmbientLightCompensationNotifications(uint32_t did, uint32_t passthrough); 8 | 9 | extern CGError DisplayServicesGetBrightness(uint32_t did, float* brightness); 10 | extern CGError DisplayServicesCanChangeBrightness(uint32_t did); 11 | extern CGError DisplayServicesAmbientLightCompensationEnabled(uint32_t did, bool* out); 12 | 13 | extern CFArrayRef SLSCopyManagedDisplaySpaces(int cid); 14 | extern uint32_t SLSGetActiveSpace(int cid); 15 | extern CFStringRef SLSCopyManagedDisplayForSpace(int cid, uint64_t sid); 16 | extern CFArrayRef SLSHWCaptureSpace(int64_t cid, int64_t sid, int64_t flags); 17 | 18 | extern CGError SLSGetWindowOwner(int cid, uint32_t wid, int* out_cid); 19 | extern CGError SLSConnectionGetPID(int cid, pid_t *pid); 20 | extern CFArrayRef SLSCopyWindowsWithOptionsAndTags(int cid, uint32_t owner, CFArrayRef spaces, uint32_t options, uint64_t *set_tags, uint64_t *clear_tags); 21 | extern CFTypeRef SLSWindowQueryWindows(int cid, CFArrayRef windows, uint32_t options); 22 | extern CFTypeRef SLSWindowQueryResultCopyWindows(CFTypeRef window_query); 23 | extern int SLSWindowIteratorGetCount(CFTypeRef iterator); 24 | extern bool SLSWindowIteratorAdvance(CFTypeRef iterator); 25 | extern uint32_t SLSWindowIteratorGetParentID(CFTypeRef iterator); 26 | extern uint32_t SLSWindowIteratorGetWindowID(CFTypeRef iterator); 27 | extern uint64_t SLSWindowIteratorGetTags(CFTypeRef iterator); 28 | extern uint64_t SLSWindowIteratorGetAttributes(CFTypeRef iterator); 29 | extern CGError SLSRegisterNotifyProc(void* callback, uint32_t event, void* context); 30 | extern CGError SLSRequestNotificationsForWindows(int cid, uint32_t* wid_list, uint32_t list_count); 31 | 32 | extern CFUUIDRef CGDisplayCreateUUIDFromDisplayID(uint32_t did); 33 | extern CFArrayRef SLSCopyManagedDisplays(int cid); 34 | extern uint64_t SLSManagedDisplayGetCurrentSpace(int cid, CFStringRef uuid); 35 | 36 | extern CFStringRef SLSCopyBestManagedDisplayForRect(int cid, CGRect rect); 37 | extern CGError SLSGetCurrentCursorLocation(int cid, CGPoint *point); 38 | extern CFStringRef SLSCopyActiveMenuBarDisplayIdentifier(int cid); 39 | extern CGError SLSGetMenuBarAutohideEnabled(int cid, int *enabled); 40 | extern CGError SLSGetRevealedMenuBarBounds(CGRect *rect, int cid, uint64_t sid); 41 | extern CFStringRef SLSCopyBestManagedDisplayForPoint(int cid, CGPoint point); 42 | extern CGError SLSSetMenuBarVisibilityOverrideOnDisplay(int cid, int did, bool override); 43 | extern CGError SLSSetMenuBarAutohideEnabled(int cid, bool enabled); 44 | extern CGError SLSFlushWindowContentRegion(int cid, uint32_t wid, void* dirty); 45 | extern CFTypeRef SLSTransactionCreate(int cid); 46 | extern CGError SLSTransactionOrderWindow(CFTypeRef transaction, uint32_t wid, int mode, uint32_t relativeToWID); 47 | extern CGError SLSTransactionSetWindowLevel(CFTypeRef transaction, uint32_t wid, int level); 48 | extern CGError SLSTransactionSetWindowShape(CFTypeRef transaction, uint32_t wid, float x_offset, float y_offset, CFTypeRef shape); 49 | extern CGError SLSTransactionMoveWindowWithGroup(CFTypeRef transaction, uint32_t wid, CGPoint point); 50 | extern CGError SLSTransactionCommitUsingMethod(CFTypeRef transaction, uint32_t method); 51 | extern CGError SLSTransactionCommit(CFTypeRef transaction, uint32_t async); 52 | 53 | extern CFTypeRef CGRegionCreateEmptyRegion(void); 54 | extern CGError SLSDisableUpdate(int cid); 55 | extern CGError SLSReenableUpdate(int cid); 56 | extern CGError SLSNewWindowWithOpaqueShapeAndContext(int cid, int type, CFTypeRef region, CFTypeRef opaque_shape, int options, uint64_t *tags, float x, float y, int tag_size, uint32_t *wid, void *context); 57 | extern CGError SLSNewWindow(int cid, int type, float x, float y, CFTypeRef region, uint64_t *wid); 58 | extern CGError SLSReleaseWindow(int cid, uint32_t wid); 59 | extern CGError SLSSetWindowTags(int cid, uint32_t wid, uint64_t* tags, int tag_size); 60 | extern CGError SLSClearWindowTags(int cid, uint32_t wid, uint64_t* tags, int tag_size); 61 | extern CGError SLSSetWindowShape(int cid, uint32_t wid, float x_offset, float y_offset, CFTypeRef shape); 62 | extern CGError SLSSetWindowOpaqueShape(int cid, uint32_t wid, float x_offset, float y_offset, CFTypeRef region); 63 | extern CGError SLSSetWindowResolution(int cid, uint32_t wid, double res); 64 | extern CGError SLSSetWindowOpacity(int cid, uint32_t wid, bool isOpaque); 65 | extern CGError SLSSetWindowAlpha(int cid, uint32_t wid, float alpha); 66 | extern CGError SLSSetWindowBackgroundBlurRadius(int cid, uint32_t wid, uint32_t radius); 67 | extern CGError SLSOrderWindow(int cid, uint32_t wid, int mode, uint32_t relativeToWID); 68 | extern CGError SLSSetWindowLevel(int cid, uint32_t wid, int level); 69 | extern CGContextRef SLWindowContextCreate(int cid, uint32_t wid, CFDictionaryRef options); 70 | extern CGError CGSNewRegionWithRect(CGRect *rect, CFTypeRef *outRegion); 71 | extern CGError SLSAddActivationRegion(uint32_t cid, uint32_t wid, CFTypeRef region); 72 | extern CGError SLSAddTrackingRect(uint32_t cid, uint32_t wid, CGRect rect); 73 | extern CGError SLSClearActivationRegion(uint32_t cid, uint32_t wid); 74 | extern CGError SLSRemoveAllTrackingAreas(uint32_t cid, uint32_t wid); 75 | extern CGError SLSMoveWindow(int cid, uint32_t wid, CGPoint* point); 76 | extern CGError SLSWindowSetShadowProperties(uint32_t wid, CFDictionaryRef properties); 77 | extern CGError SLSAddWindowToWindowOrderingGroup(int cid, uint32_t parent_wid, uint32_t child_wid, int order); 78 | extern CGError SLSRemoveFromOrderingGroup(int cid, uint32_t wid); 79 | extern CGError SLSReassociateWindowsSpacesByGeometry(int cid, CFArrayRef wids); 80 | extern CGError SLSMoveWindowsToManagedSpace(int cid, CFArrayRef window_list, uint64_t sid); 81 | extern CGError SLSMoveWindowWithGroup(int cid, uint32_t wid, CGPoint* point); 82 | 83 | extern void SLSCaptureWindowsContentsToRectWithOptions(uint32_t cid, uint64_t* wid, bool meh, CGRect bounds, uint32_t flags, CGImageRef* image); 84 | extern int SLSGetScreenRectForWindow(uint32_t cid, uint32_t wid, CGRect* out); 85 | 86 | extern int SLSSpaceGetType(int cid, uint64_t sid); 87 | 88 | extern CGError SLSAddSurface(int cid, uint32_t wid, uint32_t* outSID); 89 | extern CGError SLSRemoveSurface(int cid, uint32_t wid, uint32_t sid); 90 | extern CGError SLSBindSurface(int cid, uint32_t wid, uint32_t sid, int param1, int param2, unsigned int context_id); 91 | extern CGError SLSSetSurfaceBounds(int cid, uint32_t wid, uint32_t sid, CGRect bounds); 92 | extern CGError SLSSetSurfaceOpacity(int cid, uint32_t wid, uint32_t sid, bool opaque); 93 | extern CGError SLSOrderSurface(int cid, uint32_t wid, uint32_t surface, int mode, uint32_t other_surface); 94 | extern CGError SLSSetSurfaceResolution(int cid, uint32_t wid, uint32_t sid, CGFloat scale); 95 | extern CGError SLSFlushSurface(int cid, uint32_t wid, uint32_t surface, int param); 96 | extern CGError SLSSetSurfaceColorSpace(int cid, uint32_t wid, uint32_t surface, CGColorSpaceRef color_space); 97 | 98 | extern int SLSSpaceCreate(int cid, int one, int zero); 99 | extern CGError SLSSpaceSetAbsoluteLevel(int cid, int sid, int level); 100 | extern CGError SLSShowSpaces(int cid, CFArrayRef space_list); 101 | extern CGError SLSHideSpaces(int cid, CFArrayRef space_list); 102 | extern CGError SLSSpaceAddWindowsAndRemoveFromSpaces(int cid, int sid, CFArrayRef array, int seven); 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/misc/help.h: -------------------------------------------------------------------------------- 1 | static const char help_str[] = { 2 | "Usage: %s [options]\n\n" 3 | "Startup: \n" 4 | " -c, --config CONFIGFILE\tRead CONFIGFILE as the configuration file\n" 5 | " \tDefault CONFIGFILE is ~/.config/sketchybar/sketchybarrc\n\n" 6 | "Set global bar properties, see https://felixkratz.github.io/SketchyBar/config/bar\n" 7 | " --bar = ... =\n\n" 8 | "Items and their properties, see https://felixkratz.github.io/SketchyBar/config/items\n" 9 | " --add item \tAdd item to bar\n" 10 | " --set = ... =\n" 11 | " \tChange item properties\n" 12 | " --default = ... =\n" 13 | " \tChange default properties for new items\n" 14 | " --set popup.=\n" 15 | " \tConfigure item popup menu\n" 16 | " \tSee https://felixkratz.github.io/SketchyBar/config/popups\n" 17 | " --reorder ... \tReorder items\n" 18 | " --move before \n" 19 | " --move after \n" 20 | " \tMove item relative to reference item\n" 21 | " --clone [optional: before/after]\n" 22 | " \tClone parent to create new item\n" 23 | " --rename \tRename item\n" 24 | " --remove \tRemove item\n\n" 25 | "Special components, see https://felixkratz.github.io/SketchyBar/config/components\n" 26 | " --add graph \n" 27 | " \tAdd graph component\n" 28 | " --push ... \n" 29 | " \tPush data points to a graph\n" 30 | " --add space \tAdd space component\n" 31 | " --add bracket ... \n" 32 | " \tAdd bracket component\n" 33 | " --add alias \n" 34 | " \tAdd alias component\n" 35 | " --add slider \n" 36 | " \tAdd slider component\n\n" 37 | "Events and Scripting, see https://felixkratz.github.io/SketchyBar/config/events\n" 38 | " --subscribe ... \n" 39 | " \tSubscribe to events\n" 40 | " --add event [optional: ]\n" 41 | " \tCreate custom event\n" 42 | " --trigger [optional: = ... =]\n" 43 | " \tTrigger custom event\n\n" 44 | "Querying information, see https://felixkratz.github.io/SketchyBar/config/querying\n" 45 | " --query bar \tQuery bar properties\n" 46 | " --query \tQuery item properties\n" 47 | " --query defaults \tQuery default properties\n" 48 | " --query events \tQuery events\n" 49 | " --query default_menu_items\tQuery names of available items for aliases\n\n" 50 | "Animations, see https://felixkratz.github.io/SketchyBar/config/animations\n" 51 | " --animate \\\n" 52 | " --bar ... \\\n" 53 | " --set ... \n" 54 | " \tAnimate from given source to target property values\n\n" 55 | "Reloading the config\n" 56 | " --hotload \tEnable or disable the config hotloader\n" 57 | " --reload [optional: ]\tReload the current or the given config\n\n" 58 | }; 59 | -------------------------------------------------------------------------------- /src/mouse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mouse.h" 3 | 4 | static const EventTypeSpec mouse_events [] = { 5 | { kEventClassMouse, kEventMouseUp }, 6 | { kEventClassMouse, kEventMouseDragged }, 7 | { kEventClassMouse, kEventMouseEntered }, 8 | { kEventClassMouse, kEventMouseExited }, 9 | { kEventClassMouse, kEventMouseWheelMoved }, 10 | { kEventClassMouse, kEventMouseScroll } 11 | }; 12 | 13 | static int carbon_event_translation[] = { 14 | [kEventMouseUp] = MOUSE_UP, 15 | [kEventMouseDragged] = MOUSE_DRAGGED, 16 | [kEventMouseEntered] = MOUSE_ENTERED, 17 | [kEventMouseExited] = MOUSE_EXITED, 18 | [kEventMouseWheelMoved] = MOUSE_SCROLLED, 19 | [kEventMouseScroll] = MOUSE_SCROLLED 20 | }; 21 | 22 | static pascal OSStatus mouse_handler(EventHandlerCallRef next, EventRef e, void *data) { 23 | enum event_type event_type = carbon_event_translation[GetEventKind(e)]; 24 | 25 | CGEventRef cg_event = CopyEventCGEvent(e); 26 | struct event event = { (void *) cg_event, event_type }; 27 | event_post(&event); 28 | CFRelease(cg_event); 29 | 30 | return CallNextEventHandler(next, e); 31 | } 32 | 33 | void mouse_begin(void) { 34 | InstallEventHandler(GetEventDispatcherTarget(), 35 | NewEventHandlerUPP(mouse_handler), 36 | GetEventTypeCount(mouse_events), 37 | mouse_events, 0, 0); 38 | } 39 | -------------------------------------------------------------------------------- /src/mouse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "event.h" 3 | 4 | void mouse_begin(void); 5 | -------------------------------------------------------------------------------- /src/popup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "background.h" 3 | #include "misc/helpers.h" 4 | #include "window.h" 5 | 6 | struct bar_item; 7 | struct bar; 8 | 9 | struct popup { 10 | bool drawing; 11 | bool horizontal; 12 | bool overrides_cell_size; 13 | bool mouse_over; 14 | bool needs_ordering; 15 | bool topmost; 16 | 17 | char align; 18 | 19 | uint32_t adid; 20 | uint32_t cell_size; 21 | uint32_t blur_radius; 22 | int y_offset; 23 | 24 | CGPoint anchor; 25 | struct window window; 26 | 27 | struct bar_item* host; 28 | struct bar_item** items; 29 | uint32_t num_items; 30 | 31 | struct background background; 32 | }; 33 | 34 | void popup_init(struct popup* popup, struct bar_item* host); 35 | void popup_set_anchor(struct popup* popup, CGPoint anchor, uint32_t adid); 36 | void popup_add_item(struct popup* popup, struct bar_item* item); 37 | bool popup_set_drawing(struct popup* popup, bool drawing); 38 | void popup_remove_item(struct popup* popup, struct bar_item* bar_item); 39 | 40 | void popup_clear_pointers(struct popup* popup); 41 | 42 | uint32_t popup_get_width(struct popup* popup); 43 | void popup_calculate_bounds(struct popup* popup, struct bar* bar); 44 | void popup_draw(struct popup* popup); 45 | void popup_destroy(struct popup* popup); 46 | 47 | void popup_change_space(struct popup* popup, uint64_t dsid, uint32_t adid); 48 | void popup_serialize(struct popup* popup, char* indent, FILE* rsp); 49 | bool popup_parse_sub_domain(struct popup* popup, FILE* rsp, struct token property, char* message); 50 | -------------------------------------------------------------------------------- /src/power.c: -------------------------------------------------------------------------------- 1 | #include "power.h" 2 | #include "event.h" 3 | 4 | uint32_t g_power_source = 0; 5 | 6 | void power_handler(void* context) { 7 | CFTypeRef info = IOPSCopyPowerSourcesInfo(); 8 | CFStringRef type = IOPSGetProvidingPowerSourceType(info); 9 | 10 | if (CFStringCompare(type, POWER_AC_KEY, 0) == 0) { 11 | if (g_power_source != POWER_AC) { 12 | g_power_source = POWER_AC; 13 | char source[8]; 14 | snprintf(source, 8, "AC"); 15 | struct event event = { (void*) source, POWER_SOURCE_CHANGED }; 16 | event_post(&event); 17 | } 18 | } else if (CFStringCompare(type, POWER_BATTERY_KEY, 0) == 0) { 19 | if (g_power_source != POWER_BATTERY) { 20 | g_power_source = POWER_BATTERY; 21 | char source[8]; 22 | snprintf(source, 8, "BATTERY"); 23 | 24 | struct event event = { (void*) source, POWER_SOURCE_CHANGED }; 25 | event_post(&event); 26 | } 27 | } 28 | CFRelease(info); 29 | } 30 | 31 | void forced_power_event() { 32 | g_power_source = 0; 33 | power_handler(NULL); 34 | } 35 | 36 | void begin_receiving_power_events() { 37 | CFRunLoopSourceRef source = IOPSNotificationCreateRunLoopSource(power_handler, NULL); 38 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); 39 | } 40 | -------------------------------------------------------------------------------- /src/power.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define POWER_AC_KEY CFSTR(kIOPMACPowerKey) 5 | #define POWER_BATTERY_KEY CFSTR(kIOPMBatteryPowerKey) 6 | #define POWER_UPS_KEY CFSTR(kIOPMUPSPowerKey) 7 | 8 | #define POWER_AC 1 9 | #define POWER_BATTERY 2 10 | 11 | void forced_power_event(); 12 | void begin_receiving_power_events(); 13 | -------------------------------------------------------------------------------- /src/shadow.c: -------------------------------------------------------------------------------- 1 | #include "shadow.h" 2 | #include "bar_manager.h" 3 | 4 | void shadow_init(struct shadow* shadow) { 5 | shadow->enabled = false; 6 | shadow->angle = 30; 7 | shadow->distance = 5; 8 | shadow->offset.x = ((float)shadow->distance) 9 | *cos(((double)shadow->angle)*deg_to_rad); 10 | shadow->offset.y = -((float)shadow->distance) 11 | *sin(((double)shadow->angle)*deg_to_rad); 12 | 13 | color_init(&shadow->color, 0xff000000); 14 | } 15 | 16 | static bool shadow_set_enabled(struct shadow* shadow, bool enabled) { 17 | if (shadow->enabled == enabled) return false; 18 | shadow->enabled = enabled; 19 | return true; 20 | } 21 | 22 | static bool shadow_set_angle(struct shadow* shadow, uint32_t angle) { 23 | if (shadow->angle == angle) return false; 24 | shadow->angle = angle; 25 | shadow->offset.x = ((float)shadow->distance)*cos(((double)shadow->angle)*deg_to_rad); 26 | shadow->offset.y = -((float)shadow->distance)*sin(((double)shadow->angle)*deg_to_rad); 27 | return true; 28 | } 29 | 30 | static bool shadow_set_distance(struct shadow* shadow, uint32_t distance) { 31 | if (shadow->distance == distance) return false; 32 | shadow->distance = distance; 33 | shadow->offset.x = ((float)shadow->distance) 34 | *cos(((double)shadow->angle)*deg_to_rad); 35 | shadow->offset.y = -((float)shadow->distance) 36 | *sin(((double)shadow->angle)*deg_to_rad); 37 | return true; 38 | } 39 | 40 | static bool shadow_set_color(struct shadow* shadow, uint32_t color) { 41 | bool changed = shadow_set_enabled(shadow, true); 42 | return color_set_hex(&shadow->color, color) || changed; 43 | } 44 | 45 | CGRect shadow_get_bounds(struct shadow* shadow, CGRect reference_bounds) { 46 | return (CGRect){{reference_bounds.origin.x + shadow->offset.x, 47 | reference_bounds.origin.y + shadow->offset.y }, 48 | reference_bounds.size }; 49 | } 50 | 51 | void shadow_serialize(struct shadow* shadow, char* indent, FILE* rsp) { 52 | fprintf(rsp, "%s\"drawing\": \"%s\",\n" 53 | "%s\"color\": \"0x%x\",\n" 54 | "%s\"angle\": %u,\n" 55 | "%s\"distance\": %u", 56 | indent, format_bool(shadow->enabled), 57 | indent, shadow->color.hex, 58 | indent, shadow->angle, 59 | indent, shadow->distance ); 60 | } 61 | 62 | bool shadow_parse_sub_domain(struct shadow* shadow, FILE* rsp, struct token property, char* message) { 63 | bool needs_refresh = false; 64 | if (token_equals(property, PROPERTY_DRAWING)) { 65 | needs_refresh = shadow_set_enabled(shadow, 66 | evaluate_boolean_state(get_token(&message), 67 | shadow->enabled )); 68 | } 69 | else if (token_equals(property, PROPERTY_DISTANCE)) { 70 | struct token token = get_token(&message); 71 | ANIMATE(shadow_set_distance, 72 | shadow, 73 | shadow->distance, 74 | token_to_int(token) ); 75 | } 76 | else if (token_equals(property, PROPERTY_ANGLE)) { 77 | struct token token = get_token(&message); 78 | ANIMATE(shadow_set_angle, 79 | shadow, 80 | shadow->angle, 81 | token_to_int(token)); 82 | } 83 | else if (token_equals(property, PROPERTY_COLOR)) { 84 | struct token token = get_token(&message); 85 | ANIMATE_BYTES(shadow_set_color, 86 | shadow, 87 | shadow->color.hex, 88 | token_to_int(token) ); 89 | } 90 | else { 91 | struct key_value_pair key_value_pair = get_key_value_pair(property.text, 92 | '.' ); 93 | if (key_value_pair.key && key_value_pair.value) { 94 | struct token subdom = {key_value_pair.key,strlen(key_value_pair.key)}; 95 | struct token entry = {key_value_pair.value,strlen(key_value_pair.value)}; 96 | if (token_equals(subdom, SUB_DOMAIN_COLOR)) { 97 | return color_parse_sub_domain(&shadow->color, rsp, entry, message); 98 | } 99 | else { 100 | respond(rsp, "[!] Shadow: Invalid subdomain '%s'\n", subdom.text); 101 | } 102 | } else { 103 | respond(rsp, "[!] Shadow: Invalid property '%s'\n", property.text); 104 | } 105 | } 106 | 107 | return needs_refresh; 108 | } 109 | 110 | -------------------------------------------------------------------------------- /src/shadow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "misc/helpers.h" 3 | #include "color.h" 4 | 5 | struct shadow { 6 | bool enabled; 7 | 8 | uint32_t angle; 9 | uint32_t distance; 10 | CGPoint offset; 11 | 12 | struct color color; 13 | }; 14 | 15 | void shadow_init(struct shadow* shadow); 16 | CGRect shadow_get_bounds(struct shadow* shadow, CGRect reference_bounds); 17 | 18 | void shadow_serialize(struct shadow* shadow, char* indent, FILE* rsp); 19 | bool shadow_parse_sub_domain(struct shadow* shadow, FILE* rsp, struct token property, char* message); 20 | -------------------------------------------------------------------------------- /src/sketchybar.c: -------------------------------------------------------------------------------- 1 | #include "bar_manager.h" 2 | #include "event.h" 3 | #include "workspace.h" 4 | #include "mach.h" 5 | #include "mouse.h" 6 | #include "message.h" 7 | #include "power.h" 8 | #include "wifi.h" 9 | #include "misc/help.h" 10 | #include "media.h" 11 | #include "hotload.h" 12 | #include 13 | 14 | #define LCFILE_PATH_FMT "/tmp/%s_%s.lock" 15 | 16 | #define CLIENT_OPT_LONG "--message" 17 | #define CLIENT_OPT_SHRT "-m" 18 | 19 | #define VERSION_OPT_LONG "--version" 20 | #define VERSION_OPT_SHRT "-v" 21 | 22 | #define CONFIG_OPT_LONG "--config" 23 | #define CONFIG_OPT_SHRT "-c" 24 | 25 | #define HELP_OPT_LONG "--help" 26 | #define HELP_OPT_SHRT "-h" 27 | 28 | #define MAJOR 2 29 | #define MINOR 22 30 | #define PATCH 1 31 | 32 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 33 | extern CGError SLSWindowManagementBridgeSetDelegate(void* delegate); 34 | #endif 35 | 36 | extern CGError SLSRegisterNotifyProc(void* callback, uint32_t event, void* context); 37 | extern int SLSGetSpaceManagementMode(int cid); 38 | extern int SLSMainConnectionID(void); 39 | extern int RunApplicationEventLoop(void); 40 | 41 | int g_connection; 42 | CFTypeRef g_transaction; 43 | int g_space_management_mode; 44 | 45 | struct bar_manager g_bar_manager; 46 | struct mach_server g_mach_server; 47 | void *g_workspace_context; 48 | 49 | char g_name[256]; 50 | char g_config_file[4096]; 51 | char g_lock_file[MAXLEN]; 52 | bool g_volume_events; 53 | bool g_brightness_events; 54 | int64_t g_disable_capture = 0; 55 | pid_t g_pid = 0; 56 | 57 | static int client_send_message(int argc, char **argv) { 58 | if (argc <= 1) { 59 | return EXIT_SUCCESS; 60 | } 61 | 62 | char *user = getenv("USER"); 63 | if (!user) { 64 | error("sketchybar-msg: 'env USER' not set! abort..\n"); 65 | } 66 | 67 | int message_length = argc; 68 | int argl[argc]; 69 | 70 | for (int i = 1; i < argc; ++i) { 71 | argl[i] = strlen(argv[i]); 72 | message_length += argl[i] + 1; 73 | } 74 | 75 | char* message = malloc((sizeof(char) * (message_length + 1))); 76 | char* temp = message; 77 | 78 | for (int i = 1; i < argc; ++i) { 79 | memcpy(temp, argv[i], argl[i]); 80 | temp += argl[i]; 81 | *temp++ = '\0'; 82 | } 83 | *temp++ = '\0'; 84 | 85 | char bs_name[256]; 86 | snprintf(bs_name, 256, MACH_BS_NAME_FMT, g_name); 87 | 88 | char* rsp = mach_send_message(mach_get_bs_port(bs_name), 89 | message, 90 | message_length, 91 | true ); 92 | 93 | free(message); 94 | if (!rsp) return EXIT_SUCCESS; 95 | 96 | if (strlen(rsp) > 2 && rsp[1] == '!') { 97 | fprintf(stderr, "%s", rsp); 98 | return EXIT_FAILURE; 99 | } else { 100 | fprintf(stdout, "%s", rsp); 101 | } 102 | free(rsp); 103 | 104 | return EXIT_SUCCESS; 105 | } 106 | 107 | static void acquire_lockfile(void) { 108 | int handle = open(g_lock_file, O_CREAT | O_WRONLY, 0600); 109 | if (handle == -1) { 110 | error("%s: could not create lock-file! abort..\n", g_name); 111 | } 112 | 113 | struct flock lockfd = { 114 | .l_start = 0, 115 | .l_len = 0, 116 | .l_pid = getpid(), 117 | .l_type = F_WRLCK, 118 | .l_whence = SEEK_SET 119 | }; 120 | 121 | if (fcntl(handle, F_SETLK, &lockfd) == -1) { 122 | error("%s: could not acquire lock-file... already running?\n", g_name); 123 | } 124 | } 125 | 126 | #pragma clang diagnostic push 127 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 128 | static inline void init_misc_settings(void) { 129 | char *user = getenv("USER"); 130 | if (!user) { 131 | error("%s: 'env USER' not set! abort..\n", g_name); 132 | } 133 | 134 | snprintf(g_lock_file, sizeof(g_lock_file), LCFILE_PATH_FMT, g_name, user); 135 | 136 | signal(SIGCHLD, SIG_IGN); 137 | signal(SIGPIPE, SIG_IGN); 138 | CGSetLocalEventsSuppressionInterval(0.0f); 139 | CGEnableEventStateCombining(false); 140 | 141 | g_connection = SLSMainConnectionID(); 142 | g_space_management_mode = SLSGetSpaceManagementMode(g_connection); 143 | 144 | g_volume_events = false; 145 | g_brightness_events = false; 146 | } 147 | #pragma clang diagnostic pop 148 | 149 | static void parse_arguments(int argc, char **argv) { 150 | if ((string_equals(argv[1], VERSION_OPT_LONG)) 151 | || (string_equals(argv[1], VERSION_OPT_SHRT))) { 152 | fprintf(stdout, "sketchybar-v%d.%d.%d\n", MAJOR, MINOR, PATCH); 153 | exit(EXIT_SUCCESS); 154 | } else if ((string_equals(argv[1], HELP_OPT_LONG)) 155 | || (string_equals(argv[1], HELP_OPT_SHRT))) { 156 | printf(help_str, argv[0]); 157 | exit(EXIT_SUCCESS); 158 | } else if ((string_equals(argv[1], CLIENT_OPT_LONG)) 159 | || (string_equals(argv[1], CLIENT_OPT_SHRT))) { 160 | exit(client_send_message(argc-1, argv+1)); 161 | } else if ((string_equals(argv[1], CONFIG_OPT_LONG)) 162 | || (string_equals(argv[1], CONFIG_OPT_SHRT))) { 163 | if (argc < 3) { 164 | printf("[!] Error: Too few arguments for argument 'config'.\n"); 165 | } else { 166 | if (set_config_file_path(argv[2])) return; 167 | printf("[!] Error: Specified config file path invalid.\n"); 168 | } 169 | exit(EXIT_FAILURE); 170 | } 171 | 172 | exit(client_send_message(argc, argv)); 173 | } 174 | 175 | static void space_events(uint32_t event, void* data, size_t data_length, void* context) { 176 | struct event ev = { NULL, SPACE_CHANGED }; 177 | event_post(&ev); 178 | } 179 | 180 | static void system_events(uint32_t event, void* data, size_t data_length, void* context) { 181 | if (event == 1322) { 182 | g_disable_capture = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX); 183 | } else if (event == 905) { 184 | g_disable_capture = -1; 185 | } else { 186 | g_disable_capture = 0; 187 | } 188 | } 189 | 190 | int main(int argc, char **argv) { 191 | snprintf(g_name, sizeof(g_name), "%s", basename(argv[0])); 192 | 193 | if (is_root()) 194 | error("%s: running as root is not allowed! abort..\n", g_name); 195 | 196 | setenv("BAR_NAME", g_name, 1); 197 | 198 | if (argc > 1) parse_arguments(argc, argv); 199 | 200 | pid_for_task(mach_task_self(), &g_pid); 201 | init_misc_settings(); 202 | acquire_lockfile(); 203 | 204 | SLSRegisterNotifyProc((void*)system_events, 904, NULL); 205 | SLSRegisterNotifyProc((void*)system_events, 905, NULL); 206 | SLSRegisterNotifyProc((void*)system_events, 1401, NULL); 207 | SLSRegisterNotifyProc((void*)system_events, 1508, NULL); 208 | SLSRegisterNotifyProc((void*)system_events, 1322, NULL); 209 | 210 | if (__builtin_available(macOS 13.0, *)) { 211 | SLSRegisterNotifyProc((void*)space_events, 1327, NULL); 212 | SLSRegisterNotifyProc((void*)space_events, 1328, NULL); 213 | } 214 | 215 | struct event init = { NULL, INIT_MUTEX }; 216 | event_post(&init); 217 | 218 | workspace_event_handler_init(&g_workspace_context); 219 | bar_manager_init(&g_bar_manager); 220 | 221 | mouse_begin(); 222 | display_begin(); 223 | workspace_event_handler_begin(&g_workspace_context); 224 | 225 | windows_freeze(); 226 | bar_manager_begin(&g_bar_manager); 227 | windows_unfreeze(); 228 | 229 | if (!mach_server_begin(&g_mach_server, mach_message_handler)) 230 | error("%s: could not initialize daemon! abort..\n", g_name); 231 | 232 | begin_receiving_power_events(); 233 | begin_receiving_network_events(); 234 | initialize_media_events(); 235 | 236 | exec_config_file(); 237 | begin_receiving_config_change_events(); 238 | 239 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 240 | if (__builtin_available(macos 14.0, *)) { 241 | SLSWindowManagementBridgeSetDelegate(NULL); 242 | } 243 | #endif 244 | 245 | RunApplicationEventLoop(); 246 | return 0; 247 | } 248 | -------------------------------------------------------------------------------- /src/slider.c: -------------------------------------------------------------------------------- 1 | #include "slider.h" 2 | #include "bar_manager.h" 3 | #include "animation.h" 4 | 5 | static bool slider_set_width(struct slider* slider, uint32_t width) { 6 | if (width == slider->background.bounds.size.width) return false; 7 | slider->background.bounds.size.width = width; 8 | return true; 9 | } 10 | 11 | static bool slider_set_foreground_color(struct slider* slider, uint32_t color) { 12 | if (slider->foreground_color == color) return false; 13 | slider->foreground_color = color; 14 | return background_set_color(&slider->foreground, color); 15 | } 16 | 17 | static bool slider_set_percentage(struct slider* slider, uint32_t percentage) { 18 | if (percentage == slider->percentage) return false; 19 | slider->percentage = max(min(percentage, 100), 0); 20 | return true; 21 | } 22 | 23 | uint32_t slider_get_percentage_for_point(struct slider* slider, CGPoint point) { 24 | float delta = point.x - slider->background.bounds.origin.x; 25 | if (delta < 0) delta = 0; 26 | uint32_t percentage = delta / slider->background.bounds.size.width * 100.f 27 | + 0.5f; 28 | 29 | return min(percentage, 100); 30 | } 31 | 32 | void slider_cancel_drag(struct slider* slider) { 33 | slider->is_dragged = false; 34 | } 35 | 36 | bool slider_handle_drag(struct slider* slider, CGPoint point) { 37 | uint32_t percentage = slider_get_percentage_for_point(slider, point); 38 | slider->is_dragged = true; 39 | return slider_set_percentage(slider, percentage); 40 | } 41 | 42 | void slider_init(struct slider* slider) { 43 | slider->percentage = 0; 44 | slider->background.bounds.size.width = 100; 45 | slider->is_dragged = false; 46 | 47 | slider->foreground_color = 0xff0000ff; 48 | text_init(&slider->knob); 49 | background_init(&slider->background); 50 | background_init(&slider->foreground); 51 | background_set_color(&slider->background, 0xff000000); 52 | background_set_color(&slider->foreground, slider->foreground_color); 53 | } 54 | 55 | void slider_clear_pointers(struct slider* slider) { 56 | background_clear_pointers(&slider->background); 57 | background_clear_pointers(&slider->foreground); 58 | text_clear_pointers(&slider->knob); 59 | } 60 | 61 | void slider_setup(struct slider* slider, uint32_t width) { 62 | slider->background.bounds.size.width = width; 63 | background_set_enabled(&slider->background, true); 64 | background_set_enabled(&slider->foreground, true); 65 | } 66 | 67 | uint32_t slider_get_length(struct slider* slider) { 68 | return slider->background.bounds.size.width; 69 | } 70 | 71 | void slider_calculate_bounds(struct slider* slider, uint32_t x, uint32_t y) { 72 | background_calculate_bounds(&slider->background, 73 | x, 74 | y, 75 | slider->background.bounds.size.width, 76 | slider->background.bounds.size.height); 77 | 78 | background_calculate_bounds(&slider->foreground, 79 | x, 80 | y, 81 | slider->background.bounds.size.width 82 | * ((float)slider->percentage)/100.f, 83 | slider->background.bounds.size.height); 84 | 85 | int32_t raw_offset = ((float)slider->percentage)/100.f 86 | * slider->background.bounds.size.width 87 | - slider->knob.bounds.size.width / 2.; 88 | 89 | uint32_t knob_offset = max(min(raw_offset, 90 | slider->background.bounds.size.width 91 | - (slider->knob.bounds.size.width + 1.f)), 0.f); 92 | 93 | text_calculate_bounds(&slider->knob, x + knob_offset, y); 94 | } 95 | 96 | void slider_draw(struct slider* slider, CGContextRef context) { 97 | background_draw(&slider->background, context); 98 | background_draw(&slider->foreground, context); 99 | text_draw(&slider->knob, context); 100 | } 101 | 102 | void slider_destroy(struct slider* slider) { 103 | background_destroy(&slider->background); 104 | background_destroy(&slider->foreground); 105 | text_destroy(&slider->knob); 106 | slider_clear_pointers(slider); 107 | } 108 | 109 | void slider_serialize(struct slider* slider, char* indent, FILE* rsp) { 110 | fprintf(rsp, "%s\"highlight_color\": \"0x%x\",\n" 111 | "%s\"percentage\": \"%d\",\n" 112 | "%s\"width\": \"%d\",\n", 113 | indent, slider->foreground_color, 114 | indent, slider->percentage, 115 | indent, (int)slider->background.bounds.size.width); 116 | 117 | char deeper_indent[strlen(indent) + 2]; 118 | snprintf(deeper_indent, strlen(indent) + 2, "%s\t", indent); 119 | 120 | fprintf(rsp, "%s\"background\": {\n", indent); 121 | background_serialize(&slider->background, deeper_indent, rsp, false); 122 | fprintf(rsp, "\n%s},\n", indent); 123 | 124 | fprintf(rsp, "%s\"knob\": {\n", indent); 125 | text_serialize(&slider->knob, deeper_indent, rsp); 126 | fprintf(rsp, "\n%s}", indent); 127 | } 128 | 129 | bool slider_parse_sub_domain(struct slider* slider, FILE* rsp, struct token property, char* message) { 130 | bool needs_refresh = false; 131 | if (token_equals(property, PROPERTY_PERCENTAGE)) { 132 | struct token token = get_token(&message); 133 | if (!slider->is_dragged) { 134 | ANIMATE(slider_set_percentage, 135 | slider, 136 | slider->percentage, 137 | token_to_uint32t(token)); 138 | } 139 | } 140 | else if (token_equals(property, PROPERTY_HIGHLIGHT_COLOR)) { 141 | struct token token = get_token(&message); 142 | ANIMATE_BYTES(slider_set_foreground_color, 143 | slider, 144 | slider->foreground_color, 145 | token_to_uint32t(token) ); 146 | } 147 | else if (token_equals(property, PROPERTY_WIDTH)) { 148 | struct token token = get_token(&message); 149 | if (!slider->is_dragged) { 150 | ANIMATE(slider_set_width, 151 | slider, 152 | slider->background.bounds.size.width, 153 | token_to_uint32t(token) ); 154 | } 155 | } 156 | else if (token_equals(property, SUB_DOMAIN_KNOB)) { 157 | struct token dummy = { PROPERTY_STRING, strlen(PROPERTY_STRING)}; 158 | needs_refresh = text_parse_sub_domain(&slider->knob, 159 | rsp, 160 | dummy, 161 | message ); 162 | } 163 | else { 164 | struct key_value_pair key_value_pair = get_key_value_pair(property.text, '.'); 165 | if (key_value_pair.key && key_value_pair.value) { 166 | struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) }; 167 | struct token entry = { key_value_pair.value, strlen(key_value_pair.value) }; 168 | if (token_equals(subdom, SUB_DOMAIN_BACKGROUND)) { 169 | background_parse_sub_domain(&slider->foreground, rsp, entry, message); 170 | background_set_color(&slider->foreground, slider->foreground_color); 171 | return background_parse_sub_domain(&slider->background, rsp, entry, message); 172 | } 173 | else if (token_equals(subdom, SUB_DOMAIN_KNOB)) 174 | return text_parse_sub_domain(&slider->knob, rsp, entry, message); 175 | else { 176 | respond(rsp, "[!] Slider: Invalid subdomain '%s' \n", subdom.text); 177 | } 178 | } 179 | else { 180 | respond(rsp, "[!] Slider: Invalid property '%s'\n", property.text); 181 | } 182 | } 183 | 184 | return needs_refresh; 185 | } 186 | -------------------------------------------------------------------------------- /src/slider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "background.h" 4 | #include "text.h" 5 | 6 | struct slider { 7 | bool is_dragged; 8 | uint32_t percentage; 9 | uint32_t foreground_color; 10 | 11 | struct text knob; 12 | struct background background; 13 | struct background foreground; 14 | }; 15 | 16 | void slider_init(struct slider* slider); 17 | void slider_clear_pointers(struct slider* slider); 18 | void slider_setup(struct slider* slider, uint32_t width); 19 | void slider_calculate_bounds(struct slider* slider, uint32_t x, uint32_t y); 20 | void slider_draw(struct slider* slider, CGContextRef context); 21 | bool slider_handle_drag(struct slider* slider, CGPoint point); 22 | 23 | uint32_t slider_get_percentage_for_point(struct slider* slider, CGPoint point); 24 | uint32_t slider_get_length(struct slider* slider); 25 | 26 | void slider_cancel_drag(struct slider* slider); 27 | void slider_destroy(struct slider* slider); 28 | void slider_serialize(struct slider* slider, char* indent, FILE* rsp); 29 | bool slider_parse_sub_domain(struct slider* graph, FILE* rsp, struct token property, char* message); 30 | -------------------------------------------------------------------------------- /src/text.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "background.h" 4 | #include "font.h" 5 | 6 | struct text_line { 7 | CTLineRef line; 8 | CGFloat ascent; 9 | CGFloat descent; 10 | }; 11 | 12 | struct text { 13 | bool highlight; 14 | bool drawing; 15 | bool has_const_width; 16 | 17 | char align; 18 | char* string; 19 | 20 | int y_offset; 21 | int padding_left; 22 | int padding_right; 23 | uint32_t custom_width; 24 | uint32_t max_chars; 25 | uint32_t scroll_duration; 26 | float scroll; 27 | float width; 28 | 29 | CGRect bounds; 30 | 31 | struct font font; 32 | struct text_line line; 33 | struct color color; 34 | struct color highlight_color; 35 | struct shadow shadow; 36 | 37 | struct background background; 38 | }; 39 | 40 | void text_init(struct text* text); 41 | void text_clear_pointers(struct text* text); 42 | uint32_t text_get_length(struct text* text, bool override); 43 | uint32_t text_get_height(struct text* text); 44 | bool text_set_string(struct text* text, char* string, bool forced); 45 | bool text_set_font(struct text* text, char* font_string, bool forced); 46 | void text_copy(struct text* text, struct text* source); 47 | 48 | bool text_animate_scroll(struct text* text); 49 | void text_calculate_bounds(struct text* text, uint32_t x, uint32_t y); 50 | void text_draw(struct text* text, CGContextRef context); 51 | void text_destroy(struct text* text); 52 | 53 | void text_serialize(struct text* text, char* indent, FILE* rsp); 54 | bool text_parse_sub_domain(struct text* text, FILE* rsp, struct token property, char* message); 55 | -------------------------------------------------------------------------------- /src/volume.c: -------------------------------------------------------------------------------- 1 | #include "volume.h" 2 | #include "event.h" 3 | 4 | extern bool g_volume_events; 5 | 6 | #if __MAC_OS_X_VERSION_MAX_ALLOWED < 120000 7 | #define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster 8 | #endif 9 | 10 | static AudioObjectPropertyAddress kHardwareDevicePropertyAddress = { 11 | kAudioHardwarePropertyDefaultOutputDevice, 12 | kAudioObjectPropertyScopeGlobal, 13 | kAudioObjectPropertyElementMain }; 14 | 15 | static AudioObjectPropertyAddress kVolumeMainPropertyAddress = { 16 | kAudioDevicePropertyVolumeScalar, 17 | kAudioObjectPropertyScopeOutput, 18 | kAudioObjectPropertyElementMain }; 19 | 20 | static AudioObjectPropertyAddress kVolumeLeftPropertyAddress = { 21 | kAudioDevicePropertyVolumeScalar, 22 | kAudioObjectPropertyScopeOutput, 23 | 1 }; 24 | 25 | static AudioObjectPropertyAddress kMuteMainPropertyAddress = { 26 | kAudioDevicePropertyMute, 27 | kAudioObjectPropertyScopeOutput, 28 | kAudioObjectPropertyElementMain }; 29 | 30 | static AudioObjectPropertyAddress kMuteLeftPropertyAddress = { 31 | kAudioDevicePropertyMute, 32 | kAudioObjectPropertyScopeOutput, 33 | 1 }; 34 | 35 | static float g_last_volume = -1.f; 36 | static OSStatus handler(AudioObjectID id, uint32_t address_count, const AudioObjectPropertyAddress* addresses, void* context) { 37 | float v = 0; 38 | float* volume = &v; 39 | 40 | uint32_t muted_main = 0; 41 | uint32_t size = sizeof(uint32_t); 42 | 43 | AudioObjectGetPropertyData(id, 44 | &kMuteMainPropertyAddress, 45 | 0, 46 | NULL, 47 | &size, 48 | &muted_main ); 49 | 50 | uint32_t muted_left = 0; 51 | size = sizeof(uint32_t); 52 | AudioObjectGetPropertyData(id, 53 | &kMuteLeftPropertyAddress, 54 | 0, 55 | NULL, 56 | &size, 57 | &muted_left ); 58 | 59 | 60 | size = sizeof(float); 61 | float volume_main = 0.f; 62 | AudioObjectGetPropertyData(id, 63 | &kVolumeMainPropertyAddress, 64 | 0, 65 | NULL, 66 | &size, 67 | &volume_main ); 68 | 69 | size = sizeof(float); 70 | float volume_left = 0.f; 71 | AudioObjectGetPropertyData(id, 72 | &kVolumeLeftPropertyAddress, 73 | 0, 74 | NULL, 75 | &size, 76 | &volume_left ); 77 | 78 | if (volume_left > 0.f) { 79 | *volume = (muted_left || muted_main) ? 0.f : volume_left; 80 | } else { 81 | *volume = muted_main ? 0.f : volume_main; 82 | } 83 | 84 | if (*volume > g_last_volume + 1e-2 || *volume < g_last_volume - 1e-2) { 85 | g_last_volume = *volume; 86 | struct event event = { (void*) volume, VOLUME_CHANGED }; 87 | event_post(&event); 88 | } 89 | return KERN_SUCCESS; 90 | } 91 | 92 | static AudioObjectID g_audio_id = 0; 93 | OSStatus device_changed(AudioObjectID id, uint32_t address_count, const AudioObjectPropertyAddress* addresses, void* context) { 94 | AudioObjectID new_id = 0; 95 | uint32_t size = sizeof(AudioObjectID); 96 | AudioObjectGetPropertyData(kAudioObjectSystemObject, 97 | &kHardwareDevicePropertyAddress, 98 | 0, 99 | NULL, 100 | &size, 101 | &new_id ); 102 | 103 | if (g_audio_id) { 104 | AudioObjectRemovePropertyListener(g_audio_id, 105 | &kMuteMainPropertyAddress, 106 | &handler, 107 | NULL ); 108 | 109 | AudioObjectRemovePropertyListener(g_audio_id, 110 | &kMuteLeftPropertyAddress, 111 | &handler, 112 | NULL ); 113 | 114 | AudioObjectRemovePropertyListener(g_audio_id, 115 | &kVolumeMainPropertyAddress, 116 | &handler, 117 | NULL ); 118 | 119 | AudioObjectRemovePropertyListener(g_audio_id, 120 | &kVolumeLeftPropertyAddress, 121 | &handler, 122 | NULL ); 123 | } 124 | 125 | AudioObjectAddPropertyListener(new_id, 126 | &kMuteMainPropertyAddress, 127 | &handler, 128 | NULL ); 129 | 130 | AudioObjectAddPropertyListener(new_id, 131 | &kMuteLeftPropertyAddress, 132 | &handler, 133 | NULL ); 134 | 135 | AudioObjectAddPropertyListener(new_id, 136 | &kVolumeMainPropertyAddress, 137 | &handler, 138 | NULL ); 139 | 140 | AudioObjectAddPropertyListener(new_id, 141 | &kVolumeLeftPropertyAddress, 142 | &handler, 143 | NULL ); 144 | g_last_volume = -1.f; 145 | g_audio_id = new_id; 146 | handler(g_audio_id, address_count, addresses, context); 147 | return KERN_SUCCESS; 148 | } 149 | 150 | void forced_volume_event() { 151 | g_last_volume = -1.f; 152 | handler(g_audio_id, 0, 0, 0); 153 | } 154 | 155 | void begin_receiving_volume_events() { 156 | if (g_volume_events) return; 157 | g_volume_events = true; 158 | 159 | AudioObjectID id = 0; 160 | uint32_t size = sizeof(AudioObjectID); 161 | AudioObjectGetPropertyData(kAudioObjectSystemObject, 162 | &kHardwareDevicePropertyAddress, 163 | 0, 164 | NULL, 165 | &size, 166 | &id ); 167 | 168 | g_audio_id = id; 169 | AudioObjectAddPropertyListener(id, 170 | &kMuteLeftPropertyAddress, 171 | &handler, 172 | NULL ); 173 | 174 | AudioObjectAddPropertyListener(id, 175 | &kMuteMainPropertyAddress, 176 | &handler, 177 | NULL ); 178 | 179 | AudioObjectAddPropertyListener(id, 180 | &kVolumeLeftPropertyAddress, 181 | &handler, 182 | NULL ); 183 | 184 | AudioObjectAddPropertyListener(id, 185 | &kVolumeMainPropertyAddress, 186 | &handler, 187 | NULL ); 188 | 189 | AudioObjectAddPropertyListener(kAudioObjectSystemObject, 190 | &kHardwareDevicePropertyAddress, 191 | &device_changed, 192 | NULL ); 193 | } 194 | -------------------------------------------------------------------------------- /src/volume.h: -------------------------------------------------------------------------------- 1 | #include "CoreAudio/CoreAudio.h" 2 | #include 3 | 4 | void forced_volume_event(); 5 | void begin_receiving_volume_events(); 6 | -------------------------------------------------------------------------------- /src/wifi.h: -------------------------------------------------------------------------------- 1 | void forced_network_event(); 2 | void begin_receiving_network_events(); 3 | -------------------------------------------------------------------------------- /src/wifi.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "wifi.h" 4 | #include "event.h" 5 | 6 | void update_ssid(SCDynamicStoreRef store, CFArrayRef keys, void* info) { 7 | @autoreleasepool { 8 | NSData* data = [[[CWWiFiClient sharedWiFiClient] interface] ssidData]; 9 | char ssid[[data length] + 1]; 10 | memcpy(ssid, [data bytes], [data length]); 11 | ssid[[data length]] = '\0'; 12 | 13 | struct event event = { (void*) ssid, WIFI_CHANGED }; 14 | event_post(&event); 15 | } 16 | } 17 | 18 | void forced_network_event() { 19 | update_ssid(NULL, NULL, NULL); 20 | } 21 | 22 | void begin_receiving_network_events() { 23 | SCDynamicStoreContext context = { 0, NULL, NULL, NULL, NULL }; 24 | SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("network"), 25 | update_ssid, 26 | &context ); 27 | 28 | const void* values[] = { CFSTR(".*/Network/Global/IPv4") }; 29 | CFArrayRef keys = CFArrayCreate(NULL, values, 1, &kCFTypeArrayCallBacks); 30 | SCDynamicStoreSetNotificationKeys(store, NULL, keys); 31 | CFRunLoopSourceRef loop_source = SCDynamicStoreCreateRunLoopSource(NULL, 32 | store, 33 | 0 ); 34 | 35 | CFRunLoopAddSource(CFRunLoopGetCurrent(), 36 | loop_source, 37 | kCFRunLoopDefaultMode ); 38 | CFRelease(keys); 39 | } 40 | -------------------------------------------------------------------------------- /src/window.c: -------------------------------------------------------------------------------- 1 | #include "window.h" 2 | #include "bar_manager.h" 3 | 4 | extern struct bar_manager g_bar_manager; 5 | extern int64_t g_disable_capture; 6 | int g_space = 0; 7 | 8 | void window_init(struct window* window) { 9 | window->context = NULL; 10 | window->parent = NULL; 11 | window->frame = CGRectNull; 12 | window->id = 0; 13 | window->origin = CGPointZero; 14 | window->surface_id = 0; 15 | window->needs_move = false; 16 | window->needs_resize = false; 17 | window->order_mode = W_ABOVE; 18 | } 19 | 20 | static CFTypeRef window_create_region(struct window* window, CGRect frame) { 21 | CFTypeRef frame_region; 22 | CGSNewRegionWithRect(&frame, &frame_region); 23 | return frame_region; 24 | } 25 | 26 | void window_create(struct window* window, CGRect frame) { 27 | uint64_t set_tags = kCGSExposeFadeTagBit | kCGSPreventsActivationTagBit; 28 | uint64_t clear_tags = 0; 29 | 30 | window->origin = frame.origin; 31 | window->frame.origin = CGPointZero; 32 | window->frame.size = frame.size; 33 | frame.origin = CGPointZero; 34 | 35 | uint32_t id; 36 | CFTypeRef frame_region = window_create_region(window, frame); 37 | CFTypeRef empty_region = CGRegionCreateEmptyRegion(); 38 | SLSNewWindowWithOpaqueShapeAndContext(g_connection, 39 | kCGBackingStoreBuffered, 40 | frame_region, 41 | empty_region, 42 | 13 | (1 << 18), 43 | &set_tags, 44 | window->origin.x, 45 | window->origin.y, 46 | 64, 47 | &id, 48 | NULL ); 49 | CFRelease(empty_region); 50 | CFRelease(frame_region); 51 | 52 | window->id = id; 53 | 54 | SLSSetWindowResolution(g_connection, window->id, 2.0f); 55 | SLSSetWindowTags(g_connection, window->id, &set_tags, 64); 56 | SLSClearWindowTags(g_connection, window->id, &clear_tags, 64); 57 | SLSSetWindowOpacity(g_connection, window->id, 0); 58 | 59 | window->context = SLWindowContextCreate(g_connection, window->id, NULL); 60 | 61 | CGContextSetInterpolationQuality(window->context, kCGInterpolationNone); 62 | window->needs_move = false; 63 | window->needs_resize = false; 64 | 65 | 66 | if (g_bar_manager.sticky) { 67 | if (!g_space) { 68 | g_space = SLSSpaceCreate(g_connection, 1, 0); 69 | SLSSpaceSetAbsoluteLevel(g_connection, g_space, 0); 70 | 71 | CFArrayRef space_list = cfarray_of_cfnumbers(&g_space, 72 | sizeof(uint32_t), 73 | 1, 74 | kCFNumberSInt32Type); 75 | SLSShowSpaces(g_connection, space_list); 76 | CFRelease(space_list); 77 | } 78 | 79 | CFArrayRef window_list = cfarray_of_cfnumbers(&window->id, 80 | sizeof(uint32_t), 81 | 1, 82 | kCFNumberSInt32Type); 83 | 84 | SLSSpaceAddWindowsAndRemoveFromSpaces(g_connection, 85 | g_space, 86 | window_list, 87 | 0x7 ); 88 | 89 | CFRelease(window_list); 90 | } 91 | } 92 | 93 | void window_clear(struct window* window) { 94 | window->context = NULL; 95 | window->parent = NULL; 96 | window->id = 0; 97 | window->origin = CGPointZero; 98 | window->frame = CGRectNull; 99 | window->needs_move = false; 100 | window->needs_resize = false; 101 | } 102 | 103 | void window_flush(struct window* window) { 104 | SLSFlushWindowContentRegion(g_connection, window->id, NULL); 105 | } 106 | 107 | void windows_freeze() { 108 | if (g_transaction) return; 109 | 110 | SLSDisableUpdate(g_connection); 111 | g_transaction = SLSTransactionCreate(g_connection); 112 | } 113 | 114 | void windows_unfreeze() { 115 | if (g_transaction) { 116 | SLSTransactionCommit(g_transaction, 0); 117 | CFRelease(g_transaction); 118 | g_transaction = NULL; 119 | SLSReenableUpdate(g_connection); 120 | } 121 | } 122 | 123 | void window_set_frame(struct window* window, CGRect frame) { 124 | if (window->needs_move 125 | || !CGPointEqualToPoint(window->origin, frame.origin)) { 126 | window->needs_move = true; 127 | window->origin = frame.origin; 128 | } 129 | 130 | if (window->needs_resize 131 | || !CGSizeEqualToSize(window->frame.size, frame.size)) { 132 | window->needs_resize = true; 133 | window->frame.size = frame.size; 134 | } 135 | } 136 | 137 | void window_move(struct window* window, CGPoint point) { 138 | window->origin = point; 139 | 140 | if (__builtin_available(macOS 12.0, *)) { 141 | // Monterey and later 142 | windows_freeze(); 143 | SLSTransactionMoveWindowWithGroup(g_transaction, window->id, point); 144 | } else { 145 | // Big Sur and previous 146 | SLSMoveWindow(g_connection, window->id, &point); 147 | CFNumberRef number = CFNumberCreate(NULL, 148 | kCFNumberSInt32Type, 149 | &window->id ); 150 | 151 | const void* values[1] = { number }; 152 | CFArrayRef array = CFArrayCreate(NULL, values , 1, &kCFTypeArrayCallBacks); 153 | SLSReassociateWindowsSpacesByGeometry(g_connection, array); 154 | CFRelease(array); 155 | CFRelease(number); 156 | } 157 | } 158 | 159 | bool window_apply_frame(struct window* window, bool forced) { 160 | windows_freeze(); 161 | if (window->needs_resize || forced) { 162 | CFTypeRef frame_region = window_create_region(window, window->frame); 163 | 164 | if (__builtin_available(macOS 13.0, *)) { 165 | // Ventura and later 166 | SLSSetWindowShape(g_connection, window->id, 167 | g_nirvana.x, 168 | g_nirvana.y, 169 | frame_region); 170 | } else { 171 | // Monterey and previous 172 | if (window->parent) { 173 | SLSOrderWindow(g_connection, window->id, 0, window->parent->id); 174 | } 175 | 176 | SLSSetWindowShape(g_connection, window->id, 0, 0, frame_region); 177 | 178 | if (window->parent) { 179 | CGContextClearRect(window->context, window->frame); 180 | CGContextFlush(window->context); 181 | window_order(window, window->parent, window->order_mode); 182 | } 183 | } 184 | 185 | CFRelease(frame_region); 186 | window_move(window, window->origin); 187 | 188 | window->needs_move = false; 189 | window->needs_resize = false; 190 | return true; 191 | } else if (window->needs_move) { 192 | window_move(window, window->origin); 193 | window->needs_move = false; 194 | return false; 195 | } 196 | return false; 197 | } 198 | 199 | void window_send_to_space(struct window* window, uint64_t dsid) { 200 | CFArrayRef window_list = cfarray_of_cfnumbers(&window->id, 201 | sizeof(uint32_t), 202 | 1, 203 | kCFNumberSInt32Type); 204 | 205 | SLSMoveWindowsToManagedSpace(g_connection, window_list, dsid); 206 | if (CGPointEqualToPoint(window->origin, g_nirvana)) { 207 | SLSMoveWindow(g_connection, window->id, &g_nirvana); 208 | } 209 | CFRelease(window_list); 210 | } 211 | 212 | void window_close(struct window* window) { 213 | if (!window->id) return; 214 | windows_unfreeze(); 215 | 216 | SLSOrderWindow(g_connection, window->id, 0, 0); 217 | CGContextRelease(window->context); 218 | SLSReleaseWindow(g_connection, window->id); 219 | 220 | window_clear(window); 221 | } 222 | 223 | void window_set_level(struct window* window, uint32_t level) { 224 | windows_freeze(); 225 | 226 | if (__builtin_available(macOS 14.0, *)) { 227 | // Sonoma and later 228 | SLSTransactionSetWindowLevel(g_transaction, window->id, level); 229 | } else { 230 | // Ventura and previous 231 | SLSSetWindowLevel(g_connection, window->id, level); 232 | } 233 | } 234 | 235 | void window_order(struct window* window, struct window* parent, int mode) { 236 | windows_freeze(); 237 | window->parent = parent; 238 | if (mode != W_OUT) window->order_mode = mode; 239 | 240 | if (__builtin_available(macOS 14.0, *)) { 241 | // Sonoma and later 242 | SLSTransactionOrderWindow(g_transaction, 243 | window->id, 244 | mode, 245 | parent ? parent->id : 0); 246 | } else { 247 | // Ventura and previous 248 | SLSOrderWindow(g_connection, window->id, mode, parent ? parent->id : 0); 249 | } 250 | } 251 | 252 | void window_assign_mouse_tracking_area(struct window* window, CGRect rect) { 253 | SLSRemoveAllTrackingAreas(g_connection, window->id); 254 | SLSAddTrackingRect(g_connection, window->id, rect); 255 | } 256 | 257 | void window_set_blur_radius(struct window* window, uint32_t blur_radius) { 258 | SLSSetWindowBackgroundBlurRadius(g_connection, window->id, blur_radius); 259 | } 260 | 261 | void context_set_font_smoothing(CGContextRef context, bool smoothing) { 262 | CGContextSetAllowsFontSmoothing(context, smoothing); 263 | } 264 | 265 | void window_disable_shadow(struct window* window) { 266 | CFIndex shadow_density = 0; 267 | CFNumberRef shadow_density_cf = CFNumberCreate(kCFAllocatorDefault, 268 | kCFNumberCFIndexType, 269 | &shadow_density ); 270 | 271 | const void *keys[1] = { CFSTR("com.apple.WindowShadowDensity") }; 272 | const void *values[1] = { shadow_density_cf }; 273 | CFDictionaryRef shadow_props_cf = CFDictionaryCreate(NULL, 274 | keys, 275 | values, 276 | 1, 277 | &kCFTypeDictionaryKeyCallBacks, 278 | &kCFTypeDictionaryValueCallBacks); 279 | 280 | SLSWindowSetShadowProperties(window->id, shadow_props_cf); 281 | CFRelease(shadow_density_cf); 282 | CFRelease(shadow_props_cf); 283 | } 284 | 285 | CGImageRef window_capture(struct window* window, bool* disabled) { 286 | if (g_disable_capture) { 287 | int64_t time = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX); 288 | if (g_disable_capture < 0) { 289 | *disabled = true; 290 | return NULL; 291 | } else if (time - g_disable_capture > (1ULL << 30)) { 292 | g_disable_capture = 0; 293 | } else { 294 | *disabled = true; 295 | return NULL; 296 | } 297 | } 298 | 299 | *disabled = false; 300 | CGImageRef image_ref = NULL; 301 | 302 | uint64_t wid = window->id; 303 | SLSCaptureWindowsContentsToRectWithOptions(g_connection, 304 | &wid, 305 | true, 306 | CGRectNull, 307 | 1 << 8, 308 | &image_ref ); 309 | 310 | CGRect bounds; 311 | SLSGetScreenRectForWindow(g_connection, wid, &bounds); 312 | bounds.size.width = (uint32_t) (bounds.size.width + 0.5); 313 | window->frame.size = bounds.size; 314 | 315 | return image_ref; 316 | } 317 | -------------------------------------------------------------------------------- /src/window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "misc/helpers.h" 3 | 4 | #define kCGSExposeFadeTagBit (1ULL << 1) 5 | #define kCGSPreventsActivationTagBit (1ULL << 16) 6 | 7 | #define W_ABOVE 1 8 | #define W_OUT 0 9 | #define W_BELOW -1 10 | 11 | extern CFTypeRef g_transaction; 12 | 13 | struct window { 14 | struct window* parent; 15 | int order_mode; 16 | bool needs_move; 17 | bool needs_resize; 18 | 19 | uint32_t id; 20 | uint32_t surface_id; 21 | 22 | CGRect frame; 23 | CGPoint origin; 24 | CGContextRef context; 25 | }; 26 | 27 | void window_init(struct window* window); 28 | void window_create(struct window* window, CGRect frame); 29 | void window_close(struct window* window); 30 | void window_clear(struct window* window); 31 | void window_flush(struct window* window); 32 | 33 | void window_move(struct window* window, CGPoint point); 34 | void window_set_frame(struct window* window, CGRect frame); 35 | bool window_apply_frame(struct window* window, bool forced); 36 | void window_send_to_space(struct window* window, uint64_t dsid); 37 | 38 | void window_set_blur_radius(struct window* window, uint32_t blur_radius); 39 | void window_disable_shadow(struct window* window); 40 | void window_set_level(struct window* window, uint32_t level); 41 | void window_order(struct window* window, struct window* parent, int mode); 42 | void window_assign_mouse_tracking_area(struct window* window, CGRect rect); 43 | 44 | CGImageRef window_capture(struct window* window, bool* disabled); 45 | 46 | void context_set_font_smoothing(CGContextRef context, bool smoothing); 47 | 48 | void windows_freeze(); 49 | void windows_unfreeze(); 50 | -------------------------------------------------------------------------------- /src/workspace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "event.h" 3 | 4 | void workspace_create_custom_observer (void **context, char* notification); 5 | void workspace_event_handler_init(void **context); 6 | void workspace_event_handler_begin(void **context); 7 | void workspace_event_handler_end(void *context); 8 | int workspace_display_notch_height(uint32_t did); 9 | float workspace_get_scale(); 10 | 11 | CGImageRef workspace_icon_for_app(char* app); 12 | char* workspace_copy_app_name_for_pid(pid_t pid); 13 | -------------------------------------------------------------------------------- /src/workspace.m: -------------------------------------------------------------------------------- 1 | #include "workspace.h" 2 | #include "misc/helpers.h" 3 | 4 | #include 5 | @interface workspace_context : NSObject { 6 | } 7 | - (id)init; 8 | - (void)addCustomObserver:(NSString *)name; 9 | @end 10 | 11 | float workspace_get_scale() { 12 | @autoreleasepool { 13 | float scale = 1.f; 14 | NSArray* screens = [NSScreen screens]; 15 | for (int i = 0; i < [screens count]; i++) { 16 | NSScreen* screen = screens[i]; 17 | float screen_scale = [screen backingScaleFactor]; 18 | if (screen_scale > scale) scale = screen_scale; 19 | } 20 | return scale; 21 | } 22 | } 23 | 24 | void workspace_event_handler_init(void **context) { 25 | workspace_context *ws_context = [workspace_context alloc]; 26 | *context = ws_context; 27 | } 28 | 29 | void workspace_event_handler_begin(void **context) { 30 | workspace_context *ws_context = *context; 31 | [ws_context init]; 32 | } 33 | 34 | void workspace_event_handler_end(void *context) { 35 | workspace_context *ws_context = (workspace_context *) context; 36 | [ws_context dealloc]; 37 | } 38 | 39 | void workspace_create_custom_observer (void **context, char* notification) { 40 | workspace_context *ws_context = *context; 41 | [ws_context addCustomObserver:@(notification)]; 42 | } 43 | 44 | int workspace_display_notch_height(uint32_t did) 45 | { 46 | if (!CGDisplayIsBuiltin(did)) return 0; 47 | 48 | int height = 0; 49 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000 50 | if (__builtin_available(macos 12.0, *)) { 51 | @autoreleasepool { 52 | for (NSScreen *screen in [NSScreen screens]) { 53 | if ([[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue] == did) { 54 | height = screen.safeAreaInsets.top; 55 | } 56 | } 57 | } 58 | } 59 | #endif 60 | 61 | return height; 62 | } 63 | 64 | void forced_front_app_event() { 65 | @autoreleasepool { 66 | NSString* name = [[[NSWorkspace sharedWorkspace] frontmostApplication] localizedName]; 67 | if (name) { 68 | const char* front_app = [name cStringUsingEncoding:NSUTF8StringEncoding]; 69 | 70 | if (front_app) { 71 | struct event event = { string_copy((char*)front_app), 72 | APPLICATION_FRONT_SWITCHED }; 73 | event_post(&event); 74 | } 75 | } 76 | } 77 | } 78 | 79 | char* workspace_copy_app_name_for_pid(pid_t pid) { 80 | @autoreleasepool { 81 | NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; 82 | const char* result = [[app localizedName] UTF8String]; 83 | return result ? string_copy((char*)result) : NULL; 84 | } 85 | } 86 | 87 | CGImageRef workspace_icon_for_app(char* app) { 88 | @autoreleasepool { 89 | NSString* ns_app = [NSString stringWithUTF8String:app]; 90 | NSURL* path = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:ns_app]; 91 | if (!path) { 92 | bool recovered = false; 93 | NSArray* running_apps = [[NSWorkspace sharedWorkspace] runningApplications]; 94 | for (NSRunningApplication* app in running_apps) { 95 | if ([[app localizedName] isEqualToString:ns_app]) { 96 | ns_app = [app bundleIdentifier]; 97 | if (ns_app) { 98 | path = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:ns_app]; 99 | recovered = true; 100 | } 101 | break; 102 | } 103 | } 104 | if (!recovered) return NULL; 105 | } 106 | 107 | NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile:path.path]; 108 | if (!image) return NULL; 109 | 110 | float scale = workspace_get_scale(); 111 | NSRect rect = NSMakeRect( 0, 0, 32 * scale, 32 * scale); 112 | return (CGImageRef)CFRetain([image CGImageForProposedRect: &rect 113 | context: NULL 114 | hints: NULL]); 115 | } 116 | } 117 | 118 | @implementation workspace_context 119 | - (id)init { 120 | if ((self = [super init])) { 121 | [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 122 | selector:@selector(activeDisplayDidChange:) 123 | name:@"NSWorkspaceActiveDisplayDidChangeNotification" 124 | object:nil]; 125 | [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 126 | selector:@selector(activeSpaceDidChange:) 127 | name:NSWorkspaceActiveSpaceDidChangeNotification 128 | object:nil]; 129 | [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 130 | selector:@selector(appSwitched:) 131 | name:NSWorkspaceDidActivateApplicationNotification 132 | object:nil]; 133 | [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 134 | selector:@selector(willSleep:) 135 | name:NSWorkspaceWillSleepNotification 136 | object:nil]; 137 | [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 138 | selector:@selector(didWake:) 139 | name:NSWorkspaceDidWakeNotification 140 | object:nil]; 141 | [[NSDistributedNotificationCenter defaultCenter] addObserver:self 142 | selector:@selector(didChangeMenuBarHiding:) 143 | name:@"AppleInterfaceMenuBarHidingChangedNotification" 144 | object:nil]; 145 | [[NSDistributedNotificationCenter defaultCenter] addObserver:self 146 | selector:@selector(didWake:) 147 | name:@"com.apple.screenIsUnlocked" 148 | object:nil]; 149 | } 150 | 151 | return self; 152 | } 153 | 154 | - (void)addCustomObserver:(NSString *)name { 155 | [[NSDistributedNotificationCenter defaultCenter] addObserver:self 156 | selector:@selector(allDistributedNotifications:) 157 | name:name 158 | object:nil]; 159 | } 160 | 161 | - (void)dealloc { 162 | [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; 163 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 164 | [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; 165 | [super dealloc]; 166 | } 167 | 168 | - (void) allDistributedNotifications:(NSNotification *)note { 169 | @autoreleasepool { 170 | struct notification* notification = notification_create(); 171 | notification->name = string_copy((char*)[[note name] UTF8String]); 172 | if (note.userInfo && [NSJSONSerialization isValidJSONObject:note.userInfo]) { 173 | NSData* data = [NSJSONSerialization dataWithJSONObject:note.userInfo options:NSJSONWritingPrettyPrinted error:NULL]; 174 | if (data && [data length] > 0) { 175 | char* info = malloc([data length] + 1); 176 | memcpy(info, [data bytes], [data length]); 177 | info[[data length]] = '\0'; 178 | notification->info = info; 179 | } 180 | } 181 | 182 | struct event event = { notification, DISTRIBUTED_NOTIFICATION }; 183 | event_post(&event); 184 | } 185 | } 186 | 187 | - (void)willSleep:(NSNotification *)notification { 188 | struct event event = { NULL, SYSTEM_WILL_SLEEP }; 189 | event_post(&event); 190 | } 191 | 192 | - (void)didWake:(NSNotification *)notification { 193 | struct event event = { NULL, SYSTEM_WOKE }; 194 | event_post(&event); 195 | } 196 | 197 | - (void)appSwitched:(NSNotification *)notification { 198 | @autoreleasepool { 199 | char* name = NULL; 200 | if (notification && notification.userInfo) { 201 | NSRunningApplication* app = [notification.userInfo objectForKey:NSWorkspaceApplicationKey]; 202 | if (app) { 203 | char* app_name_tmp = (char*)[[app localizedName] UTF8String]; 204 | if (app_name_tmp) name = string_copy(app_name_tmp); 205 | } 206 | } 207 | struct event event = { name, APPLICATION_FRONT_SWITCHED }; 208 | event_post(&event); 209 | } 210 | } 211 | 212 | - (void)didChangeMenuBarHiding:(NSNotification *)notification { 213 | struct event event = { NULL, MENU_BAR_HIDDEN_CHANGED }; 214 | event_post(&event); 215 | } 216 | 217 | - (void)activeDisplayDidChange:(NSNotification *)notification { 218 | struct event event = { NULL, DISPLAY_CHANGED }; 219 | event_post(&event); 220 | } 221 | 222 | - (void)activeSpaceDidChange:(NSNotification *)notification { 223 | struct event event = { NULL, SPACE_CHANGED }; 224 | event_post(&event); 225 | } 226 | 227 | @end 228 | --------------------------------------------------------------------------------