rofi  1.6.1
dmenu.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2020 Qball Cow <qball@gmpclient.org>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
29 #define G_LOG_DOMAIN "Dialogs.DMenu"
30 
31 #include <config.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <strings.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <stdint.h>
39 #include <errno.h>
40 #include <gio/gio.h>
41 #include <gio/gunixinputstream.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include "rofi.h"
46 #include "settings.h"
47 #include "widgets/textbox.h"
48 #include "dialogs/dmenu.h"
49 #include "helper.h"
50 #include "xrmoptions.h"
51 #include "view.h"
52 #include "rofi-icon-fetcher.h"
53 
55 
56 static int dmenu_mode_init ( Mode *sw );
57 static int dmenu_token_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index );
58 static cairo_surface_t *dmenu_get_icon ( const Mode *sw, unsigned int selected_line, int height );
59 static char *dmenu_get_message ( const Mode *sw );
60 
61 static inline unsigned int bitget ( uint32_t *array, unsigned int index )
62 {
63  uint32_t bit = index % 32;
64  uint32_t val = array[index / 32];
65  return ( val >> bit ) & 1;
66 }
67 
68 static inline void bittoggle ( uint32_t *array, unsigned int index )
69 {
70  uint32_t bit = index % 32;
71  uint32_t *v = &array[index / 32];
72  *v ^= 1 << bit;
73 }
74 
75 typedef struct
76 {
78  // Separator.
79  char separator;
80 
81  unsigned int selected_line;
82  char *message;
83  char *format;
85  unsigned int num_urgent_list;
87  unsigned int num_active_list;
88  uint32_t *selected_list;
89  unsigned int num_selected_list;
90  unsigned int do_markup;
91  // List with entries.
93  unsigned int cmd_list_real_length;
94  unsigned int cmd_list_length;
95  unsigned int only_selected;
96  unsigned int selected_count;
97 
98  gchar **columns;
100  gboolean multi_select;
101 
102  GCancellable *cancel;
104  GInputStream *input_stream;
105  GDataInputStream *data_input_stream;
107 
108 static void async_close_callback ( GObject *source_object, GAsyncResult *res, G_GNUC_UNUSED gpointer user_data )
109 {
110  g_input_stream_close_finish ( G_INPUT_STREAM ( source_object ), res, NULL );
111  g_debug ( "Closing data stream." );
112 }
113 
114 static void read_add ( DmenuModePrivateData * pd, char *data, gsize len )
115 {
116  gsize data_len = len;
117  if ( ( pd->cmd_list_length + 2 ) > pd->cmd_list_real_length ) {
118  pd->cmd_list_real_length = MAX ( pd->cmd_list_real_length * 2, 512 );
119  pd->cmd_list = g_realloc ( pd->cmd_list, ( pd->cmd_list_real_length ) * sizeof ( DmenuScriptEntry ) );
120  }
121  // Init.
123  pd->cmd_list[pd->cmd_list_length].icon_name = NULL;
124  pd->cmd_list[pd->cmd_list_length].meta = NULL;
125  pd->cmd_list[pd->cmd_list_length].info = NULL;
126  char *end = data;
127  while ( end < data + len && *end != '\0' ) {
128  end++;
129  }
130  if ( end != data + len ) {
131  data_len = end - data;
132  dmenuscript_parse_entry_extras ( NULL, &( pd->cmd_list[pd->cmd_list_length] ), end + 1, len - data_len );
133  }
134  char *utfstr = rofi_force_utf8 ( data, data_len );
135  pd->cmd_list[pd->cmd_list_length].entry = utfstr;
136  pd->cmd_list[pd->cmd_list_length + 1].entry = NULL;
137 
138  pd->cmd_list_length++;
139 }
140 static void async_read_callback ( GObject *source_object, GAsyncResult *res, gpointer user_data )
141 {
142  GDataInputStream *stream = (GDataInputStream *) source_object;
143  DmenuModePrivateData *pd = (DmenuModePrivateData *) user_data;
144  gsize len;
145  char *data = g_data_input_stream_read_upto_finish ( stream, res, &len, NULL );
146  if ( data != NULL ) {
147  // Absorb separator, already in buffer so should not block.
148  g_data_input_stream_read_byte ( stream, NULL, NULL );
149  read_add ( pd, data, len );
150  g_free ( data );
151  rofi_view_reload ();
152 
153  g_data_input_stream_read_upto_async ( pd->data_input_stream, &( pd->separator ), 1, G_PRIORITY_LOW, pd->cancel,
154  async_read_callback, pd );
155  return;
156  }
157  else {
158  GError *error = NULL;
159  // Absorb separator, already in buffer so should not block.
160  // If error == NULL end of stream..
161  g_data_input_stream_read_byte ( stream, NULL, &error );
162  if ( error == NULL ) {
163  // Add empty line.
164  read_add ( pd, "", 0 );
165  rofi_view_reload ();
166 
167  g_data_input_stream_read_upto_async ( pd->data_input_stream, &( pd->separator ), 1, G_PRIORITY_LOW, pd->cancel,
168  async_read_callback, pd );
169  return;
170  }
171  else {
172  g_error_free ( error );
173  }
174  }
175  if ( !g_cancellable_is_cancelled ( pd->cancel ) ) {
176  // Hack, don't use get active.
177  g_debug ( "Clearing overlay" );
179  g_input_stream_close_async ( G_INPUT_STREAM ( stream ), G_PRIORITY_LOW, pd->cancel, async_close_callback, pd );
180  }
181 }
182 
183 static void async_read_cancel ( G_GNUC_UNUSED GCancellable *cancel, G_GNUC_UNUSED gpointer data )
184 {
185  g_debug ( "Cancelled the async read." );
186 }
187 
188 static int get_dmenu_async ( DmenuModePrivateData *pd, int sync_pre_read )
189 {
190  while ( sync_pre_read-- ) {
191  gsize len = 0;
192  char *data = g_data_input_stream_read_upto ( pd->data_input_stream, &( pd->separator ), 1, &len, NULL, NULL );
193  if ( data == NULL ) {
194  g_input_stream_close_async ( G_INPUT_STREAM ( pd->input_stream ), G_PRIORITY_LOW, pd->cancel, async_close_callback, pd );
195  return FALSE;
196  }
197  g_data_input_stream_read_byte ( pd->data_input_stream, NULL, NULL );
198  read_add ( pd, data, len );
199  g_free ( data );
200  }
201  g_data_input_stream_read_upto_async ( pd->data_input_stream, &( pd->separator ), 1, G_PRIORITY_LOW, pd->cancel,
202  async_read_callback, pd );
203  return TRUE;
204 }
206 {
207  while ( TRUE ) {
208  gsize len = 0;
209  char *data = g_data_input_stream_read_upto ( pd->data_input_stream, &( pd->separator ), 1, &len, NULL, NULL );
210  if ( data == NULL ) {
211  break;
212  }
213  g_data_input_stream_read_byte ( pd->data_input_stream, NULL, NULL );
214  read_add ( pd, data, len );
215  g_free ( data );
216  }
217  g_input_stream_close_async ( G_INPUT_STREAM ( pd->input_stream ), G_PRIORITY_LOW, pd->cancel, async_close_callback, pd );
218 }
219 
220 static unsigned int dmenu_mode_get_num_entries ( const Mode *sw )
221 {
222  const DmenuModePrivateData *rmpd = (const DmenuModePrivateData *) mode_get_private_data ( sw );
223  return rmpd->cmd_list_length;
224 }
225 
226 static gchar * dmenu_format_output_string ( const DmenuModePrivateData *pd, const char *input )
227 {
228  if ( pd->columns == NULL ) {
229  return g_strdup ( input );
230  }
231  char *retv = NULL;
232  char ** splitted = g_regex_split_simple ( pd->column_separator, input, G_REGEX_CASELESS, 00 );
233  uint32_t ns = 0;
234  for (; splitted && splitted[ns]; ns++ ) {
235  ;
236  }
237  for ( uint32_t i = 0; pd->columns && pd->columns[i]; i++ ) {
238  unsigned int index = (unsigned int ) g_ascii_strtoull ( pd->columns[i], NULL, 10 );
239  if ( index < ns && index > 0 ) {
240  if ( retv == NULL ) {
241  retv = g_strdup ( splitted[index - 1] );
242  }
243  else {
244  gchar *t = g_strjoin ( "\t", retv, splitted[index - 1], NULL );
245  g_free ( retv );
246  retv = t;
247  }
248  }
249  }
250  g_strfreev ( splitted );
251  return retv ? retv : g_strdup ( "" );
252 }
253 
254 static inline unsigned int get_index ( unsigned int length, int index )
255 {
256  if ( index >= 0 ) {
257  return index;
258  }
259  if ( ( (unsigned int) -index ) <= length ) {
260  return length + index;
261  }
262  // Out of range.
263  return UINT_MAX;
264 }
265 
266 static char *get_display_data ( const Mode *data, unsigned int index, int *state, G_GNUC_UNUSED GList **list, int get_entry )
267 {
268  Mode *sw = (Mode *) data;
271  for ( unsigned int i = 0; i < pd->num_active_list; i++ ) {
272  unsigned int start = get_index ( pd->cmd_list_length, pd->active_list[i].start );
273  unsigned int stop = get_index ( pd->cmd_list_length, pd->active_list[i].stop );
274  if ( index >= start && index <= stop ) {
275  *state |= ACTIVE;
276  }
277  }
278  for ( unsigned int i = 0; i < pd->num_urgent_list; i++ ) {
279  unsigned int start = get_index ( pd->cmd_list_length, pd->urgent_list[i].start );
280  unsigned int stop = get_index ( pd->cmd_list_length, pd->urgent_list[i].stop );
281  if ( index >= start && index <= stop ) {
282  *state |= URGENT;
283  }
284  }
285  if ( pd->selected_list && bitget ( pd->selected_list, index ) == TRUE ) {
286  *state |= SELECTED;
287  }
288  if ( pd->do_markup ) {
289  *state |= MARKUP;
290  }
291  return get_entry ? dmenu_format_output_string ( pd, retv[index].entry ) : NULL;
292 }
293 
294 static void dmenu_mode_free ( Mode *sw )
295 {
296  if ( mode_get_private_data ( sw ) == NULL ) {
297  return;
298  }
300  if ( pd != NULL ) {
301  if ( pd->cancel ) {
302  // If open, cancel reads.
303  if ( pd->input_stream && !g_input_stream_is_closed ( pd->input_stream ) ) {
304  g_cancellable_cancel ( pd->cancel );
305  }
306  // This blocks until cancel is done.
307  g_cancellable_disconnect ( pd->cancel, pd->cancel_source );
308  if ( pd->input_stream ) {
309  // Should close the stream if not yet done.
310  g_object_unref ( pd->data_input_stream );
311  g_object_unref ( pd->input_stream );
312  }
313  g_object_unref ( pd->cancel );
314  }
315 
316  for ( size_t i = 0; i < pd->cmd_list_length; i++ ) {
317  if ( pd->cmd_list[i].entry ) {
318  g_free ( pd->cmd_list[i].entry );
319  g_free ( pd->cmd_list[i].icon_name );
320  g_free ( pd->cmd_list[i].meta );
321  g_free ( pd->cmd_list[i].info );
322  }
323  }
324  g_free ( pd->cmd_list );
325  g_free ( pd->urgent_list );
326  g_free ( pd->active_list );
327  g_free ( pd->selected_list );
328 
329  g_free ( pd );
330  mode_set_private_data ( sw, NULL );
331  }
332 }
333 
334 #include "mode-private.h"
337 {
338  .name = "dmenu",
339  .cfg_name_key = "display-combi",
340  ._init = dmenu_mode_init,
341  ._get_num_entries = dmenu_mode_get_num_entries,
342  ._result = NULL,
343  ._destroy = dmenu_mode_free,
344  ._token_match = dmenu_token_match,
345  ._get_display_value = get_display_data,
346  ._get_icon = dmenu_get_icon,
347  ._get_completion = NULL,
348  ._preprocess_input = NULL,
349  ._get_message = dmenu_get_message,
350  .private_data = NULL,
351  .free = NULL,
352  .display_name = "dmenu"
353 };
354 
355 static int dmenu_mode_init ( Mode *sw )
356 {
357  if ( mode_get_private_data ( sw ) != NULL ) {
358  return TRUE;
359  }
360  mode_set_private_data ( sw, g_malloc0 ( sizeof ( DmenuModePrivateData ) ) );
362 
363  pd->separator = '\n';
364  pd->selected_line = UINT32_MAX;
365 
366  find_arg_str ( "-mesg", &( pd->message ) );
367 
368  // Input data separator.
369  find_arg_char ( "-sep", &( pd->separator ) );
370 
371  find_arg_uint ( "-selected-row", &( pd->selected_line ) );
372  // By default we print the unescaped line back.
373  pd->format = "s";
374 
375  // Allow user to override the output format.
376  find_arg_str ( "-format", &( pd->format ) );
377  // Urgent.
378  char *str = NULL;
379  find_arg_str ( "-u", &str );
380  if ( str != NULL ) {
381  parse_ranges ( str, &( pd->urgent_list ), &( pd->num_urgent_list ) );
382  }
383  // Active
384  str = NULL;
385  find_arg_str ( "-a", &str );
386  if ( str != NULL ) {
387  parse_ranges ( str, &( pd->active_list ), &( pd->num_active_list ) );
388  }
389 
390  // DMENU COMPATIBILITY
391  find_arg_uint ( "-l", &( config.menu_lines ) );
392 
393  str = NULL;
394  find_arg_str ( "-window-title", &str );
395  if ( str ) {
396  dmenu_mode.display_name = str;
397  }
398 
403  if ( find_arg ( "-b" ) >= 0 ) {
404  config.location = 6;
405  }
406  /* -i case insensitive */
407  config.case_sensitive = TRUE;
408  if ( find_arg ( "-i" ) >= 0 ) {
409  config.case_sensitive = FALSE;
410  }
411  int fd = STDIN_FILENO;
412  str = NULL;
413  if ( find_arg_str ( "-input", &str ) ) {
414  char *estr = rofi_expand_path ( str );
415  fd = open ( str, O_RDONLY );
416  if ( fd < 0 ) {
417  char *msg = g_markup_printf_escaped ( "Failed to open file: <b>%s</b>:\n\t<i>%s</i>", estr, g_strerror ( errno ) );
418  rofi_view_error_dialog ( msg, TRUE );
419  g_free ( msg );
420  g_free ( estr );
421  return TRUE;
422  }
423  g_free ( estr );
424  }
425  // If input is stdin, and a tty, do not read as rofi grabs input and therefor blocks.
426  if ( !( fd == STDIN_FILENO && isatty ( fd ) == 1 ) ) {
427  pd->cancel = g_cancellable_new ();
428  pd->cancel_source = g_cancellable_connect ( pd->cancel, G_CALLBACK ( async_read_cancel ), pd, NULL );
429  pd->input_stream = g_unix_input_stream_new ( fd, fd != STDIN_FILENO );
430  pd->data_input_stream = g_data_input_stream_new ( pd->input_stream );
431  }
432  gchar *columns = NULL;
433  if ( find_arg_str ( "-display-columns", &columns ) ) {
434  pd->columns = g_strsplit ( columns, ",", 0 );
435  pd->column_separator = "\t";
436  find_arg_str ( "-display-column-separator", &pd->column_separator );
437  }
438  return TRUE;
439 }
440 
441 static int dmenu_token_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
442 {
445  char *esc = NULL;
446  if ( rmpd->do_markup ) {
447  pango_parse_markup ( rmpd->cmd_list[index].entry, -1, 0, NULL, &esc, NULL, NULL );
448  }
449  else {
450  esc = rmpd->cmd_list[index].entry;
451  }
452  if ( esc ) {
453  // int retv = helper_token_match ( tokens, esc );
454  int match = 1;
455  if ( tokens ) {
456  for ( int j = 0; match && tokens != NULL && tokens[j] != NULL; j++ ) {
457  rofi_int_matcher *ftokens[2] = { tokens[j], NULL };
458  int test = 0;
459  test = helper_token_match ( ftokens, esc );
460  if ( test == tokens[j]->invert && rmpd->cmd_list[index].meta ) {
461  test = helper_token_match ( ftokens, rmpd->cmd_list[index].meta );
462  }
463 
464  if ( test == 0 ) {
465  match = 0;
466  }
467  }
468  }
469  if ( rmpd->do_markup ) {
470  g_free ( esc );
471  }
472  return match;
473  }
474  return FALSE;
475 }
476 static char *dmenu_get_message ( const Mode *sw )
477 {
479  if ( pd->message ) {
480  return g_strdup ( pd->message );
481  }
482  return NULL;
483 }
484 static cairo_surface_t *dmenu_get_icon ( const Mode *sw, unsigned int selected_line, int height )
485 {
487  g_return_val_if_fail ( pd->cmd_list != NULL, NULL );
488  DmenuScriptEntry *dr = &( pd->cmd_list[selected_line] );
489  if ( dr->icon_name == NULL ) {
490  return NULL;
491  }
492  if ( dr->icon_fetch_uid > 0 ) {
493  return rofi_icon_fetcher_get ( dr->icon_fetch_uid );
494  }
495  dr->icon_fetch_uid = rofi_icon_fetcher_query ( dr->icon_name, height );
496  return rofi_icon_fetcher_get ( dr->icon_fetch_uid );
497 }
498 
499 static void dmenu_finish ( RofiViewState *state, int retv )
500 {
501  if ( retv == FALSE ) {
502  rofi_set_return_code ( EXIT_FAILURE );
503  }
504  else if ( retv >= 10 ) {
505  rofi_set_return_code ( retv );
506  }
507  else{
508  rofi_set_return_code ( EXIT_SUCCESS );
509  }
510  rofi_view_set_active ( NULL );
511  rofi_view_free ( state );
513 }
514 
515 static void dmenu_print_results ( DmenuModePrivateData *pd, const char *input )
516 {
517  DmenuScriptEntry *cmd_list = pd->cmd_list;
518  int seen = FALSE;
519  if ( pd->selected_list != NULL ) {
520  for ( unsigned int st = 0; st < pd->cmd_list_length; st++ ) {
521  if ( bitget ( pd->selected_list, st ) ) {
522  seen = TRUE;
523  rofi_output_formatted_line ( pd->format, cmd_list[st].entry, st, input );
524  }
525  }
526  }
527  if ( !seen ) {
528  const char *cmd = input;
529  if ( pd->selected_line != UINT32_MAX ) {
530  cmd = cmd_list[pd->selected_line].entry;
531  }
532  rofi_output_formatted_line ( pd->format, cmd, pd->selected_line, input );
533  }
534 }
535 
536 static void dmenu_finalize ( RofiViewState *state )
537 {
538  int retv = FALSE;
540  unsigned int cmd_list_length = pd->cmd_list_length;
541  DmenuScriptEntry *cmd_list = pd->cmd_list;
542 
543  char *input = g_strdup ( rofi_view_get_user_input ( state ) );
545  MenuReturn mretv = rofi_view_get_return_value ( state );
546  unsigned int next_pos = rofi_view_get_next_position ( state );
547  int restart = 0;
548  // Special behavior.
549  if ( pd->only_selected ) {
553  restart = 1;
554  // Skip if no valid item is selected.
555  if ( ( mretv & MENU_CANCEL ) == MENU_CANCEL ) {
556  // In no custom mode we allow canceling.
557  restart = ( find_arg ( "-only-match" ) >= 0 );
558  }
559  else if ( pd->selected_line != UINT32_MAX ) {
560  if ( ( mretv & MENU_CUSTOM_ACTION ) && pd->multi_select ) {
561  restart = TRUE;
562  if ( pd->selected_list == NULL ) {
563  pd->selected_list = g_malloc0 ( sizeof ( uint32_t ) * ( pd->cmd_list_length / 32 + 1 ) );
564  }
565  pd->selected_count += ( bitget ( pd->selected_list, pd->selected_line ) ? ( -1 ) : ( 1 ) );
567  // Move to next line.
568  pd->selected_line = MIN ( next_pos, cmd_list_length - 1 );
569  if ( pd->selected_count > 0 ) {
570  char *str = g_strdup_printf ( "%u/%u", pd->selected_count, pd->cmd_list_length );
571  rofi_view_set_overlay ( state, str );
572  g_free ( str );
573  }
574  else {
575  rofi_view_set_overlay ( state, NULL );
576  }
577  }
578  else if ( ( mretv & ( MENU_OK | MENU_CUSTOM_COMMAND ) ) && cmd_list[pd->selected_line].entry != NULL ) {
579  if ( cmd_list[pd->selected_line].nonselectable == TRUE ) {
580  g_free ( input );
581  return;
582  }
583  dmenu_print_results ( pd, input );
584  retv = TRUE;
585  if ( ( mretv & MENU_CUSTOM_COMMAND ) ) {
586  retv = 10 + ( mretv & MENU_LOWER_MASK );
587  }
588  g_free ( input );
589  dmenu_finish ( state, retv );
590  return;
591  }
592  else {
593  pd->selected_line = next_pos - 1;
594  }
595  }
596  // Restart
597  rofi_view_restart ( state );
599  if ( !restart ) {
600  dmenu_finish ( state, retv );
601  }
602  return;
603  }
604  // We normally do not want to restart the loop.
605  restart = FALSE;
606  // Normal mode
607  if ( ( mretv & MENU_OK ) && pd->selected_line != UINT32_MAX && cmd_list[pd->selected_line].entry != NULL ) {
608  // Check if entry is non-selectable.
609  if ( cmd_list[pd->selected_line].nonselectable == TRUE ) {
610  g_free ( input );
611  return;
612  }
613  if ( ( mretv & MENU_CUSTOM_ACTION ) && pd->multi_select ) {
614  restart = TRUE;
615  if ( pd->selected_list == NULL ) {
616  pd->selected_list = g_malloc0 ( sizeof ( uint32_t ) * ( pd->cmd_list_length / 32 + 1 ) );
617  }
618  pd->selected_count += ( bitget ( pd->selected_list, pd->selected_line ) ? ( -1 ) : ( 1 ) );
620  // Move to next line.
621  pd->selected_line = MIN ( next_pos, cmd_list_length - 1 );
622  if ( pd->selected_count > 0 ) {
623  char *str = g_strdup_printf ( "%u/%u", pd->selected_count, pd->cmd_list_length );
624  rofi_view_set_overlay ( state, str );
625  g_free ( str );
626  }
627  else {
628  rofi_view_set_overlay ( state, NULL );
629  }
630  }
631  else {
632  dmenu_print_results ( pd, input );
633  }
634  retv = TRUE;
635  }
636  // Custom input
637  else if ( ( mretv & ( MENU_CUSTOM_INPUT ) ) ) {
638  dmenu_print_results ( pd, input );
639 
640  retv = TRUE;
641  }
642  // Quick switch with entry selected.
643  else if ( ( mretv & MENU_CUSTOM_COMMAND ) ) {
644  dmenu_print_results ( pd, input );
645 
646  restart = FALSE;
647  retv = 10 + ( mretv & MENU_LOWER_MASK );
648  }
649  g_free ( input );
650  if ( restart ) {
651  rofi_view_restart ( state );
653  }
654  else {
655  dmenu_finish ( state, retv );
656  }
657 }
658 
660 {
661  mode_init ( &dmenu_mode );
662  MenuFlags menu_flags = MENU_NORMAL;
664  int async = TRUE;
665 
666  // For now these only work in sync mode.
667  if ( find_arg ( "-sync" ) >= 0 || find_arg ( "-dump" ) >= 0 || find_arg ( "-select" ) >= 0
668  || find_arg ( "-no-custom" ) >= 0 || find_arg ( "-only-match" ) >= 0 || config.auto_select ||
669  find_arg ( "-selected-row" ) >= 0 ) {
670  async = FALSE;
671  }
672 
673  // Check if the subsystem is setup for reading, otherwise do not read.
674  if ( pd->cancel != NULL ) {
675  if ( async ) {
676  unsigned int pre_read = 25;
677  find_arg_uint ( "-async-pre-read", &pre_read );
678  async = get_dmenu_async ( pd, pre_read );
679  }
680  else {
681  get_dmenu_sync ( pd );
682  }
683  }
684  char *input = NULL;
685  unsigned int cmd_list_length = pd->cmd_list_length;
686  DmenuScriptEntry *cmd_list = pd->cmd_list;
687 
688  pd->only_selected = FALSE;
689  pd->multi_select = FALSE;
690  if ( find_arg ( "-multi-select" ) >= 0 ) {
691  menu_flags = MENU_INDICATOR;
692  pd->multi_select = TRUE;
693  }
694  if ( find_arg ( "-markup-rows" ) >= 0 ) {
695  pd->do_markup = TRUE;
696  }
697  if ( find_arg ( "-only-match" ) >= 0 || find_arg ( "-no-custom" ) >= 0 ) {
698  pd->only_selected = TRUE;
699  if ( cmd_list_length == 0 ) {
700  return TRUE;
701  }
702  }
703  if ( config.auto_select && cmd_list_length == 1 ) {
704  rofi_output_formatted_line ( pd->format, cmd_list[0].entry, 0, config.filter );
705  return TRUE;
706  }
707  if ( find_arg ( "-password" ) >= 0 ) {
708  menu_flags |= MENU_PASSWORD;
709  }
710  /* copy filter string */
711  input = g_strdup ( config.filter );
712 
713  char *select = NULL;
714  find_arg_str ( "-select", &select );
715  if ( select != NULL ) {
716  rofi_int_matcher **tokens = helper_tokenize ( select, config.case_sensitive );
717  unsigned int i = 0;
718  for ( i = 0; i < cmd_list_length; i++ ) {
719  if ( helper_token_match ( tokens, cmd_list[i].entry ) ) {
720  pd->selected_line = i;
721  break;
722  }
723  }
724  helper_tokenize_free ( tokens );
725  }
726  if ( find_arg ( "-dump" ) >= 0 ) {
728  unsigned int i = 0;
729  for ( i = 0; i < cmd_list_length; i++ ) {
730  if ( tokens == NULL || helper_token_match ( tokens, cmd_list[i].entry ) ) {
731  rofi_output_formatted_line ( pd->format, cmd_list[i].entry, i, config.filter );
732  }
733  }
734  helper_tokenize_free ( tokens );
736  g_free ( input );
737  return TRUE;
738  }
739  find_arg_str ( "-p", &( dmenu_mode.display_name ) );
740  RofiViewState *state = rofi_view_create ( &dmenu_mode, input, menu_flags, dmenu_finalize );
741 
742  if ( find_arg ( "-keep-right" ) >= 0 ) {
743  rofi_view_ellipsize_start ( state );
744  }
745  // @TODO we should do this better.
746  if ( async && ( pd->cancel != NULL ) ) {
747  rofi_view_set_overlay ( state, "Loading.. " );
748  }
750  rofi_view_set_active ( state );
751 
752  return FALSE;
753 }
754 
755 void print_dmenu_options ( void )
756 {
757  int is_term = isatty ( fileno ( stdout ) );
758  print_help_msg ( "-mesg", "[string]", "Print a small user message under the prompt (uses pango markup)", NULL, is_term );
759  print_help_msg ( "-p", "[string]", "Prompt to display left of entry field", NULL, is_term );
760  print_help_msg ( "-selected-row", "[integer]", "Select row", NULL, is_term );
761  print_help_msg ( "-format", "[string]", "Output format string", "s", is_term );
762  print_help_msg ( "-u", "[list]", "List of row indexes to mark urgent", NULL, is_term );
763  print_help_msg ( "-a", "[list]", "List of row indexes to mark active", NULL, is_term );
764  print_help_msg ( "-l", "[integer] ", "Number of rows to display", NULL, is_term );
765  print_help_msg ( "-window-title", "[string] ", "Set the dmenu window title", NULL, is_term );
766  print_help_msg ( "-i", "", "Set filter to be case insensitive", NULL, is_term );
767  print_help_msg ( "-only-match", "", "Force selection to be given entry, disallow no match", NULL, is_term );
768  print_help_msg ( "-no-custom", "", "Don't accept custom entry, allow no match", NULL, is_term );
769  print_help_msg ( "-select", "[string]", "Select the first row that matches", NULL, is_term );
770  print_help_msg ( "-password", "", "Do not show what the user inputs. Show '*' instead.", NULL, is_term );
771  print_help_msg ( "-markup-rows", "", "Allow and render pango markup as input data.", NULL, is_term );
772  print_help_msg ( "-sep", "[char]", "Element separator.", "'\\n'", is_term );
773  print_help_msg ( "-input", "[filename]", "Read input from file instead from standard input.", NULL, is_term );
774  print_help_msg ( "-sync", "", "Force dmenu to first read all input data, then show dialog.", NULL, is_term );
775  print_help_msg ( "-async-pre-read", "[number]", "Read several entries blocking before switching to async mode", "25", is_term );
776  print_help_msg ( "-w", "windowid", "Position over window with X11 windowid.", NULL, is_term );
777  print_help_msg ( "-keep-right", "", "Set ellipsize to end.", NULL, is_term );
778 }
rofi_range_pair
Definition: rofi-types.h:260
helper_tokenize_free
void helper_tokenize_free(rofi_int_matcher **tokens)
Definition: helper.c:125
dmenu_switcher_dialog
int dmenu_switcher_dialog(void)
Definition: dmenu.c:659
get_display_data
static char * get_display_data(const Mode *data, unsigned int index, int *state, G_GNUC_UNUSED GList **list, int get_entry)
Definition: dmenu.c:266
MenuFlags
MenuFlags
Definition: view.h:44
xrmoptions.h
DmenuScriptEntry::nonselectable
gboolean nonselectable
Definition: dmenuscriptshared.h:19
ACTIVE
@ ACTIVE
Definition: textbox.h:110
rofi_mode::name
char * name
Definition: mode-private.h:156
settings.h
get_index
static unsigned int get_index(unsigned int length, int index)
Definition: dmenu.c:254
dmenu_mode
Mode dmenu_mode
Definition: dmenu.c:336
MENU_INDICATOR
@ MENU_INDICATOR
Definition: view.h:54
DmenuScriptEntry::info
char * info
Definition: dmenuscriptshared.h:16
DmenuModePrivateData::selected_list
uint32_t * selected_list
Definition: dmenu.c:88
helper_tokenize
rofi_int_matcher ** helper_tokenize(const char *input, int case_sensitive)
Definition: helper.c:260
MARKUP
@ MARKUP
Definition: textbox.h:114
DmenuModePrivateData::data_input_stream
GDataInputStream * data_input_stream
Definition: dmenu.c:105
DmenuModePrivateData::cancel
GCancellable * cancel
Definition: dmenu.c:102
DmenuModePrivateData
Definition: dmenu.c:76
MENU_OK
@ MENU_OK
Definition: mode.h:69
rofi_view_get_next_position
unsigned int rofi_view_get_next_position(const RofiViewState *state)
Definition: view.c:573
rofi_view_get_active
RofiViewState * rofi_view_get_active(void)
Definition: view.c:491
rofi_int_matcher_t
Definition: rofi-types.h:269
bitget
static unsigned int bitget(uint32_t *array, unsigned int index)
Definition: dmenu.c:61
DmenuModePrivateData::input_stream
GInputStream * input_stream
Definition: dmenu.c:104
DmenuScriptEntry::icon_fetch_uid
uint32_t icon_fetch_uid
Definition: dmenuscriptshared.h:11
dmenu_mode_free
static void dmenu_mode_free(Mode *sw)
Definition: dmenu.c:294
mode_set_private_data
void mode_set_private_data(Mode *mode, void *pd)
Definition: mode.c:145
rofi_view_error_dialog
int rofi_view_error_dialog(const char *msg, int markup)
Definition: view.c:1898
DmenuModePrivateData::column_separator
gchar * column_separator
Definition: dmenu.c:99
dmenu_mode_init
static int dmenu_mode_init(Mode *sw)
Definition: dmenu.c:355
dmenu_finalize
static void dmenu_finalize(RofiViewState *state)
Definition: dmenu.c:536
DmenuModePrivateData::cmd_list_length
unsigned int cmd_list_length
Definition: dmenu.c:94
dmenu_get_icon
static cairo_surface_t * dmenu_get_icon(const Mode *sw, unsigned int selected_line, int height)
Definition: dmenu.c:484
rofi_range_pair::start
int start
Definition: rofi-types.h:261
print_help_msg
void print_help_msg(const char *option, const char *type, const char *text, const char *def, int isatty)
Definition: xrmoptions.c:775
rofi_view_get_selected_line
unsigned int rofi_view_get_selected_line(const RofiViewState *state)
Definition: view.c:568
mode_init
int mode_init(Mode *mode)
Definition: mode.c:42
rofi_view_get_mode
Mode * rofi_view_get_mode(RofiViewState *state)
Definition: view.c:2021
MENU_NORMAL
@ MENU_NORMAL
Definition: view.h:46
rofi_view_ellipsize_start
void rofi_view_ellipsize_start(RofiViewState *state)
Definition: view.c:2049
find_arg
int find_arg(const char *const key)
Definition: helper.c:299
bittoggle
static void bittoggle(uint32_t *array, unsigned int index)
Definition: dmenu.c:68
rofi_view_free
void rofi_view_free(RofiViewState *state)
Definition: view.c:544
MenuReturn
MenuReturn
Definition: mode.h:67
Settings::location
WindowLocation location
Definition: settings.h:103
get_dmenu_sync
static void get_dmenu_sync(DmenuModePrivateData *pd)
Definition: dmenu.c:205
SELECTED
@ SELECTED
Definition: textbox.h:112
rofi_view_get_return_value
MenuReturn rofi_view_get_return_value(const RofiViewState *state)
Definition: view.c:563
mode-private.h
DmenuScriptEntry::meta
char * meta
Definition: dmenuscriptshared.h:13
rofi_view_set_active
void rofi_view_set_active(RofiViewState *state)
Definition: view.c:505
DmenuScriptEntry::icon_name
char * icon_name
Definition: dmenuscriptshared.h:9
async_read_cancel
static void async_read_cancel(G_GNUC_UNUSED GCancellable *cancel, G_GNUC_UNUSED gpointer data)
Definition: dmenu.c:183
DmenuModePrivateData::num_urgent_list
unsigned int num_urgent_list
Definition: dmenu.c:85
DmenuModePrivateData::urgent_list
struct rofi_range_pair * urgent_list
Definition: dmenu.c:84
DmenuModePrivateData::num_active_list
unsigned int num_active_list
Definition: dmenu.c:87
DmenuModePrivateData::cancel_source
gulong cancel_source
Definition: dmenu.c:103
find_arg_uint
int find_arg_uint(const char *const key, unsigned int *val)
Definition: helper.c:351
dmenu_token_match
static int dmenu_token_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: dmenu.c:441
DmenuModePrivateData::selected_count
unsigned int selected_count
Definition: dmenu.c:96
dmenuscriptshared.h
DmenuModePrivateData::columns
gchar ** columns
Definition: dmenu.c:98
DmenuModePrivateData::cmd_list_real_length
unsigned int cmd_list_real_length
Definition: dmenu.c:93
mode_destroy
void mode_destroy(Mode *mode)
Definition: mode.c:49
rofi_icon_fetcher_get
cairo_surface_t * rofi_icon_fetcher_get(const uint32_t uid)
Definition: rofi-icon-fetcher.c:360
rofi_range_pair::stop
int stop
Definition: rofi-types.h:262
DmenuModePrivateData::separator
char separator
Definition: dmenu.c:79
dmenu_finish
static void dmenu_finish(RofiViewState *state, int retv)
Definition: dmenu.c:499
rofi_expand_path
char * rofi_expand_path(const char *input)
Definition: helper.c:711
rofi_view_restart
void rofi_view_restart(RofiViewState *state)
Definition: view.c:485
DmenuModePrivateData::only_selected
unsigned int only_selected
Definition: dmenu.c:95
URGENT
@ URGENT
Definition: textbox.h:108
print_dmenu_options
void print_dmenu_options(void)
Definition: dmenu.c:755
rofi_mode
Definition: mode-private.h:152
DmenuScriptEntry::entry
char * entry
Definition: dmenuscriptshared.h:7
rofi.h
dmenu_format_output_string
static gchar * dmenu_format_output_string(const DmenuModePrivateData *pd, const char *input)
Definition: dmenu.c:226
DmenuModePrivateData::num_selected_list
unsigned int num_selected_list
Definition: dmenu.c:89
rofi_mode::private_data
void * private_data
Definition: mode-private.h:185
MENU_PASSWORD
@ MENU_PASSWORD
Definition: view.h:48
DmenuModePrivateData::multi_select
gboolean multi_select
Definition: dmenu.c:100
find_arg_str
int find_arg_str(const char *const key, char **val)
Definition: helper.c:309
DmenuModePrivateData::cmd_list
DmenuScriptEntry * cmd_list
Definition: dmenu.c:92
MENU_CUSTOM_ACTION
@ MENU_CUSTOM_ACTION
Definition: mode.h:85
get_dmenu_async
static int get_dmenu_async(DmenuModePrivateData *pd, int sync_pre_read)
Definition: dmenu.c:188
DmenuModePrivateData::format
char * format
Definition: dmenu.c:83
find_arg_char
int find_arg_char(const char *const key, char *val)
Definition: helper.c:403
MENU_CUSTOM_INPUT
@ MENU_CUSTOM_INPUT
Definition: mode.h:75
DmenuModePrivateData::do_markup
unsigned int do_markup
Definition: dmenu.c:90
rofi_mode::display_name
char * display_name
Definition: mode-private.h:158
DmenuModePrivateData::active_list
struct rofi_range_pair * active_list
Definition: dmenu.c:86
mode_get_private_data
void * mode_get_private_data(const Mode *mode)
Definition: mode.c:139
dmenuscript_parse_entry_extras
void dmenuscript_parse_entry_extras(G_GNUC_UNUSED Mode *sw, DmenuScriptEntry *entry, char *buffer, size_t length)
Definition: script.c:79
parse_ranges
void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length)
Definition: helper.c:1170
Settings::auto_select
unsigned int auto_select
Definition: settings.h:145
dmenu.h
rofi_output_formatted_line
void rofi_output_formatted_line(const char *format, const char *string, int selected_line, const char *filter)
Definition: helper.c:1204
view.h
rofi_icon_fetcher_query
uint32_t rofi_icon_fetcher_query(const char *name, const int size)
Definition: rofi-icon-fetcher.c:326
rofi_set_return_code
void rofi_set_return_code(int code)
Definition: rofi.c:126
read_add
static void read_add(DmenuModePrivateData *pd, char *data, gsize len)
Definition: dmenu.c:114
helper_token_match
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
Definition: helper.c:488
Settings::filter
char * filter
Definition: settings.h:161
textbox.h
MENU_CANCEL
@ MENU_CANCEL
Definition: mode.h:71
rofi-icon-fetcher.h
async_close_callback
static void async_close_callback(GObject *source_object, GAsyncResult *res, G_GNUC_UNUSED gpointer user_data)
Definition: dmenu.c:108
DmenuScriptEntry
Definition: dmenuscriptshared.h:5
rofi_view_create
RofiViewState * rofi_view_create(Mode *sw, const char *input, MenuFlags menu_flags, void(*finalize)(RofiViewState *))
Definition: view.c:1821
MENU_CUSTOM_COMMAND
@ MENU_CUSTOM_COMMAND
Definition: mode.h:81
Settings::menu_lines
unsigned int menu_lines
Definition: settings.h:68
Settings::case_sensitive
unsigned int case_sensitive
Definition: settings.h:135
rofi_view_get_user_input
const char * rofi_view_get_user_input(const RofiViewState *state)
Definition: view.c:588
MENU_LOWER_MASK
@ MENU_LOWER_MASK
Definition: mode.h:87
helper.h
async_read_callback
static void async_read_callback(GObject *source_object, GAsyncResult *res, gpointer user_data)
Definition: dmenu.c:140
RofiViewState
Definition: view-internal.h:48
config
Settings config
DmenuModePrivateData::selected_line
unsigned int selected_line
Definition: dmenu.c:81
DmenuModePrivateData::message
char * message
Definition: dmenu.c:82
rofi_force_utf8
char * rofi_force_utf8(const gchar *data, ssize_t length)
Definition: helper.c:795
rofi_view_set_selected_line
void rofi_view_set_selected_line(RofiViewState *state, unsigned int selected_line)
Definition: view.c:528
dmenu_mode_get_num_entries
static unsigned int dmenu_mode_get_num_entries(const Mode *sw)
Definition: dmenu.c:220
rofi_view_set_overlay
void rofi_view_set_overlay(RofiViewState *state, const char *text)
Definition: view.c:2026
rofi_view_reload
void rofi_view_reload(void)
Definition: view.c:469
dmenu_print_results
static void dmenu_print_results(DmenuModePrivateData *pd, const char *input)
Definition: dmenu.c:515
dmenu_get_message
static char * dmenu_get_message(const Mode *sw)
Definition: dmenu.c:476