Initial commit

This commit is contained in:
2022-08-28 14:27:03 -07:00
commit 3810e65cb5
28 changed files with 2540 additions and 0 deletions

153
modules/battery.c Normal file
View File

@ -0,0 +1,153 @@
#include "battery.h"
#include "../util.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ADAPTER_BASE_PATH "/sys/class/power_supply/"
#define BATTERY_PATH "/sys/class/power_supply/BAT0"
static const char *adapter_search_names[] = {"AC0", "ADP1"};
static size_t adapter_search_names_count =
sizeof(adapter_search_names) / sizeof(char *);
static char *adapter_status_path;
static int max_batery_charge;
static int show_notifiaction = TRUE;
static void find_max_battery_charge() {
FILE *max_file = fopen(BATTERY_PATH "/charge_full", "r");
fseeko(max_file, 0, SEEK_END);
off_t len = ftello(max_file);
rewind(max_file);
char text[len + 1];
text[len] = '\0';
fread(text, 1, len, max_file);
max_batery_charge = strtoul(text, NULL, 10);
fclose(max_file);
}
static int is_battery_full() {
FILE *status_file = fopen(BATTERY_PATH "/status", "r");
if (!status_file) {
return FALSE;
}
fseeko(status_file, 0, SEEK_END);
off_t len = ftello(status_file);
rewind(status_file);
char status_text[len + 1];
status_text[len] = '\0';
fread(status_text, 1, len, status_file);
fclose(status_file);
return strcmp(status_text, "Full\n") == 0;
}
static int get_battery_percent() {
if (is_battery_full()) {
return 100;
}
FILE *battery_file = fopen(BATTERY_PATH "/charge_now", "r");
if (!battery_file) {
return -1;
}
fseeko(battery_file, 0, SEEK_END);
off_t len = ftello(battery_file);
rewind(battery_file);
char charge_text[len + 1];
charge_text[len] = '\0';
fread(charge_text, 1, len, battery_file);
fclose(battery_file);
long charge = strtoul(charge_text, NULL, 10);
float scale = (float)charge / (float)max_batery_charge;
return round(scale * 100.0f);
}
static int is_adapter_connected() {
FILE *adapter_file = fopen(adapter_status_path, "r");
if (!adapter_file) {
return -1;
}
char text[2] = {'\0', '\0'};
fread(text, 1, 1, adapter_file);
fclose(adapter_file);
return text[0] == '1'; /* 1 means connected, anything else is not */
}
static void find_adapter_status_path() {
size_t i;
for (i = 0; i < adapter_search_names_count; ++i) {
size_t name_len = strlen(adapter_search_names[i]);
/* -1 is to compensate for '\0' at end of "/online" */
char *name = qtb_malloc(sizeof(ADAPTER_BASE_PATH) + name_len + sizeof("/online") - 1);
sprintf(name, "%s%s/online", ADAPTER_BASE_PATH, adapter_search_names[i]);
FILE *file = fopen(name, "r");
if (file) {
adapter_status_path = name;
} else {
qtb_free(name);
}
}
}
void battery_module_init(void) {
find_max_battery_charge();
find_adapter_status_path();
}
static const char *get_battery_icon(int percent, int adapter) {
if (adapter) {
return "";
} else if (percent <= 10) {
return "";
} else if (percent <= 20) {
return "";
} else if (percent <= 30) {
return "";
} else if (percent <= 40) {
return "";
} else if (percent <= 50) {
return "";
} else if (percent <= 60) {
return "";
} else if (percent <= 70) {
return "";
} else if (percent <= 90) {
return "";
} else if (percent < 100) {
return "";
} else {
return "";
}
}
static void warn_low_battery() {
#ifdef HAS_X11
if (qtb_get_x_display()) { /* if x is running */
system("notify-send -t 0 'Battery is less than 10%!'");
} else {
#endif
system("wall -t 0 'Battery is less than 10%!'");
#ifdef HAS_X11
}
#endif
}
char *battery_module_poll() {
char *output = qtb_calloc(16, 1);
int percent = get_battery_percent();
int adapter = is_adapter_connected();
if (percent == -1 || adapter == -1) {
sprintf(output, "N/A%%");
} else {
const char *icon = get_battery_icon(percent, adapter);
sprintf(output, "%s%3d%%", icon, percent);
if (show_notifiaction && percent <= 10) {
warn_low_battery();
show_notifiaction = FALSE;
} else if (percent > 10) {
show_notifiaction = TRUE;
}
}
return output;
}

2
modules/battery.h Normal file
View File

@ -0,0 +1,2 @@
extern void battery_module_init(void);
extern char *battery_module_poll(void);

118
modules/bluetooth.c Normal file
View File

@ -0,0 +1,118 @@
#include "bluetooth.h"
#include "../util.h"
#include <dirent.h>
#include <gio/gio.h>
static long long powered_devices = 0;
static GPtrArray *get_bluetooth_device_paths() {
DIR *dir = opendir("/sys/class/bluetooth");
if (!dir) {
return NULL;
}
GPtrArray *arr = g_ptr_array_new();
struct dirent *current;
while ((current = readdir(dir))) {
if (strcmp(current->d_name, ".") != 0 &&
strcmp(current->d_name, "..") != 0) {
char *path = g_strdup_printf("/org/bluez/%s", current->d_name);
g_ptr_array_add(arr, path);
}
}
closedir(dir);
return arr;
}
static void load_initial_device_states(GDBusConnection *dbus,
GPtrArray *devices) {
gsize i;
for (i = 0; i < devices->len; ++i) {
const char *path = g_ptr_array_index(devices, i);
GError *err = NULL;
GVariant *response = g_dbus_connection_call_sync(
dbus, "org.bluez", path, "org.freedesktop.DBus.Properties", "Get",
g_variant_new_parsed("('org.bluez.Adapter1', 'Powered')"),
(GVariantType *)"(v)", G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
if (response) {
GVariant *wrapper = g_variant_get_child_value(response, 0);
GVariant *value = g_variant_get_child_value(wrapper, 0);
gboolean powered = g_variant_get_boolean(value);
if (powered) {
++powered_devices;
} /* sice powered_devices is already 0, we dont have to increment it
if the device is not powered */
g_variant_unref(value);
g_variant_unref(wrapper);
g_variant_unref(response);
}
}
qtb_signal_modules(4);
}
static void device_changed_callback(GDBusConnection *dbus,
const char *sender_name,
const char *object_path,
const char *interface_name,
const char *signal_name, GVariant *params,
gpointer user_data) {
GVariant *changed = g_variant_get_child_value(params, 1);
GVariant *value_wrapper = g_variant_lookup_value(changed, "Powered", G_VARIANT_TYPE_BOOLEAN);
if (value_wrapper) {
gboolean powered = g_variant_get_boolean(value_wrapper);
if (powered) {
++powered_devices;
} else {
--powered_devices;
}
g_variant_unref(value_wrapper);
}
g_variant_unref(changed);
qtb_signal_modules(4);
}
static void subscribe_to_events(GDBusConnection *dbus, GPtrArray *devices) {
gsize i;
for (i = 0; i < devices->len; ++i) {
const char *path = g_ptr_array_index(devices, i);
g_dbus_connection_signal_subscribe(
dbus, NULL, "org.freedesktop.DBus.Properties", "PropertiesChanged",
path, "org.bluez.Adapter1", G_DBUS_SIGNAL_FLAGS_NONE,
&device_changed_callback, NULL, NULL);
}
}
static void *thread_action() {
GMainContext *context = g_main_context_new();
g_main_context_push_thread_default(context);
GError *err = NULL;
GDBusConnection *dbus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
if (err) {
qtb_log("could not connect to bluez on the system bus: %s",
err->message);
qtb_die();
}
GPtrArray *devices = get_bluetooth_device_paths();
load_initial_device_states(dbus, devices);
subscribe_to_events(dbus, devices);
gsize i;
for (i = 0; i < devices->len; ++i) {
g_free(g_ptr_array_index(devices, i));
}
g_ptr_array_free(devices, TRUE);
GMainLoop *main_loop = g_main_loop_new(context, FALSE);
g_main_loop_run(main_loop);
return NULL;
}
void bluetooth_module_init() {
g_thread_new("bluetooth-module", &thread_action, NULL);
}
char *bluetooth_module_poll() {
char *output = NULL;
if (powered_devices != 0) {
output = qtb_strdup("");
}
return output;
}

2
modules/bluetooth.h Normal file
View File

@ -0,0 +1,2 @@
extern void bluetooth_module_init(void);
extern char *bluetooth_module_poll(void);

89
modules/fcitx4.c Normal file
View File

@ -0,0 +1,89 @@
#include "fcitx4.h"
#include "../util.h"
#include <gio/gio.h>
#include <stdio.h>
#include <string.h>
struct input_method {
const char *name;
const char *symbol;
};
const struct input_method input_methods[] = {
{"fcitx-keyboard-us", ""},
{"kkc", ""},
{"mozc", ""}
};
static const size_t im_count = sizeof(input_methods) / sizeof(struct input_method);
static const struct input_method *current_im = NULL;
static const struct input_method *find_im(const char *name) {
size_t i;
for (i = 0; i < im_count; ++i) {
if (strcmp(name, input_methods[i].name) == 0) {
return &input_methods[i];
}
}
return NULL;
}
static void set_current_im(GDBusConnection *dbus) {
current_im = NULL;
GVariant *tuple = g_dbus_connection_call_sync(
dbus, "org.fcitx.Fcitx", "/inputmethod", "org.fcitx.Fcitx.InputMethod",
"GetCurrentIM", NULL, G_VARIANT_TYPE_TUPLE, G_DBUS_CALL_FLAGS_NONE, -1,
NULL, NULL);
if (tuple) {
GVariant *response = g_variant_get_child_value(tuple, 0);
size_t response_len;
const char *response_im = g_variant_get_string(response, &response_len);
current_im = find_im(response_im);
g_variant_unref(response);
g_variant_unref(tuple);
}
}
static void im_changed_callback(GDBusConnection *dbus, const char *sender_name,
const char *object_path, const char *interface_name,
const char *signal_name, GVariant *params,
gpointer user_data) {
set_current_im(dbus);
qtb_signal_modules(3);
}
static void *thread_action(gpointer user_data) {
GMainContext *context = g_main_context_new();
g_main_context_push_thread_default(context);
GError *err = NULL;
GDBusConnection *dbus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err);
if (err) {
qtb_log("could not connect to fcitx on dbus: %s", err->message);
qtb_die();
}
set_current_im(dbus);
qtb_signal_modules(3);
g_dbus_connection_signal_subscribe(
dbus, NULL, "org.freedesktop.DBus.Properties", "PropertiesChanged",
"/inputmethod", "org.fcitx.Fcitx.InputMethod", G_DBUS_SIGNAL_FLAGS_NONE,
&im_changed_callback, NULL, NULL);
GMainLoop *main_loop = g_main_loop_new(context, FALSE);
g_main_loop_run(main_loop);
return NULL;
}
void fcitx4_module_init() {
g_thread_new("fcitx-module", &thread_action, NULL);
}
char *fcitx4_module_poll() {
const char *symbol;
if (current_im) {
symbol = current_im->symbol;
} else {
symbol = "";
}
size_t sym_len = strlen(symbol);
char *output = qtb_malloc(sizeof("") + sym_len);
sprintf(output, " %s", symbol);
return output;
}

2
modules/fcitx4.h Normal file
View File

@ -0,0 +1,2 @@
extern void fcitx4_module_init(void);
extern char *fcitx4_module_poll(void);

96
modules/linuxnm.c Normal file
View File

@ -0,0 +1,96 @@
#include "linuxnm.h"
#include "../util.h"
#include <NetworkManager.h>
#include <signal.h>
#include <string.h>
#define TYPE_NO_MANAGER 0x1
#define TYPE_WIRED 0x2
#define TYPE_WIRELESS 0x4
#define TYPE_VPN 0x8
typedef int connection_type;
static connection_type active_types;
static int is_connection_vpn(NMConnection *connection) {
if (nm_connection_get_setting_vpn(connection)) {
return TRUE;
}
const char *type = nm_connection_get_connection_type(connection);
return strcmp(type, "wireguard") == 0;
}
static void update_connections(NMClient *client) {
const GPtrArray *active_connections =
nm_client_get_active_connections(client);
guint i;
for (i = 0; i < active_connections->len; ++i) {
NMActiveConnection *current_active =
g_ptr_array_index(active_connections, i);
NMConnection *current =
NM_CONNECTION(nm_active_connection_get_connection(current_active));
const char *type = nm_connection_get_connection_type(current);
if (is_connection_vpn(current)) {
active_types |= TYPE_VPN;
} else if (nm_connection_get_setting_wired(current)) {
active_types |= TYPE_WIRED;
} else if (nm_connection_get_setting_wireless(current)) {
active_types |= TYPE_WIRELESS;
}
}
}
static void connections_changed(NMClient *client, GParamSpec *pspec,
gpointer user_data) {
active_types = 0;
if (!nm_client_get_nm_running(client)) {
active_types = TYPE_NO_MANAGER;
} else {
update_connections(client);
}
qtb_signal_modules(2);
}
static void *thread_action(gpointer user_data) {
GMainContext *context = g_main_context_new();
g_main_context_push_thread_default(context);
GError *err = NULL;
NMClient *client = nm_client_new(NULL, &err);
if (err) {
return err->message;
}
g_signal_connect(client, "notify::" NM_CLIENT_ACTIVE_CONNECTIONS,
G_CALLBACK(connections_changed), NULL);
g_signal_connect(client, "notify::" NM_CLIENT_NM_RUNNING,
G_CALLBACK(connections_changed), NULL);
connections_changed(client, NULL, NULL);
GMainLoop *main_loop = g_main_loop_new(context, FALSE);
g_main_loop_run(main_loop);
return NULL;
}
void linuxnm_module_init() {
g_thread_new("network-module", &thread_action, NULL);
}
char *linuxnm_module_poll() {
char *output = qtb_calloc(8, 1);
if ((active_types & TYPE_NO_MANAGER) == TYPE_NO_MANAGER) {
strcat(output, "");
}
if ((active_types & TYPE_VPN) == TYPE_VPN) {
strcat(output, "");
}
if ((active_types & TYPE_WIRED) == TYPE_WIRED) {
strcat(output, "");
}
if ((active_types & TYPE_WIRELESS) == TYPE_WIRELESS) {
strcat(output, "");
}
if (active_types == 0) { /* no valid connections found */
strcat(output, "");
}
return output;
}

2
modules/linuxnm.h Normal file
View File

@ -0,0 +1,2 @@
extern void linuxnm_module_init(void);
extern char *linuxnm_module_poll(void);

44
modules/optimus.c Normal file
View File

@ -0,0 +1,44 @@
#include "optimus.h"
#include "../util.h"
#include <stdlib.h>
static const char *card_string;
void optimus_module_init() {
int status = system("card_status=\"`optimus-manager --status 2>/dev/null | "
"grep -Po '(?<=Current GPU mode : )[a-z]'`\"\n"
"case \"$card_status\" in\n"
" 'i')\n"
" exit 1\n"
" ;;\n"
" 'h')\n"
" exit 2\n"
" ;;\n"
" 'n')\n"
" exit 3\n"
" ;;\n"
" *)\n"
" exit 4\n"
" ;;\n"
"esac\n");
switch (WEXITSTATUS(status)) {
case 1:
card_string = "";
break;
case 2:
card_string = "";
break;
case 3:
card_string = "";
break;
/* case 4: */
default:
card_string = "";
break;
}
}
char *optimus_module_poll() {
return qtb_strdup(card_string);
}

2
modules/optimus.h Normal file
View File

@ -0,0 +1,2 @@
extern void optimus_module_init(void);
extern char *optimus_module_poll(void);

63
modules/oss.c Normal file
View File

@ -0,0 +1,63 @@
#include "oss.h"
#include "../util.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/soundcard.h>
#include <stdio.h>
typedef struct {
int fd;
int pcm;
int vol;
int mute;
} mixer_data;
static mixer_data main_mixer;
#define ioctl_checked(fd, request, arg) _ioctl_checked(fd, request, #request, arg)
static void _ioctl_checked(int fd, unsigned long request, const char *rq_name, void *arg) {
if (ioctl(fd, request, arg) < 0) {
qtb_log("ioctl request %s failed", rq_name);
qtb_die();
}
}
static void open_mixer(const char *path, mixer_data *mixer) {
mixer->fd = open(path, O_RDONLY);
if (mixer->fd < 0) {
qtb_log("could not open mixer '%s'", path);
qtb_die();
}
}
static void read_mixer(mixer_data *mixer) {
ioctl_checked(mixer->fd, SOUND_MIXER_READ_PCM, &mixer->pcm);
mixer->pcm >>= 8;
ioctl_checked(mixer->fd, SOUND_MIXER_READ_VOLUME, &mixer->vol);
mixer->vol >>= 8;
mixer->mute = !(mixer->vol * mixer->pcm);
}
void oss_module_init() {
open_mixer("/dev/mixer", &main_mixer);
}
static const char *get_volume_icon(int volume, int mute) {
if (mute) {
return "";
} else if (volume > 50) {
return "";
} else if (volume > 0) {
return "奔";
}
return "";
}
char *oss_module_poll() {
read_mixer(&main_mixer);
const char *icon = get_volume_icon(main_mixer.vol, main_mixer.mute);
char *buff = qtb_malloc(32);
sprintf(buff, "%s%3d%%",icon, main_mixer.vol);
return buff;
}

2
modules/oss.h Normal file
View File

@ -0,0 +1,2 @@
extern void oss_module_init(void);
extern char *oss_module_poll(void);

132
modules/pulse.c Normal file
View File

@ -0,0 +1,132 @@
#include "pulse.h"
#include "../util.h"
#include <math.h>
#include <pthread.h>
#include <pulse/pulseaudio.h>
#include <stdio.h>
#include <string.h>
#define CONTEXT_NAME "quick-text-bar-audio"
#define CONTEXT_CONNECTING 0
#define CONTEXT_CONNECTED 1
#define CONTEXT_FAILED 2
static pa_threaded_mainloop *mainloop;
static pa_mainloop_api *mainloop_api;
static pa_context *context;
static int volume;
static int mute;
static void sink_info_callback(pa_context *ctx, const pa_sink_info *info,
int eol, void *user_data) {
if (info) {
pa_volume_t arg = pa_cvolume_avg(&info->volume);
float float_volume = ((float)arg / (float)PA_VOLUME_NORM) * 100.0f;
int new_volume = round(float_volume);
if (mute != info->mute || volume != new_volume) {
mute = info->mute;
volume = new_volume;
qtb_signal_modules(1);
}
}
}
static void server_info_callback(pa_context *ctx, const pa_server_info *info,
void *user_data) {
pa_operation *op = pa_context_get_sink_info_by_name(
ctx, info->default_sink_name, &sink_info_callback, NULL);
pa_operation_unref(op);
}
static void subscribe_callback(pa_context *ctx,
pa_subscription_event_type_t type, uint32_t idx,
void *user_data) {
unsigned int facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
pa_operation *op = NULL;
switch (facility) {
case PA_SUBSCRIPTION_EVENT_SINK:
op = pa_context_get_sink_info_by_index(ctx, idx, &sink_info_callback,
NULL);
break;
default:
break;
}
if (op) {
pa_operation_unref(op);
}
}
static void state_callback(pa_context *ctx, void *user_data) {
pa_operation *op;
switch (pa_context_get_state(ctx)) {
case PA_CONTEXT_READY:
op = pa_context_get_server_info(ctx, &server_info_callback, NULL);
pa_operation_unref(op);
pa_context_set_subscribe_callback(ctx, &subscribe_callback, NULL);
op = pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
pa_operation_unref(op);
break;
case PA_CONTEXT_FAILED:
volume = -1;
qtb_signal_modules(1);
break;
default:
break;
}
}
void pulse_module_init() {
mainloop = pa_threaded_mainloop_new();
if (!mainloop) {
qtb_log("could not create mainloop");
qtb_die();
}
mainloop_api = pa_threaded_mainloop_get_api(mainloop);
if (!mainloop_api) {
qtb_log("could not get api");
qtb_die();
}
context = pa_context_new(mainloop_api, CONTEXT_NAME);
if (!context) {
qtb_log("could not create context");
qtb_die();
}
pa_context_set_state_callback(
context, (pa_context_notify_cb_t)&state_callback, NULL);
if (pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) {
qtb_log("could not connect");
qtb_die();
}
if (pa_threaded_mainloop_start(mainloop) < 0) {
qtb_log("could not start mainloop");
qtb_die();
}
}
static const char *get_volume_icon() {
if (mute) {
return "";
} else if (volume > 50) {
return "";
} else if (volume > 0) {
return "奔";
}
return "";
}
char *pulse_module_poll() {
char *output = qtb_calloc(32, 1);
if (volume < 0) { /* not connected */
sprintf(output, "ﱝ ??%%");
pa_threaded_mainloop_stop(
mainloop); /* free the old mainloop and reset this module */
pa_threaded_mainloop_free(mainloop);
pulse_module_init();
} else {
const char *volume_icon = get_volume_icon();
sprintf(output, "%s%3d%%", volume_icon, volume);
}
return output;
}

3
modules/pulse.h Normal file
View File

@ -0,0 +1,3 @@
/* an example module that produces the current system volume */
extern void pulse_module_init(void);
extern char *pulse_module_poll(void);

21
modules/time.c Normal file
View File

@ -0,0 +1,21 @@
#include "time.h"
#include "../util.h"
#include <time.h>
#include <string.h>
#include <stdlib.h>
#define TIME_STRING_SIZE 64
#define TIME_FORMAT " %a %b %d  %R"
char *time_module_poll() {
time_t time_num = time(NULL);
struct tm time_tm;
localtime_r(&time_num, &time_tm);
char *output = qtb_malloc(TIME_STRING_SIZE);
int result = strftime(output, TIME_STRING_SIZE, TIME_FORMAT, &time_tm);
if (result == 0) {
strcpy(output, " ?? ?? ??  ??:??");
}
return output;
}

3
modules/time.h Normal file
View File

@ -0,0 +1,3 @@
/* an example module that produces the current time */
extern char *time_module_poll(void);