28 #define G_LOG_DOMAIN "Dialogs.Window" 42 #include <xcb/xcb_ewmh.h> 43 #include <xcb/xcb_icccm.h> 44 #include <xcb/xcb_atom.h> 61 #define CLIENTSTATE 10 62 #define CLIENTWINDOWTYPE 10 73 WIN_MATCH_FIELD_TITLE,
74 WIN_MATCH_FIELD_CLASS,
77 WIN_MATCH_FIELD_DESKTOP,
79 } WinModeMatchingFields;
81 static WinModeField matching_window_fields[WIN_MATCH_NUM_FIELDS] = {
82 { .field_name =
"title", .enabled = TRUE, },
83 { .field_name =
"class", .enabled = TRUE, },
84 { .field_name =
"role", .enabled = TRUE, },
85 { .field_name =
"name", .enabled = TRUE, },
86 { .field_name =
"desktop", .enabled = TRUE, }
89 static gboolean window_matching_fields_parsed = FALSE;
95 xcb_get_window_attributes_reply_t xattr;
101 xcb_atom_t state[CLIENTSTATE];
103 xcb_atom_t window_type[CLIENTWINDOWTYPE];
109 cairo_surface_t *icon;
110 gboolean icon_checked;
128 unsigned int wmdn_len;
129 unsigned int clf_len;
130 unsigned int name_len;
131 unsigned int title_len;
132 unsigned int role_len;
133 GRegex *window_regex;
134 } ModeModePrivateData;
136 winlist *cache_client = NULL;
143 static winlist* winlist_new ()
145 winlist *l = g_malloc (
sizeof ( winlist ) );
147 l->array = g_malloc_n ( WINLIST + 1,
sizeof ( xcb_window_t ) );
148 l->data = g_malloc_n ( WINLIST + 1,
sizeof ( client* ) );
161 static int winlist_append ( winlist *l, xcb_window_t w, client *d )
163 if ( l->len > 0 && !( l->len % WINLIST ) ) {
164 l->array = g_realloc ( l->array, sizeof ( xcb_window_t ) * ( l->len + WINLIST + 1 ) );
165 l->data = g_realloc ( l->data, sizeof ( client* ) * ( l->len + WINLIST + 1 ) );
169 if ( l->data == NULL || l->array == NULL ) {
174 l->array[l->len++] = w;
178 static void winlist_empty ( winlist *l )
180 while ( l->len > 0 ) {
181 client *c = l->data[--l->len];
184 cairo_surface_destroy ( c->icon );
190 g_free ( c->wmdesktopstr );
201 static void winlist_free ( winlist *l )
219 static int winlist_find ( winlist *l, xcb_window_t w )
225 for ( i = ( l->len - 1 ); i >= 0; i-- ) {
226 if ( l->array[i] == w ) {
236 static void x11_cache_create (
void )
238 if ( cache_client == NULL ) {
239 cache_client = winlist_new ();
246 static void x11_cache_free (
void )
248 winlist_free ( cache_client );
261 static xcb_get_window_attributes_reply_t * window_get_attributes ( xcb_window_t w )
263 xcb_get_window_attributes_cookie_t c = xcb_get_window_attributes (
xcb->
connection, w );
264 xcb_get_window_attributes_reply_t *r = xcb_get_window_attributes_reply (
xcb->
connection, c, NULL );
271 static int client_has_state ( client *c, xcb_atom_t state )
273 for (
int i = 0; i < c->states; i++ ) {
274 if ( c->state[i] == state ) {
281 static int client_has_window_type ( client *c, xcb_atom_t type )
283 for (
int i = 0; i < c->window_types; i++ ) {
284 if ( c->window_type[i] == type ) {
292 static client* window_client ( ModeModePrivateData *pd, xcb_window_t win )
294 if ( win == XCB_WINDOW_NONE ) {
298 int idx = winlist_find ( cache_client, win );
301 return cache_client->data[idx];
305 xcb_get_window_attributes_reply_t *attr = window_get_attributes ( win );
310 client *c = g_malloc0 (
sizeof ( client ) );
314 memmove ( &c->xattr, attr, sizeof ( xcb_get_window_attributes_reply_t ) );
316 xcb_get_property_cookie_t cky = xcb_ewmh_get_wm_state ( &
xcb->
ewmh, win );
317 xcb_ewmh_get_atoms_reply_t states;
318 if ( xcb_ewmh_get_wm_state_reply ( &
xcb->
ewmh, cky, &states, NULL ) ) {
319 c->states = MIN ( CLIENTSTATE, states.atoms_len );
320 memcpy ( c->state, states.atoms, MIN ( CLIENTSTATE, states.atoms_len ) * sizeof ( xcb_atom_t ) );
321 xcb_ewmh_get_atoms_reply_wipe ( &states );
323 cky = xcb_ewmh_get_wm_window_type ( &
xcb->
ewmh, win );
324 if ( xcb_ewmh_get_wm_window_type_reply ( &
xcb->
ewmh, cky, &states, NULL ) ) {
325 c->window_types = MIN ( CLIENTWINDOWTYPE, states.atoms_len );
326 memcpy ( c->window_type, states.atoms, MIN ( CLIENTWINDOWTYPE, states.atoms_len ) * sizeof ( xcb_atom_t ) );
327 xcb_ewmh_get_atoms_reply_wipe ( &states );
331 if ( c->title == NULL ) {
334 pd->title_len = MAX ( c->title ? g_utf8_strlen ( c->title, -1 ) : 0, pd->title_len );
337 pd->role_len = MAX ( c->role ? g_utf8_strlen ( c->role, -1 ) : 0, pd->role_len );
340 xcb_icccm_get_wm_class_reply_t wcr;
341 if ( xcb_icccm_get_wm_class_reply (
xcb->
connection, cky, &wcr, NULL ) ) {
344 pd->name_len = MAX ( c->name ? g_utf8_strlen ( c->name, -1 ) : 0, pd->name_len );
345 xcb_icccm_get_wm_class_reply_wipe ( &wcr );
348 xcb_get_property_cookie_t cc = xcb_icccm_get_wm_hints (
xcb->
connection, c->window );
349 xcb_icccm_wm_hints_t r;
350 if ( xcb_icccm_get_wm_hints_reply (
xcb->
connection, cc, &r, NULL ) ) {
351 c->hint_flags = r.flags;
354 winlist_append ( cache_client, c->window, c );
362 const winlist *ids = ( winlist * ) rmpd->ids;
364 int idx = winlist_find ( cache_client, ids->array[index] );
365 g_assert ( idx >= 0 );
366 client *c = cache_client->data[idx];
369 for (
int j = 0; match && tokens != NULL && tokens[j] != NULL; j++ ) {
376 if ( c->title != NULL && c->title[0] !=
'\0' && matching_window_fields[WIN_MATCH_FIELD_TITLE].enabled ) {
380 if ( test == tokens[j]->invert && c->class != NULL && c->class[0] !=
'\0' && matching_window_fields[WIN_MATCH_FIELD_CLASS].enabled ) {
384 if ( test == tokens[j]->invert && c->role != NULL && c->role[0] !=
'\0' && matching_window_fields[WIN_MATCH_FIELD_ROLE].enabled ) {
388 if ( test == tokens[j]->invert && c->name != NULL && c->name[0] !=
'\0' && matching_window_fields[WIN_MATCH_FIELD_NAME].enabled ) {
391 if ( test == tokens[j]->invert && c->wmdesktopstr != NULL && c->wmdesktopstr[0] !=
'\0' && matching_window_fields[WIN_MATCH_FIELD_DESKTOP].enabled ) {
404 static void window_mode_parse_fields ()
406 window_matching_fields_parsed = TRUE;
410 const char *
const sep =
",#";
412 for (
unsigned int i = 0; i < WIN_MATCH_NUM_FIELDS; i++ ) {
413 matching_window_fields[i].enabled = FALSE;
415 for (
char *token = strtok_r ( switcher_str, sep, &savept ); token != NULL;
416 token = strtok_r ( NULL, sep, &savept ) ) {
417 if ( strcmp ( token,
"all" ) == 0 ) {
418 for (
unsigned int i = 0; i < WIN_MATCH_NUM_FIELDS; i++ ) {
419 matching_window_fields[i].enabled = TRUE;
424 gboolean matched = FALSE;
425 for (
unsigned int i = 0; i < WIN_MATCH_NUM_FIELDS; i++ ) {
426 const char * field_name = matching_window_fields[i].field_name;
427 if ( strcmp ( token, field_name ) == 0 ) {
428 matching_window_fields[i].enabled = TRUE;
433 g_warning (
"Invalid window field name :%s", token );
438 g_free ( switcher_str );
441 static unsigned int window_mode_get_num_entries (
const Mode *sw )
445 return pd->ids ? pd->ids->len : 0;
451 static const char * _window_name_list_entry (
const char *str, uint32_t length,
int entry )
455 while ( index < entry && offset < length ) {
456 if ( str[offset] == 0 ) {
463 static void _window_mode_load_data (
Mode *sw,
unsigned int cd )
468 xcb_window_t wins[100];
469 xcb_window_t curr_win_id;
475 if ( !xcb_ewmh_get_active_window_reply ( &
xcb->
ewmh, c, &curr_win_id, NULL ) ) {
480 unsigned int current_desktop = 0;
482 if ( !xcb_ewmh_get_current_desktop_reply ( &
xcb->
ewmh, c, ¤t_desktop, NULL ) ) {
486 c = xcb_ewmh_get_client_list_stacking ( &
xcb->
ewmh, 0 );
487 xcb_ewmh_get_windows_reply_t clients;
488 if ( xcb_ewmh_get_client_list_stacking_reply ( &
xcb->
ewmh, c, &clients, NULL ) ) {
489 nwins = MIN ( 100, clients.windows_len );
490 memcpy ( wins, clients.windows, nwins * sizeof ( xcb_window_t ) );
491 xcb_ewmh_get_windows_reply_wipe ( &clients );
495 if ( xcb_ewmh_get_client_list_reply ( &
xcb->
ewmh, c, &clients, NULL ) ) {
496 nwins = MIN ( 100, clients.windows_len );
497 memcpy ( wins, clients.windows, nwins * sizeof ( xcb_window_t ) );
498 xcb_ewmh_get_windows_reply_wipe ( &clients );
505 pd->ids = winlist_new ();
508 xcb_ewmh_get_utf8_strings_reply_t names;
509 int has_names = FALSE;
510 if ( xcb_ewmh_get_desktop_names_reply ( &
xcb->
ewmh, c, &names, NULL ) ) {
514 for ( i = nwins - 1; i > -1; i-- ) {
515 client *c = window_client ( pd, wins[i] );
517 && !c->xattr.override_redirect
518 && !client_has_window_type ( c,
xcb->
ewmh._NET_WM_WINDOW_TYPE_DOCK )
519 && !client_has_window_type ( c,
xcb->
ewmh._NET_WM_WINDOW_TYPE_DESKTOP )
520 && !client_has_state ( c,
xcb->
ewmh._NET_WM_STATE_SKIP_PAGER )
521 && !client_has_state ( c,
xcb->
ewmh._NET_WM_STATE_SKIP_TASKBAR ) ) {
522 pd->clf_len = MAX ( pd->clf_len, ( c->class != NULL ) ? ( g_utf8_strlen ( c->class, -1 ) ) : 0 );
524 if ( client_has_state ( c,
xcb->
ewmh._NET_WM_STATE_DEMANDS_ATTENTION ) ) {
527 if ( ( c->hint_flags & XCB_ICCCM_WM_HINT_X_URGENCY ) != 0 ) {
531 if ( c->window == curr_win_id ) {
535 xcb_get_property_cookie_t cookie;
536 xcb_get_property_reply_t *r;
538 c->wmdesktop = 0xFFFFFFFF;
540 xcb_get_property (
xcb->
connection, 0, c->window,
xcb->
ewmh._NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 0,
542 r = xcb_get_property_reply (
xcb->
connection, cookie, NULL );
544 if ( r->type == XCB_ATOM_CARDINAL ) {
545 c->wmdesktop = *( (uint32_t *) xcb_get_property_value ( r ) );
549 if ( c->wmdesktop != 0xFFFFFFFF ) {
553 if ( pango_parse_markup ( _window_name_list_entry ( names.strings, names.strings_len,
554 c->wmdesktop ), -1, 0, NULL, &output, NULL, NULL ) ) {
555 c->wmdesktopstr = output;
558 c->wmdesktopstr = g_strdup (
"Invalid name" );
562 c->wmdesktopstr = g_strdup ( _window_name_list_entry ( names.strings, names.strings_len, c->wmdesktop ) );
566 c->wmdesktopstr = g_strdup_printf (
"%u", (uint32_t) c->wmdesktop );
570 c->wmdesktopstr = g_strdup (
"" );
572 pd->wmdn_len = MAX ( pd->wmdn_len, g_utf8_strlen ( c->wmdesktopstr, -1 ) );
573 if ( cd && c->wmdesktop != current_desktop ) {
576 winlist_append ( pd->ids, c->window, NULL );
581 xcb_ewmh_get_utf8_strings_reply_wipe ( &names );
585 static int window_mode_init (
Mode *sw )
588 ModeModePrivateData *pd = g_malloc0 (
sizeof ( *pd ) );
589 pd->window_regex = g_regex_new (
"{[-\\w]+(:-?[0-9]+)?}", 0, 0, NULL );
591 _window_mode_load_data ( sw, FALSE );
592 if ( !window_matching_fields_parsed ) {
593 window_mode_parse_fields ();
598 static int window_mode_init_cd (
Mode *sw )
601 ModeModePrivateData *pd = g_malloc0 (
sizeof ( *pd ) );
602 pd->window_regex = g_regex_new (
"{[-\\w]+(:-?[0-9]+)?}", 0, 0, NULL );
604 _window_mode_load_data ( sw, TRUE );
605 if ( !window_matching_fields_parsed ) {
606 window_mode_parse_fields ();
612 static inline int act_on_window ( xcb_window_t window )
617 char window_regex[100];
619 g_snprintf ( window_regex,
sizeof window_regex,
"%d", window );
623 GError *error = NULL;
624 g_spawn_async ( NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error );
625 if ( error != NULL ) {
626 char *msg = g_strdup_printf (
"Failed to execute action for window: '%s'\nError: '%s'", window_regex, error->message );
630 g_error_free ( error );
639 static ModeMode window_mode_result (
Mode *sw,
int mretv, G_GNUC_UNUSED
char **input,
640 unsigned int selected_line )
653 else if ( ( mretv & (
MENU_OK ) ) ) {
655 act_on_window ( rmpd->ids->array[selected_line] );
661 uint32_t wmdesktop = 0;
662 xcb_get_property_cookie_t cookie;
663 xcb_get_property_reply_t *r;
665 unsigned int current_desktop = 0;
667 if ( !xcb_ewmh_get_current_desktop_reply ( &
xcb->
ewmh, c, ¤t_desktop, NULL ) ) {
671 cookie = xcb_get_property (
xcb->
connection, 0, rmpd->ids->array[selected_line],
672 xcb->
ewmh._NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 0, 1 );
673 r = xcb_get_property_reply (
xcb->
connection, cookie, NULL );
674 if ( r && r->type == XCB_ATOM_CARDINAL ) {
675 wmdesktop = *( (uint32_t *) xcb_get_property_value ( r ) );
677 if ( r && r->type != XCB_ATOM_CARDINAL ) {
679 wmdesktop = current_desktop;
684 if ( wmdesktop != current_desktop ) {
685 xcb_ewmh_request_change_current_desktop ( &
xcb->
ewmh,
690 xcb_ewmh_request_change_active_window ( &
xcb->
ewmh,
xcb->
screen_nbr, rmpd->ids->array[selected_line],
691 XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER,
697 xcb_ewmh_request_close_window ( &(
xcb->
ewmh ),
xcb->
screen_nbr, rmpd->ids->array[selected_line], XCB_CURRENT_TIME, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER );
703 static void window_mode_destroy (
Mode *sw )
706 if ( rmpd != NULL ) {
707 winlist_free ( rmpd->ids );
709 g_free ( rmpd->cache );
710 g_regex_unref ( rmpd->window_regex );
717 const ModeModePrivateData *pd;
721 static void helper_eval_add_str ( GString *str,
const char *input,
int l,
int max_len )
724 const char *input_nn = input ? input :
"";
726 int nc = g_utf8_strlen ( input_nn, -1 );
729 spaces = MAX ( 0, max_len - nc );
730 g_string_append ( str, input_nn );
734 int bl = g_utf8_offset_to_pointer ( input_nn, l ) - input_nn;
735 g_string_append_len ( str, input_nn, bl );
739 g_string_append ( str, input_nn );
743 g_string_append_c ( str,
' ' );
746 static gboolean
helper_eval_cb (
const GMatchInfo *info, GString *str, gpointer data )
748 struct arg *d = (
struct arg *) data;
751 match = g_match_info_fetch ( info, 0 );
752 if ( match != NULL ) {
754 if ( match[2] ==
':' ) {
755 l = (int) g_ascii_strtoll ( &match[3], NULL, 10 );
763 if ( match[1] ==
'w' ) {
764 helper_eval_add_str ( str, d->c->wmdesktopstr, l, d->pd->wmdn_len );
766 else if ( match[1] ==
'c' ) {
767 helper_eval_add_str ( str, d->c->class, l, d->pd->clf_len );
769 else if ( match[1] ==
't' ) {
770 helper_eval_add_str ( str, d->c->title, l, d->pd->title_len );
772 else if ( match[1] ==
'n' ) {
773 helper_eval_add_str ( str, d->c->name, l, d->pd->name_len );
775 else if ( match[1] ==
'r' ) {
776 helper_eval_add_str ( str, d->c->role, l, d->pd->role_len );
782 static char * _generate_display_string (
const ModeModePrivateData *pd, client *c )
784 struct arg d = { pd, c };
787 return g_strchomp ( res );
790 static char *
_get_display_value (
const Mode *sw,
unsigned int selected_line,
int *state, G_GNUC_UNUSED GList **list,
int get_entry )
793 client *c = window_client ( rmpd, rmpd->ids->array[selected_line] );
795 return get_entry ? g_strdup (
"Window has fanished" ) : NULL;
803 return get_entry ? _generate_display_string ( rmpd, c ) : NULL;
809 static cairo_user_data_key_t data_key;
816 static cairo_surface_t * draw_surface_from_data (
int width,
int height, uint32_t *data )
818 unsigned long int len = width * height;
820 uint32_t *buffer = g_new0 ( uint32_t, len );
821 cairo_surface_t *surface;
824 for ( i = 0; i < len; i++ ) {
825 uint8_t a = ( data[i] >> 24 ) & 0xff;
826 double alpha = a / 255.0;
827 uint8_t r = ( ( data[i] >> 16 ) & 0xff ) * alpha;
828 uint8_t g = ( ( data[i] >> 8 ) & 0xff ) * alpha;
829 uint8_t b = ( ( data[i] >> 0 ) & 0xff ) * alpha;
830 buffer[i] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
833 surface = cairo_image_surface_create_for_data ( (
unsigned char *) buffer,
839 cairo_surface_set_user_data ( surface, &data_key, buffer, g_free );
843 static cairo_surface_t * ewmh_window_icon_from_reply ( xcb_get_property_reply_t *r, uint32_t preferred_size )
845 uint32_t *data, *end, *found_data = 0;
846 uint32_t found_size = 0;
848 if ( !r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || r->length < 2 ) {
852 data = (uint32_t *) xcb_get_property_value ( r );
857 end = data + r->length;
863 while ( data + 1 < end ) {
865 uint64_t data_size = (uint64_t) data[0] * data[1];
866 if ( data_size > (uint64_t) ( end - data - 2 ) ) {
871 uint32_t size = MAX ( data[0], data[1] );
874 gboolean found_icon_too_small = found_size < preferred_size;
875 gboolean found_icon_too_large = found_size > preferred_size;
876 gboolean icon_empty = data[0] == 0 || data[1] == 0;
877 gboolean better_because_bigger = found_icon_too_small && size > found_size;
878 gboolean better_because_smaller = found_icon_too_large &&
879 size >= preferred_size && size < found_size;
880 if ( !icon_empty && ( better_because_bigger || better_because_smaller || found_size == 0 ) ) {
885 data += data_size + 2;
892 return draw_surface_from_data ( found_data[0], found_data[1], found_data + 2 );
895 static cairo_surface_t * get_net_wm_icon ( xcb_window_t xid, uint32_t preferred_size )
897 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked (
899 xcb->
ewmh._NET_WM_ICON, XCB_ATOM_CARDINAL, 0, UINT32_MAX );
900 xcb_get_property_reply_t *r = xcb_get_property_reply (
xcb->
connection, cookie, NULL );
901 cairo_surface_t *surface = ewmh_window_icon_from_reply ( r, preferred_size );
905 static cairo_surface_t *_get_icon (
const Mode *sw,
unsigned int selected_line,
int size )
908 client *c = window_client ( rmpd, rmpd->ids->array[selected_line] );
909 if ( c->icon_checked == FALSE ) {
910 c->icon = get_net_wm_icon ( rmpd->ids->array[selected_line], size );
911 c->icon_checked = TRUE;
920 .cfg_name_key =
"display-window",
921 ._init = window_mode_init,
922 ._get_num_entries = window_mode_get_num_entries,
923 ._result = window_mode_result,
924 ._destroy = window_mode_destroy,
925 ._token_match = window_match,
927 ._get_icon = _get_icon,
928 ._get_completion = NULL,
929 ._preprocess_input = NULL,
930 .private_data = NULL,
933 Mode window_mode_cd =
936 .cfg_name_key =
"display-windowcd",
937 ._init = window_mode_init_cd,
938 ._get_num_entries = window_mode_get_num_entries,
939 ._result = window_mode_result,
940 ._destroy = window_mode_destroy,
941 ._token_match = window_match,
943 ._get_icon = _get_icon,
944 ._get_completion = NULL,
945 ._preprocess_input = NULL,
946 .private_data = NULL,
950 #endif // WINDOW_MODE
xcb_ewmh_connection_t ewmh
void * mode_get_private_data(const Mode *mode)
void rofi_view_hide(void)
xcb_window_t rofi_view_get_window(void)
char * window_match_fields
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
char * rofi_latin_to_utf8_strdup(const char *input, gssize length)
int rofi_view_error_dialog(const char *msg, int markup)
WindowManagerQuirk current_window_manager
xcb_connection_t * connection
static gboolean helper_eval_cb(const GMatchInfo *info, GString *res, gpointer data)
int helper_parse_setup(char *string, char ***output, int *length,...)
char * window_get_text_prop(xcb_window_t w, xcb_atom_t atom)
void mode_set_private_data(Mode *mode, void *pd)
xcb_atom_t netatoms[NUM_NETATOMS]
static char * _get_display_value(const Mode *sw, unsigned int selected_line, int *state, G_GNUC_UNUSED GList **list, int get_entry)