From 5ee5bb1e23b303477c4335dd0ff412735152af5a Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Sat, 17 Sep 2016 18:57:44 +0200 Subject: [PATCH] First rough version of async loading for dmenu. --- configure.ac | 4 +- source/dialogs/dmenu.c | 127 +++++++++++++++++++++++++++-------------- source/view.c | 1 + 3 files changed, 86 insertions(+), 46 deletions(-) diff --git a/configure.ac b/configure.ac index 3f103a88..2a0d444e 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,7 @@ dnl --------------------------------------------------------------------- dnl Check for C functions. dnl --------------------------------------------------------------------- AC_CHECK_FUNC([getline],, AC_MSG_ERROR("Could not find getline in c library")) -AC_CHECK_FUNC([getdelim],, AC_MSG_ERROR("Could not find getdelim in c library")) +AC_CHECK_FUNC([open],, AC_MSG_ERROR("Could not find open in c library")) AC_CHECK_FUNC([sysconf],, AC_MSG_ERROR("Could not find sysconf")) AC_CHECK_FUNC([getenv],, AC_MSG_ERROR("Could not find getenv")) AC_CHECK_FUNC([strtok_r],, AC_MSG_ERROR("Could not find strtok_r")) @@ -93,7 +93,7 @@ PKG_PROG_PKG_CONFIG dnl --------------------------------------------------------------------- dnl PKG_CONFIG based dependencies dnl --------------------------------------------------------------------- -PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.40]) +PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.40 gio-unix-2.0]) GW_CHECK_XCB([xcb-aux xcb-xkb xkbcommon >= 0.5.0 xkbcommon-x11 xcb-ewmh xcb-icccm xcb-xrm xcb-randr xcb-xinerama]) PKG_CHECK_MODULES([pango], [pango pangocairo]) PKG_CHECK_MODULES([cairo], [cairo cairo-xcb]) diff --git a/source/dialogs/dmenu.c b/source/dialogs/dmenu.c index 3516b9d7..cf1b3c75 100644 --- a/source/dialogs/dmenu.c +++ b/source/dialogs/dmenu.c @@ -34,6 +34,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "rofi.h" #include "settings.h" #include "textbox.h" @@ -41,8 +46,8 @@ #include "helper.h" #include "xrmoptions.h" #include "view.h" -// We limit at 1000000 rows for now. -#define DMENU_MAX_ROWS 1000000 + +#define LOG_DOMAIN "Dialogs.DMenu" struct range_pair { @@ -84,6 +89,7 @@ typedef struct unsigned int do_markup; // List with entries. char **cmd_list; + unsigned int cmd_list_real_length; unsigned int cmd_list_length; unsigned int only_selected; unsigned int selected_count; @@ -91,47 +97,62 @@ typedef struct gchar **columns; gchar *column_separator; gboolean multi_select; + + GCancellable *cancel; + gulong cancel_source; + GInputStream *input_stream; + GDataInputStream *data_input_stream; } DmenuModePrivateData; -static char **get_dmenu ( DmenuModePrivateData *pd, FILE *fd, unsigned int *length ) +static void async_close_callback ( GObject *source_object, GAsyncResult *res, G_GNUC_UNUSED gpointer user_data ) { - TICK_N ( "Read stdin START" ); - char **retv = NULL; - unsigned int rvlength = 1; + g_input_stream_close_finish ( G_INPUT_STREAM ( source_object ), res, NULL ); + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Closing data stream." ); +} - *length = 0; - gchar *data = NULL; - size_t data_l = 0; - ssize_t l = 0; - while ( ( l = getdelim ( &data, &data_l, pd->separator, fd ) ) > 0 ) { - if ( rvlength < ( *length + 2 ) ) { - rvlength *= 2; - retv = g_realloc ( retv, ( rvlength ) * sizeof ( char* ) ); - } - if ( data[l - 1] == pd->separator ) { - data[l - 1] = '\0'; - l--; - } - char *utfstr = rofi_force_utf8 ( data, l ); - - retv[( *length )] = utfstr; - - ( *length )++; - // Stop when we hit 2³¹ entries. - if ( ( *length ) >= DMENU_MAX_ROWS ) { - break; - } - } +static void async_read_callback ( GObject *source_object, GAsyncResult *res, gpointer user_data ) +{ + GDataInputStream *stream = (GDataInputStream *) source_object; + DmenuModePrivateData *pd = (DmenuModePrivateData *) user_data; + gsize len; + char *data = g_data_input_stream_read_upto_finish ( stream, res, &len, NULL ); if ( data != NULL ) { - free ( data ); - data = NULL; + // Absorb separator, already in buffer so should not block. + g_data_input_stream_read_byte ( stream, NULL, NULL ); + + if ( ( pd->cmd_list_length + 2 ) > pd->cmd_list_real_length ) { + pd->cmd_list_real_length = MAX ( pd->cmd_list_real_length * 2, 512 ); + pd->cmd_list = g_realloc ( pd->cmd_list, ( pd->cmd_list_real_length ) * sizeof ( char* ) ); + } + char *utfstr = rofi_force_utf8 ( data, len ); + + pd->cmd_list[pd->cmd_list_length] = utfstr; + pd->cmd_list[pd->cmd_list_length + 1] = NULL; + + g_free ( data ); + pd->cmd_list_length++; + rofi_view_reload (); + + g_data_input_stream_read_upto_async ( pd->data_input_stream, &( pd->separator ), 1, G_PRIORITY_DEFAULT, pd->cancel, + async_read_callback, pd ); + return; } - if ( retv != NULL ) { - retv = g_realloc ( retv, ( *length + 1 ) * sizeof ( char* ) ); - retv[( *length ) ] = NULL; + if ( !g_cancellable_is_cancelled ( pd->cancel ) ) { + // Hack, don't use get active. + rofi_view_set_overlay ( rofi_view_get_active (), NULL ); + g_input_stream_close_async ( G_INPUT_STREAM ( stream ), G_PRIORITY_DEFAULT, pd->cancel, async_close_callback, pd ); } - TICK_N ( "Read stdin STOP" ); - return retv; +} + +static void async_read_cancel ( G_GNUC_UNUSED GCancellable *cancel, G_GNUC_UNUSED gpointer data ) +{ + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Cancelled the async read." ); +} + +static void get_dmenu ( DmenuModePrivateData *pd ) +{ + g_data_input_stream_read_upto_async ( pd->data_input_stream, &( pd->separator ), 1, G_PRIORITY_DEFAULT, pd->cancel, + async_read_callback, pd ); } static unsigned int dmenu_mode_get_num_entries ( const Mode *sw ) @@ -287,6 +308,21 @@ static void dmenu_mode_free ( Mode *sw ) } DmenuModePrivateData *pd = (DmenuModePrivateData *) mode_get_private_data ( sw ); if ( pd != NULL ) { + if ( pd->cancel ) { + // If open, cancel reads. + if ( pd->input_stream && !g_input_stream_is_closed ( pd->input_stream ) ) { + g_cancellable_cancel ( pd->cancel ); + } + // This blocks until cancel is done. + g_cancellable_disconnect ( pd->cancel, pd->cancel_source ); + if ( pd->input_stream ) { + // Should close the stream if not yet done. + g_object_unref ( pd->data_input_stream ); + g_object_unref ( pd->input_stream ); + } + g_object_unref ( pd->cancel ); + } + for ( size_t i = 0; i < pd->cmd_list_length; i++ ) { if ( pd->cmd_list[i] ) { free ( pd->cmd_list[i] ); @@ -355,12 +391,12 @@ static int dmenu_mode_init ( Mode *sw ) if ( find_arg ( "-i" ) >= 0 ) { config.case_sensitive = FALSE; } - FILE *fd = NULL; + int fd = STDIN_FILENO; str = NULL; if ( find_arg_str ( "-input", &str ) ) { char *estr = rofi_expand_path ( str ); - fd = fopen ( str, "r" ); - if ( fd == NULL ) { + fd = open ( str, O_RDONLY ); + if ( fd < 0 ) { char *msg = g_markup_printf_escaped ( "Failed to open file: %s:\n\t%s", estr, strerror ( errno ) ); rofi_view_error_dialog ( msg, TRUE ); g_free ( msg ); @@ -369,10 +405,12 @@ static int dmenu_mode_init ( Mode *sw ) } g_free ( estr ); } - pd->cmd_list = get_dmenu ( pd, fd == NULL ? stdin : fd, &( pd->cmd_list_length ) ); - if ( fd != NULL ) { - fclose ( fd ); - } + pd->cancel = g_cancellable_new (); + pd->cancel_source = g_cancellable_connect ( pd->cancel, G_CALLBACK ( async_read_cancel ), pd, NULL ); + pd->input_stream = g_unix_input_stream_new ( fd, fd != STDIN_FILENO ); + pd->data_input_stream = g_data_input_stream_new ( pd->input_stream ); + + get_dmenu ( pd ); gchar *columns = NULL; if ( find_arg_str ( "-display-columns", &columns ) ) { @@ -597,8 +635,9 @@ int dmenu_switcher_dialog ( void ) g_free ( input ); return TRUE; } - // TODO remove RofiViewState *state = rofi_view_create ( &dmenu_mode, input, pd->prompt, pd->message, menu_flags, dmenu_finalize ); + // @TODO we should do this better. + rofi_view_set_overlay ( state, "Loading.. " ); rofi_view_set_selected_line ( state, pd->selected_line ); rofi_view_set_active ( state ); diff --git a/source/view.c b/source/view.c index 5e489573..4c04d20c 100644 --- a/source/view.c +++ b/source/view.c @@ -250,6 +250,7 @@ static gboolean rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data ) void rofi_view_reload ( void ) { + // @TODO add check if current view is equal to the callee if ( CacheState.idle_timeout == 0 ) { CacheState.idle_timeout = g_timeout_add ( 1000 / 10, rofi_view_reload_idle, NULL ); }