summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/dialogs/drun.c33
-rw-r--r--source/dialogs/run.c4
-rw-r--r--source/dialogs/ssh.c25
-rw-r--r--source/helper.c59
-rw-r--r--source/xcb.c51
5 files changed, 134 insertions, 38 deletions
diff --git a/source/dialogs/drun.c b/source/dialogs/drun.c
index bc0e2612..046adb26 100644
--- a/source/dialogs/drun.c
+++ b/source/dialogs/drun.c
@@ -65,6 +65,8 @@ typedef struct
char *root;
/* Path to desktop file */
char *path;
+ /* Application id (.desktop filename) */
+ char *app_id;
/* Icon stuff */
int icon_size;
char *icon_name;
@@ -187,15 +189,27 @@ static void exec_cmd_entry ( DRunModeEntry *e )
exec_path = NULL;
}
+ RofiHelperExecuteContext context = {
+ .name = e->name,
+ .icon = e->icon_name,
+ .app_id = e->app_id,
+ };
+ gboolean sn = g_key_file_get_boolean ( e->key_file, "Desktop Entry", "StartupNotify", NULL );
+ gchar *wmclass = NULL;
+ if ( sn && g_key_file_has_key ( e->key_file, "Desktop Entry", "StartupWMClass", NULL ) ) {
+ context.wmclass = wmclass = g_key_file_get_string ( e->key_file, "Desktop Entry", "StartupWMClass", NULL );
+ }
+
// Returns false if not found, if key not found, we don't want run in terminal.
gboolean terminal = g_key_file_get_boolean ( e->key_file, "Desktop Entry", "Terminal", NULL );
- if ( helper_execute_command ( exec_path, fp, terminal ) ) {
+ if ( helper_execute_command ( exec_path, fp, terminal, sn ? &context : NULL ) ) {
char *path = g_build_filename ( cache_dir, DRUN_CACHE_FILE, NULL );
char *key = g_strdup_printf ( "%s:::%s", e->root, e->path );
history_set ( path, key );
g_free ( key );
g_free ( path );
}
+ g_free ( wmclass );
g_free ( exec_path );
g_free ( str );
g_free ( fp );
@@ -203,7 +217,7 @@ static void exec_cmd_entry ( DRunModeEntry *e )
/**
* This function absorbs/freeÅ› path, so this is no longer available afterwards.
*/
-static gboolean read_desktop_file ( DRunModePrivateData *pd, const char *root, const char *path )
+static gboolean read_desktop_file ( DRunModePrivateData *pd, const char *root, const char *path, const gchar *basename )
{
// Create ID on stack.
// We know strlen (path ) > strlen(root)+1
@@ -280,8 +294,9 @@ static gboolean read_desktop_file ( DRunModePrivateData *pd, const char *root, c
pd->entry_list = g_realloc ( pd->entry_list, pd->cmd_list_length_actual * sizeof ( *( pd->entry_list ) ) );
}
pd->entry_list[pd->cmd_list_length].icon_size = 0;
- pd->entry_list[pd->cmd_list_length].root = g_strdup ( root );
- pd->entry_list[pd->cmd_list_length].path = g_strdup ( path );
+ pd->entry_list[pd->cmd_list_length].root = g_strdup ( root );
+ pd->entry_list[pd->cmd_list_length].path = g_strdup ( path );
+ pd->entry_list[pd->cmd_list_length].app_id = g_strndup ( basename, strlen ( basename ) - strlen ( ".desktop" ) );
gchar *n = g_key_file_get_locale_string ( kf, "Desktop Entry", "Name", NULL, NULL );
pd->entry_list[pd->cmd_list_length].name = n;
gchar *gn = g_key_file_get_locale_string ( kf, "Desktop Entry", "GenericName", NULL, NULL );
@@ -356,7 +371,7 @@ static void walk_dir ( DRunModePrivateData *pd, const char *root, const char *di
case DT_REG:
// Skip files not ending on .desktop.
if ( g_str_has_suffix ( file->d_name, ".desktop" ) ) {
- read_desktop_file ( pd, root, filename );
+ read_desktop_file ( pd, root, filename, file->d_name );
}
break;
case DT_DIR:
@@ -391,7 +406,8 @@ static void get_apps_history ( DRunModePrivateData *pd )
for ( unsigned int index = 0; index < length; index++ ) {
char **st = g_strsplit ( retv[index], ":::", 2 );
if ( st && st[0] && st[1] ) {
- if ( !read_desktop_file ( pd, st[0], st[1] ) ) {
+ const gchar *basename = g_utf8_strrchr ( st[1], -1, G_DIR_SEPARATOR );
+ if ( basename == NULL || !read_desktop_file ( pd, st[0], st[1], ++basename ) ) {
history_remove ( path, retv[index] );
}
}
@@ -490,6 +506,7 @@ static void drun_entry_clear ( DRunModeEntry *e )
{
g_free ( e->root );
g_free ( e->path );
+ g_free ( e->app_id );
if ( e->icon != NULL ) {
cairo_surface_destroy ( e->icon );
}
@@ -521,7 +538,9 @@ static ModeMode drun_mode_result ( Mode *sw, int mretv, char **input, unsigned i
exec_cmd_entry ( &( rmpd->entry_list[selected_line] ) );
}
else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) {
- helper_execute_command ( NULL, *input, run_in_term );
+ RofiHelperExecuteContext context = { .name = NULL };
+ // FIXME: We assume startup notification in terminals, not in others
+ helper_execute_command ( NULL, *input, run_in_term, run_in_term ? &context : NULL );
}
else if ( ( mretv & MENU_ENTRY_DELETE ) && selected_line < rmpd->cmd_list_length ) {
if ( selected_line < rmpd->history_length ) {
diff --git a/source/dialogs/run.c b/source/dialogs/run.c
index 9a19d662..8b20c290 100644
--- a/source/dialogs/run.c
+++ b/source/dialogs/run.c
@@ -92,7 +92,9 @@ static void exec_cmd ( const char *cmd, int run_in_term )
}
char *path = g_build_filename ( cache_dir, RUN_CACHE_FILE, NULL );
- if ( helper_execute_command ( NULL, lf_cmd, run_in_term ) ) {
+ RofiHelperExecuteContext context = { .name = NULL };
+ // FIXME: assume startup notification support for terminals
+ if ( helper_execute_command ( NULL, lf_cmd, run_in_term, run_in_term ? &context : NULL ) ) {
/**
* This happens in non-critical time (After launching app)
* It is allowed to be a bit slower.
diff --git a/source/dialogs/ssh.c b/source/dialogs/ssh.c
index 96230bf8..ccba8eb9 100644
--- a/source/dialogs/ssh.c
+++ b/source/dialogs/ssh.c
@@ -78,25 +78,20 @@ static inline int execshssh ( const char *host )
{
char **args = NULL;
int argsv = 0;
- helper_parse_setup ( config.ssh_command, &args, &argsv, "{host}", host, NULL );
- GError *error = NULL;
- g_spawn_async ( NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error );
+ helper_parse_setup ( config.ssh_command, &args, &argsv, "{host}", host, NULL );
- if ( error != NULL ) {
- char *msg = g_strdup_printf ( "Failed to execute: 'ssh %s'\nError: '%s'", host, error->message );
- rofi_view_error_dialog ( msg, FALSE );
- g_free ( msg );
- // print error.
- g_error_free ( error );
+ gsize l = strlen ( "Connecting to '' via rofi" ) + strlen ( host ) + 1;
+ gchar *desc = g_newa ( gchar, l );
- g_strfreev ( args );
- return FALSE;
- }
- // Free the args list.
- g_strfreev ( args );
+ g_snprintf ( desc, l, "Connecting to '%s' via rofi", host );
- return TRUE;
+ RofiHelperExecuteContext context = {
+ .name = "ssh",
+ .description = desc,
+ .command = "ssh",
+ };
+ return helper_execute ( NULL, args, "ssh ", host, &context );
}
/**
diff --git a/source/helper.c b/source/helper.c
index b1220fbd..33180096 100644
--- a/source/helper.c
+++ b/source/helper.c
@@ -47,6 +47,7 @@
#include <pango/pango-fontmap.h>
#include <pango/pangocairo.h>
#include <librsvg/rsvg.h>
+#include "display.h"
#include "xcb.h"
#include "helper.h"
#include "helper-theme.h"
@@ -962,21 +963,19 @@ int utf8_strncmp ( const char* a, const char* b, size_t n )
return r;
}
-int helper_execute_command ( const char *wd, const char *cmd, int run_in_term )
+gboolean helper_execute ( const char *wd, char **args, const char *error_precmd, const char *error_cmd, RofiHelperExecuteContext *context )
{
- int retv = TRUE;
- char **args = NULL;
- int argc = 0;
- if ( run_in_term ) {
- helper_parse_setup ( config.run_shell_command, &args, &argc, "{cmd}", cmd, NULL );
- }
- else {
- helper_parse_setup ( config.run_command, &args, &argc, "{cmd}", cmd, NULL );
- }
- GError *error = NULL;
- g_spawn_async ( wd, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error );
+ gboolean retv = TRUE;
+ GError *error = NULL;
+
+ GSpawnChildSetupFunc child_setup = NULL;
+ gpointer user_data = NULL;
+
+ display_startup_notification ( context, &child_setup, &user_data );
+
+ g_spawn_async ( wd, args, NULL, G_SPAWN_SEARCH_PATH, child_setup, user_data, NULL, &error );
if ( error != NULL ) {
- char *msg = g_strdup_printf ( "Failed to execute: '%s'\nError: '%s'", cmd, error->message );
+ char *msg = g_strdup_printf ( "Failed to execute: '%s%s'\nError: '%s'", error_precmd, error_cmd, error->message );
rofi_view_error_dialog ( msg, FALSE );
g_free ( msg );
// print error.
@@ -989,6 +988,40 @@ int helper_execute_command ( const char *wd, const char *cmd, int run_in_term )
return retv;
}
+gboolean helper_execute_command ( const char *wd, const char *cmd, gboolean run_in_term, RofiHelperExecuteContext *context )
+{
+ char **args = NULL;
+ int argc = 0;
+
+ if ( run_in_term ) {
+ helper_parse_setup ( config.run_shell_command, &args, &argc, "{cmd}", cmd, NULL );
+ }
+ else {
+ helper_parse_setup ( config.run_command, &args, &argc, "{cmd}", cmd, NULL );
+ }
+
+ if ( context != NULL ) {
+ if ( context->name == NULL ) {
+ context->name = args[0];
+ }
+ if ( context->binary == NULL ) {
+ context->binary = args[0];
+ }
+ if ( context->description == NULL ) {
+ gsize l = strlen ( "Launching '' via rofi" ) + strlen ( cmd ) + 1;
+ gchar *description = g_newa ( gchar, l );
+
+ g_snprintf ( description, l, "Launching '%s' via rofi", cmd );
+ context->description = description;
+ }
+ if ( context->command == NULL ) {
+ context->command = cmd;
+ }
+ }
+
+ return helper_execute ( wd, args, "", cmd, context );
+}
+
char *helper_get_theme_path ( const char *file )
{
char *filename = rofi_expand_path ( file );
diff --git a/source/xcb.c b/source/xcb.c
index 13dbdd6a..7bc80a84 100644
--- a/source/xcb.c
+++ b/source/xcb.c
@@ -49,6 +49,10 @@
#include <xcb/xkb.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
+#define SN_API_NOT_YET_FROZEN
+/* This function is declared as sn_launcher_context_set_application_id but implemented as sn_launcher_set_application_id */
+#define sn_launcher_context_set_application_id sn_launcher_set_application_id
+#include <libsn/sn.h>
#include "display.h"
#include "xcb-internal.h"
#include "xcb.h"
@@ -359,6 +363,45 @@ void display_dump_monitor_layout ( void )
}
}
+void display_startup_notification ( RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data )
+{
+ if ( context == NULL ) {
+ return;
+ }
+
+ SnLauncherContext *sncontext;
+
+ sncontext = sn_launcher_context_new ( xcb->sndisplay, xcb->screen_nbr );
+
+ sn_launcher_context_set_name ( sncontext, context->name );
+ sn_launcher_context_set_description ( sncontext, context->description );
+ if ( context->binary != NULL ) {
+ sn_launcher_context_set_binary_name ( sncontext, context->binary );
+ }
+ if ( context->icon != NULL ) {
+ sn_launcher_context_set_icon_name ( sncontext, context->icon );
+ }
+ if ( context->app_id != NULL ) {
+ sn_launcher_context_set_application_id ( sncontext, context->app_id );
+ }
+ if ( context->wmclass != NULL ) {
+ sn_launcher_context_set_wmclass ( sncontext, context->wmclass );
+ }
+
+ xcb_get_property_cookie_t c;
+ unsigned int current_desktop = 0;
+
+ c = xcb_ewmh_get_current_desktop ( &xcb->ewmh, xcb->screen_nbr );
+ if ( xcb_ewmh_get_current_desktop_reply ( &xcb->ewmh, c, &current_desktop, NULL ) ) {
+ sn_launcher_context_set_workspace ( sncontext, current_desktop );
+ }
+
+ sn_launcher_context_initiate ( sncontext, "rofi", context->command, xcb->last_timestamp );
+
+ *child_setup = (GSpawnChildSetupFunc) sn_launcher_context_setup_child_process;
+ *user_data = sncontext;
+}
+
static int monitor_get_dimension ( int monitor_id, workarea *mon )
{
memset ( mon, 0, sizeof ( workarea ) );
@@ -597,6 +640,7 @@ static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event )
case XCB_BUTTON_PRESS:
{
xcb_button_press_event_t *bpe = (xcb_button_press_event_t *) event;
+ xcb->last_timestamp = bpe->time;
rofi_view_handle_mouse_motion ( state, bpe->event_x, bpe->event_y );
nk_bindings_seat_handle_button ( xcb->bindings_seat, bpe->detail, NK_BINDINGS_BUTTON_STATE_PRESS, bpe->time );
break;
@@ -604,6 +648,7 @@ static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event )
case XCB_BUTTON_RELEASE:
{
xcb_button_release_event_t *bre = (xcb_button_release_event_t *) event;
+ xcb->last_timestamp = bre->time;
nk_bindings_seat_handle_button ( xcb->bindings_seat, bre->detail, NK_BINDINGS_BUTTON_STATE_RELEASE, bre->time );
if ( config.click_to_exit == TRUE ) {
if ( ! xcb->mouse_seen ) {
@@ -635,8 +680,9 @@ static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event )
xcb_key_press_event_t *xkpe = (xcb_key_press_event_t *) event;
gchar *text;
- text = nk_bindings_seat_handle_key ( xcb->bindings_seat, xkpe->detail, NK_BINDINGS_KEY_STATE_PRESS );
- if ( text != NULL ) {
+ xcb->last_timestamp = xkpe->time;
+ text = nk_bindings_seat_handle_key ( xcb->bindings_seat, xkpe->detail, NK_BINDINGS_KEY_STATE_PRESS );
+ if ( text != NULL ) {
rofi_view_handle_text ( state, text );
}
break;
@@ -644,6 +690,7 @@ static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event )
case XCB_KEY_RELEASE:
{
xcb_key_release_event_t *xkre = (xcb_key_release_event_t *) event;
+ xcb->last_timestamp = xkre->time;
nk_bindings_seat_handle_key ( xcb->bindings_seat, xkre->detail, NK_BINDINGS_KEY_STATE_RELEASE );
break;
}