├── .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 | 
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 | 
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 |
--------------------------------------------------------------------------------