10 #include <sys/types.h>
17 #ifndef AUTORANDR_PATH
18 #define AUTORANDR_PATH "/usr/bin/autorandr"
22 static int VERBOSE = 0;
23 extern char **environ;
25 static void sigterm_handler(int signum)
27 signal(signum, SIG_DFL);
28 kill(getpid(), signum);
31 __attribute__((format(printf, 1, 2))) static void ar_log(const char *format, ...)
37 va_start(args, format);
38 vprintf(format, args);
43 static int ar_launch(void)
47 static char *argv[] = { AUTORANDR_PATH, "--change", "--default", "default", NULL};
48 if (execve(argv[0], argv, environ) == -1) {
50 fprintf(stderr, "Error executing file: %s\n", strerror(errsv));
61 int main(int argc, char **argv)
67 const struct option long_options[] = {
68 { "help", no_argument, &help, 1 },
69 { "daemonize", no_argument, &daemonize, 1 },
70 { "version", no_argument, &version, 1 },
71 { "verbose", no_argument, &VERBOSE, 1 },
73 static const char *short_options = "hd";
75 const char *help_str =
76 "Usage: autorandr_launcher [OPTION]\n"
78 "Listens to X server screen change events and launches autorandr after an event occurs.\n"
80 "\t-h,--help\t\t\tDisplay this help and exit\n"
81 "\t-d, --daemonize\t\t\tDaemonize program\n"
82 "\t--verbose\t\t\tOutput debugging information (prevents daemonizing)\n"
83 "\t--version\t\t\tDisplay version and exit\n";
84 const char *version_str = "v.5\n";
89 ch = getopt_long(argc, argv, short_options, long_options,
103 printf("%s", help_str);
107 printf("%s", version_str);
110 // Check for already running daemon?
113 if (daemonize == 1 && VERBOSE != 1) {
115 sa.sa_handler = sigterm_handler;
116 sigemptyset(&sa.sa_mask);
118 sigaction(SIGINT, &sa, NULL);
119 sigaction(SIGTERM, &sa, NULL);
120 sigaction(SIGQUIT, &sa, NULL);
121 signal(SIGHUP, SIG_IGN);
123 fprintf(stderr, "Failed to daemonize!\n");
129 xcb_connection_t *c = xcb_connect(NULL, &screenNum);
130 int conn_error = xcb_connection_has_error(c);
132 fprintf(stderr, "Connection error!\n");
135 // Get the screen whose number is screenNum
136 const xcb_setup_t *setup = xcb_get_setup(c);
137 xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
139 // we want the screen at index screenNum of the iterator
140 for (int i = 0; i < screenNum; ++i) {
141 xcb_screen_next(&iter);
143 xcb_screen_t *default_screen = iter.data;
144 ar_log("Connected to server\n");
146 // Subscribe to screen change events
147 xcb_randr_select_input(c, default_screen->root,
148 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
151 xcb_timestamp_t last_timestamp = (xcb_timestamp_t) 0;
152 time_t last_time = time(NULL);
153 ar_log("Waiting for event\n");
154 xcb_generic_event_t *evt;
155 while ( (evt = xcb_wait_for_event(c)) ) {
156 ar_log("Event type: %" PRIu8 "\n", evt->response_type);
157 ar_log("screen change masked: %" PRIu8 "\n",
159 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
161 if (evt->response_type &
162 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE) {
163 xcb_randr_screen_change_notify_event_t *randr_evt =
164 (xcb_randr_screen_change_notify_event_t *) evt;
165 time_t evt_time = time(NULL);
166 if ((randr_evt->timestamp > last_timestamp)
167 && (evt_time > last_time + 1)) {
168 ar_log("Launch autorandr!\n");
170 last_time = evt_time;
171 last_timestamp = randr_evt->timestamp;