Refactoring
This commit is contained in:
parent
6852e25372
commit
1e45abe988
28 changed files with 931 additions and 836 deletions
|
@ -116,7 +116,9 @@ include_directories( ${PROJECT_BINARY_DIR}
|
|||
set( SOURCES src/config.c
|
||||
src/panel.c
|
||||
src/server.c
|
||||
src/tint.c
|
||||
src/main.c
|
||||
src/init.c
|
||||
src/signals.c
|
||||
src/mouse_actions.c
|
||||
src/drag_and_drop.c
|
||||
src/clock/clock.c
|
||||
|
@ -137,6 +139,7 @@ set( SOURCES src/config.c
|
|||
src/tint2rc.c
|
||||
src/util/area.c
|
||||
src/util/common.c
|
||||
src/util/fps_distribution.c
|
||||
src/util/strnatcmp.c
|
||||
src/util/timer.c
|
||||
src/util/cache.c
|
||||
|
|
|
@ -33,6 +33,8 @@ static int dnd_sent_request;
|
|||
static char *dnd_launcher_exec;
|
||||
static gboolean dnd_debug = FALSE;
|
||||
|
||||
gboolean hidden_panel_shown_for_dnd;
|
||||
|
||||
void dnd_init()
|
||||
{
|
||||
dnd_source_window = 0;
|
||||
|
@ -42,6 +44,7 @@ void dnd_init()
|
|||
dnd_atom = None;
|
||||
dnd_sent_request = 0;
|
||||
dnd_launcher_exec = 0;
|
||||
hidden_panel_shown_for_dnd = FALSE;
|
||||
}
|
||||
|
||||
void handle_dnd_enter(XClientMessageEvent *e)
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
#define DRAG_AND_DROP_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
extern gboolean hidden_panel_shown_for_dnd;
|
||||
|
||||
void dnd_init();
|
||||
|
||||
|
|
270
src/init.c
Normal file
270
src/init.c
Normal file
|
@ -0,0 +1,270 @@
|
|||
#include "init.h"
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "fps_distribution.h"
|
||||
#include "panel.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
#include "tooltip.h"
|
||||
#include "uevent.h"
|
||||
#include "version.h"
|
||||
|
||||
void print_usage()
|
||||
{
|
||||
printf("Usage: tint2 [OPTION...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -c path_to_config_file Loads the configuration file from a\n"
|
||||
" custom location.\n"
|
||||
" -v, --version Prints version information and exits.\n"
|
||||
" -h, --help Display this help and exits.\n"
|
||||
"\n"
|
||||
"For more information, run `man tint2` or visit the project page\n"
|
||||
"<https://gitlab.com/o9000/tint2>.\n");
|
||||
}
|
||||
|
||||
void handle_cli_arguments(int argc, char **argv)
|
||||
{
|
||||
// Read command line arguments
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
gboolean error = FALSE;
|
||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||
printf("tint2 version %s\n", VERSION_STRING);
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "-c") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
config_path = strdup(argv[i]);
|
||||
} else {
|
||||
error = TRUE;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-s") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
snapshot_path = strdup(argv[i]);
|
||||
} else {
|
||||
error = TRUE;
|
||||
}
|
||||
} else if (i + 1 == argc) {
|
||||
config_path = strdup(argv[i]);
|
||||
}
|
||||
#ifdef ENABLE_BATTERY
|
||||
else if (strcmp(argv[i], "--battery-sys-prefix") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
battery_sys_prefix = strdup(argv[i]);
|
||||
} else {
|
||||
error = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
error = TRUE;
|
||||
}
|
||||
if (error) {
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_env_vars()
|
||||
{
|
||||
debug_geometry = getenv("DEBUG_GEOMETRY") != NULL;
|
||||
debug_gradients = getenv("DEBUG_GRADIENTS") != NULL;
|
||||
debug_fps = getenv("DEBUG_FPS") != NULL;
|
||||
debug_frames = getenv("DEBUG_FRAMES") != NULL;
|
||||
if (debug_fps)
|
||||
init_fps_distribution();
|
||||
}
|
||||
|
||||
static timeout *detect_compositor_timer = NULL;
|
||||
static int detect_compositor_timer_counter = 0;
|
||||
|
||||
void detect_compositor(void *arg)
|
||||
{
|
||||
if (server.composite_manager) {
|
||||
stop_timeout(detect_compositor_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
detect_compositor_timer_counter--;
|
||||
if (detect_compositor_timer_counter < 0) {
|
||||
stop_timeout(detect_compositor_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
// No compositor, check for one
|
||||
if (XGetSelectionOwner(server.display, server.atom._NET_WM_CM_S0) != None) {
|
||||
stop_timeout(detect_compositor_timer);
|
||||
// Restart tint2
|
||||
fprintf(stderr, "Detected compositor, restarting tint2...\n");
|
||||
kill(getpid(), SIGUSR1);
|
||||
}
|
||||
}
|
||||
|
||||
void start_detect_compositor()
|
||||
{
|
||||
// Already have a compositor, nothing to do
|
||||
if (server.composite_manager)
|
||||
return;
|
||||
|
||||
stop_timeout(detect_compositor_timer);
|
||||
// Check every 0.5 seconds for up to 30 seconds
|
||||
detect_compositor_timer_counter = 60;
|
||||
detect_compositor_timer = add_timeout(500, 500, detect_compositor, 0, &detect_compositor_timer);
|
||||
}
|
||||
|
||||
void create_default_elements()
|
||||
{
|
||||
default_config();
|
||||
default_timeout();
|
||||
default_systray();
|
||||
memset(&server, 0, sizeof(server));
|
||||
#ifdef ENABLE_BATTERY
|
||||
default_battery();
|
||||
#endif
|
||||
default_clock();
|
||||
default_launcher();
|
||||
default_taskbar();
|
||||
default_tooltip();
|
||||
default_execp();
|
||||
default_button();
|
||||
default_panel();
|
||||
}
|
||||
|
||||
void init_post_config()
|
||||
{
|
||||
server_init_visual();
|
||||
server_init_xdamage();
|
||||
|
||||
imlib_context_set_display(server.display);
|
||||
imlib_context_set_visual(server.visual);
|
||||
imlib_context_set_colormap(server.colormap);
|
||||
|
||||
init_signals_postconfig();
|
||||
|
||||
// load default icon
|
||||
const gchar *const *data_dirs = g_get_system_data_dirs();
|
||||
for (int i = 0; data_dirs[i] != NULL; i++) {
|
||||
gchar *path = g_build_filename(data_dirs[i], "tint2", "default_icon.png", NULL);
|
||||
if (g_file_test(path, G_FILE_TEST_EXISTS))
|
||||
default_icon = load_image(path, TRUE);
|
||||
g_free(path);
|
||||
}
|
||||
if (!default_icon) {
|
||||
fprintf(stderr,
|
||||
RED "Could not load default_icon.png. Please check that tint2 has been installed correctly!" RESET
|
||||
"\n");
|
||||
}
|
||||
}
|
||||
|
||||
void init_X11_pre_config()
|
||||
{
|
||||
server.display = XOpenDisplay(NULL);
|
||||
if (!server.display) {
|
||||
fprintf(stderr, "tint2: could not open display.\n");
|
||||
exit(1);
|
||||
}
|
||||
server.x11_fd = ConnectionNumber(server.display);
|
||||
XSetErrorHandler((XErrorHandler)server_catch_error);
|
||||
XSetIOErrorHandler((XIOErrorHandler)x11_io_error);
|
||||
server_init_atoms();
|
||||
server.screen = DefaultScreen(server.display);
|
||||
server.root_win = RootWindow(server.display, server.screen);
|
||||
server.desktop = get_current_desktop();
|
||||
|
||||
// Needed since the config file uses '.' as decimal separator
|
||||
setlocale(LC_ALL, "");
|
||||
setlocale(LC_NUMERIC, "POSIX");
|
||||
|
||||
/* Catch events */
|
||||
XSelectInput(server.display, server.root_win, PropertyChangeMask | StructureNotifyMask);
|
||||
|
||||
// get monitor and desktop config
|
||||
get_monitors();
|
||||
get_desktops();
|
||||
|
||||
server.disable_transparency = 0;
|
||||
|
||||
xsettings_client = xsettings_client_new(server.display, server.screen, xsettings_notify_cb, NULL, NULL);
|
||||
}
|
||||
|
||||
void init(int argc, char **argv)
|
||||
{
|
||||
setlinebuf(stdout);
|
||||
setlinebuf(stderr);
|
||||
handle_cli_arguments(argc, argv);
|
||||
create_default_elements();
|
||||
init_signals();
|
||||
handle_env_vars();
|
||||
|
||||
init_X11_pre_config();
|
||||
if (!config_read()) {
|
||||
fprintf(stderr, "Could not read config file.\n");
|
||||
print_usage();
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
init_post_config();
|
||||
start_detect_compositor();
|
||||
init_panel();
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
#ifdef HAVE_SN
|
||||
if (startup_notifications) {
|
||||
sn_display_unref(server.sn_display);
|
||||
server.sn_display = NULL;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
|
||||
cleanup_button();
|
||||
cleanup_execp();
|
||||
cleanup_systray();
|
||||
cleanup_tooltip();
|
||||
cleanup_clock();
|
||||
cleanup_launcher();
|
||||
#ifdef ENABLE_BATTERY
|
||||
cleanup_battery();
|
||||
#endif
|
||||
cleanup_panel();
|
||||
cleanup_config();
|
||||
|
||||
if (default_icon) {
|
||||
imlib_context_set_image(default_icon);
|
||||
imlib_free_image();
|
||||
default_icon = NULL;
|
||||
}
|
||||
imlib_context_disconnect_display();
|
||||
|
||||
xsettings_client_destroy(xsettings_client);
|
||||
xsettings_client = NULL;
|
||||
|
||||
cleanup_server();
|
||||
cleanup_timeout();
|
||||
|
||||
if (server.display)
|
||||
XCloseDisplay(server.display);
|
||||
server.display = NULL;
|
||||
|
||||
if (sigchild_pipe_valid) {
|
||||
sigchild_pipe_valid = FALSE;
|
||||
close(sigchild_pipe[1]);
|
||||
close(sigchild_pipe[0]);
|
||||
}
|
||||
|
||||
uevent_cleanup();
|
||||
cleanup_fps_distribution();
|
||||
}
|
7
src/init.h
Normal file
7
src/init.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#ifndef INIT_H
|
||||
#define INIT_H
|
||||
|
||||
void init(int argc, char **argv);
|
||||
void cleanup();
|
||||
|
||||
#endif
|
|
@ -43,627 +43,33 @@
|
|||
#include <libsn/sn.h>
|
||||
#endif
|
||||
|
||||
#include <version.h>
|
||||
#include "server.h"
|
||||
#include "window.h"
|
||||
#include "config.h"
|
||||
#include "drag_and_drop.h"
|
||||
#include "task.h"
|
||||
#include "taskbar.h"
|
||||
#include "systraybar.h"
|
||||
#include "fps_distribution.h"
|
||||
#include "init.h"
|
||||
#include "launcher.h"
|
||||
#include "mouse_actions.h"
|
||||
#include "panel.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
#include "systraybar.h"
|
||||
#include "task.h"
|
||||
#include "taskbar.h"
|
||||
#include "tooltip.h"
|
||||
#include "timer.h"
|
||||
#include "xsettings-client.h"
|
||||
#include "uevent.h"
|
||||
|
||||
#ifdef ENABLE_LIBUNWIND
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#else
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#endif
|
||||
#include "version.h"
|
||||
#include "window.h"
|
||||
#include "xsettings-client.h"
|
||||
|
||||
// Global process state variables
|
||||
|
||||
static gboolean hidden_dnd;
|
||||
|
||||
XSettingsClient *xsettings_client = NULL;
|
||||
|
||||
timeout *detect_compositor_timer = NULL;
|
||||
int detect_compositor_timer_counter = 0;
|
||||
|
||||
gboolean debug_fps = FALSE;
|
||||
gboolean debug_frames = FALSE;
|
||||
float *fps_distribution = NULL;
|
||||
int frame = 0;
|
||||
|
||||
void create_fps_distribution()
|
||||
{
|
||||
// measure FPS with resolution:
|
||||
// 0-59: 1 (60 samples)
|
||||
// 60-199: 10 (14)
|
||||
// 200-1,999: 25 (72)
|
||||
// 1k-19,999: 1000 (19)
|
||||
// 20x+: inf (1)
|
||||
// => 166 samples
|
||||
if (fps_distribution)
|
||||
return;
|
||||
fps_distribution = calloc(170, sizeof(float));
|
||||
}
|
||||
|
||||
void cleanup_fps_distribution()
|
||||
{
|
||||
free(fps_distribution);
|
||||
fps_distribution = NULL;
|
||||
}
|
||||
|
||||
void sample_fps(double fps)
|
||||
{
|
||||
int fps_rounded = (int)(fps + 0.5);
|
||||
int i = 1;
|
||||
if (fps_rounded < 60) {
|
||||
i += fps_rounded;
|
||||
} else {
|
||||
i += 60;
|
||||
if (fps_rounded < 200) {
|
||||
i += (fps_rounded - 60) / 10;
|
||||
} else {
|
||||
i += 14;
|
||||
if (fps_rounded < 2000) {
|
||||
i += (fps_rounded - 200) / 25;
|
||||
} else {
|
||||
i += 72;
|
||||
if (fps_rounded < 20000) {
|
||||
i += (fps_rounded - 2000) / 1000;
|
||||
} else {
|
||||
i += 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fprintf(stderr, "fps = %.0f => i = %d\n", fps, i);
|
||||
fps_distribution[i] += 1.;
|
||||
fps_distribution[0] += 1.;
|
||||
}
|
||||
|
||||
void fps_compute_stats(double *low, double *median, double *high, double *samples)
|
||||
{
|
||||
*median = *low = *high = *samples = -1;
|
||||
if (!fps_distribution || fps_distribution[0] < 1)
|
||||
return;
|
||||
float total = fps_distribution[0];
|
||||
*samples = (double)fps_distribution[0];
|
||||
float cum_low = 0.05f * total;
|
||||
float cum_median = 0.5f * total;
|
||||
float cum_high = 0.95f * total;
|
||||
float cum = 0;
|
||||
for (int i = 1; i <= 166; i++) {
|
||||
double value =
|
||||
(i < 60) ? i : (i < 74) ? (60 + (i - 60) * 10) : (i < 146) ? (200 + (i - 74) * 25)
|
||||
: (i < 165) ? (2000 + (i - 146) * 1000) : 20000;
|
||||
// fprintf(stderr, "%6.0f (i = %3d) : %.0f | ", value, i, (double)fps_distribution[i]);
|
||||
cum += fps_distribution[i];
|
||||
if (*low < 0 && cum >= cum_low)
|
||||
*low = value;
|
||||
if (*median < 0 && cum >= cum_median)
|
||||
*median = value;
|
||||
if (*high < 0 && cum >= cum_high)
|
||||
*high = value;
|
||||
}
|
||||
}
|
||||
|
||||
void detect_compositor(void *arg)
|
||||
{
|
||||
if (server.composite_manager) {
|
||||
stop_timeout(detect_compositor_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
detect_compositor_timer_counter--;
|
||||
if (detect_compositor_timer_counter < 0) {
|
||||
stop_timeout(detect_compositor_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
// No compositor, check for one
|
||||
if (XGetSelectionOwner(server.display, server.atom._NET_WM_CM_S0) != None) {
|
||||
stop_timeout(detect_compositor_timer);
|
||||
// Restart tint2
|
||||
fprintf(stderr, "Detected compositor, restarting tint2...\n");
|
||||
kill(getpid(), SIGUSR1);
|
||||
}
|
||||
}
|
||||
|
||||
void start_detect_compositor()
|
||||
{
|
||||
// Already have a compositor, nothing to do
|
||||
if (server.composite_manager)
|
||||
return;
|
||||
|
||||
stop_timeout(detect_compositor_timer);
|
||||
// Check every 0.5 seconds for up to 30 seconds
|
||||
detect_compositor_timer_counter = 60;
|
||||
detect_compositor_timer = add_timeout(500, 500, detect_compositor, 0, &detect_compositor_timer);
|
||||
}
|
||||
|
||||
void signal_handler(int sig)
|
||||
{
|
||||
// signal handler is light as it should be
|
||||
signal_pending = sig;
|
||||
}
|
||||
|
||||
void write_string(int fd, const char *s)
|
||||
{
|
||||
int len = strlen(s);
|
||||
while (len > 0) {
|
||||
int count = write(fd, s, len);
|
||||
if (count >= 0) {
|
||||
s += count;
|
||||
len -= count;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *signal_name(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGHUP:
|
||||
return "SIGHUP: Hangup (POSIX).";
|
||||
case SIGINT:
|
||||
return "SIGINT: Interrupt (ANSI).";
|
||||
case SIGQUIT:
|
||||
return "SIGQUIT: Quit (POSIX).";
|
||||
case SIGILL:
|
||||
return "SIGILL: Illegal instruction (ANSI).";
|
||||
case SIGTRAP:
|
||||
return "SIGTRAP: Trace trap (POSIX).";
|
||||
case SIGABRT:
|
||||
return "SIGABRT/SIGIOT: Abort (ANSI) / IOT trap (4.2 BSD).";
|
||||
case SIGBUS:
|
||||
return "SIGBUS: BUS error (4.2 BSD).";
|
||||
case SIGFPE:
|
||||
return "SIGFPE: Floating-point exception (ANSI).";
|
||||
case SIGKILL:
|
||||
return "SIGKILL: Kill, unblockable (POSIX).";
|
||||
case SIGUSR1:
|
||||
return "SIGUSR1: User-defined signal 1 (POSIX).";
|
||||
case SIGSEGV:
|
||||
return "SIGSEGV: Segmentation violation (ANSI).";
|
||||
case SIGUSR2:
|
||||
return "SIGUSR2: User-defined signal 2 (POSIX).";
|
||||
case SIGPIPE:
|
||||
return "SIGPIPE: Broken pipe (POSIX).";
|
||||
case SIGALRM:
|
||||
return "SIGALRM: Alarm clock (POSIX).";
|
||||
case SIGTERM:
|
||||
return "SIGTERM: Termination (ANSI).";
|
||||
// case SIGSTKFLT: return "SIGSTKFLT: Stack fault.";
|
||||
case SIGCHLD:
|
||||
return "SIGCHLD: Child status has changed (POSIX).";
|
||||
case SIGCONT:
|
||||
return "SIGCONT: Continue (POSIX).";
|
||||
case SIGSTOP:
|
||||
return "SIGSTOP: Stop, unblockable (POSIX).";
|
||||
case SIGTSTP:
|
||||
return "SIGTSTP: Keyboard stop (POSIX).";
|
||||
case SIGTTIN:
|
||||
return "SIGTTIN: Background read from tty (POSIX).";
|
||||
case SIGTTOU:
|
||||
return "SIGTTOU: Background write to tty (POSIX).";
|
||||
case SIGURG:
|
||||
return "SIGURG: Urgent condition on socket (4.2 BSD).";
|
||||
case SIGXCPU:
|
||||
return "SIGXCPU: CPU limit exceeded (4.2 BSD).";
|
||||
case SIGXFSZ:
|
||||
return "SIGXFSZ: File size limit exceeded (4.2 BSD).";
|
||||
case SIGVTALRM:
|
||||
return "SIGVTALRM: Virtual alarm clock (4.2 BSD).";
|
||||
case SIGPROF:
|
||||
return "SIGPROF: Profiling alarm clock (4.2 BSD).";
|
||||
// case SIGPWR: return "SIGPWR: Power failure restart (System V).";
|
||||
case SIGSYS:
|
||||
return "SIGSYS: Bad system call.";
|
||||
}
|
||||
static char s[64];
|
||||
sprintf(s, "SIG=%d: Unknown", sig);
|
||||
return s;
|
||||
}
|
||||
|
||||
void log_string(int fd, const char *s)
|
||||
{
|
||||
write_string(2, s);
|
||||
write_string(fd, s);
|
||||
}
|
||||
|
||||
const char *get_home_dir()
|
||||
{
|
||||
const char *s = getenv("HOME");
|
||||
if (s)
|
||||
return s;
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (!pw)
|
||||
return NULL;
|
||||
return pw->pw_dir;
|
||||
}
|
||||
|
||||
void dump_backtrace(int log_fd)
|
||||
{
|
||||
#ifndef DISABLE_BACKTRACE
|
||||
log_string(log_fd, "\n" YELLOW "Backtrace:" RESET "\n");
|
||||
|
||||
#ifdef ENABLE_LIBUNWIND
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t context;
|
||||
unw_getcontext(&context);
|
||||
unw_init_local(&cursor, &context);
|
||||
|
||||
while (unw_step(&cursor) > 0) {
|
||||
unw_word_t offset;
|
||||
char fname[128];
|
||||
fname[0] = '\0';
|
||||
(void)unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
|
||||
log_string(log_fd, fname);
|
||||
log_string(log_fd, "\n");
|
||||
}
|
||||
#else
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#define MAX_TRACE_SIZE 128
|
||||
void *array[MAX_TRACE_SIZE];
|
||||
size_t size = backtrace(array, MAX_TRACE_SIZE);
|
||||
char **strings = backtrace_symbols(array, size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
log_string(log_fd, strings[i]);
|
||||
log_string(log_fd, "\n");
|
||||
}
|
||||
|
||||
free(strings);
|
||||
#endif // ENABLE_EXECINFO
|
||||
#endif // ENABLE_LIBUNWIND
|
||||
#endif // DISABLE_BACKTRACE
|
||||
}
|
||||
|
||||
// sleep() returns early when signals arrive. This function does not.
|
||||
void safe_sleep(int seconds)
|
||||
{
|
||||
double t0 = get_time();
|
||||
while (1) {
|
||||
double t = get_time();
|
||||
if (t > t0 + seconds)
|
||||
return;
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_crash(const char *reason)
|
||||
{
|
||||
#ifndef DISABLE_BACKTRACE
|
||||
char path[4096];
|
||||
sprintf(path, "%s/.tint2-crash.log", get_home_dir());
|
||||
int log_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
log_string(log_fd, RED "tint2 crashed, reason: ");
|
||||
log_string(log_fd, reason);
|
||||
log_string(log_fd, RESET "\n");
|
||||
dump_backtrace(log_fd);
|
||||
log_string(log_fd, RED "Please create a bug report with this log output." RESET "\n");
|
||||
close(log_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BACKTRACE_ON_SIGNAL
|
||||
void crash_handler(int sig)
|
||||
{
|
||||
handle_crash(signal_name(sig));
|
||||
struct sigaction sa = {.sa_handler = SIG_DFL};
|
||||
sigaction(sig, &sa, 0);
|
||||
raise(sig);
|
||||
}
|
||||
#endif
|
||||
|
||||
void x11_io_error(Display *display)
|
||||
{
|
||||
handle_crash("X11 I/O error");
|
||||
}
|
||||
|
||||
void print_usage()
|
||||
{
|
||||
printf("Usage: tint2 [OPTION...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -c path_to_config_file Loads the configuration file from a\n"
|
||||
" custom location.\n"
|
||||
" -v, --version Prints version information and exits.\n"
|
||||
" -h, --help Display this help and exits.\n"
|
||||
"\n"
|
||||
"For more information, run `man tint2` or visit the project page\n"
|
||||
"<https://gitlab.com/o9000/tint2>.\n");
|
||||
}
|
||||
|
||||
void init(int argc, char *argv[])
|
||||
{
|
||||
// Make stdout/stderr flush after a newline (for some reason they don't even if tint2 is started from a terminal)
|
||||
setlinebuf(stdout);
|
||||
setlinebuf(stderr);
|
||||
|
||||
// set global data
|
||||
default_config();
|
||||
default_timeout();
|
||||
default_systray();
|
||||
memset(&server, 0, sizeof(server));
|
||||
#ifdef ENABLE_BATTERY
|
||||
default_battery();
|
||||
#endif
|
||||
default_clock();
|
||||
default_launcher();
|
||||
default_taskbar();
|
||||
default_tooltip();
|
||||
default_execp();
|
||||
default_button();
|
||||
default_panel();
|
||||
|
||||
// Read command line arguments
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
int error = 0;
|
||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||
printf("tint2 version %s\n", VERSION_STRING);
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "-c") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
config_path = strdup(argv[i]);
|
||||
} else {
|
||||
error = 1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-s") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
snapshot_path = strdup(argv[i]);
|
||||
} else {
|
||||
error = 1;
|
||||
}
|
||||
} else if (i + 1 == argc) {
|
||||
config_path = strdup(argv[i]);
|
||||
}
|
||||
#ifdef ENABLE_BATTERY
|
||||
else if (strcmp(argv[i], "--battery-sys-prefix") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
battery_sys_prefix = strdup(argv[i]);
|
||||
} else {
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
else {
|
||||
error = 1;
|
||||
}
|
||||
if (error) {
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// Set signal handlers
|
||||
signal_pending = 0;
|
||||
|
||||
struct sigaction sa_chld = {.sa_handler = SIG_DFL, .sa_flags = SA_NOCLDWAIT | SA_RESTART};
|
||||
sigaction(SIGCHLD, &sa_chld, 0);
|
||||
|
||||
struct sigaction sa = {.sa_handler = signal_handler, .sa_flags = SA_RESTART};
|
||||
sigaction(SIGUSR1, &sa, 0);
|
||||
sigaction(SIGUSR2, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
|
||||
#ifdef BACKTRACE_ON_SIGNAL
|
||||
struct sigaction sa_crash = {.sa_handler = crash_handler};
|
||||
sigaction(SIGSEGV, &sa_crash, 0);
|
||||
sigaction(SIGFPE, &sa_crash, 0);
|
||||
sigaction(SIGPIPE, &sa_crash, 0);
|
||||
sigaction(SIGBUS, &sa_crash, 0);
|
||||
sigaction(SIGABRT, &sa_crash, 0);
|
||||
sigaction(SIGSYS, &sa_crash, 0);
|
||||
#endif
|
||||
|
||||
debug_geometry = getenv("DEBUG_GEOMETRY") != NULL;
|
||||
debug_gradients = getenv("DEBUG_GRADIENTS") != NULL;
|
||||
debug_fps = getenv("DEBUG_FPS") != NULL;
|
||||
debug_frames = getenv("DEBUG_FRAMES") != NULL;
|
||||
if (debug_fps)
|
||||
create_fps_distribution();
|
||||
}
|
||||
|
||||
static int sigchild_pipe_valid = FALSE;
|
||||
static int sigchild_pipe[2];
|
||||
|
||||
#ifdef HAVE_SN
|
||||
static int error_trap_depth = 0;
|
||||
|
||||
static void error_trap_push(SnDisplay *display, Display *xdisplay)
|
||||
{
|
||||
++error_trap_depth;
|
||||
}
|
||||
|
||||
static void error_trap_pop(SnDisplay *display, Display *xdisplay)
|
||||
{
|
||||
if (error_trap_depth == 0) {
|
||||
fprintf(stderr, "Error trap underflow!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
XSync(xdisplay, False); /* get all errors out of the queue */
|
||||
--error_trap_depth;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
|
||||
static void sigchld_handler(int sig)
|
||||
{
|
||||
if (!sigchild_pipe_valid)
|
||||
return;
|
||||
int savedErrno = errno;
|
||||
ssize_t unused = write(sigchild_pipe[1], "x", 1);
|
||||
(void)unused;
|
||||
fsync(sigchild_pipe[1]);
|
||||
errno = savedErrno;
|
||||
}
|
||||
|
||||
static void sigchld_handler_async()
|
||||
{
|
||||
// Wait for all dead processes
|
||||
pid_t pid;
|
||||
int status;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
|
||||
#ifdef HAVE_SN
|
||||
if (startup_notifications) {
|
||||
SnLauncherContext *ctx = (SnLauncherContext *)g_tree_lookup(server.pids, GINT_TO_POINTER(pid));
|
||||
if (ctx) {
|
||||
g_tree_remove(server.pids, GINT_TO_POINTER(pid));
|
||||
sn_launcher_context_complete(ctx);
|
||||
sn_launcher_context_unref(ctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (GList *l = panel_config.execp_list; l; l = l->next) {
|
||||
Execp *execp = (Execp *)l->data;
|
||||
if (g_tree_lookup(execp->backend->cmd_pids, GINT_TO_POINTER(pid)))
|
||||
execp_cmd_completed(execp, pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_X11_pre_config()
|
||||
{
|
||||
server.display = XOpenDisplay(NULL);
|
||||
if (!server.display) {
|
||||
fprintf(stderr, "tint2: could not open display.\n");
|
||||
exit(1);
|
||||
}
|
||||
XSetErrorHandler((XErrorHandler)server_catch_error);
|
||||
XSetIOErrorHandler((XIOErrorHandler)x11_io_error);
|
||||
server_init_atoms();
|
||||
server.screen = DefaultScreen(server.display);
|
||||
server.root_win = RootWindow(server.display, server.screen);
|
||||
server.desktop = get_current_desktop();
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
// config file use '.' as decimal separator
|
||||
setlocale(LC_NUMERIC, "POSIX");
|
||||
|
||||
/* Catch events */
|
||||
XSelectInput(server.display, server.root_win, PropertyChangeMask | StructureNotifyMask);
|
||||
|
||||
// get monitor and desktop config
|
||||
get_monitors();
|
||||
get_desktops();
|
||||
|
||||
server.disable_transparency = 0;
|
||||
|
||||
xsettings_client = xsettings_client_new(server.display, server.screen, xsettings_notify_cb, NULL, NULL);
|
||||
}
|
||||
|
||||
void init_X11_post_config()
|
||||
{
|
||||
server_init_visual();
|
||||
server_init_xdamage();
|
||||
|
||||
gboolean need_sigchld = FALSE;
|
||||
#ifdef HAVE_SN
|
||||
// Initialize startup-notification
|
||||
if (startup_notifications) {
|
||||
server.sn_display = sn_display_new(server.display, error_trap_push, error_trap_pop);
|
||||
server.pids = g_tree_new(cmp_ptr);
|
||||
need_sigchld = TRUE;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
if (panel_config.execp_list)
|
||||
need_sigchld = TRUE;
|
||||
|
||||
if (need_sigchld) {
|
||||
// Setup a handler for child termination
|
||||
if (pipe(sigchild_pipe) != 0) {
|
||||
fprintf(stderr, "Creating pipe failed.\n");
|
||||
} else {
|
||||
fcntl(sigchild_pipe[0], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[0], F_GETFL));
|
||||
fcntl(sigchild_pipe[1], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[1], F_GETFL));
|
||||
sigchild_pipe_valid = 1;
|
||||
struct sigaction act = {.sa_handler = sigchld_handler, .sa_flags = SA_RESTART};
|
||||
if (sigaction(SIGCHLD, &act, 0)) {
|
||||
perror("sigaction");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imlib_context_set_display(server.display);
|
||||
imlib_context_set_visual(server.visual);
|
||||
imlib_context_set_colormap(server.colormap);
|
||||
|
||||
// load default icon
|
||||
const gchar *const *data_dirs = g_get_system_data_dirs();
|
||||
for (int i = 0; data_dirs[i] != NULL; i++) {
|
||||
gchar *path = g_build_filename(data_dirs[i], "tint2", "default_icon.png", NULL);
|
||||
if (g_file_test(path, G_FILE_TEST_EXISTS))
|
||||
default_icon = load_image(path, TRUE);
|
||||
g_free(path);
|
||||
}
|
||||
if (!default_icon) {
|
||||
fprintf(stderr,
|
||||
RED "Could not load default_icon.png. Please check that tint2 has been installed correctly!" RESET
|
||||
"\n");
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
cleanup_button();
|
||||
cleanup_execp();
|
||||
cleanup_systray();
|
||||
cleanup_tooltip();
|
||||
cleanup_clock();
|
||||
cleanup_launcher();
|
||||
#ifdef ENABLE_BATTERY
|
||||
cleanup_battery();
|
||||
#endif
|
||||
cleanup_panel();
|
||||
cleanup_config();
|
||||
|
||||
if (default_icon) {
|
||||
imlib_context_set_image(default_icon);
|
||||
imlib_free_image();
|
||||
default_icon = NULL;
|
||||
}
|
||||
imlib_context_disconnect_display();
|
||||
|
||||
xsettings_client_destroy(xsettings_client);
|
||||
xsettings_client = NULL;
|
||||
|
||||
cleanup_server();
|
||||
cleanup_timeout();
|
||||
|
||||
if (server.display)
|
||||
XCloseDisplay(server.display);
|
||||
server.display = NULL;
|
||||
|
||||
if (sigchild_pipe_valid) {
|
||||
sigchild_pipe_valid = FALSE;
|
||||
close(sigchild_pipe[1]);
|
||||
close(sigchild_pipe[0]);
|
||||
}
|
||||
|
||||
uevent_cleanup();
|
||||
cleanup_fps_distribution();
|
||||
}
|
||||
|
||||
void dump_panel_to_file(const Panel *panel, const char *path)
|
||||
{
|
||||
imlib_context_set_drawable(panel->temp_pmap);
|
||||
|
@ -705,7 +111,7 @@ void dump_panel_to_file(const Panel *panel, const char *path)
|
|||
}
|
||||
}
|
||||
|
||||
void get_snapshot(const char *path)
|
||||
void save_screenshot(const char *path)
|
||||
{
|
||||
Panel *panel = &panels[0];
|
||||
|
||||
|
@ -721,51 +127,7 @@ void get_snapshot(const char *path)
|
|||
dump_panel_to_file(panel, path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void update_desktop_names()
|
||||
{
|
||||
if (!taskbarname_enabled)
|
||||
return;
|
||||
GSList *list = get_desktop_names();
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
int j;
|
||||
GSList *l;
|
||||
for (j = 0, l = list; j < panels[i].num_desktops; j++) {
|
||||
gchar *name;
|
||||
if (l) {
|
||||
name = g_strdup(l->data);
|
||||
l = l->next;
|
||||
} else {
|
||||
name = g_strdup_printf("%d", j + 1);
|
||||
}
|
||||
Taskbar *taskbar = &panels[i].taskbar[j];
|
||||
if (strcmp(name, taskbar->bar_name.name) != 0) {
|
||||
g_free(taskbar->bar_name.name);
|
||||
taskbar->bar_name.name = name;
|
||||
taskbar->bar_name.area.resize_needed = 1;
|
||||
} else {
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (GSList *l = list; l; l = l->next)
|
||||
g_free(l->data);
|
||||
g_slist_free(list);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
void update_task_desktop(Task *task)
|
||||
{
|
||||
// fprintf(stderr, "%s %d:\n", __FUNCTION__, __LINE__);
|
||||
Window win = task->win;
|
||||
remove_task(task);
|
||||
task = add_task(win);
|
||||
reset_active_task();
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
void event_property_notify(XEvent *e)
|
||||
void handle_event_property_notify(XEvent *e)
|
||||
{
|
||||
gboolean debug = FALSE;
|
||||
|
||||
|
@ -880,7 +242,7 @@ void event_property_notify(XEvent *e)
|
|||
}
|
||||
for (l = need_update; l; l = l->next) {
|
||||
Task *task = l->data;
|
||||
update_task_desktop(task);
|
||||
task_update_desktop(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -995,7 +357,7 @@ void event_property_notify(XEvent *e)
|
|||
// printf(" Window desktop changed %d, %d\n", task->desktop, desktop);
|
||||
// bug in windowmaker : send unecessary 'desktop changed' when focus changed
|
||||
if (desktop != task->desktop) {
|
||||
update_task_desktop(task);
|
||||
task_update_desktop(task);
|
||||
}
|
||||
} else if (at == server.atom.WM_HINTS) {
|
||||
XWMHints *wmhints = XGetWMHints(server.display, win);
|
||||
|
@ -1012,7 +374,7 @@ void event_property_notify(XEvent *e)
|
|||
}
|
||||
}
|
||||
|
||||
void event_expose(XEvent *e)
|
||||
void handle_event_expose(XEvent *e)
|
||||
{
|
||||
Panel *panel;
|
||||
panel = get_panel(e->xany.window);
|
||||
|
@ -1022,27 +384,13 @@ void event_expose(XEvent *e)
|
|||
schedule_panel_redraw();
|
||||
}
|
||||
|
||||
void event_configure_notify(XEvent *e)
|
||||
void handle_event_configure_notify(XEvent *e)
|
||||
{
|
||||
Window win = e->xconfigure.window;
|
||||
|
||||
if (0) {
|
||||
Task *task = get_task(win);
|
||||
fprintf(stderr,
|
||||
"%s %d: win = %ld, task = %s\n",
|
||||
__FUNCTION__,
|
||||
__LINE__,
|
||||
win,
|
||||
task ? (task->title ? task->title : "??") : "null");
|
||||
}
|
||||
|
||||
// change in root window (xrandr)
|
||||
if (win == server.root_win) {
|
||||
fprintf(stderr,
|
||||
YELLOW "%s %d: triggering tint2 restart due to configuration change in the root window" RESET "\n",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
signal_pending = SIGUSR1;
|
||||
emit_self_restart("configuration change in the root window");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1077,7 +425,7 @@ void event_configure_notify(XEvent *e)
|
|||
if (task) {
|
||||
int desktop = get_window_desktop(win);
|
||||
if (task->desktop != desktop) {
|
||||
update_task_desktop(task);
|
||||
task_update_desktop(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1095,15 +443,15 @@ gboolean handle_x_event_autohide(XEvent *e)
|
|||
autohide_trigger_hide(panel);
|
||||
if (panel->is_hidden) {
|
||||
if (e->type == ClientMessage && e->xclient.message_type == server.atom.XdndPosition) {
|
||||
hidden_dnd = TRUE;
|
||||
hidden_panel_shown_for_dnd = TRUE;
|
||||
autohide_show(panel);
|
||||
} else {
|
||||
// discard further processing of this event because the panel is not visible yet
|
||||
return TRUE;
|
||||
}
|
||||
} else if (hidden_dnd && e->type == ClientMessage &&
|
||||
} else if (hidden_panel_shown_for_dnd && e->type == ClientMessage &&
|
||||
e->xclient.message_type == server.atom.XdndLeave) {
|
||||
hidden_dnd = FALSE;
|
||||
hidden_panel_shown_for_dnd = FALSE;
|
||||
autohide_hide(panel);
|
||||
}
|
||||
}
|
||||
|
@ -1162,15 +510,15 @@ void handle_x_event(XEvent *e)
|
|||
}
|
||||
|
||||
case Expose:
|
||||
event_expose(e);
|
||||
handle_event_expose(e);
|
||||
break;
|
||||
|
||||
case PropertyNotify:
|
||||
event_property_notify(e);
|
||||
handle_event_property_notify(e);
|
||||
break;
|
||||
|
||||
case ConfigureNotify:
|
||||
event_configure_notify(e);
|
||||
handle_event_configure_notify(e);
|
||||
break;
|
||||
|
||||
case ConfigureRequest: {
|
||||
|
@ -1213,11 +561,7 @@ void handle_x_event(XEvent *e)
|
|||
case DestroyNotify:
|
||||
if (e->xany.window == server.composite_manager) {
|
||||
// Stop real_transparency
|
||||
fprintf(stderr,
|
||||
YELLOW "%s %d: triggering tint2 restart due to compositor shutdown" RESET "\n",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
signal_pending = SIGUSR1;
|
||||
emit_self_restart("compositor shutdown");
|
||||
break;
|
||||
}
|
||||
if (e->xany.window == g_tooltip.window || !systray_enabled)
|
||||
|
@ -1235,18 +579,10 @@ void handle_x_event(XEvent *e)
|
|||
if (ev->data.l[1] == server.atom._NET_WM_CM_S0) {
|
||||
if (ev->data.l[2] == None) {
|
||||
// Stop real_transparency
|
||||
fprintf(stderr,
|
||||
YELLOW "%s %d: triggering tint2 restart due to change in transparency" RESET "\n",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
signal_pending = SIGUSR1;
|
||||
emit_self_restart("compositor changed");
|
||||
} else {
|
||||
// Start real_transparency
|
||||
fprintf(stderr,
|
||||
YELLOW "%s %d: triggering tint2 restart due to change in transparency" RESET "\n",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
signal_pending = SIGUSR1;
|
||||
emit_self_restart("compositor changed");
|
||||
}
|
||||
}
|
||||
if (systray_enabled && e->xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE &&
|
||||
|
@ -1277,45 +613,15 @@ void handle_x_event(XEvent *e)
|
|||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
void run_tint2_event_loop()
|
||||
{
|
||||
start:
|
||||
init(argc, argv);
|
||||
|
||||
init_X11_pre_config();
|
||||
|
||||
if (!config_read()) {
|
||||
fprintf(stderr, "Could not read config file.\n");
|
||||
print_usage();
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
init_X11_post_config();
|
||||
start_detect_compositor();
|
||||
|
||||
init_panel();
|
||||
|
||||
if (snapshot_path) {
|
||||
get_snapshot(snapshot_path);
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
int x11_fd = ConnectionNumber(server.display);
|
||||
XSync(server.display, False);
|
||||
|
||||
dnd_init();
|
||||
int ufd = uevent_init();
|
||||
hidden_dnd = FALSE;
|
||||
|
||||
double ts_event_read = 0;
|
||||
double ts_event_processed = 0;
|
||||
double ts_render_finished = 0;
|
||||
double ts_flush_finished = 0;
|
||||
gboolean first_render = TRUE;
|
||||
while (1) {
|
||||
|
||||
while (!get_signal_pending()) {
|
||||
if (panel_refresh) {
|
||||
if (debug_fps)
|
||||
ts_event_processed = get_time();
|
||||
|
@ -1442,35 +748,33 @@ start:
|
|||
// Create a File Description Set containing x11_fd
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(x11_fd, &fdset);
|
||||
int maxfd = x11_fd;
|
||||
FD_SET(server.x11_fd, &fdset);
|
||||
int maxfd = server.x11_fd;
|
||||
if (sigchild_pipe_valid) {
|
||||
FD_SET(sigchild_pipe[0], &fdset);
|
||||
maxfd = maxfd < sigchild_pipe[0] ? sigchild_pipe[0] : maxfd;
|
||||
maxfd = MAX(maxfd, sigchild_pipe[0]);
|
||||
}
|
||||
for (GList *l = panel_config.execp_list; l; l = l->next) {
|
||||
Execp *execp = (Execp *)l->data;
|
||||
int fd = execp->backend->child_pipe_stdout;
|
||||
if (fd > 0) {
|
||||
FD_SET(fd, &fdset);
|
||||
maxfd = maxfd < fd ? fd : maxfd;
|
||||
maxfd = MAX(maxfd, fd);
|
||||
}
|
||||
fd = execp->backend->child_pipe_stderr;
|
||||
if (fd > 0) {
|
||||
FD_SET(fd, &fdset);
|
||||
maxfd = maxfd < fd ? fd : maxfd;
|
||||
maxfd = MAX(maxfd, fd);
|
||||
}
|
||||
}
|
||||
if (ufd > 0) {
|
||||
FD_SET(ufd, &fdset);
|
||||
maxfd = maxfd < ufd ? ufd : maxfd;
|
||||
if (uevent_fd > 0) {
|
||||
FD_SET(uevent_fd, &fdset);
|
||||
maxfd = MAX(maxfd, uevent_fd);
|
||||
}
|
||||
update_next_timeout();
|
||||
struct timeval *select_timeout = (next_timeout.tv_sec >= 0 && next_timeout.tv_usec >= 0) ? &next_timeout : NULL;
|
||||
|
||||
// Wait for X Event or a Timer
|
||||
// Wait for an event and handle it
|
||||
ts_event_read = 0;
|
||||
if (XPending(server.display) > 0 || select(maxfd + 1, &fdset, 0, 0, select_timeout) >= 0) {
|
||||
if (XPending(server.display) > 0 || select(maxfd + 1, &fdset, 0, 0, get_next_timeout()) >= 0) {
|
||||
uevent_handler();
|
||||
|
||||
if (sigchild_pipe_valid) {
|
||||
|
@ -1499,25 +803,53 @@ start:
|
|||
}
|
||||
}
|
||||
|
||||
callback_timeout_expired();
|
||||
handle_expired_timers();
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_pending) {
|
||||
cleanup();
|
||||
if (signal_pending == SIGUSR1) {
|
||||
fprintf(stderr, YELLOW "%s %d: restarting tint2..." RESET "\n", __FILE__, __LINE__);
|
||||
// restart tint2
|
||||
// SIGUSR1 used when : user's signal, composite manager stop/start or xrandr
|
||||
goto start;
|
||||
} else if (signal_pending == SIGUSR2) {
|
||||
fprintf(stderr, YELLOW "%s %d: reexecuting tint2..." RESET "\n", __FILE__, __LINE__);
|
||||
if (execvp(argv[0], argv) == -1) {
|
||||
fprintf(stderr, RED "%s %d: failed!" RESET "\n", __FILE__, __LINE__);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// SIGINT, SIGTERM, SIGHUP
|
||||
exit(0);
|
||||
void tint2(int argc, char **argv, gboolean *restart)
|
||||
{
|
||||
*restart = FALSE;
|
||||
|
||||
init(argc, argv);
|
||||
|
||||
if (snapshot_path) {
|
||||
save_screenshot(snapshot_path);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
XSync(server.display, False);
|
||||
|
||||
dnd_init();
|
||||
|
||||
uevent_init();
|
||||
run_tint2_event_loop();
|
||||
|
||||
if (get_signal_pending()) {
|
||||
cleanup();
|
||||
if (get_signal_pending() == SIGUSR1) {
|
||||
fprintf(stderr, YELLOW "%s %d: restarting tint2..." RESET "\n", __FILE__, __LINE__);
|
||||
*restart = TRUE;
|
||||
return;
|
||||
} else if (get_signal_pending() == SIGUSR2) {
|
||||
fprintf(stderr, YELLOW "%s %d: reexecuting tint2..." RESET "\n", __FILE__, __LINE__);
|
||||
if (execvp(argv[0], argv) == -1) {
|
||||
fprintf(stderr, RED "%s %d: failed!" RESET "\n", __FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// SIGINT, SIGTERM, SIGHUP etc.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gboolean run = TRUE;
|
||||
while (run) {
|
||||
tint2(argc, argv, &run);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -38,8 +38,6 @@
|
|||
|
||||
void panel_clear_background(void *obj);
|
||||
|
||||
int signal_pending;
|
||||
|
||||
MouseAction mouse_left;
|
||||
MouseAction mouse_middle;
|
||||
MouseAction mouse_right;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "battery.h"
|
||||
#endif
|
||||
|
||||
extern int signal_pending;
|
||||
// --------------------------------------------------
|
||||
// mouse events
|
||||
extern MouseAction mouse_left;
|
||||
|
|
44
src/server.c
44
src/server.c
|
@ -27,8 +27,10 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "server.h"
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
#include "window.h"
|
||||
|
||||
Server server;
|
||||
|
@ -733,3 +735,43 @@ void forward_click(XEvent *e)
|
|||
// XSetInputFocus(server.display, e->xbutton.window, RevertToParent, e->xbutton.time);
|
||||
XSendEvent(server.display, e->xbutton.window, False, ButtonPressMask, e);
|
||||
}
|
||||
|
||||
void handle_crash(const char *reason)
|
||||
{
|
||||
#ifndef DISABLE_BACKTRACE
|
||||
char path[4096];
|
||||
sprintf(path, "%s/.tint2-crash.log", get_home_dir());
|
||||
int log_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
log_string(log_fd, RED "tint2 crashed, reason: ");
|
||||
log_string(log_fd, reason);
|
||||
log_string(log_fd, RESET "\n");
|
||||
dump_backtrace(log_fd);
|
||||
log_string(log_fd, RED "Please create a bug report with this log output." RESET "\n");
|
||||
close(log_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void x11_io_error(Display *display)
|
||||
{
|
||||
handle_crash("X11 I/O error");
|
||||
}
|
||||
|
||||
#ifdef HAVE_SN
|
||||
static int error_trap_depth = 0;
|
||||
|
||||
void error_trap_push(SnDisplay *display, Display *xdisplay)
|
||||
{
|
||||
++error_trap_depth;
|
||||
}
|
||||
|
||||
void error_trap_pop(SnDisplay *display, Display *xdisplay)
|
||||
{
|
||||
if (error_trap_depth == 0) {
|
||||
fprintf(stderr, "Error trap underflow!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
XSync(xdisplay, False); /* get all errors out of the queue */
|
||||
--error_trap_depth;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
|
|
|
@ -135,6 +135,7 @@ typedef struct Viewport {
|
|||
|
||||
typedef struct Server {
|
||||
Display *display;
|
||||
int x11_fd;
|
||||
Window root_win;
|
||||
Window composite_manager;
|
||||
gboolean real_transparency;
|
||||
|
@ -181,6 +182,9 @@ void server_init_atoms();
|
|||
void server_init_visual();
|
||||
void server_init_xdamage();
|
||||
|
||||
void x11_io_error(Display *display);
|
||||
void handle_crash(const char *reason);
|
||||
|
||||
// detect root background
|
||||
void get_root_pixmap();
|
||||
|
||||
|
@ -197,4 +201,9 @@ void change_desktop(int desktop);
|
|||
// Forward mouse click to the desktop window
|
||||
void forward_click(XEvent *e);
|
||||
|
||||
#ifdef HAVE_SN
|
||||
void error_trap_push(SnDisplay *display, Display *xdisplay);
|
||||
void error_trap_pop(SnDisplay *display, Display *xdisplay);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
144
src/signals.c
Normal file
144
src/signals.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#ifdef HAVE_SN
|
||||
#include <libsn/sn.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "panel.h"
|
||||
#include "launcher.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
|
||||
static sig_atomic_t signal_pending;
|
||||
|
||||
void signal_handler(int sig)
|
||||
{
|
||||
// signal handler is light as it should be
|
||||
signal_pending = sig;
|
||||
}
|
||||
|
||||
void init_signals()
|
||||
{
|
||||
// Set signal handlers
|
||||
signal_pending = 0;
|
||||
|
||||
struct sigaction sa_chld = {.sa_handler = SIG_DFL, .sa_flags = SA_NOCLDWAIT | SA_RESTART};
|
||||
sigaction(SIGCHLD, &sa_chld, 0);
|
||||
|
||||
struct sigaction sa = {.sa_handler = signal_handler, .sa_flags = SA_RESTART};
|
||||
sigaction(SIGUSR1, &sa, 0);
|
||||
sigaction(SIGUSR2, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
|
||||
#ifdef BACKTRACE_ON_SIGNAL
|
||||
struct sigaction sa_crash = {.sa_handler = crash_handler};
|
||||
sigaction(SIGSEGV, &sa_crash, 0);
|
||||
sigaction(SIGFPE, &sa_crash, 0);
|
||||
sigaction(SIGPIPE, &sa_crash, 0);
|
||||
sigaction(SIGBUS, &sa_crash, 0);
|
||||
sigaction(SIGABRT, &sa_crash, 0);
|
||||
sigaction(SIGSYS, &sa_crash, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BACKTRACE_ON_SIGNAL
|
||||
void crash_handler(int sig)
|
||||
{
|
||||
handle_crash(signal_name(sig));
|
||||
struct sigaction sa = {.sa_handler = SIG_DFL};
|
||||
sigaction(sig, &sa, 0);
|
||||
raise(sig);
|
||||
}
|
||||
#endif
|
||||
|
||||
int sigchild_pipe_valid = FALSE;
|
||||
int sigchild_pipe[2];
|
||||
|
||||
static void sigchld_handler(int sig)
|
||||
{
|
||||
if (!sigchild_pipe_valid)
|
||||
return;
|
||||
int savedErrno = errno;
|
||||
ssize_t unused = write(sigchild_pipe[1], "x", 1);
|
||||
(void)unused;
|
||||
fsync(sigchild_pipe[1]);
|
||||
errno = savedErrno;
|
||||
}
|
||||
|
||||
void sigchld_handler_async()
|
||||
{
|
||||
// Wait for all dead processes
|
||||
pid_t pid;
|
||||
int status;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
|
||||
#ifdef HAVE_SN
|
||||
if (startup_notifications) {
|
||||
SnLauncherContext *ctx = (SnLauncherContext *)g_tree_lookup(server.pids, GINT_TO_POINTER(pid));
|
||||
if (ctx) {
|
||||
g_tree_remove(server.pids, GINT_TO_POINTER(pid));
|
||||
sn_launcher_context_complete(ctx);
|
||||
sn_launcher_context_unref(ctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (GList *l = panel_config.execp_list; l; l = l->next) {
|
||||
Execp *execp = (Execp *)l->data;
|
||||
if (g_tree_lookup(execp->backend->cmd_pids, GINT_TO_POINTER(pid)))
|
||||
execp_cmd_completed(execp, pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_signals_postconfig()
|
||||
{
|
||||
gboolean need_sigchld = FALSE;
|
||||
#ifdef HAVE_SN
|
||||
// Initialize startup-notification
|
||||
if (startup_notifications) {
|
||||
server.sn_display = sn_display_new(server.display, error_trap_push, error_trap_pop);
|
||||
server.pids = g_tree_new(cmp_ptr);
|
||||
need_sigchld = TRUE;
|
||||
}
|
||||
#endif // HAVE_SN
|
||||
if (panel_config.execp_list)
|
||||
need_sigchld = TRUE;
|
||||
|
||||
if (need_sigchld) {
|
||||
// Setup a handler for child termination
|
||||
if (pipe(sigchild_pipe) != 0) {
|
||||
fprintf(stderr, "Creating pipe failed.\n");
|
||||
} else {
|
||||
fcntl(sigchild_pipe[0], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[0], F_GETFL));
|
||||
fcntl(sigchild_pipe[1], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[1], F_GETFL));
|
||||
sigchild_pipe_valid = 1;
|
||||
struct sigaction act = {.sa_handler = sigchld_handler, .sa_flags = SA_RESTART};
|
||||
if (sigaction(SIGCHLD, &act, 0)) {
|
||||
perror("sigaction");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void emit_self_restart(const char *reason)
|
||||
{
|
||||
fprintf(stderr,
|
||||
YELLOW "%s %d: triggering tint2 restart, reason: %s" RESET "\n",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
reason);
|
||||
signal_pending = SIGUSR1;
|
||||
}
|
||||
|
||||
int get_signal_pending()
|
||||
{
|
||||
return signal_pending;
|
||||
}
|
14
src/signals.h
Normal file
14
src/signals.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef SIGNALS_H
|
||||
#define SIGNALS_H
|
||||
|
||||
void init_signals();
|
||||
void init_signals_postconfig();
|
||||
void emit_self_restart(const char *reason);
|
||||
int get_signal_pending();
|
||||
|
||||
void sigchld_handler_async();
|
||||
|
||||
extern int sigchild_pipe_valid;
|
||||
extern int sigchild_pipe[2];
|
||||
|
||||
#endif
|
|
@ -859,55 +859,6 @@ gboolean embed_icon(TrayWindow *traywin)
|
|||
error = FALSE;
|
||||
XErrorHandler old = XSetErrorHandler(window_error_handler);
|
||||
|
||||
if (0) {
|
||||
Atom acttype;
|
||||
int actfmt;
|
||||
unsigned long nbitem, bytes;
|
||||
unsigned long *data = 0;
|
||||
int ret;
|
||||
|
||||
if (systray_profile)
|
||||
fprintf(stderr,
|
||||
"XGetWindowProperty(server.display, traywin->win, server.atom._XEMBED_INFO, 0, 2, False, "
|
||||
"server.atom._XEMBED_INFO, &acttype, &actfmt, &nbitem, &bytes, &data)\n");
|
||||
ret = XGetWindowProperty(server.display,
|
||||
traywin->win,
|
||||
server.atom._XEMBED_INFO,
|
||||
0,
|
||||
2,
|
||||
False,
|
||||
server.atom._XEMBED_INFO,
|
||||
&acttype,
|
||||
&actfmt,
|
||||
&nbitem,
|
||||
&bytes,
|
||||
(unsigned char **)&data);
|
||||
if (ret == Success) {
|
||||
if (data) {
|
||||
if (nbitem >= 2) {
|
||||
int hide = ((data[1] & XEMBED_MAPPED) == 0);
|
||||
if (hide) {
|
||||
// In theory we have to check the embedding with this and remove icons that refuse embedding.
|
||||
// In practice we have no idea when the other application processes the event and accepts the
|
||||
// embed so we cannot check now without a race.
|
||||
// Race can be triggered with PyGtk(2) apps.
|
||||
// We could defer this for later (if we set PropertyChangeMask in XSelectInput we get notified)
|
||||
// but for some reason it breaks transparency for Qt icons. So we don't.
|
||||
// fprintf(stderr, RED "tint2: window refused embedding" RESET "\n");
|
||||
// remove_icon(traywin);
|
||||
// XFree(data);
|
||||
// return FALSE;
|
||||
}
|
||||
}
|
||||
XFree(data);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, RED "tint2 : xembed error" RESET "\n");
|
||||
remove_icon(traywin);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect rendering when using compositing
|
||||
if (systray_composited) {
|
||||
if (systray_profile)
|
||||
|
|
|
@ -330,8 +330,6 @@ void task_update_icon(Task *task)
|
|||
if (img == NULL) {
|
||||
imlib_context_set_image(default_icon);
|
||||
img = imlib_clone_image();
|
||||
if (0)
|
||||
fprintf(stderr, "%s: Using default icon for %s\n", __FUNCTION__, task->title ? task->title : "task");
|
||||
}
|
||||
|
||||
// transform icons
|
||||
|
@ -782,3 +780,13 @@ void task_handle_mouse_event(Task *task, MouseAction action)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void task_update_desktop(Task *task)
|
||||
{
|
||||
// fprintf(stderr, "%s %d:\n", __FUNCTION__, __LINE__);
|
||||
Window win = task->win;
|
||||
remove_task(task);
|
||||
task = add_task(win);
|
||||
reset_active_task();
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ void draw_task(void *obj, cairo_t *c);
|
|||
void on_change_task(void *obj);
|
||||
|
||||
void task_update_icon(Task *task);
|
||||
void task_update_desktop(Task *task);
|
||||
gboolean task_update_title(Task *task);
|
||||
void reset_active_task();
|
||||
void set_task_state(Task *task, TaskState state);
|
||||
|
|
|
@ -299,16 +299,6 @@ void init_taskbar_panel(void *p)
|
|||
top_bottom_border_width(&panel->g_task.area));
|
||||
panel->g_task.text_posx += panel->g_task.icon_size1 + panel->g_task.area.paddingx;
|
||||
panel->g_task.icon_posy = (panel->g_task.area.height - panel->g_task.icon_size1) / 2;
|
||||
if (0)
|
||||
printf("task: icon_size = %d, textx = %f, texth = %f, icony = %d, w = %d, h = %d, maxw = %d, maxh = %d\n",
|
||||
panel->g_task.icon_size1,
|
||||
panel->g_task.text_posx,
|
||||
panel->g_task.text_height,
|
||||
panel->g_task.icon_posy,
|
||||
panel->g_task.area.width,
|
||||
panel->g_task.area.height,
|
||||
panel->g_task.maximum_width,
|
||||
panel->g_task.maximum_height);
|
||||
}
|
||||
|
||||
Taskbar *taskbar;
|
||||
|
|
|
@ -214,3 +214,35 @@ void draw_taskbarname(void *obj, cairo_t *c)
|
|||
|
||||
g_object_unref(layout);
|
||||
}
|
||||
|
||||
void update_desktop_names()
|
||||
{
|
||||
if (!taskbarname_enabled)
|
||||
return;
|
||||
GSList *list = get_desktop_names();
|
||||
for (int i = 0; i < num_panels; i++) {
|
||||
int j;
|
||||
GSList *l;
|
||||
for (j = 0, l = list; j < panels[i].num_desktops; j++) {
|
||||
gchar *name;
|
||||
if (l) {
|
||||
name = g_strdup(l->data);
|
||||
l = l->next;
|
||||
} else {
|
||||
name = g_strdup_printf("%d", j + 1);
|
||||
}
|
||||
Taskbar *taskbar = &panels[i].taskbar[j];
|
||||
if (strcmp(name, taskbar->bar_name.name) != 0) {
|
||||
g_free(taskbar->bar_name.name);
|
||||
taskbar->bar_name.name = name;
|
||||
taskbar->bar_name.area.resize_needed = 1;
|
||||
} else {
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (GSList *l = list; l; l = l->next)
|
||||
g_free(l->data);
|
||||
g_slist_free(list);
|
||||
schedule_panel_redraw();
|
||||
}
|
||||
|
|
|
@ -23,4 +23,6 @@ gboolean resize_taskbarname(void *obj);
|
|||
|
||||
void taskbarname_default_font_changed();
|
||||
|
||||
void update_desktop_names();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,6 +22,7 @@ include_directories( ../util
|
|||
set(SOURCES ../util/common.c
|
||||
../util/strnatcmp.c
|
||||
../util/cache.c
|
||||
../util/timer.c
|
||||
../config.c
|
||||
../server.c
|
||||
../launcher/apps-common.c
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "../server.h"
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -44,7 +45,162 @@
|
|||
#include <librsvg/rsvg.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LIBUNWIND
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#else
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "../panel.h"
|
||||
#include "timer.h"
|
||||
|
||||
void write_string(int fd, const char *s)
|
||||
{
|
||||
int len = strlen(s);
|
||||
while (len > 0) {
|
||||
int count = write(fd, s, len);
|
||||
if (count >= 0) {
|
||||
s += count;
|
||||
len -= count;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void log_string(int fd, const char *s)
|
||||
{
|
||||
write_string(2, s);
|
||||
write_string(fd, s);
|
||||
}
|
||||
|
||||
void dump_backtrace(int log_fd)
|
||||
{
|
||||
#ifndef DISABLE_BACKTRACE
|
||||
log_string(log_fd, "\n" YELLOW "Backtrace:" RESET "\n");
|
||||
|
||||
#ifdef ENABLE_LIBUNWIND
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t context;
|
||||
unw_getcontext(&context);
|
||||
unw_init_local(&cursor, &context);
|
||||
|
||||
while (unw_step(&cursor) > 0) {
|
||||
unw_word_t offset;
|
||||
char fname[128];
|
||||
fname[0] = '\0';
|
||||
(void)unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
|
||||
log_string(log_fd, fname);
|
||||
log_string(log_fd, "\n");
|
||||
}
|
||||
#else
|
||||
#ifdef ENABLE_EXECINFO
|
||||
#define MAX_TRACE_SIZE 128
|
||||
void *array[MAX_TRACE_SIZE];
|
||||
size_t size = backtrace(array, MAX_TRACE_SIZE);
|
||||
char **strings = backtrace_symbols(array, size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
log_string(log_fd, strings[i]);
|
||||
log_string(log_fd, "\n");
|
||||
}
|
||||
|
||||
free(strings);
|
||||
#endif // ENABLE_EXECINFO
|
||||
#endif // ENABLE_LIBUNWIND
|
||||
#endif // DISABLE_BACKTRACE
|
||||
}
|
||||
|
||||
// sleep() returns early when signals arrive. This function does not.
|
||||
void safe_sleep(int seconds)
|
||||
{
|
||||
double t0 = get_time();
|
||||
while (1) {
|
||||
double t = get_time();
|
||||
if (t > t0 + seconds)
|
||||
return;
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
const char *signal_name(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGHUP:
|
||||
return "SIGHUP: Hangup (POSIX).";
|
||||
case SIGINT:
|
||||
return "SIGINT: Interrupt (ANSI).";
|
||||
case SIGQUIT:
|
||||
return "SIGQUIT: Quit (POSIX).";
|
||||
case SIGILL:
|
||||
return "SIGILL: Illegal instruction (ANSI).";
|
||||
case SIGTRAP:
|
||||
return "SIGTRAP: Trace trap (POSIX).";
|
||||
case SIGABRT:
|
||||
return "SIGABRT/SIGIOT: Abort (ANSI) / IOT trap (4.2 BSD).";
|
||||
case SIGBUS:
|
||||
return "SIGBUS: BUS error (4.2 BSD).";
|
||||
case SIGFPE:
|
||||
return "SIGFPE: Floating-point exception (ANSI).";
|
||||
case SIGKILL:
|
||||
return "SIGKILL: Kill, unblockable (POSIX).";
|
||||
case SIGUSR1:
|
||||
return "SIGUSR1: User-defined signal 1 (POSIX).";
|
||||
case SIGSEGV:
|
||||
return "SIGSEGV: Segmentation violation (ANSI).";
|
||||
case SIGUSR2:
|
||||
return "SIGUSR2: User-defined signal 2 (POSIX).";
|
||||
case SIGPIPE:
|
||||
return "SIGPIPE: Broken pipe (POSIX).";
|
||||
case SIGALRM:
|
||||
return "SIGALRM: Alarm clock (POSIX).";
|
||||
case SIGTERM:
|
||||
return "SIGTERM: Termination (ANSI).";
|
||||
// case SIGSTKFLT: return "SIGSTKFLT: Stack fault.";
|
||||
case SIGCHLD:
|
||||
return "SIGCHLD: Child status has changed (POSIX).";
|
||||
case SIGCONT:
|
||||
return "SIGCONT: Continue (POSIX).";
|
||||
case SIGSTOP:
|
||||
return "SIGSTOP: Stop, unblockable (POSIX).";
|
||||
case SIGTSTP:
|
||||
return "SIGTSTP: Keyboard stop (POSIX).";
|
||||
case SIGTTIN:
|
||||
return "SIGTTIN: Background read from tty (POSIX).";
|
||||
case SIGTTOU:
|
||||
return "SIGTTOU: Background write to tty (POSIX).";
|
||||
case SIGURG:
|
||||
return "SIGURG: Urgent condition on socket (4.2 BSD).";
|
||||
case SIGXCPU:
|
||||
return "SIGXCPU: CPU limit exceeded (4.2 BSD).";
|
||||
case SIGXFSZ:
|
||||
return "SIGXFSZ: File size limit exceeded (4.2 BSD).";
|
||||
case SIGVTALRM:
|
||||
return "SIGVTALRM: Virtual alarm clock (4.2 BSD).";
|
||||
case SIGPROF:
|
||||
return "SIGPROF: Profiling alarm clock (4.2 BSD).";
|
||||
// case SIGPWR: return "SIGPWR: Power failure restart (System V).";
|
||||
case SIGSYS:
|
||||
return "SIGSYS: Bad system call.";
|
||||
}
|
||||
static char s[64];
|
||||
sprintf(s, "SIG=%d: Unknown", sig);
|
||||
return s;
|
||||
}
|
||||
|
||||
const char *get_home_dir()
|
||||
{
|
||||
const char *s = getenv("HOME");
|
||||
if (s)
|
||||
return s;
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (!pw)
|
||||
return NULL;
|
||||
return pw->pw_dir;
|
||||
}
|
||||
|
||||
void copy_file(const char *path_src, const char *path_dest)
|
||||
{
|
||||
|
|
|
@ -41,6 +41,18 @@ typedef enum MouseAction {
|
|||
|
||||
#define ALL_DESKTOPS 0xFFFFFFFF
|
||||
|
||||
void write_string(int fd, const char *s);
|
||||
void log_string(int fd, const char *s);
|
||||
|
||||
void dump_backtrace(int log_fd);
|
||||
|
||||
// sleep() returns early when signals arrive. This function does not.
|
||||
void safe_sleep(int seconds);
|
||||
|
||||
const char *signal_name(int sig);
|
||||
|
||||
const char *get_home_dir();
|
||||
|
||||
// Copies a file to another path
|
||||
void copy_file(const char *path_src, const char *path_dest);
|
||||
|
||||
|
|
97
src/util/fps_distribution.c
Normal file
97
src/util/fps_distribution.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2017 tint2 authors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fps_distribution.h"
|
||||
|
||||
static float *fps_distribution = NULL;
|
||||
|
||||
void init_fps_distribution()
|
||||
{
|
||||
// measure FPS with resolution:
|
||||
// 0-59: 1 (60 samples)
|
||||
// 60-199: 10 (14)
|
||||
// 200-1,999: 25 (72)
|
||||
// 1k-19,999: 1000 (19)
|
||||
// 20x+: inf (1)
|
||||
// => 166 samples
|
||||
if (fps_distribution)
|
||||
return;
|
||||
fps_distribution = calloc(170, sizeof(float));
|
||||
}
|
||||
|
||||
void cleanup_fps_distribution()
|
||||
{
|
||||
free(fps_distribution);
|
||||
fps_distribution = NULL;
|
||||
}
|
||||
|
||||
void sample_fps(double fps)
|
||||
{
|
||||
int fps_rounded = (int)(fps + 0.5);
|
||||
int i = 1;
|
||||
if (fps_rounded < 60) {
|
||||
i += fps_rounded;
|
||||
} else {
|
||||
i += 60;
|
||||
if (fps_rounded < 200) {
|
||||
i += (fps_rounded - 60) / 10;
|
||||
} else {
|
||||
i += 14;
|
||||
if (fps_rounded < 2000) {
|
||||
i += (fps_rounded - 200) / 25;
|
||||
} else {
|
||||
i += 72;
|
||||
if (fps_rounded < 20000) {
|
||||
i += (fps_rounded - 2000) / 1000;
|
||||
} else {
|
||||
i += 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fprintf(stderr, "fps = %.0f => i = %d\n", fps, i);
|
||||
fps_distribution[i] += 1.;
|
||||
fps_distribution[0] += 1.;
|
||||
}
|
||||
|
||||
void fps_compute_stats(double *low, double *median, double *high, double *samples)
|
||||
{
|
||||
*median = *low = *high = *samples = -1;
|
||||
if (!fps_distribution || fps_distribution[0] < 1)
|
||||
return;
|
||||
float total = fps_distribution[0];
|
||||
*samples = (double)fps_distribution[0];
|
||||
float cum_low = 0.05f * total;
|
||||
float cum_median = 0.5f * total;
|
||||
float cum_high = 0.95f * total;
|
||||
float cum = 0;
|
||||
for (int i = 1; i <= 166; i++) {
|
||||
double value =
|
||||
(i < 60) ? i : (i < 74) ? (60 + (i - 60) * 10) : (i < 146) ? (200 + (i - 74) * 25)
|
||||
: (i < 165) ? (2000 + (i - 146) * 1000) : 20000;
|
||||
// fprintf(stderr, "%6.0f (i = %3d) : %.0f | ", value, i, (double)fps_distribution[i]);
|
||||
cum += fps_distribution[i];
|
||||
if (*low < 0 && cum >= cum_low)
|
||||
*low = value;
|
||||
if (*median < 0 && cum >= cum_median)
|
||||
*median = value;
|
||||
if (*high < 0 && cum >= cum_high)
|
||||
*high = value;
|
||||
}
|
||||
}
|
9
src/util/fps_distribution.h
Normal file
9
src/util/fps_distribution.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef FPS_DISTRIBUTION_H
|
||||
#define FPS_DISTRIBUTION_H
|
||||
|
||||
void init_fps_distribution();
|
||||
void cleanup_fps_distribution();
|
||||
void sample_fps(double fps);
|
||||
void fps_compute_stats(double *low, double *median, double *high, double *samples);
|
||||
|
||||
#endif
|
|
@ -129,7 +129,7 @@ void change_timeout(timeout **t, int value_msec, int interval_msec, void (*_call
|
|||
}
|
||||
}
|
||||
|
||||
void update_next_timeout()
|
||||
struct timeval *get_next_timeout()
|
||||
{
|
||||
if (timeout_list) {
|
||||
timeout *t = timeout_list->data;
|
||||
|
@ -145,9 +145,10 @@ void update_next_timeout()
|
|||
}
|
||||
} else
|
||||
next_timeout.tv_sec = -1;
|
||||
return (next_timeout.tv_sec >= 0 && next_timeout.tv_usec >= 0) ? &next_timeout : NULL;
|
||||
}
|
||||
|
||||
void callback_timeout_expired()
|
||||
void handle_expired_timers()
|
||||
{
|
||||
struct timespec cur_time;
|
||||
timeout *t;
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
// Periodic timeouts are aligned to each other whenever possible, i.e. one interval_msec is an
|
||||
// integral multiple of the other.
|
||||
|
||||
extern struct timeval next_timeout;
|
||||
typedef struct _timeout timeout;
|
||||
|
||||
// Initializes default global data.
|
||||
|
@ -54,11 +53,12 @@ void change_timeout(timeout **t, int value_msec, int interval_msec, void (*_call
|
|||
// Stops the timer 't'
|
||||
void stop_timeout(timeout *t);
|
||||
|
||||
// Updates next_timeout to the value, when the next installed timeout will expire
|
||||
void update_next_timeout();
|
||||
// Get the time when the next installed timer will expire, or NULL if there is no timer.
|
||||
// Do not free the pointer; but it is safe to change its contents.
|
||||
struct timeval *get_next_timeout();
|
||||
|
||||
// Callback of all expired timeouts
|
||||
void callback_timeout_expired();
|
||||
void handle_expired_timers();
|
||||
|
||||
// Returns -1 if t1 < t2, 0 if t1 == t2, 1 if t1 > t2
|
||||
gint compare_timespecs(const struct timespec *t1, const struct timespec *t2);
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**************************************************************************/
|
||||
|
||||
#include "uevent.h"
|
||||
int uevent_fd = -1;
|
||||
|
||||
#ifdef ENABLE_UEVENT
|
||||
|
||||
#include <string.h>
|
||||
|
@ -30,9 +33,7 @@
|
|||
#include <linux/netlink.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "uevent.h"
|
||||
|
||||
static int ueventfd = -1;
|
||||
static struct sockaddr_nl nls;
|
||||
static GList *notifiers = NULL;
|
||||
|
||||
|
@ -146,11 +147,11 @@ void uevent_unregister_notifier(struct uevent_notify *nb)
|
|||
|
||||
void uevent_handler()
|
||||
{
|
||||
if (ueventfd < 0)
|
||||
if (uevent_fd < 0)
|
||||
return;
|
||||
|
||||
char buf[512];
|
||||
int len = recv(ueventfd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
int len = recv(uevent_fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (len < 0)
|
||||
return;
|
||||
|
||||
|
@ -181,27 +182,27 @@ int uevent_init()
|
|||
nls.nl_groups = -1;
|
||||
|
||||
/* open socket */
|
||||
ueventfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||
if (ueventfd < 0) {
|
||||
uevent_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||
if (uevent_fd < 0) {
|
||||
fprintf(stderr, "Error: socket open failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Listen to netlink socket */
|
||||
if (bind(ueventfd, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
||||
if (bind(uevent_fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
||||
fprintf(stderr, "Bind failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Kernel uevent interface initialized...\n");
|
||||
|
||||
return ueventfd;
|
||||
return uevent_fd;
|
||||
}
|
||||
|
||||
void uevent_cleanup()
|
||||
{
|
||||
if (ueventfd >= 0)
|
||||
close(ueventfd);
|
||||
if (uevent_fd >= 0)
|
||||
close(uevent_fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#ifndef UEVENT_H
|
||||
#define UEVENT_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
enum uevent_action {
|
||||
UEVENT_UNKNOWN = 0x01,
|
||||
UEVENT_ADD = 0x02,
|
||||
|
@ -48,6 +50,8 @@ struct uevent_notify {
|
|||
void (*cb)(struct uevent *e, void *userdata);
|
||||
};
|
||||
|
||||
extern int uevent_fd;
|
||||
|
||||
#if ENABLE_UEVENT
|
||||
int uevent_init();
|
||||
void uevent_cleanup();
|
||||
|
|
|
@ -68,7 +68,7 @@ src/panel.c
|
|||
src/panel.h
|
||||
src/server.c
|
||||
src/server.h
|
||||
src/tint.c
|
||||
src/main.c
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
CMakeLists.txt
|
||||
|
@ -229,3 +229,9 @@ src/drag_and_drop.c
|
|||
src/drag_and_drop.h
|
||||
src/mouse_actions.c
|
||||
src/mouse_actions.h
|
||||
src/util/fps_distribution.c
|
||||
src/util/fps_distribution.h
|
||||
src/init.h
|
||||
src/init.c
|
||||
src/signals.c
|
||||
src/signals.h
|
||||
|
|
Loading…
Reference in a new issue