diff --git a/Makefile.am b/Makefile.am index a55dd526..94e8b712 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,7 @@ AM_CFLAGS=\ rofi_SOURCES=\ source/rofi.c\ + source/rofi-i3.c\ source/textbox.c\ source/xrmoptions.c\ source/dmenu-dialog.c\ @@ -33,6 +34,7 @@ rofi_SOURCES=\ config/config.c\ source/helper.c\ include/rofi.h\ + include/rofi-i3.h\ include/run-dialog.h\ include/ssh-dialog.h\ include/dmenu-dialog.h\ diff --git a/configure.ac b/configure.ac index 9c87ce22..605cf875 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,13 @@ AC_USE_SYSTEM_EXTENSIONS ## # I3 check ## -AC_CHECK_HEADERS([i3/ipc.h], - [i3_header=yes; break;]) +AC_ARG_ENABLE(i3support, + [AS_HELP_STRING([--disable-i3support], [Disable check for i3 support])]) +if test "x$enable_i3support" != xno; +then + AC_CHECK_HEADERS([i3/ipc.h], + [i3_header=yes; break;]) +fi dnl --------------------------------------------------------------------- diff --git a/include/rofi-i3.h b/include/rofi-i3.h new file mode 100644 index 00000000..4187fc26 --- /dev/null +++ b/include/rofi-i3.h @@ -0,0 +1,32 @@ +#ifndef __ROFI_I3_H__ +#define __ROFI_I3_H__ + +/** + * These functions are dummies when i3 support is not compiled in. + */ + +/** + * @param socket_path The I3 IPC socket. + * @param id The window to focus on. + * + * If we want to switch windows in I3, we use I3 IPC mode. + * This works more better then sending messages via X11. + * Hopefully at some point, I3 gets fixed and this is not needed. + * This function takes the path to the i3 IPC socket, and the XID of the window. + */ +void i3_support_focus_window ( Window id ); + +/** + * @param display The display to read the i3 property from. + * + * Get the i3 socket from the X root window. + * @returns TRUE when i3 is running, FALSE when not. + */ + +int i3_support_initialize ( Display *display ); + +/** + * Cleanup. + */ +void i3_support_free_internals ( void ); +#endif // __ROFI_I3_H__ diff --git a/include/rofi.h b/include/rofi.h index e9eb5396..49cc4305 100644 --- a/include/rofi.h +++ b/include/rofi.h @@ -219,4 +219,14 @@ extern Settings config; */ void error_dialog ( char *msg ); +/** + * @param w The Window to read property from. + * @param atom The property identifier + * + * Get text property defined by atom from window. + * Support utf8. + * + * @returns a newly allocated string with the result or NULL + */ +char* window_get_text_prop ( Window w, Atom atom ); #endif diff --git a/source/rofi-i3.c b/source/rofi-i3.c new file mode 100644 index 00000000..f853338d --- /dev/null +++ b/source/rofi-i3.c @@ -0,0 +1,154 @@ +/** + * rofi + * + * MIT/X11 License + * Copyright (c) 2013-2014 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rofi.h" +#include "rofi-i3.h" + +#ifdef HAVE_I3_IPC_H +#include +// Path to HAVE_I3_IPC_H socket. +char *i3_socket_path = NULL; + +void i3_support_focus_window ( Window id ) +{ + i3_ipc_header_t head; + char command[128]; + int s, len; + ssize_t t; + struct sockaddr_un remote; + + if ( strlen ( i3_socket_path ) > UNIX_PATH_MAX ) { + fprintf ( stderr, "Socket path is to long. %zd > %d\n", strlen ( i3_socket_path ), UNIX_PATH_MAX ); + return; + } + + if ( ( s = socket ( AF_UNIX, SOCK_STREAM, 0 ) ) == -1 ) { + fprintf ( stderr, "Failed to open connection to I3: %s\n", strerror ( errno ) ); + return; + } + + remote.sun_family = AF_UNIX; + strcpy ( remote.sun_path, i3_socket_path ); + len = strlen ( remote.sun_path ) + sizeof ( remote.sun_family ); + + if ( connect ( s, ( struct sockaddr * ) &remote, len ) == -1 ) { + fprintf ( stderr, "Failed to connect to I3 (%s): %s\n", i3_socket_path, strerror ( errno ) ); + close ( s ); + return; + } + + + // Formulate command + snprintf ( command, 128, "[id=\"%lu\"] focus", id ); + // Prepare header. + memcpy ( head.magic, I3_IPC_MAGIC, 6 ); + head.size = strlen ( command ); + head.type = I3_IPC_MESSAGE_TYPE_COMMAND; + // Send header. + t = send ( s, &head, sizeof ( i3_ipc_header_t ), 0 ); + if ( t == -1 ) { + char *msg = g_strdup_printf ( "Failed to send message header to i3: %s\n", strerror ( errno ) ); + error_dialog ( msg ); + g_free ( msg ); + close ( s ); + return; + } + // Send message + t = send ( s, command, strlen ( command ), 0 ); + if ( t == -1 ) { + char *msg = g_strdup_printf ( "Failed to send message body to i3: %s\n", strerror ( errno ) ); + error_dialog ( msg ); + g_free ( msg ); + close ( s ); + return; + } + // Receive header. + t = recv ( s, &head, sizeof ( head ), 0 ); + + if ( t == sizeof ( head ) ) { + t = recv ( s, command, head.size, 0 ); + if ( t == head.size ) { + // Response. + } + } + + close ( s ); +} + +int i3_support_initialize ( Display *display ) +{ + Atom i3_sp_atom = XInternAtom ( display, "I3_SOCKET_PATH", False ); + + if ( i3_sp_atom != None ) { + // Get the default screen. + Screen *screen = DefaultScreenOfDisplay ( display ); + // Find the root window (each X has one.). + Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) ); + // Get the i3 path property. + i3_socket_path = window_get_text_prop ( root, i3_sp_atom ); + } + // If we find it, go into i3 mode. + return ( i3_socket_path != NULL ) ? TRUE : FALSE; +} + +void i3_support_free_internals ( void ) +{ + if ( i3_socket_path != NULL ) { + g_free ( i3_socket_path ); + } +} + +#else + + +void i3_support_focus_window ( Window id ) +{ + fprintf ( stderr, "Trying to control i3, when i3 support is not enabled.\n" ); + abort (); +} +void i3_support_free_internals ( void ) +{ +} + +int i3_support_initialize ( Display *display ) +{ + return FALSE; +} +#endif // HAVE_I3_IPC_H diff --git a/source/rofi.c b/source/rofi.c index 738a1764..79931d7e 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -49,14 +49,10 @@ #include #include -#ifdef HAVE_I3_IPC_H -#include -#include -#include -#endif #include "helper.h" #include "rofi.h" +#include "rofi-i3.h" #include "run-dialog.h" #include "ssh-dialog.h" @@ -68,12 +64,8 @@ #define LINE_MARGIN 3 -#ifdef HAVE_I3_IPC_H // This setting is no longer user configurable, but partial to this file: int config_i3_mode = 0; -// Path to HAVE_I3_IPC_H socket. -char *i3_socket_path = NULL; -#endif char *pidfile = NULL; static void create_pid_file ( const char *pidfile ); @@ -99,7 +91,6 @@ static int ( *xerror )( Display *, XErrorEvent * ); X ( _NET_WM_STATE_SKIP_PAGER ), \ X ( _NET_WM_STATE_ABOVE ), \ X ( _NET_WM_DESKTOP ), \ - X ( I3_SOCKET_PATH ), \ X ( CLIPBOARD ), \ X ( UTF8_STRING ), \ X ( _NET_WM_WINDOW_OPACITY ) @@ -151,82 +142,6 @@ static int switcher_get ( const char *name ) } -#ifdef HAVE_I3_IPC_H -/** - * @param socket_path The I3 IPC socket. - * @param id The window to focus on. - * - * If we want to switch windows in I3, we use I3 IPC mode. - * This works more better then sending messages via X11. - * Hopefully at some point, I3 gets fixed and this is not needed. - * This function takes the path to the i3 IPC socket, and the XID of the window. - */ -static void focus_window_i3 ( const char *socket_path, Window id ) -{ - i3_ipc_header_t head; - char command[128]; - int s, len; - ssize_t t; - struct sockaddr_un remote; - - if ( strlen ( socket_path ) > UNIX_PATH_MAX ) { - fprintf ( stderr, "Socket path is to long. %zd > %d\n", strlen ( socket_path ), UNIX_PATH_MAX ); - return; - } - - if ( ( s = socket ( AF_UNIX, SOCK_STREAM, 0 ) ) == -1 ) { - fprintf ( stderr, "Failed to open connection to I3: %s\n", strerror ( errno ) ); - return; - } - - remote.sun_family = AF_UNIX; - strcpy ( remote.sun_path, socket_path ); - len = strlen ( remote.sun_path ) + sizeof ( remote.sun_family ); - - if ( connect ( s, ( struct sockaddr * ) &remote, len ) == -1 ) { - fprintf ( stderr, "Failed to connect to I3 (%s): %s\n", socket_path, strerror ( errno ) ); - close ( s ); - return; - } - - - // Formulate command - snprintf ( command, 128, "[id=\"%lu\"] focus", id ); - // Prepare header. - memcpy ( head.magic, I3_IPC_MAGIC, 6 ); - head.size = strlen ( command ); - head.type = I3_IPC_MESSAGE_TYPE_COMMAND; - // Send header. - t = send ( s, &head, sizeof ( i3_ipc_header_t ), 0 ); - if ( t == -1 ) { - char *msg = g_strdup_printf ( "Failed to send message header to i3: %s\n", strerror ( errno ) ); - error_dialog ( msg ); - g_free ( msg ); - close ( s ); - return; - } - // Send message - t = send ( s, command, strlen ( command ), 0 ); - if ( t == -1 ) { - char *msg = g_strdup_printf ( "Failed to send message body to i3: %s\n", strerror ( errno ) ); - error_dialog ( msg ); - g_free ( msg ); - close ( s ); - return; - } - // Receive header. - t = recv ( s, &head, sizeof ( head ), 0 ); - - if ( t == sizeof ( head ) ) { - t = recv ( s, command, head.size, 0 ); - if ( t == head.size ) { - // Response. - } - } - - close ( s ); -} -#endif void catch_exit ( __attribute__( ( unused ) ) int sig ) { @@ -578,7 +493,7 @@ static int window_get_prop ( Window w, Atom prop, Atom *type, int *items, void * // retrieve a text property from a window // technically we could use window_get_prop(), but this is better for character set support -static char* window_get_text_prop ( Window w, Atom atom ) +char* window_get_text_prop ( Window w, Atom atom ) { XTextProperty prop; char *res = NULL; @@ -2092,14 +2007,11 @@ SwitcherMode run_switcher_window ( char **input, G_GNUC_UNUSED void *data ) && !client_has_state ( c, netatoms[_NET_WM_STATE_SKIP_TASKBAR] ) ) { classfield = MAX ( classfield, strlen ( c->class ) ); -#ifdef HAVE_I3_IPC_H - // In i3 mode, skip the i3bar completely. if ( config_i3_mode && strstr ( c->class, "i3bar" ) != NULL ) { continue; } -#endif if ( c->window == curr_win_id ) { c->active = TRUE; } @@ -2111,18 +2023,14 @@ SwitcherMode run_switcher_window ( char **input, G_GNUC_UNUSED void *data ) if ( !window_get_cardinal_prop ( root, netatoms[_NET_NUMBER_OF_DESKTOPS], &desktops, 1 ) ) { desktops = 1; } -#ifdef HAVE_I3_IPC_H if ( config_i3_mode ) { sprintf ( pattern, "%%-%ds %%s", MAX ( 5, classfield ) ); } else{ -#endif - sprintf ( pattern, "%%-%ds %%-%ds %%s", desktops < 10 ? 1 : 2, MAX ( 5, classfield ) ); -#ifdef HAVE_I3_IPC_H - } -#endif - char **list = g_malloc0_n ( ( ids->len + 1 ), sizeof ( char* ) ); - unsigned int lines = 0; + sprintf ( pattern, "%%-%ds %%-%ds %%s", desktops < 10 ? 1 : 2, MAX ( 5, classfield ) ); + } + char **list = g_malloc0_n ( ( ids->len + 1 ), sizeof ( char* ) ); + unsigned int lines = 0; // build the actual list for ( i = 0; i < ( ids->len ); i++ ) { @@ -2135,34 +2043,30 @@ SwitcherMode run_switcher_window ( char **input, G_GNUC_UNUSED void *data ) char desktop[5]; desktop[0] = 0; char *line = g_malloc ( strlen ( c->title ) + strlen ( c->class ) + classfield + 50 ); -#ifdef HAVE_I3_IPC_H if ( !config_i3_mode ) { -#endif - // find client's desktop. this is zero-based, so we adjust by since most - // normal people don't think like this :-) - if ( !window_get_cardinal_prop ( c->window, netatoms[_NET_WM_DESKTOP], &wmdesktop, 1 ) ) { - wmdesktop = 0xFFFFFFFF; - } + // find client's desktop. this is zero-based, so we adjust by since most + // normal people don't think like this :-) + if ( !window_get_cardinal_prop ( c->window, netatoms[_NET_WM_DESKTOP], &wmdesktop, 1 ) ) { + wmdesktop = 0xFFFFFFFF; + } - if ( wmdesktop < 0xFFFFFFFF ) { - sprintf ( desktop, "%d", (int) wmdesktop + 1 ); - } + if ( wmdesktop < 0xFFFFFFFF ) { + sprintf ( desktop, "%d", (int) wmdesktop + 1 ); + } - sprintf ( line, pattern, desktop, c->class, c->title ); -#ifdef HAVE_I3_IPC_H - } - else{ - sprintf ( line, pattern, c->class, c->title ); - } -#endif + sprintf ( line, pattern, desktop, c->class, c->title ); + } + else{ + sprintf ( line, pattern, c->class, c->title ); + } list[lines++] = line; } } - Time time; - int selected_line = 0; - MenuReturn mretv = menu ( list, lines, input, "window:", &time, NULL, - window_match, ids, &selected_line, config.levenshtein_sort ); + Time time; + int selected_line = 0; + MenuReturn mretv = menu ( list, lines, input, "window:", &time, NULL, + window_match, ids, &selected_line, config.levenshtein_sort ); if ( mretv == MENU_NEXT ) { retv = NEXT_DIALOG; @@ -2174,15 +2078,11 @@ SwitcherMode run_switcher_window ( char **input, G_GNUC_UNUSED void *data ) retv = selected_line; } else if ( ( mretv == MENU_OK || mretv == MENU_CUSTOM_INPUT ) && list[selected_line] ) { -#ifdef HAVE_I3_IPC_H - if ( config_i3_mode ) { // Hack for i3. - focus_window_i3 ( i3_socket_path, ids->array[selected_line] ); + i3_support_focus_window ( ids->array[selected_line] ); } - else -#endif - { + else{ // Change to the desktop of the selected window/client. // TODO: get rid of strtol window_send_message ( root, root, netatoms[_NET_CURRENT_DESKTOP], strtol ( list[selected_line], NULL, 10 ) - 1, @@ -2399,8 +2299,8 @@ static void parse_key ( char *combo, unsigned int *mod, KeySym *key ) // bind a key combination on a root window, compensating for Lock* states static void grab_key ( Display *display, unsigned int modmask, KeySym key ) { - Screen *screen = DefaultScreenOfDisplay ( display ); - Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) ); + Screen *screen = DefaultScreenOfDisplay ( display ); + Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) ); KeyCode keycode = XKeysymToKeycode ( display, key ); XUngrabKey ( display, keycode, AnyModifier, root ); @@ -2421,22 +2321,6 @@ static void grab_key ( Display *display, unsigned int modmask, KeySym key ) } -#ifdef HAVE_I3_IPC_H -/** - * Get the i3 socket from the X root window. - */ -static inline void display_get_i3_path ( Display *display ) -{ - // Get the default screen. - Screen *screen = DefaultScreenOfDisplay ( display ); - // Find the root window (each X has one.). - Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) ); - // Get the i3 path property. - i3_socket_path = window_get_text_prop ( root, netatoms[I3_SOCKET_PATH] ); - // If we find it, go into i3 mode. - config_i3_mode = ( i3_socket_path != NULL ) ? TRUE : FALSE; -} -#endif //HAVE_I3_IPC_H /** @@ -2559,13 +2443,9 @@ static void cleanup () if ( cache_client != NULL ) { winlist_free ( cache_client ); } -#ifdef HAVE_I3_IPC_H - if ( i3_socket_path != NULL ) { - g_free ( i3_socket_path ); - } + i3_support_free_internals (); -#endif // Cleaning up memory allocated by the Xresources file. // TODO, not happy with this. @@ -2689,7 +2569,7 @@ static void setup_switchers ( void ) /** * Keep a copy of arc, argv around, so we can use the same parsing method */ -static int stored_argc; +static int stored_argc; static char **stored_argv; /** @@ -2758,9 +2638,9 @@ static void create_pid_file ( const char *pidfile ) } if ( ftruncate ( fd, (off_t) 0 ) == 0 ) { // Write pid, not needed, but for completeness sake. - char buffer[64]; - int length = snprintf ( buffer, 64, "%i", getpid () ); - ssize_t l = 0; + char buffer[64]; + int length = snprintf ( buffer, 64, "%i", getpid () ); + ssize_t l = 0; while ( l < length ) { l += write ( fd, &buffer[l], length - l ); } @@ -2816,7 +2696,7 @@ int main ( int argc, char *argv[] ) // determine numlock mask so we can bind on keys with and without it XModifierKeymap *modmap = XGetModifierMapping ( display ); - KeyCode kc = XKeysymToKeycode ( display, XK_Num_Lock ); + KeyCode kc = XKeysymToKeycode ( display, XK_Num_Lock ); for ( int i = 0; i < 8; i++ ) { for ( int j = 0; j < ( int ) modmap->max_keypermod; j++ ) { if ( modmap->modifiermap[i * modmap->max_keypermod + j] == kc ) { @@ -2834,10 +2714,8 @@ int main ( int argc, char *argv[] ) netatoms[i] = XInternAtom ( display, netatom_names[i], False ); } -#ifdef HAVE_I3_IPC_H // Check for i3 - display_get_i3_path ( display ); -#endif + config_i3_mode = i3_support_initialize ( display ); char *msg = NULL; if ( find_arg_str ( argc, argv, "-e", &( msg ) ) ) {