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
|
set( SOURCES src/config.c
|
||||||
src/panel.c
|
src/panel.c
|
||||||
src/server.c
|
src/server.c
|
||||||
src/tint.c
|
src/main.c
|
||||||
|
src/init.c
|
||||||
|
src/signals.c
|
||||||
src/mouse_actions.c
|
src/mouse_actions.c
|
||||||
src/drag_and_drop.c
|
src/drag_and_drop.c
|
||||||
src/clock/clock.c
|
src/clock/clock.c
|
||||||
|
@ -137,6 +139,7 @@ set( SOURCES src/config.c
|
||||||
src/tint2rc.c
|
src/tint2rc.c
|
||||||
src/util/area.c
|
src/util/area.c
|
||||||
src/util/common.c
|
src/util/common.c
|
||||||
|
src/util/fps_distribution.c
|
||||||
src/util/strnatcmp.c
|
src/util/strnatcmp.c
|
||||||
src/util/timer.c
|
src/util/timer.c
|
||||||
src/util/cache.c
|
src/util/cache.c
|
||||||
|
|
|
@ -33,6 +33,8 @@ static int dnd_sent_request;
|
||||||
static char *dnd_launcher_exec;
|
static char *dnd_launcher_exec;
|
||||||
static gboolean dnd_debug = FALSE;
|
static gboolean dnd_debug = FALSE;
|
||||||
|
|
||||||
|
gboolean hidden_panel_shown_for_dnd;
|
||||||
|
|
||||||
void dnd_init()
|
void dnd_init()
|
||||||
{
|
{
|
||||||
dnd_source_window = 0;
|
dnd_source_window = 0;
|
||||||
|
@ -42,6 +44,7 @@ void dnd_init()
|
||||||
dnd_atom = None;
|
dnd_atom = None;
|
||||||
dnd_sent_request = 0;
|
dnd_sent_request = 0;
|
||||||
dnd_launcher_exec = 0;
|
dnd_launcher_exec = 0;
|
||||||
|
hidden_panel_shown_for_dnd = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_dnd_enter(XClientMessageEvent *e)
|
void handle_dnd_enter(XClientMessageEvent *e)
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#define DRAG_AND_DROP_H
|
#define DRAG_AND_DROP_H
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
extern gboolean hidden_panel_shown_for_dnd;
|
||||||
|
|
||||||
void dnd_init();
|
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>
|
#include <libsn/sn.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <version.h>
|
|
||||||
#include "server.h"
|
|
||||||
#include "window.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "drag_and_drop.h"
|
#include "drag_and_drop.h"
|
||||||
#include "task.h"
|
#include "fps_distribution.h"
|
||||||
#include "taskbar.h"
|
#include "init.h"
|
||||||
#include "systraybar.h"
|
|
||||||
#include "launcher.h"
|
#include "launcher.h"
|
||||||
#include "mouse_actions.h"
|
#include "mouse_actions.h"
|
||||||
#include "panel.h"
|
#include "panel.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "signals.h"
|
||||||
|
#include "systraybar.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "taskbar.h"
|
||||||
#include "tooltip.h"
|
#include "tooltip.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "xsettings-client.h"
|
|
||||||
#include "uevent.h"
|
#include "uevent.h"
|
||||||
|
#include "version.h"
|
||||||
#ifdef ENABLE_LIBUNWIND
|
#include "window.h"
|
||||||
#define UNW_LOCAL_ONLY
|
#include "xsettings-client.h"
|
||||||
#include <libunwind.h>
|
|
||||||
#else
|
|
||||||
#ifdef ENABLE_EXECINFO
|
|
||||||
#include <execinfo.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Global process state variables
|
// Global process state variables
|
||||||
|
|
||||||
static gboolean hidden_dnd;
|
|
||||||
|
|
||||||
XSettingsClient *xsettings_client = NULL;
|
XSettingsClient *xsettings_client = NULL;
|
||||||
|
|
||||||
timeout *detect_compositor_timer = NULL;
|
|
||||||
int detect_compositor_timer_counter = 0;
|
|
||||||
|
|
||||||
gboolean debug_fps = FALSE;
|
gboolean debug_fps = FALSE;
|
||||||
gboolean debug_frames = FALSE;
|
gboolean debug_frames = FALSE;
|
||||||
float *fps_distribution = NULL;
|
|
||||||
int frame = 0;
|
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)
|
void dump_panel_to_file(const Panel *panel, const char *path)
|
||||||
{
|
{
|
||||||
imlib_context_set_drawable(panel->temp_pmap);
|
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];
|
Panel *panel = &panels[0];
|
||||||
|
|
||||||
|
@ -721,51 +127,7 @@ void get_snapshot(const char *path)
|
||||||
dump_panel_to_file(panel, path);
|
dump_panel_to_file(panel, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handle_event_property_notify(XEvent *e)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
gboolean debug = FALSE;
|
gboolean debug = FALSE;
|
||||||
|
|
||||||
|
@ -880,7 +242,7 @@ void event_property_notify(XEvent *e)
|
||||||
}
|
}
|
||||||
for (l = need_update; l; l = l->next) {
|
for (l = need_update; l; l = l->next) {
|
||||||
Task *task = l->data;
|
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);
|
// printf(" Window desktop changed %d, %d\n", task->desktop, desktop);
|
||||||
// bug in windowmaker : send unecessary 'desktop changed' when focus changed
|
// bug in windowmaker : send unecessary 'desktop changed' when focus changed
|
||||||
if (desktop != task->desktop) {
|
if (desktop != task->desktop) {
|
||||||
update_task_desktop(task);
|
task_update_desktop(task);
|
||||||
}
|
}
|
||||||
} else if (at == server.atom.WM_HINTS) {
|
} else if (at == server.atom.WM_HINTS) {
|
||||||
XWMHints *wmhints = XGetWMHints(server.display, win);
|
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 *panel;
|
||||||
panel = get_panel(e->xany.window);
|
panel = get_panel(e->xany.window);
|
||||||
|
@ -1022,27 +384,13 @@ void event_expose(XEvent *e)
|
||||||
schedule_panel_redraw();
|
schedule_panel_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void event_configure_notify(XEvent *e)
|
void handle_event_configure_notify(XEvent *e)
|
||||||
{
|
{
|
||||||
Window win = e->xconfigure.window;
|
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)
|
// change in root window (xrandr)
|
||||||
if (win == server.root_win) {
|
if (win == server.root_win) {
|
||||||
fprintf(stderr,
|
emit_self_restart("configuration change in the root window");
|
||||||
YELLOW "%s %d: triggering tint2 restart due to configuration change in the root window" RESET "\n",
|
|
||||||
__FILE__,
|
|
||||||
__LINE__);
|
|
||||||
signal_pending = SIGUSR1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,7 +425,7 @@ void event_configure_notify(XEvent *e)
|
||||||
if (task) {
|
if (task) {
|
||||||
int desktop = get_window_desktop(win);
|
int desktop = get_window_desktop(win);
|
||||||
if (task->desktop != desktop) {
|
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);
|
autohide_trigger_hide(panel);
|
||||||
if (panel->is_hidden) {
|
if (panel->is_hidden) {
|
||||||
if (e->type == ClientMessage && e->xclient.message_type == server.atom.XdndPosition) {
|
if (e->type == ClientMessage && e->xclient.message_type == server.atom.XdndPosition) {
|
||||||
hidden_dnd = TRUE;
|
hidden_panel_shown_for_dnd = TRUE;
|
||||||
autohide_show(panel);
|
autohide_show(panel);
|
||||||
} else {
|
} else {
|
||||||
// discard further processing of this event because the panel is not visible yet
|
// discard further processing of this event because the panel is not visible yet
|
||||||
return TRUE;
|
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) {
|
e->xclient.message_type == server.atom.XdndLeave) {
|
||||||
hidden_dnd = FALSE;
|
hidden_panel_shown_for_dnd = FALSE;
|
||||||
autohide_hide(panel);
|
autohide_hide(panel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1162,15 +510,15 @@ void handle_x_event(XEvent *e)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Expose:
|
case Expose:
|
||||||
event_expose(e);
|
handle_event_expose(e);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyNotify:
|
case PropertyNotify:
|
||||||
event_property_notify(e);
|
handle_event_property_notify(e);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigureNotify:
|
case ConfigureNotify:
|
||||||
event_configure_notify(e);
|
handle_event_configure_notify(e);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigureRequest: {
|
case ConfigureRequest: {
|
||||||
|
@ -1213,11 +561,7 @@ void handle_x_event(XEvent *e)
|
||||||
case DestroyNotify:
|
case DestroyNotify:
|
||||||
if (e->xany.window == server.composite_manager) {
|
if (e->xany.window == server.composite_manager) {
|
||||||
// Stop real_transparency
|
// Stop real_transparency
|
||||||
fprintf(stderr,
|
emit_self_restart("compositor shutdown");
|
||||||
YELLOW "%s %d: triggering tint2 restart due to compositor shutdown" RESET "\n",
|
|
||||||
__FILE__,
|
|
||||||
__LINE__);
|
|
||||||
signal_pending = SIGUSR1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->xany.window == g_tooltip.window || !systray_enabled)
|
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[1] == server.atom._NET_WM_CM_S0) {
|
||||||
if (ev->data.l[2] == None) {
|
if (ev->data.l[2] == None) {
|
||||||
// Stop real_transparency
|
// Stop real_transparency
|
||||||
fprintf(stderr,
|
emit_self_restart("compositor changed");
|
||||||
YELLOW "%s %d: triggering tint2 restart due to change in transparency" RESET "\n",
|
|
||||||
__FILE__,
|
|
||||||
__LINE__);
|
|
||||||
signal_pending = SIGUSR1;
|
|
||||||
} else {
|
} else {
|
||||||
// Start real_transparency
|
// Start real_transparency
|
||||||
fprintf(stderr,
|
emit_self_restart("compositor changed");
|
||||||
YELLOW "%s %d: triggering tint2 restart due to change in transparency" RESET "\n",
|
|
||||||
__FILE__,
|
|
||||||
__LINE__);
|
|
||||||
signal_pending = SIGUSR1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (systray_enabled && e->xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE &&
|
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_read = 0;
|
||||||
double ts_event_processed = 0;
|
double ts_event_processed = 0;
|
||||||
double ts_render_finished = 0;
|
double ts_render_finished = 0;
|
||||||
double ts_flush_finished = 0;
|
double ts_flush_finished = 0;
|
||||||
gboolean first_render = TRUE;
|
gboolean first_render = TRUE;
|
||||||
while (1) {
|
|
||||||
|
while (!get_signal_pending()) {
|
||||||
if (panel_refresh) {
|
if (panel_refresh) {
|
||||||
if (debug_fps)
|
if (debug_fps)
|
||||||
ts_event_processed = get_time();
|
ts_event_processed = get_time();
|
||||||
|
@ -1442,35 +748,33 @@ start:
|
||||||
// Create a File Description Set containing x11_fd
|
// Create a File Description Set containing x11_fd
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
FD_ZERO(&fdset);
|
FD_ZERO(&fdset);
|
||||||
FD_SET(x11_fd, &fdset);
|
FD_SET(server.x11_fd, &fdset);
|
||||||
int maxfd = x11_fd;
|
int maxfd = server.x11_fd;
|
||||||
if (sigchild_pipe_valid) {
|
if (sigchild_pipe_valid) {
|
||||||
FD_SET(sigchild_pipe[0], &fdset);
|
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) {
|
for (GList *l = panel_config.execp_list; l; l = l->next) {
|
||||||
Execp *execp = (Execp *)l->data;
|
Execp *execp = (Execp *)l->data;
|
||||||
int fd = execp->backend->child_pipe_stdout;
|
int fd = execp->backend->child_pipe_stdout;
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
FD_SET(fd, &fdset);
|
FD_SET(fd, &fdset);
|
||||||
maxfd = maxfd < fd ? fd : maxfd;
|
maxfd = MAX(maxfd, fd);
|
||||||
}
|
}
|
||||||
fd = execp->backend->child_pipe_stderr;
|
fd = execp->backend->child_pipe_stderr;
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
FD_SET(fd, &fdset);
|
FD_SET(fd, &fdset);
|
||||||
maxfd = maxfd < fd ? fd : maxfd;
|
maxfd = MAX(maxfd, fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ufd > 0) {
|
if (uevent_fd > 0) {
|
||||||
FD_SET(ufd, &fdset);
|
FD_SET(uevent_fd, &fdset);
|
||||||
maxfd = maxfd < ufd ? ufd : maxfd;
|
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;
|
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();
|
uevent_handler();
|
||||||
|
|
||||||
if (sigchild_pipe_valid) {
|
if (sigchild_pipe_valid) {
|
||||||
|
@ -1499,25 +803,53 @@ start:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback_timeout_expired();
|
handle_expired_timers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (signal_pending) {
|
void tint2(int argc, char **argv, gboolean *restart)
|
||||||
cleanup();
|
{
|
||||||
if (signal_pending == SIGUSR1) {
|
*restart = FALSE;
|
||||||
fprintf(stderr, YELLOW "%s %d: restarting tint2..." RESET "\n", __FILE__, __LINE__);
|
|
||||||
// restart tint2
|
init(argc, argv);
|
||||||
// SIGUSR1 used when : user's signal, composite manager stop/start or xrandr
|
|
||||||
goto start;
|
if (snapshot_path) {
|
||||||
} else if (signal_pending == SIGUSR2) {
|
save_screenshot(snapshot_path);
|
||||||
fprintf(stderr, YELLOW "%s %d: reexecuting tint2..." RESET "\n", __FILE__, __LINE__);
|
cleanup();
|
||||||
if (execvp(argv[0], argv) == -1) {
|
return;
|
||||||
fprintf(stderr, RED "%s %d: failed!" RESET "\n", __FILE__, __LINE__);
|
}
|
||||||
return 1;
|
|
||||||
}
|
XSync(server.display, False);
|
||||||
} else {
|
|
||||||
// SIGINT, SIGTERM, SIGHUP
|
dnd_init();
|
||||||
exit(0);
|
|
||||||
|
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);
|
void panel_clear_background(void *obj);
|
||||||
|
|
||||||
int signal_pending;
|
|
||||||
|
|
||||||
MouseAction mouse_left;
|
MouseAction mouse_left;
|
||||||
MouseAction mouse_middle;
|
MouseAction mouse_middle;
|
||||||
MouseAction mouse_right;
|
MouseAction mouse_right;
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int signal_pending;
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// mouse events
|
// mouse events
|
||||||
extern MouseAction mouse_left;
|
extern MouseAction mouse_left;
|
||||||
|
|
44
src/server.c
44
src/server.c
|
@ -27,8 +27,10 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "server.h"
|
#include "common.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "signals.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
Server server;
|
Server server;
|
||||||
|
@ -733,3 +735,43 @@ void forward_click(XEvent *e)
|
||||||
// XSetInputFocus(server.display, e->xbutton.window, RevertToParent, e->xbutton.time);
|
// XSetInputFocus(server.display, e->xbutton.window, RevertToParent, e->xbutton.time);
|
||||||
XSendEvent(server.display, e->xbutton.window, False, ButtonPressMask, e);
|
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 {
|
typedef struct Server {
|
||||||
Display *display;
|
Display *display;
|
||||||
|
int x11_fd;
|
||||||
Window root_win;
|
Window root_win;
|
||||||
Window composite_manager;
|
Window composite_manager;
|
||||||
gboolean real_transparency;
|
gboolean real_transparency;
|
||||||
|
@ -181,6 +182,9 @@ void server_init_atoms();
|
||||||
void server_init_visual();
|
void server_init_visual();
|
||||||
void server_init_xdamage();
|
void server_init_xdamage();
|
||||||
|
|
||||||
|
void x11_io_error(Display *display);
|
||||||
|
void handle_crash(const char *reason);
|
||||||
|
|
||||||
// detect root background
|
// detect root background
|
||||||
void get_root_pixmap();
|
void get_root_pixmap();
|
||||||
|
|
||||||
|
@ -197,4 +201,9 @@ void change_desktop(int desktop);
|
||||||
// Forward mouse click to the desktop window
|
// Forward mouse click to the desktop window
|
||||||
void forward_click(XEvent *e);
|
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
|
#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;
|
error = FALSE;
|
||||||
XErrorHandler old = XSetErrorHandler(window_error_handler);
|
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
|
// Redirect rendering when using compositing
|
||||||
if (systray_composited) {
|
if (systray_composited) {
|
||||||
if (systray_profile)
|
if (systray_profile)
|
||||||
|
|
|
@ -330,8 +330,6 @@ void task_update_icon(Task *task)
|
||||||
if (img == NULL) {
|
if (img == NULL) {
|
||||||
imlib_context_set_image(default_icon);
|
imlib_context_set_image(default_icon);
|
||||||
img = imlib_clone_image();
|
img = imlib_clone_image();
|
||||||
if (0)
|
|
||||||
fprintf(stderr, "%s: Using default icon for %s\n", __FUNCTION__, task->title ? task->title : "task");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform icons
|
// 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 on_change_task(void *obj);
|
||||||
|
|
||||||
void task_update_icon(Task *task);
|
void task_update_icon(Task *task);
|
||||||
|
void task_update_desktop(Task *task);
|
||||||
gboolean task_update_title(Task *task);
|
gboolean task_update_title(Task *task);
|
||||||
void reset_active_task();
|
void reset_active_task();
|
||||||
void set_task_state(Task *task, TaskState state);
|
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));
|
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.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;
|
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;
|
Taskbar *taskbar;
|
||||||
|
|
|
@ -214,3 +214,35 @@ void draw_taskbarname(void *obj, cairo_t *c)
|
||||||
|
|
||||||
g_object_unref(layout);
|
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 taskbarname_default_font_changed();
|
||||||
|
|
||||||
|
void update_desktop_names();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,6 +22,7 @@ include_directories( ../util
|
||||||
set(SOURCES ../util/common.c
|
set(SOURCES ../util/common.c
|
||||||
../util/strnatcmp.c
|
../util/strnatcmp.c
|
||||||
../util/cache.c
|
../util/cache.c
|
||||||
|
../util/timer.c
|
||||||
../config.c
|
../config.c
|
||||||
../server.c
|
../server.c
|
||||||
../launcher/apps-common.c
|
../launcher/apps-common.c
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "../server.h"
|
#include "../server.h"
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -44,7 +45,162 @@
|
||||||
#include <librsvg/rsvg.h>
|
#include <librsvg/rsvg.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_LIBUNWIND
|
||||||
|
#define UNW_LOCAL_ONLY
|
||||||
|
#include <libunwind.h>
|
||||||
|
#else
|
||||||
|
#ifdef ENABLE_EXECINFO
|
||||||
|
#include <execinfo.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "../panel.h"
|
#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)
|
void copy_file(const char *path_src, const char *path_dest)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,18 @@ typedef enum MouseAction {
|
||||||
|
|
||||||
#define ALL_DESKTOPS 0xFFFFFFFF
|
#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
|
// Copies a file to another path
|
||||||
void copy_file(const char *path_src, const char *path_dest);
|
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) {
|
if (timeout_list) {
|
||||||
timeout *t = timeout_list->data;
|
timeout *t = timeout_list->data;
|
||||||
|
@ -145,9 +145,10 @@ void update_next_timeout()
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
next_timeout.tv_sec = -1;
|
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;
|
struct timespec cur_time;
|
||||||
timeout *t;
|
timeout *t;
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
// Periodic timeouts are aligned to each other whenever possible, i.e. one interval_msec is an
|
// Periodic timeouts are aligned to each other whenever possible, i.e. one interval_msec is an
|
||||||
// integral multiple of the other.
|
// integral multiple of the other.
|
||||||
|
|
||||||
extern struct timeval next_timeout;
|
|
||||||
typedef struct _timeout timeout;
|
typedef struct _timeout timeout;
|
||||||
|
|
||||||
// Initializes default global data.
|
// 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'
|
// Stops the timer 't'
|
||||||
void stop_timeout(timeout *t);
|
void stop_timeout(timeout *t);
|
||||||
|
|
||||||
// Updates next_timeout to the value, when the next installed timeout will expire
|
// Get the time when the next installed timer will expire, or NULL if there is no timer.
|
||||||
void update_next_timeout();
|
// Do not free the pointer; but it is safe to change its contents.
|
||||||
|
struct timeval *get_next_timeout();
|
||||||
|
|
||||||
// Callback of all expired timeouts
|
// 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
|
// Returns -1 if t1 < t2, 0 if t1 == t2, 1 if t1 > t2
|
||||||
gint compare_timespecs(const struct timespec *t1, const struct timespec *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.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "uevent.h"
|
||||||
|
int uevent_fd = -1;
|
||||||
|
|
||||||
#ifdef ENABLE_UEVENT
|
#ifdef ENABLE_UEVENT
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -30,9 +33,7 @@
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "uevent.h"
|
|
||||||
|
|
||||||
static int ueventfd = -1;
|
|
||||||
static struct sockaddr_nl nls;
|
static struct sockaddr_nl nls;
|
||||||
static GList *notifiers = NULL;
|
static GList *notifiers = NULL;
|
||||||
|
|
||||||
|
@ -146,11 +147,11 @@ void uevent_unregister_notifier(struct uevent_notify *nb)
|
||||||
|
|
||||||
void uevent_handler()
|
void uevent_handler()
|
||||||
{
|
{
|
||||||
if (ueventfd < 0)
|
if (uevent_fd < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char buf[512];
|
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)
|
if (len < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -181,27 +182,27 @@ int uevent_init()
|
||||||
nls.nl_groups = -1;
|
nls.nl_groups = -1;
|
||||||
|
|
||||||
/* open socket */
|
/* open socket */
|
||||||
ueventfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
uevent_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||||
if (ueventfd < 0) {
|
if (uevent_fd < 0) {
|
||||||
fprintf(stderr, "Error: socket open failed\n");
|
fprintf(stderr, "Error: socket open failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Listen to netlink socket */
|
/* 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");
|
fprintf(stderr, "Bind failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Kernel uevent interface initialized...\n");
|
printf("Kernel uevent interface initialized...\n");
|
||||||
|
|
||||||
return ueventfd;
|
return uevent_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uevent_cleanup()
|
void uevent_cleanup()
|
||||||
{
|
{
|
||||||
if (ueventfd >= 0)
|
if (uevent_fd >= 0)
|
||||||
close(ueventfd);
|
close(uevent_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#ifndef UEVENT_H
|
#ifndef UEVENT_H
|
||||||
#define UEVENT_H
|
#define UEVENT_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
enum uevent_action {
|
enum uevent_action {
|
||||||
UEVENT_UNKNOWN = 0x01,
|
UEVENT_UNKNOWN = 0x01,
|
||||||
UEVENT_ADD = 0x02,
|
UEVENT_ADD = 0x02,
|
||||||
|
@ -48,6 +50,8 @@ struct uevent_notify {
|
||||||
void (*cb)(struct uevent *e, void *userdata);
|
void (*cb)(struct uevent *e, void *userdata);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern int uevent_fd;
|
||||||
|
|
||||||
#if ENABLE_UEVENT
|
#if ENABLE_UEVENT
|
||||||
int uevent_init();
|
int uevent_init();
|
||||||
void uevent_cleanup();
|
void uevent_cleanup();
|
||||||
|
|
|
@ -68,7 +68,7 @@ src/panel.c
|
||||||
src/panel.h
|
src/panel.h
|
||||||
src/server.c
|
src/server.c
|
||||||
src/server.h
|
src/server.h
|
||||||
src/tint.c
|
src/main.c
|
||||||
AUTHORS
|
AUTHORS
|
||||||
ChangeLog
|
ChangeLog
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
@ -229,3 +229,9 @@ src/drag_and_drop.c
|
||||||
src/drag_and_drop.h
|
src/drag_and_drop.h
|
||||||
src/mouse_actions.c
|
src/mouse_actions.c
|
||||||
src/mouse_actions.h
|
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