From 1e45abe988b8da64d6d62294c21bdb41f2ef844a Mon Sep 17 00:00:00 2001 From: o9000 Date: Thu, 31 Aug 2017 18:38:31 +0200 Subject: [PATCH] Refactoring --- CMakeLists.txt | 5 +- src/drag_and_drop.c | 3 + src/drag_and_drop.h | 3 + src/init.c | 270 +++++++++++ src/init.h | 7 + src/{tint.c => main.c} | 838 ++++------------------------------- src/panel.c | 2 - src/panel.h | 1 - src/server.c | 44 +- src/server.h | 9 + src/signals.c | 144 ++++++ src/signals.h | 14 + src/systray/systraybar.c | 49 -- src/taskbar/task.c | 12 +- src/taskbar/task.h | 1 + src/taskbar/taskbar.c | 10 - src/taskbar/taskbarname.c | 32 ++ src/taskbar/taskbarname.h | 2 + src/tint2conf/CMakeLists.txt | 1 + src/util/common.c | 156 +++++++ src/util/common.h | 12 + src/util/fps_distribution.c | 97 ++++ src/util/fps_distribution.h | 9 + src/util/timer.c | 5 +- src/util/timer.h | 8 +- src/util/uevent.c | 21 +- src/util/uevent.h | 4 + tint2.files | 8 +- 28 files changed, 931 insertions(+), 836 deletions(-) create mode 100644 src/init.c create mode 100644 src/init.h rename src/{tint.c => main.c} (57%) create mode 100644 src/signals.c create mode 100644 src/signals.h create mode 100644 src/util/fps_distribution.c create mode 100644 src/util/fps_distribution.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b49360e..3a41815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/drag_and_drop.c b/src/drag_and_drop.c index b5d4aa2..9baddd5 100644 --- a/src/drag_and_drop.c +++ b/src/drag_and_drop.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) diff --git a/src/drag_and_drop.h b/src/drag_and_drop.h index c5b940b..b2e7312 100644 --- a/src/drag_and_drop.h +++ b/src/drag_and_drop.h @@ -7,6 +7,9 @@ #define DRAG_AND_DROP_H #include +#include + +extern gboolean hidden_panel_shown_for_dnd; void dnd_init(); diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..8a91531 --- /dev/null +++ b/src/init.c @@ -0,0 +1,270 @@ +#include "init.h" + +#include +#include +#include +#include +#include + +#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" + ".\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(); +} diff --git a/src/init.h b/src/init.h new file mode 100644 index 0000000..6c4e796 --- /dev/null +++ b/src/init.h @@ -0,0 +1,7 @@ +#ifndef INIT_H +#define INIT_H + +void init(int argc, char **argv); +void cleanup(); + +#endif diff --git a/src/tint.c b/src/main.c similarity index 57% rename from src/tint.c rename to src/main.c index b38a796..869abba 100644 --- a/src/tint.c +++ b/src/main.c @@ -43,627 +43,33 @@ #include #endif -#include -#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 -#else -#ifdef ENABLE_EXECINFO -#include -#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" - ".\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; +} diff --git a/src/panel.c b/src/panel.c index 65c48b1..08024c4 100644 --- a/src/panel.c +++ b/src/panel.c @@ -38,8 +38,6 @@ void panel_clear_background(void *obj); -int signal_pending; - MouseAction mouse_left; MouseAction mouse_middle; MouseAction mouse_right; diff --git a/src/panel.h b/src/panel.h index 11033a9..f52c6fe 100644 --- a/src/panel.h +++ b/src/panel.h @@ -29,7 +29,6 @@ #include "battery.h" #endif -extern int signal_pending; // -------------------------------------------------- // mouse events extern MouseAction mouse_left; diff --git a/src/server.c b/src/server.c index 678226e..226a87f 100644 --- a/src/server.c +++ b/src/server.c @@ -27,8 +27,10 @@ #include #include -#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 diff --git a/src/server.h b/src/server.h index 076e1f0..a24e674 100644 --- a/src/server.h +++ b/src/server.h @@ -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 diff --git a/src/signals.c b/src/signals.c new file mode 100644 index 0000000..4eb18ad --- /dev/null +++ b/src/signals.c @@ -0,0 +1,144 @@ +#include +#include +#include +#ifdef HAVE_SN +#include +#endif +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/signals.h b/src/signals.h new file mode 100644 index 0000000..7dc273c --- /dev/null +++ b/src/signals.h @@ -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 diff --git a/src/systray/systraybar.c b/src/systray/systraybar.c index 755a693..f5dd2b9 100644 --- a/src/systray/systraybar.c +++ b/src/systray/systraybar.c @@ -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) diff --git a/src/taskbar/task.c b/src/taskbar/task.c index aaea815..40019e5 100644 --- a/src/taskbar/task.c +++ b/src/taskbar/task.c @@ -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(); +} diff --git a/src/taskbar/task.h b/src/taskbar/task.h index 5956e20..0c24646 100644 --- a/src/taskbar/task.h +++ b/src/taskbar/task.h @@ -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); diff --git a/src/taskbar/taskbar.c b/src/taskbar/taskbar.c index 65c25e2..b969a23 100644 --- a/src/taskbar/taskbar.c +++ b/src/taskbar/taskbar.c @@ -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; diff --git a/src/taskbar/taskbarname.c b/src/taskbar/taskbarname.c index 9f2104d..11a6c35 100644 --- a/src/taskbar/taskbarname.c +++ b/src/taskbar/taskbarname.c @@ -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(); +} diff --git a/src/taskbar/taskbarname.h b/src/taskbar/taskbarname.h index 1bc502f..e3d3609 100644 --- a/src/taskbar/taskbarname.h +++ b/src/taskbar/taskbarname.h @@ -23,4 +23,6 @@ gboolean resize_taskbarname(void *obj); void taskbarname_default_font_changed(); +void update_desktop_names(); + #endif diff --git a/src/tint2conf/CMakeLists.txt b/src/tint2conf/CMakeLists.txt index 36d97cb..d07eb40 100644 --- a/src/tint2conf/CMakeLists.txt +++ b/src/tint2conf/CMakeLists.txt @@ -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 diff --git a/src/util/common.c b/src/util/common.c index 76b95bf..14299a4 100644 --- a/src/util/common.c +++ b/src/util/common.c @@ -34,6 +34,7 @@ #include "../server.h" #include #include +#include #include #include #include @@ -44,7 +45,162 @@ #include #endif +#ifdef ENABLE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include +#else +#ifdef ENABLE_EXECINFO +#include +#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) { diff --git a/src/util/common.h b/src/util/common.h index 6552553..803fa24 100644 --- a/src/util/common.h +++ b/src/util/common.h @@ -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); diff --git a/src/util/fps_distribution.c b/src/util/fps_distribution.c new file mode 100644 index 0000000..bd666c5 --- /dev/null +++ b/src/util/fps_distribution.c @@ -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 + +#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; + } +} diff --git a/src/util/fps_distribution.h b/src/util/fps_distribution.h new file mode 100644 index 0000000..627848a --- /dev/null +++ b/src/util/fps_distribution.h @@ -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 diff --git a/src/util/timer.c b/src/util/timer.c index 26d41ef..698c77c 100644 --- a/src/util/timer.c +++ b/src/util/timer.c @@ -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; diff --git a/src/util/timer.h b/src/util/timer.h index 61901d2..87e2a03 100644 --- a/src/util/timer.h +++ b/src/util/timer.h @@ -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); diff --git a/src/util/uevent.c b/src/util/uevent.c index df8252a..9abc5c1 100644 --- a/src/util/uevent.c +++ b/src/util/uevent.c @@ -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 @@ -30,9 +33,7 @@ #include #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 diff --git a/src/util/uevent.h b/src/util/uevent.h index 556c053..a7f8e06 100644 --- a/src/util/uevent.h +++ b/src/util/uevent.h @@ -20,6 +20,8 @@ #ifndef UEVENT_H #define UEVENT_H +#include + 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(); diff --git a/tint2.files b/tint2.files index b8992ef..330dea5 100644 --- a/tint2.files +++ b/tint2.files @@ -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