mirror of
				https://github.com/swaywm/sway.git
				synced 2025-10-29 05:40:18 -04:00 
			
		
		
		
	swaybar: add StatusNotifierWatcher to tray
This commit is contained in:
		
							parent
							
								
									ef555012fa
								
							
						
					
					
						commit
						02df3f67aa
					
				
					 5 changed files with 242 additions and 0 deletions
				
			
		|  | @ -12,12 +12,16 @@ | |||
| 
 | ||||
| struct swaybar; | ||||
| struct swaybar_output; | ||||
| struct swaybar_watcher; | ||||
| 
 | ||||
| struct swaybar_tray { | ||||
| 	struct swaybar *bar; | ||||
| 
 | ||||
| 	int fd; | ||||
| 	sd_bus *bus; | ||||
| 
 | ||||
| 	struct swaybar_watcher *watcher_xdg; | ||||
| 	struct swaybar_watcher *watcher_kde; | ||||
| }; | ||||
| 
 | ||||
| struct swaybar_tray *create_tray(struct swaybar *bar); | ||||
|  |  | |||
							
								
								
									
										18
									
								
								include/swaybar/tray/watcher.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								include/swaybar/tray/watcher.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| #ifndef _SWAYBAR_TRAY_WATCHER_H | ||||
| #define _SWAYBAR_TRAY_WATCHER_H | ||||
| 
 | ||||
| #include "swaybar/tray/tray.h" | ||||
| #include "list.h" | ||||
| 
 | ||||
| struct swaybar_watcher { | ||||
| 	char *interface; | ||||
| 	sd_bus *bus; | ||||
| 	list_t *hosts; | ||||
| 	list_t *items; | ||||
| 	int version; | ||||
| }; | ||||
| 
 | ||||
| struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus); | ||||
| void destroy_watcher(struct swaybar_watcher *watcher); | ||||
| 
 | ||||
| #endif | ||||
|  | @ -1,5 +1,6 @@ | |||
| tray_files = get_option('enable-tray') ? [ | ||||
| 	'tray/tray.c', | ||||
| 	'tray/watcher.c' | ||||
| ] : [] | ||||
| 
 | ||||
| swaybar_deps = [ | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <string.h> | ||||
| #include "swaybar/bar.h" | ||||
| #include "swaybar/tray/tray.h" | ||||
| #include "swaybar/tray/watcher.h" | ||||
| #include "log.h" | ||||
| 
 | ||||
| struct swaybar_tray *create_tray(struct swaybar *bar) { | ||||
|  | @ -23,6 +24,10 @@ struct swaybar_tray *create_tray(struct swaybar *bar) { | |||
| 	tray->bar = bar; | ||||
| 	tray->bus = bus; | ||||
| 	tray->fd = sd_bus_get_fd(tray->bus); | ||||
| 
 | ||||
| 	tray->watcher_xdg = create_watcher("freedesktop", tray->bus); | ||||
| 	tray->watcher_kde = create_watcher("kde", tray->bus); | ||||
| 
 | ||||
| 	return tray; | ||||
| } | ||||
| 
 | ||||
|  | @ -30,6 +35,8 @@ void destroy_tray(struct swaybar_tray *tray) { | |||
| 	if (!tray) { | ||||
| 		return; | ||||
| 	} | ||||
| 	destroy_watcher(tray->watcher_xdg); | ||||
| 	destroy_watcher(tray->watcher_kde); | ||||
| 	sd_bus_flush_close_unref(tray->bus); | ||||
| 	free(tray); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										212
									
								
								swaybar/tray/watcher.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								swaybar/tray/watcher.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,212 @@ | |||
| #define _POSIX_C_SOURCE 200809L | ||||
| #include <stdbool.h> | ||||
| #include <stddef.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include "list.h" | ||||
| #include "log.h" | ||||
| #include "swaybar/tray/watcher.h" | ||||
| 
 | ||||
| static const char *obj_path = "/StatusNotifierWatcher"; | ||||
| 
 | ||||
| static bool using_standard_protocol(struct swaybar_watcher *watcher) { | ||||
| 	return watcher->interface[strlen("org.")] == 'f'; // freedesktop
 | ||||
| } | ||||
| 
 | ||||
| static int cmp_id(const void *item, const void *cmp_to) { | ||||
| 	return strcmp(item, cmp_to); | ||||
| } | ||||
| 
 | ||||
| static int cmp_service(const void *item, const void *cmp_to) { | ||||
| 	return strncmp(item, cmp_to, strlen(cmp_to)); | ||||
| } | ||||
| 
 | ||||
| static int handle_lost_service(sd_bus_message *msg, | ||||
| 		void *data, sd_bus_error *error) { | ||||
| 	char *service, *old_owner, *new_owner; | ||||
| 	int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner); | ||||
| 	if (ret < 0) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret)); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!*new_owner) { | ||||
| 		struct swaybar_watcher *watcher = data; | ||||
| 		int idx = list_seq_find(watcher->items, | ||||
| 				using_standard_protocol(watcher) ? cmp_id : cmp_service, service); | ||||
| 		if (idx != -1) { | ||||
| 			char *id = watcher->items->items[idx]; | ||||
| 			wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id); | ||||
| 			list_del(watcher->items, idx); | ||||
| 			sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | ||||
| 					"StatusNotifierItemUnregistered", "s", id); | ||||
| 			free(id); | ||||
| 		} | ||||
| 
 | ||||
| 		idx = list_seq_find(watcher->hosts, cmp_id, service); | ||||
| 		if (idx != -1) { | ||||
| 			wlr_log(WLR_DEBUG, "Unregistering Status Notifier Host '%s'", service); | ||||
| 			free(watcher->hosts->items[idx]); | ||||
| 			list_del(watcher->hosts, idx); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||||
| 	char *service_or_path, *id; | ||||
| 	int ret = sd_bus_message_read(msg, "s", &service_or_path); | ||||
| 	if (ret < 0) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret)); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	struct swaybar_watcher *watcher = data; | ||||
| 	if (using_standard_protocol(watcher)) { | ||||
| 		id = strdup(service_or_path); | ||||
| 	} else { | ||||
| 		const char *service, *path; | ||||
| 		if (service_or_path[0] == '/') { | ||||
| 			service = sd_bus_message_get_sender(msg); | ||||
| 			path = service_or_path; | ||||
| 		} else { | ||||
| 			service = service_or_path; | ||||
| 			path = "/StatusNotifierItem"; | ||||
| 		} | ||||
| 		size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; | ||||
| 		id = malloc(id_len); | ||||
| 		snprintf(id, id_len, "%s%s", service, path); | ||||
| 	} | ||||
| 
 | ||||
| 	if (list_seq_find(watcher->items, cmp_id, id) == -1) { | ||||
| 		wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id); | ||||
| 		list_add(watcher->items, id); | ||||
| 		sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | ||||
| 				"StatusNotifierItemRegistered", "s", id); | ||||
| 	} else { | ||||
| 		wlr_log(WLR_DEBUG, "Status Notifier Item '%s' already registered", id); | ||||
| 		free(id); | ||||
| 	} | ||||
| 
 | ||||
| 	return sd_bus_reply_method_return(msg, ""); | ||||
| } | ||||
| 
 | ||||
| static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||||
| 	char *service; | ||||
| 	int ret = sd_bus_message_read(msg, "s", &service); | ||||
| 	if (ret < 0) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to parse register host message: %s", strerror(-ret)); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	struct swaybar_watcher *watcher = data; | ||||
| 	if (list_seq_find(watcher->hosts, cmp_id, service) == -1) { | ||||
| 		wlr_log(WLR_DEBUG, "Registering Status Notifier Host '%s'", service); | ||||
| 		list_add(watcher->hosts, strdup(service)); | ||||
| 		sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | ||||
| 				"StatusNotifierHostRegistered", "s", service); | ||||
| 	} else { | ||||
| 		wlr_log(WLR_DEBUG, "Status Notifier Host '%s' already registered", service); | ||||
| 	} | ||||
| 
 | ||||
| 	return sd_bus_reply_method_return(msg, ""); | ||||
| } | ||||
| 
 | ||||
| static int get_registered_snis(sd_bus *bus, const char *obj_path, | ||||
| 		const char *interface, const char *property, sd_bus_message *reply, | ||||
| 		void *data, sd_bus_error *error) { | ||||
| 	struct swaybar_watcher *watcher = data; | ||||
| 	list_add(watcher->items, NULL); // strv expects NULL-terminated string array
 | ||||
| 	int ret = sd_bus_message_append_strv(reply, (char **)watcher->items->items); | ||||
| 	list_del(watcher->items, watcher->items->length - 1); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int is_host_registered(sd_bus *bus, const char *obj_path, | ||||
| 		const char *interface, const char *property, sd_bus_message *reply, | ||||
| 		void *data, sd_bus_error *error) { | ||||
| 	struct swaybar_watcher *watcher = data; | ||||
| 	int val = watcher->hosts->length > 0; // dbus expects int rather than bool
 | ||||
| 	return sd_bus_message_append_basic(reply, 'b', &val); | ||||
| } | ||||
| 
 | ||||
| static const sd_bus_vtable watcher_vtable[] = { | ||||
| 	SD_BUS_VTABLE_START(0), | ||||
| 	SD_BUS_METHOD("RegisterStatusNotifierItem", "s", "", register_sni, | ||||
| 			SD_BUS_VTABLE_UNPRIVILEGED), | ||||
| 	SD_BUS_METHOD("RegisterStatusNotifierHost", "s", "", register_host, | ||||
| 			SD_BUS_VTABLE_UNPRIVILEGED), | ||||
| 	SD_BUS_PROPERTY("RegisteredStatusNotifierItems", "as", get_registered_snis, | ||||
| 			0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | ||||
| 	SD_BUS_PROPERTY("IsStatusNotifierHostRegistered", "b", is_host_registered, | ||||
| 			0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | ||||
| 	SD_BUS_PROPERTY("ProtocolVersion", "i", NULL, | ||||
| 			offsetof(struct swaybar_watcher, version), | ||||
| 			SD_BUS_VTABLE_PROPERTY_CONST), | ||||
| 	SD_BUS_SIGNAL("StatusNotifierItemRegistered", "s", 0), | ||||
| 	SD_BUS_SIGNAL("StatusNotifierItemUnregistered", "s", 0), | ||||
| 	SD_BUS_SIGNAL("StatusNotifierHostRegistered", NULL, 0), | ||||
| 	SD_BUS_VTABLE_END | ||||
| }; | ||||
| 
 | ||||
| struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { | ||||
| 	struct swaybar_watcher *watcher = | ||||
| 		calloc(1, sizeof(struct swaybar_watcher)); | ||||
| 	if (!watcher) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; | ||||
| 	watcher->interface = malloc(len); | ||||
| 	snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol); | ||||
| 
 | ||||
| 	sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; | ||||
| 	int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, | ||||
| 			watcher->interface, watcher_vtable, watcher); | ||||
| 	if (ret < 0) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to add object vtable: %s", strerror(-ret)); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = sd_bus_match_signal(bus, &signal_slot, "org.freedesktop.DBus", | ||||
| 			"/org/freedesktop/DBus", "org.freedesktop.DBus", | ||||
| 			"NameOwnerChanged", handle_lost_service, watcher); | ||||
| 	if (ret < 0) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", | ||||
| 				strerror(-ret)); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = sd_bus_request_name(bus, watcher->interface, 0); | ||||
| 	if (ret < 0) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to acquire service name: %s", strerror(-ret)); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	sd_bus_slot_set_floating(signal_slot, 0); | ||||
| 	sd_bus_slot_set_floating(vtable_slot, 0); | ||||
| 
 | ||||
| 	watcher->bus = bus; | ||||
| 	watcher->hosts = create_list(); | ||||
| 	watcher->items = create_list(); | ||||
| 	watcher->version = 0; | ||||
| 	wlr_log(WLR_DEBUG, "Registered %s", watcher->interface); | ||||
| 	return watcher; | ||||
| error: | ||||
| 	sd_bus_slot_unref(signal_slot); | ||||
| 	sd_bus_slot_unref(vtable_slot); | ||||
| 	destroy_watcher(watcher); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| void destroy_watcher(struct swaybar_watcher *watcher) { | ||||
| 	if (!watcher) { | ||||
| 		return; | ||||
| 	} | ||||
| 	list_free_items_and_destroy(watcher->hosts); | ||||
| 	list_free_items_and_destroy(watcher->items); | ||||
| 	free(watcher->interface); | ||||
| 	free(watcher); | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ian Fan
						Ian Fan