From 93c01ccbd55497d1f70aef447d7a0aa04e7ecf57 Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Tue, 28 Jul 2015 22:22:18 +0200 Subject: [PATCH] Add new event handling --- include/rofi.h | 4 + source/helper.c | 44 +++++---- source/rofi.c | 231 +++++++++++++++++++++++++++++++++++--------- test/helper-test.c | 11 +++ test/textbox-test.c | 10 ++ 5 files changed, 235 insertions(+), 65 deletions(-) diff --git a/include/rofi.h b/include/rofi.h index 024e37ba..8bd02c7f 100644 --- a/include/rofi.h +++ b/include/rofi.h @@ -300,4 +300,8 @@ struct _Switcher #define color_blue_bold "\033[1;34m" #define color_magenta_bold "\033[1;35m" #define color_cyan_bold "\033[1;36m" + + + +void show_error_message ( const char *msg, int markup ); #endif diff --git a/source/helper.c b/source/helper.c index 1845022c..229d4ac1 100644 --- a/source/helper.c +++ b/source/helper.c @@ -148,11 +148,7 @@ int helper_parse_setup ( char * string, char ***output, int *length, ... ) if ( error ) { char *msg = g_strdup_printf ( "Failed to parse: '%s'\nError: '%s'", string, error->message ); -#ifdef error_dialog - error_dialog ( msg ); -#else - fputs ( msg, stderr ); -#endif + error_dialog ( msg, FALSE ); g_free ( msg ); // print error. g_error_free ( error ); @@ -396,11 +392,7 @@ int execute_generator ( const char * cmd ) if ( error != NULL ) { char *msg = g_strdup_printf ( "Failed to execute: '%s'\nError: '%s'", cmd, error->message ); -#ifdef error_dialog - error_dialog ( msg ); -#else - fputs ( msg, stderr ); -#endif + error_dialog ( msg, FALSE ); g_free ( msg ); // print error. g_error_free ( error ); @@ -464,23 +456,37 @@ void config_sanity_check ( ) "Please see the manpage: %s -help for the correct syntax.", stored_argv[0] ); exit ( EXIT_FAILURE ); } + int found_error = FALSE; + GString *msg = g_string_new ( "The configuration failed to validate:\n" ); if ( config.element_height < 1 ) { - fprintf ( stderr, "config.element_height is invalid. It needs to be atleast 1 line high.\n" ); - exit ( 1 ); + g_string_append_printf ( msg, "\tconfig.element_height=%d is invalid. An element needs to be atleast 1 line high.\n", + config.element_height ); + config.element_height = 1; + found_error = TRUE; } if ( config.menu_columns == 0 ) { - fprintf ( stderr, "config.menu_columns is invalid. You need at least one visible column.\n" ); - exit ( 1 ); + g_string_append_printf ( msg, "\tconfig.menu_columns=%d is invalid. You need at least one visible column.\n", + config.menu_columns ); + config.menu_columns = 1; + found_error = TRUE; } if ( config.menu_width == 0 ) { - fprintf ( stderr, "config.menu_width is invalid. You cannot have a window with no width.\n" ); - exit ( 1 ); + show_error_message ( "config.menu_width=0 is invalid. You cannot have a window with no width.", TRUE ); + config.menu_columns = 50; + found_error = TRUE; } if ( !( config.location >= WL_CENTER && config.location <= WL_WEST ) ) { - fprintf ( stderr, "config.location is invalid. ( %d >= %d >= %d) does not hold.\n", - WL_WEST, config.location, WL_CENTER ); - exit ( 1 ); + g_string_append_printf ( msg, "\tconfig.location=%d is invalid. Value should be between %d and %d.\n", + config.location, WL_CENTER, WL_WEST ); + config.location = WL_CENTER; + found_error = 1; } + if ( found_error ) { + g_string_append ( msg, "Please update your configuration." ); + show_error_message ( msg->str, TRUE ); + } + + g_string_free ( msg, TRUE ); // If alternative row is not set, copy the normal background color. if ( config.menu_bg_alt == NULL ) { config.menu_bg_alt = config.menu_bg; diff --git a/source/rofi.c b/source/rofi.c index ea2e50f1..5bfd2622 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -1483,31 +1483,28 @@ static int run_dmenu () XFreeColormap ( display, map ); map = None; } + // Release the window. + release_keyboard ( display ); + if ( main_window != None ) { + XUnmapWindow ( display, main_window ); + XDestroyWindow ( display, main_window ); + main_window = None; + XFreeGC ( display, gc ); + gc = NULL; + } + // Cleanup pid file. + if ( pidfile ) { + unlink ( pidfile ); + } return ret_state; } -static void run_switcher ( int do_fork, SwitcherMode mode ) +static void run_switcher ( SwitcherMode mode ) { - // we fork because it's technically possible to have multiple window - // lists up at once on a zaphod multihead X setup. - // this also happens to isolate the Xft font stuff in a child process - // that gets cleaned up every time. That library shows some valgrind - // strangeness... - if ( do_fork == TRUE ) { - if ( fork () ) { - return; - } - - display = XOpenDisplay ( display_str ); - XSync ( display, True ); - } // Create pid file to avoid multiple instances. create_pid_file ( pidfile ); // Create the colormap and the main visual. create_visual_and_colormap ( display ); - - // Because of the above fork, we want to do this here. - // Make sure this is isolated to its own thread. textbox_setup ( &vinfo, map ); // Otherwise check if requested mode is enabled. char *input = NULL; @@ -1548,13 +1545,23 @@ static void run_switcher ( int do_fork, SwitcherMode mode ) // Cleanup font setup. textbox_cleanup ( ); + + // Release the window. + release_keyboard ( display ); + if ( main_window != None ) { + XUnmapWindow ( display, main_window ); + XDestroyWindow ( display, main_window ); + main_window = None; + XFreeGC ( display, gc ); + gc = NULL; + } if ( map != None ) { XFreeColormap ( display, map ); map = None; } - - if ( do_fork == TRUE ) { - exit ( EXIT_SUCCESS ); + // Cleanup pid file. + if ( pidfile ) { + unlink ( pidfile ); } } @@ -1568,7 +1575,7 @@ static void handle_keypress ( XEvent *ev ) KeySym key = XkbKeycodeToKeysym ( display, ev->xkey.keycode, 0, 0 ); index = locate_switcher ( key, ev->xkey.state ); if ( index >= 0 ) { - run_switcher ( TRUE, index ); + run_switcher ( index ); } else { fprintf ( stderr, "Warning: Unhandled keypress in global keyhandler, keycode = %u mask = %u\n", ev->xkey.keycode, ev->xkey.state ); @@ -1604,10 +1611,7 @@ static void cleanup () XCloseDisplay ( display ); } } - // Cleanup pid file. - if ( pidfile ) { - unlink ( pidfile ); - } + // Cleaning up memory allocated by the Xresources file. config_xresource_free (); @@ -1716,7 +1720,7 @@ static inline void load_configuration_dynamic ( Display *display ) } -static void show_error_message ( const char *msg, int markup ) +void show_error_message ( const char *msg, int markup ) { // Create pid file create_pid_file ( pidfile ); @@ -1730,6 +1734,79 @@ static void show_error_message ( const char *msg, int markup ) XFreeColormap ( display, map ); map = None; } + // Cleanup font setup. + textbox_cleanup ( ); + + // Release the window. + release_keyboard ( display ); + if ( main_window != None ) { + XUnmapWindow ( display, main_window ); + XDestroyWindow ( display, main_window ); + main_window = None; + XFreeGC ( display, gc ); + gc = NULL; + } + if ( map != None ) { + XFreeColormap ( display, map ); + map = None; + } + // Cleanup pid file. + if ( pidfile ) { + unlink ( pidfile ); + } +} + +static void * pthread_signal_process ( void *arg ) +{ + int pfd = *( (int *) arg ); + + // Create same mask again. + sigset_t set; + sigemptyset ( &set ); + sigaddset ( &set, SIGHUP ); + sigaddset ( &set, SIGINT ); + sigaddset ( &set, SIGUSR1 ); + // loop forever. + while ( 1 ) { + siginfo_t info; + int sig = sigwaitinfo ( &set, &info ); + if ( sig < 0 ) { + perror ( "sigwaitinfo failed" ); + } + else { + // Send message to main thread. + if ( sig == SIGHUP ) { + write ( pfd, "c", 1 ); + } + if ( sig == SIGUSR1 ) { + write ( pfd, "i", 1 ); + } + else if ( sig == SIGINT ) { + write ( pfd, "q", 1 ); + // Close my end and exit. + return NULL; + } + } + } +} + + +static void reload_configuration () +{ + if ( find_arg ( "-no-config" ) < 0 ) { + // We need to open a new connection to X11, otherwise we get old + // configuration + Display *display = XOpenDisplay ( display_str ); + if ( display ) { + load_configuration ( display ); + load_configuration_dynamic ( display ); + + // Sanity check + config_sanity_check ( ); + parse_keys_abe (); + XCloseDisplay ( display ); + } + } } int main ( int argc, char *argv[] ) @@ -1816,6 +1893,15 @@ int main ( int argc, char *argv[] ) // Reload for dynamic part. load_configuration_dynamic ( display ); } + + // Set up X interaction. + const struct sigaction sigchld_action = { + .sa_handler = catch_exit + }; + sigaction ( SIGCHLD, &sigchld_action, NULL ); + + x11_setup ( display ); + // Sanity check config_sanity_check ( ); // Dump. @@ -1826,14 +1912,6 @@ int main ( int argc, char *argv[] ) // Parse the keybindings. parse_keys_abe (); - // Set up X interaction. - const struct sigaction sigchld_action = { - .sa_handler = catch_exit - }; - sigaction ( SIGCHLD, &sigchld_action, NULL ); - - x11_setup ( display ); - char *msg = NULL; if ( find_arg_str ( "-e", &( msg ) ) ) { int markup = FALSE; @@ -1866,7 +1944,7 @@ int main ( int argc, char *argv[] ) if ( find_arg_str ( "-show", &sname ) == TRUE ) { int index = switcher_get ( sname ); if ( index >= 0 ) { - run_switcher ( FALSE, index ); + run_switcher ( index ); } else { fprintf ( stderr, "The %s switcher has not been enabled\n", sname ); @@ -1909,24 +1987,85 @@ int main ( int argc, char *argv[] ) } } + sigset_t set; + + // Create a pipe to communicate between signal thread an main thread. + int pfds[2]; + pipe ( pfds ); + // Block all HUP signals. + // In this and other child (they inherit mask) + sigemptyset ( &set ); + sigaddset ( &set, SIGHUP ); + sigaddset ( &set, SIGINT ); + sigaddset ( &set, SIGUSR1 ); + sigprocmask ( SIG_BLOCK, &set, NULL ); + // Create signal handler process. + GThread *pid_signal_proc = g_thread_new ( + "signal_process", + pthread_signal_process, + (void *) &( pfds[1] ) ); + // Application Main loop. // This listens in the background for any events on the Xserver // catching global key presses. + // It also listens from messages from the signal process. + XSelectInput ( display, DefaultRootWindow ( display ), KeyPressMask ); + XFlush ( display ); + int x11_fd = ConnectionNumber ( display ); for (;; ) { - XEvent ev; + fd_set in_fds; + // Create a File Description Set containing x11_fd + FD_ZERO ( &in_fds ); + FD_SET ( x11_fd, &in_fds ); + FD_SET ( pfds[0], &in_fds ); - // block and wait for something - XNextEvent ( display, &ev ); - // If we get an event that does not belong to a window: - // Ignore it. - if ( ev.xany.window == None ) { - continue; - } - // If keypress, handle it. - if ( ev.type == KeyPress ) { - handle_keypress ( &ev ); + // Wait for X Event or a Timer + if ( select ( MAX ( x11_fd, pfds[0] ) + 1, &in_fds, 0, 0, NULL ) >= 0 ) { + if ( FD_ISSET ( x11_fd, &in_fds ) ) { + // X11 produced an event. Consume them. + XEvent ev; + while ( XPending ( display ) ) { + // block and wait for something + XNextEvent ( display, &ev ); + // If we get an event that does not belong to a window: + // Ignore it. + if ( ev.xany.window == None ) { + continue; + } + // If keypress, handle it. + if ( ev.type == KeyPress ) { + handle_keypress ( &ev ); + } + } + } + if ( FD_ISSET ( pfds[0], &in_fds ) ) { + // The signal thread send us a message. Process it. + char c; + read ( pfds[0], &c, 1 ); + // Reload configuration. + if ( c == 'c' ) { + fprintf ( stdout, "Reload configuration\n" ); + reload_configuration (); + } + // Got message to quit. + else if ( c == 'q' ) { + // Break out of loop. + break; + } + // Got message to print info + else if ( c == 'i' ) { + xresource_dump (); + } + } } } + // Join the signal process thread. (at this point it should have exited). + // this also unrefs (de-allocs) the GThread object. + g_thread_join ( pid_signal_proc ); + // Close pipe + close ( pfds[0] ); + close ( pfds[1] ); + fprintf ( stdout, "Quit from daemon mode.\n" ); } return EXIT_SUCCESS; diff --git a/test/helper-test.c b/test/helper-test.c index 15bd2def..2a22929f 100644 --- a/test/helper-test.c +++ b/test/helper-test.c @@ -10,6 +10,17 @@ static int test = 0; assert ( a ); \ printf ( "Test %i passed (%s)\n", ++test, # a ); \ } + +void error_dialog ( const char *msg, G_GNUC_UNUSED int markup ) +{ + fputs ( msg, stderr ); +} + +void show_error_message ( const char *msg, int markup ) +{ + error_dialog ( msg, markup ); +} + int main ( int argc, char ** argv ) { cmd_set_arguments ( argc, argv ); diff --git a/test/textbox-test.c b/test/textbox-test.c index b9de109c..9180565a 100644 --- a/test/textbox-test.c +++ b/test/textbox-test.c @@ -24,6 +24,16 @@ Display *display = NULL; Colormap map = None; XVisualInfo vinfo; +void error_dialog ( const char *msg, G_GNUC_UNUSED int markup ) +{ + fputs ( msg, stderr ); +} + +void show_error_message ( const char *msg, int markup ) +{ + error_dialog ( msg, markup ); +} + static unsigned int color_get ( Display *display, const char *const name ) { XColor color;