add space_windows_change event (#435)

This commit is contained in:
Felix Kratz 2023-11-12 19:56:28 +01:00
parent 4ba2290ac3
commit f3eafac1a3
15 changed files with 334 additions and 18 deletions

View file

@ -18,7 +18,7 @@ _OBJ = alias.o background.o bar_item.o custom_events.o event.o graph.o \
image.o mouse.o shadow.o font.o text.o message.o mouse.o bar.o color.o \
window.o bar_manager.o display.o group.o mach.o popup.o \
animation.o workspace.om volume.o slider.o power.o wifi.om media.om \
hotload.o
hotload.o app_windows.o
OBJ = $(patsubst %, $(ODIR)/%, $(_OBJ))

246
src/app_windows.c Normal file
View file

@ -0,0 +1,246 @@
#include "app_windows.h"
#include "workspace.h"
#include "misc/helpers.h"
#include "event.h"
extern pid_t g_pid;
struct app_windows g_windows = { 0 };
bool g_space_window_events = false;
static bool iterator_window_suitable(CFTypeRef iterator) {
uint64_t tags = SLSWindowIteratorGetTags(iterator);
uint64_t attributes = SLSWindowIteratorGetAttributes(iterator);
uint32_t parent_wid = SLSWindowIteratorGetParentID(iterator);
if (((parent_wid == 0)
&& ((attributes & 0x2)
|| (tags & 0x400000000000000))
&& (((tags & 0x1))
|| ((tags & 0x2)
&& (tags & 0x80000000))))) {
return true;
}
return false;
}
void app_window_clear(struct app_window* window) {
memset(window, 0, sizeof(struct app_window));
}
void app_windows_add(struct app_windows* windows, struct app_window* window) {
for (int i = 0; i < windows->num_windows; i++) {
if (!windows->windows[i].wid) {
windows->windows[i] = *window;
return;
}
}
windows->windows = realloc(windows->windows,
sizeof(struct app_window)
* ++windows->num_windows);
windows->windows[windows->num_windows - 1] = *window;
}
void app_windows_clear_space(struct app_windows* windows, uint64_t sid) {
for (int i = 0; i < windows->num_windows; i++) {
if (windows->windows[i].sid == sid)
app_window_clear(windows->windows + i);
}
}
bool app_windows_find(struct app_windows* windows, struct app_window* window) {
for (int i = 0; i < windows->num_windows; i++) {
if (windows->windows[i].wid == window->wid
&& windows->windows[i].sid == window->sid) {
return true;
}
}
return false;
}
static bool app_window_suitable(struct app_window* window) {
CFArrayRef target_ref = cfarray_of_cfnumbers(&window->wid,
sizeof(uint32_t),
1,
kCFNumberSInt32Type);
if (!target_ref) return false;
bool suitable = false;
CFTypeRef query = SLSWindowQueryWindows(g_connection, target_ref, 0x0);
if (query) {
CFTypeRef iterator = SLSWindowQueryResultCopyWindows(query);
if (iterator && SLSWindowIteratorGetCount(iterator) > 0) {
if (SLSWindowIteratorAdvance(iterator)) {
if (iterator_window_suitable(iterator)) {
suitable = true;
}
}
}
if (iterator) CFRelease(iterator);
CFRelease(query);
}
CFRelease(target_ref);
return suitable;
}
static void app_windows_post_event_for_space(struct app_windows* windows, uint64_t sid) {
int index = mission_control_index(sid);
pid_t pid_list[windows->num_windows];
uint32_t pid_count[windows->num_windows];
char* pid_name[windows->num_windows];
memset(&pid_list, 0, sizeof(pid_t)*windows->num_windows);
memset(&pid_count, 0, sizeof(uint32_t)*windows->num_windows);
memset(&pid_name, 0, sizeof(char*)*windows->num_windows);
uint32_t length = 64;
for (int i = 0; i < windows->num_windows; i++) {
for (int j = 0; j < windows->num_windows; j++) {
if ((!pid_list[j] || pid_list[j] == windows->windows[i].pid)
&& windows->windows[i].sid == sid) {
pid_list[j] = windows->windows[i].pid;
pid_count[j]++;
if (!pid_name[j]) {
pid_name[j] = workspace_copy_app_name_for_pid(pid_list[j]);
length += pid_name[j] ? strlen(pid_name[j]) + 16 : 0;
}
break;
}
}
}
char payload[length];
memset(payload, 0, length);
snprintf(payload, length, "{\n"
"\t\"space\": %d,\n"
"\t\"apps\": {\n",
index );
char* cursor = payload + strlen(payload);
bool first = true;
for (int i = 0; i < windows->num_windows; i++) {
if (!pid_list[i]) break;
if (!pid_name[i]) continue;
if (!first) {
snprintf(cursor, length - (cursor - payload), ",\n");
cursor = payload + strlen(payload);
} else first = false;
snprintf(cursor,
length - (cursor - payload),
"\t\t\"%s\": %d",
pid_name[i],
pid_count[i] );
free(pid_name[i]);
cursor = payload + strlen(payload);
}
snprintf(cursor, length - (cursor - payload), "\n\t}\n}\n");
struct event event = { payload, SPACE_WINDOWS_CHANGED };
event_post(&event);
}
static void app_windows_update_space(struct app_windows* windows, uint64_t sid, bool silent) {
app_windows_clear_space(windows, sid);
CFArrayRef space_list_ref = cfarray_of_cfnumbers(&sid,
sizeof(uint64_t),
1,
kCFNumberSInt64Type);
uint64_t set_tags = 1;
uint64_t clear_tags = 0;
CFArrayRef window_list = SLSCopyWindowsWithOptionsAndTags(g_connection,
0,
space_list_ref,
0x2,
&set_tags,
&clear_tags );
if (window_list) {
uint32_t window_count = CFArrayGetCount(window_list);
if (window_count > 0) {
CFTypeRef query = SLSWindowQueryWindows(g_connection, window_list, 0x0);
if (query) {
CFTypeRef iterator = SLSWindowQueryResultCopyWindows(query);
if (iterator) {
while(SLSWindowIteratorAdvance(iterator)) {
if (iterator_window_suitable(iterator)) {
uint32_t wid = SLSWindowIteratorGetWindowID(iterator);
int wid_cid = 0;
SLSGetWindowOwner(g_connection, wid, &wid_cid);
pid_t pid = 0;
SLSConnectionGetPID(wid_cid, &pid);
struct app_window window = { .wid = wid,
.sid = sid,
.pid = pid };
app_windows_add(windows, &window);
}
}
}
CFRelease(query);
}
}
CFRelease(window_list);
}
CFRelease(space_list_ref);
if (!silent) app_windows_post_event_for_space(windows, sid);
}
struct window_spawn_data {
uint64_t sid;
uint32_t wid;
};
static void window_spawn_handler(uint32_t event, struct window_spawn_data* data, size_t _, int cid) {
uint32_t wid = data->wid;
uint64_t sid = data->sid;
if (!wid || !sid) return;
struct app_window window = { .wid = wid, .sid = sid, .pid = 0 };
if (event == 1325 && app_window_suitable(&window)) {
app_windows_update_space(&g_windows, sid, false);
} else if (event == 1326) {
bool found = app_windows_find(&g_windows, &window);
if (found) {
app_windows_update_space(&g_windows, sid, false);
}
}
}
static void update_all_spaces(struct app_windows* windows, bool silent) {
uint32_t display_count = 0;
uint32_t* displays = display_active_display_list(&display_count);
for (int i = 0; i < display_count; i++) {
int space_count = 0;
uint64_t* spaces = display_space_list(displays[i], &space_count);
for (int j = 0; j < space_count; j++) {
app_windows_update_space(windows, spaces[j], silent);
}
if (spaces) free(spaces);
}
if (displays) free(displays);
}
void forced_space_windows_event() {
if (g_space_window_events) update_all_spaces(&g_windows, false);
}
void begin_receiving_space_window_events() {
if (!g_space_window_events) {
SLSRegisterNotifyProc(window_spawn_handler,
1325,
(void*)(intptr_t)g_connection);
SLSRegisterNotifyProc(window_spawn_handler,
1326,
(void*)(intptr_t)g_connection);
g_space_window_events = true;
update_all_spaces(&g_windows, true);
}
}

30
src/app_windows.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include "event.h"
extern CGError SLSGetWindowOwner(int cid, uint32_t wid, int* out_cid);
extern CGError SLSConnectionGetPID(int cid, pid_t *pid);
extern CFArrayRef SLSCopyWindowsWithOptionsAndTags(int cid, uint32_t owner, CFArrayRef spaces, uint32_t options, uint64_t *set_tags, uint64_t *clear_tags);
extern CFTypeRef SLSWindowQueryWindows(int cid, CFArrayRef windows, uint32_t options);
extern CFTypeRef SLSWindowQueryResultCopyWindows(CFTypeRef window_query);
extern int SLSWindowIteratorGetCount(CFTypeRef iterator);
extern bool SLSWindowIteratorAdvance(CFTypeRef iterator);
extern uint32_t SLSWindowIteratorGetParentID(CFTypeRef iterator);
extern uint32_t SLSWindowIteratorGetWindowID(CFTypeRef iterator);
extern uint64_t SLSWindowIteratorGetTags(CFTypeRef iterator);
extern uint64_t SLSWindowIteratorGetAttributes(CFTypeRef iterator);
extern CGError SLSRegisterNotifyProc(void* callback, uint32_t event, void* context);
struct app_window {
uint32_t wid;
uint64_t sid;
pid_t pid;
};
struct app_windows {
struct app_window* windows;
uint32_t num_windows;
};
void begin_receiving_space_window_events();
void forced_space_windows_event();

View file

@ -4,6 +4,7 @@
#include "volume.h"
#include "power.h"
#include "media.h"
#include "app_windows.h"
struct bar_item* bar_item_create() {
struct bar_item* bar_item = malloc(sizeof(struct bar_item));
@ -1234,6 +1235,10 @@ void bar_item_parse_subscribe_message(struct bar_item* bar_item, char* message,
begin_receiving_media_events();
}
if (event_flag & UPDATE_SPACE_WINDOWS_CHANGE) {
begin_receiving_space_window_events();
}
bar_item->update_mask |= event_flag;
if (!event_flag) {

View file

@ -8,6 +8,7 @@
#include "power.h"
#include "mouse.h"
#include "media.h"
#include "app_windows.h"
extern void forced_front_app_event();
@ -524,6 +525,7 @@ void bar_manager_update(struct bar_manager* bar_manager, bool forced) {
forced_power_event();
forced_front_app_event();
forced_media_change_event();
forced_space_windows_event();
}
bool needs_refresh = false;
@ -906,6 +908,16 @@ void bar_manager_handle_front_app_switch(struct bar_manager* bar_manager, char*
env_vars_destroy(&env_vars);
}
void bar_manager_handle_space_windows_change(struct bar_manager* bar_manager, char* info) {
struct env_vars env_vars;
env_vars_init(&env_vars);
if (info) env_vars_set(&env_vars, string_copy("INFO"), string_copy(info));
bar_manager_custom_events_trigger(bar_manager,
COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE,
&env_vars );
env_vars_destroy(&env_vars);
}
void bar_manager_handle_space_change(struct bar_manager* bar_manager, bool forced) {
struct env_vars env_vars;
env_vars_init(&env_vars);

View file

@ -119,6 +119,7 @@ void bar_manager_handle_brightness_change(struct bar_manager* bar_manager, float
void bar_manager_handle_power_source_change(struct bar_manager* bar_manager, char* state);
void bar_manager_handle_media_change(struct bar_manager* bar_manager, char* info);
void bar_manager_handle_media_cover_change(struct bar_manager* bar_manager, CGImageRef image);
void bar_manager_handle_space_windows_change(struct bar_manager* bar_manager, char* info);
void bar_manager_custom_events_trigger(struct bar_manager* bar_manager, char* name, struct env_vars* env_vars);
void bar_manager_destroy(struct bar_manager* bar_manager);

View file

@ -37,6 +37,7 @@ void custom_events_init(struct custom_events* custom_events) {
custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_POWER_SOURCE_CHANGE), NULL);
custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_WIFI_CHANGE), NULL);
custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MEDIA_CHANGE), NULL);
custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE), NULL);
}
void custom_events_append(struct custom_events* custom_events, char* name, char* notification) {

View file

@ -18,6 +18,7 @@
#define UPDATE_POWER_SOURCE_CHANGE 1ULL << 14
#define UPDATE_WIFI_CHANGE 1ULL << 15
#define UPDATE_MEDIA_CHANGE 1ULL << 16
#define UPDATE_SPACE_WINDOWS_CHANGE 1ULL << 17
extern void* g_workspace_context;
extern void workspace_create_custom_observer(void** context, char* name);

View file

@ -34,6 +34,7 @@ uint32_t display_arrangement_display_id(int arrangement);
bool display_menu_bar_visible(void);
CGRect display_menu_bar_rect(uint32_t did);
uint32_t display_active_display_count(void);
uint32_t *display_active_display_list(uint32_t *count);
bool display_begin(void);
bool display_end(void);

View file

@ -329,6 +329,10 @@ static void event_cover_changed(void* context) {
bar_manager_handle_media_cover_change(&g_bar_manager, (CGImageRef)context);
}
static void event_space_windows_changed(void* context) {
bar_manager_handle_space_windows_change(&g_bar_manager, (char*)context);
}
static void event_hotload(void* context) {
bar_manager_destroy(&g_bar_manager);
bar_manager_init(&g_bar_manager);
@ -364,6 +368,7 @@ static callback_type* event_handler[] = {
[ANIMATOR_REFRESH] = event_animator_refresh,
[MACH_MESSAGE] = event_mach_message,
[HOTLOAD] = event_hotload,
[SPACE_WINDOWS_CHANGED] = event_space_windows_changed,
};
extern pthread_mutex_t g_event_mutex;

View file

@ -28,6 +28,7 @@ enum event_type {
POWER_SOURCE_CHANGED,
MEDIA_CHANGED,
COVER_CHANGED,
SPACE_WINDOWS_CHANGED,
DISTRIBUTED_NOTIFICATION,
HOTLOAD,

View file

@ -131,6 +131,7 @@
#define COMMAND_SUBSCRIBE_MOUSE_ENTERED_GLOBAL "mouse.entered.global"
#define COMMAND_SUBSCRIBE_MOUSE_EXITED_GLOBAL "mouse.exited.global"
#define COMMAND_SUBSCRIBE_MOUSE_SCROLLED_GLOBAL "mouse.scrolled.global"
#define COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE "space_windows_change"
#define DOMAIN_QUERY "--query"
#define COMMAND_QUERY_DEFAULT_ITEMS "default_menu_items"

View file

@ -50,6 +50,7 @@ char g_lock_file[MAXLEN];
bool g_volume_events;
bool g_brightness_events;
int64_t g_disable_capture = 0;
pid_t g_pid = 0;
static int client_send_message(int argc, char **argv) {
if (argc <= 1) {
@ -187,6 +188,7 @@ int main(int argc, char **argv) {
if (is_root())
error("%s: running as root is not allowed! abort..\n", g_name);
pid_for_task(mach_task_self(), &g_pid);
init_misc_settings();
acquire_lockfile();

View file

@ -9,3 +9,4 @@ int workspace_display_notch_height(uint32_t did);
float workspace_get_scale();
CGImageRef workspace_icon_for_app(char* app);
char* workspace_copy_app_name_for_pid(pid_t pid);

View file

@ -74,6 +74,15 @@ void forced_front_app_event() {
}
}
char* workspace_copy_app_name_for_pid(pid_t pid) {
const char* result = NULL;
@autoreleasepool {
NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
result = [[app localizedName] UTF8String];
}
return result ? string_copy((char*)result) : NULL;
}
CGImageRef workspace_icon_for_app(char* app) {
@autoreleasepool {
NSString* ns_app = [NSString stringWithUTF8String:app];