Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cant toggle Control Center Wifi #634

Open
horumyy opened this issue Oct 19, 2024 · 0 comments
Open

Cant toggle Control Center Wifi #634

horumyy opened this issue Oct 19, 2024 · 0 comments

Comments

@horumyy
Copy link

horumyy commented Oct 19, 2024

ive been loving so far sketchybar, however im trying to toggle the original macos wifi popup, ive read the docs and created this helper (a modified version of the one used in this dotfiles

#include <Carbon/Carbon.h>
#include <stdio.h>
#include <string.h>

void ax_init() {
    const void *keys[] = { kAXTrustedCheckOptionPrompt };
    const void *values[] = { kCFBooleanTrue };

    CFDictionaryRef options = CFDictionaryCreate(
        kCFAllocatorDefault,
        keys,
        values,
        sizeof(keys) / sizeof(*keys),
        &kCFCopyStringDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks
    );

    bool trusted = AXIsProcessTrustedWithOptions(options);
    CFRelease(options);
    if (!trusted) {
        printf("Error: Application is not trusted. Please grant accessibility permissions.\n");
        exit(1);
    }
}

void ax_perform_click(AXUIElementRef element) {
    if (!element) return;
    AXUIElementPerformAction(element, kAXCancelAction);
    usleep(150000);
    AXUIElementPerformAction(element, kAXPressAction);
}

CFStringRef ax_get_title(AXUIElementRef element) {
    CFTypeRef title = NULL;
    AXError error = AXUIElementCopyAttributeValue(element, kAXTitleAttribute, &title);
    if (error != kAXErrorSuccess) return NULL;
    return title;
}

void ax_select_menu_option(AXUIElementRef app, int id) {
    AXUIElementRef menubars_ref = NULL;
    CFArrayRef children_ref = NULL;

    AXError error = AXUIElementCopyAttributeValue(app, kAXMenuBarAttribute, (CFTypeRef*)&menubars_ref);
    if (error == kAXErrorSuccess) {
        error = AXUIElementCopyAttributeValue(menubars_ref, kAXVisibleChildrenAttribute, (CFTypeRef*)&children_ref);

        if (error == kAXErrorSuccess) {
            uint32_t count = CFArrayGetCount(children_ref);
            if (id < count) {
                AXUIElementRef item = CFArrayGetValueAtIndex(children_ref, id);
                ax_perform_click(item);
            }
            if (children_ref) CFRelease(children_ref);
        }
        if (menubars_ref) CFRelease(menubars_ref);
    }
}

void ax_print_menu_options(AXUIElementRef app) {
    AXUIElementRef menubars_ref = NULL;
    CFArrayRef children_ref = NULL;

    AXError error = AXUIElementCopyAttributeValue(app, kAXMenuBarAttribute, (CFTypeRef*)&menubars_ref);
    if (error == kAXErrorSuccess) {
        error = AXUIElementCopyAttributeValue(menubars_ref, kAXVisibleChildrenAttribute, (CFTypeRef*)&children_ref);

        if (error == kAXErrorSuccess) {
            uint32_t count = CFArrayGetCount(children_ref);

            for (int i = 0; i < count; i++) {
                AXUIElementRef item = CFArrayGetValueAtIndex(children_ref, i);
                CFTypeRef title = ax_get_title(item);

                if (title) {
                    uint32_t buffer_len = 2*CFStringGetLength(title);
                    char buffer[buffer_len];
                    CFStringGetCString(title, buffer, buffer_len, kCFStringEncodingUTF8);
                    printf("%d: %s\n", i, buffer);
                    CFRelease(title);
                }
            }
        }
        if (menubars_ref) CFRelease(menubars_ref);
        if (children_ref) CFRelease(children_ref);
    }
}

AXUIElementRef ax_get_extra_menu_item(char* alias) {
    pid_t pid = 0;
    CGRect bounds = CGRectNull;
    CFArrayRef window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
    char owner_buffer[256];
    char name_buffer[256];
    char buffer[512];
    int window_count = CFArrayGetCount(window_list);
    
    char* alias_owner = strtok(alias, ",");
    char* alias_name = strtok(NULL, ",");

    printf("Debug: Searching for alias - Owner: %s, Name: %s\n", alias_owner, alias_name);

    for (int i = 0; i < window_count; ++i) {
        CFDictionaryRef dictionary = CFArrayGetValueAtIndex(window_list, i);
        if (!dictionary) continue;

        CFStringRef owner_ref = CFDictionaryGetValue(dictionary, kCGWindowOwnerName);
        CFNumberRef owner_pid_ref = CFDictionaryGetValue(dictionary, kCGWindowOwnerPID);
        CFStringRef name_ref = CFDictionaryGetValue(dictionary, kCGWindowName);
        CFNumberRef layer_ref = CFDictionaryGetValue(dictionary, kCGWindowLayer);
        CFDictionaryRef bounds_ref = CFDictionaryGetValue(dictionary, kCGWindowBounds);

        if (!owner_ref || !owner_pid_ref || !layer_ref || !bounds_ref) continue;

        long long int layer = 0;
        CFNumberGetValue(layer_ref, CFNumberGetType(layer_ref), &layer);
        uint64_t owner_pid = 0;
        CFNumberGetValue(owner_pid_ref, CFNumberGetType(owner_pid_ref), &owner_pid);

        if (layer != 0x19) {
            printf("Debug: Skipping window due to layer != 0x19\n");
            continue;
        }
        bounds = CGRectNull;
        if (!CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) continue;
        CFStringGetCString(owner_ref, owner_buffer, sizeof(owner_buffer), kCFStringEncodingUTF8);

        if (name_ref) {
            CFStringGetCString(name_ref, name_buffer, sizeof(name_buffer), kCFStringEncodingUTF8);
            snprintf(buffer, sizeof(buffer), "%s (%s)", name_buffer, owner_buffer);
        } else {
            strncpy(buffer, owner_buffer, sizeof(buffer));
        }

        printf("Debug: Checking window - %s\n", buffer);

        if (strstr(buffer, alias_owner) != NULL && (alias_name == NULL || strstr(buffer, alias_name) != NULL)) {
            pid = owner_pid;
            printf("Debug: Match found - %s\n", buffer);
            break;
        }
    }
    CFRelease(window_list);
    if (!pid) {
        printf("Debug: No matching window found\n");
        return NULL;
    }

    AXUIElementRef app = AXUIElementCreateApplication(pid);
    if (!app) {
        printf("Debug: Failed to create AXUIElement for pid %d\n", pid);
        return NULL;
    }
    AXUIElementRef result = NULL;
    CFTypeRef extras = NULL;
    CFArrayRef children_ref = NULL;
    AXError error = AXUIElementCopyAttributeValue(app, kAXExtrasMenuBarAttribute, &extras);
    if (error == kAXErrorSuccess) {
        error = AXUIElementCopyAttributeValue(extras, kAXVisibleChildrenAttribute, (CFTypeRef*)&children_ref);

        if (error == kAXErrorSuccess) {
            uint32_t count = CFArrayGetCount(children_ref);
            printf("Debug: Found %d extras menu items\n", count);
            for (uint32_t i = 0; i < count; i++) {
                AXUIElementRef item = CFArrayGetValueAtIndex(children_ref, i);
                CFTypeRef position_ref = NULL;
                CFTypeRef size_ref = NULL;
                CFTypeRef title_ref = NULL;
                AXUIElementCopyAttributeValue(item, kAXPositionAttribute, &position_ref);
                AXUIElementCopyAttributeValue(item, kAXSizeAttribute, &size_ref);
                AXUIElementCopyAttributeValue(item, kAXTitleAttribute, &title_ref);
                
                if (!position_ref || !size_ref) continue;

                CGPoint position = CGPointZero;
                AXValueGetValue(position_ref, kAXValueCGPointType, &position);
                CGSize size = CGSizeZero;
                AXValueGetValue(size_ref, kAXValueCGSizeType, &size);
                
                char title_buffer[256] = {0};
                if (title_ref) {
                    CFStringGetCString(title_ref, title_buffer, sizeof(title_buffer), kCFStringEncodingUTF8);
                    CFRelease(title_ref);
                }
                
                printf("Debug: Extras item %d - Title: %s, Position: (%.2f, %.2f)\n", i, title_buffer, position.x, position.y);

                if (fabs(position.x - bounds.origin.x) <= 10 || (alias_name != NULL && strstr(title_buffer, alias_name) != NULL)) {
                    result = item;
                    printf("Debug: Selected extras item %d\n", i);
                    break;
                }

                CFRelease(position_ref);
                CFRelease(size_ref);
            }
        } else {
            printf("Debug: Failed to get visible children of extras menu\n");
        }
        if (children_ref) CFRelease(children_ref);
    } else {
        printf("Debug: Failed to get extras menu bar attribute\n");
    }

    CFRelease(app);
    return result;
}

extern int SLSMainConnectionID();
extern void SLSSetMenuBarVisibilityOverrideOnDisplay(int cid, int did, bool enabled);
extern void SLSSetMenuBarInsetAndAlpha(int cid, double u1, double u2, float alpha);

void ax_select_menu_extra(char* alias) {
    AXUIElementRef item = ax_get_extra_menu_item(alias);
    if (!item) {
        printf("Debug: Menu item not found for alias - %s\n", alias);
        return;
    }
    SLSSetMenuBarInsetAndAlpha(SLSMainConnectionID(), 0, 1, 0.0);
    SLSSetMenuBarVisibilityOverrideOnDisplay(SLSMainConnectionID(), 0, true);
    SLSSetMenuBarInsetAndAlpha(SLSMainConnectionID(), 0, 1, 0.0);
    ax_perform_click(item);
    SLSSetMenuBarVisibilityOverrideOnDisplay(SLSMainConnectionID(), 0, false);
    SLSSetMenuBarInsetAndAlpha(SLSMainConnectionID(), 0, 1, 1.0);
    CFRelease(item);
}

extern void _SLPSGetFrontProcess(ProcessSerialNumber* psn);
extern void SLSGetConnectionIDForPSN(int cid, ProcessSerialNumber* psn, int* cid_out);
extern void SLSConnectionGetPID(int cid, pid_t* pid_out);

AXUIElementRef ax_get_front_app() {
    ProcessSerialNumber psn;
    _SLPSGetFrontProcess(&psn);
    int target_cid;
    SLSGetConnectionIDForPSN(SLSMainConnectionID(), &psn, &target_cid);

    pid_t pid;
    SLSConnectionGetPID(target_cid, &pid);
    return AXUIElementCreateApplication(pid);
}

int main(int argc, char **argv) {
    if (argc == 1) {
        printf("Usage: %s [-l | -s id/alias ]\n", argv[0]);
        exit(0);
    }
    ax_init();
    if (strcmp(argv[1], "-l") == 0) {
        AXUIElementRef app = ax_get_front_app();
        if (!app) return 1;
        ax_print_menu_options(app);
        CFRelease(app);
    } else if (argc == 3 && strcmp(argv[1], "-s") == 0) {
        int id = 0;
        if (sscanf(argv[2], "%d", &id) == 1) {
            AXUIElementRef app = ax_get_front_app();
            if (!app) return 1;
            ax_select_menu_option(app, id);
            CFRelease(app);
        } else {
            ax_select_menu_extra(argv[2]);
        }
    }
    return 0;
}

but this is the output im getting :c

Debug: Searching for alias - Owner: Control Center, Name: WiFi
Debug: No matching window found
Debug: Menu item not found for alias - Control Center

while using sketchybar --query default_menu_items i get

[
	"Control Center,Clock",
	"SystemUIServer,Siri",
	"Control Center,BentoBox",
	"Spotlight,Item-0",
	"Control Center,WiFi",
	"Control Center,Battery",
	"Control Center,NowPlaying",
	"Raycast,raycastIcon",
	"TextInputMenuAgent,Item-0"
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant