rofi  1.6.1
view.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 "View"
30 
31 #include <config.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <stdint.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <time.h>
40 #include <locale.h>
41 #include <xkbcommon/xkbcommon-x11.h>
42 #include <xcb/xkb.h>
43 #include <xcb/xcb_ewmh.h>
44 #include <xcb/xcb_icccm.h>
45 
46 #include <cairo.h>
47 #include <cairo-xcb.h>
48 
50 #define SN_API_NOT_YET_FROZEN
51 #include <libsn/sn.h>
52 #include "rofi.h"
53 
54 #include "timings.h"
55 #include "settings.h"
56 
57 #include "mode.h"
58 #include "display.h"
59 #include "xcb-internal.h"
60 #include "helper.h"
61 #include "helper-theme.h"
62 #include "xrmoptions.h"
63 #include "dialogs/dialogs.h"
64 
65 #include "view.h"
66 #include "view-internal.h"
67 
68 #include "theme.h"
69 
70 #include "xcb.h"
71 
78 void rofi_view_update ( RofiViewState *state, gboolean qr );
79 
80 static int rofi_view_calculate_height ( RofiViewState *state );
81 
83 GThreadPool *tpool = NULL;
84 
87 
91 struct
92 {
94  xcb_window_t main_window;
96  cairo_surface_t *fake_bg;
98  xcb_gcontext_t gc;
100  xcb_pixmap_t edit_pixmap;
102  cairo_surface_t *edit_surf;
104  cairo_t *edit_draw;
110  GQueue views;
116  unsigned long long count;
120  gboolean fullscreen;
121 } CacheState = {
122  .main_window = XCB_WINDOW_NONE,
123  .fake_bg = NULL,
124  .edit_surf = NULL,
125  .edit_draw = NULL,
126  .fake_bgrel = FALSE,
127  .flags = MENU_NORMAL,
128  .views = G_QUEUE_INIT,
129  .idle_timeout = 0,
130  .count = 0L,
131  .repaint_source = 0,
132  .fullscreen = FALSE,
133 };
134 
135 void rofi_view_get_current_monitor ( int *width, int *height )
136 {
137  if ( width ) {
138  *width = CacheState.mon.w;
139  }
140  if ( height ) {
141  *height = CacheState.mon.h;
142  }
143 }
144 static char * get_matching_state ( void )
145 {
146  if ( config.case_sensitive ) {
147  if ( config.sort ) {
148  return "±";
149  }
150  else {
151  return "-";
152  }
153  }
154  else{
155  if ( config.sort ) {
156  return "+";
157  }
158  }
159  return " ";
160 }
161 
165 static int lev_sort ( const void *p1, const void *p2, void *arg )
166 {
167  const int *a = p1;
168  const int *b = p2;
169  int *distances = arg;
170 
171  return distances[*a] - distances[*b];
172 }
173 
178 {
179  const char *outp = g_getenv ( "ROFI_PNG_OUTPUT" );
180  if ( CacheState.edit_surf == NULL ) {
181  // Nothing to store.
182  g_warning ( "There is no rofi surface to store" );
183  return;
184  }
185  const char *xdg_pict_dir = g_get_user_special_dir ( G_USER_DIRECTORY_PICTURES );
186  if ( outp == NULL && xdg_pict_dir == NULL ) {
187  g_warning ( "XDG user picture directory or ROFI_PNG_OUTPUT is not set. Cannot store screenshot." );
188  return;
189  }
190  // Get current time.
191  GDateTime *now = g_date_time_new_now_local ();
192  // Format filename.
193  char *timestmp = g_date_time_format ( now, "rofi-%Y-%m-%d-%H%M" );
194  char *filename = g_strdup_printf ( "%s-%05d.png", timestmp, 0 );
195  // Build full path
196  char *fpath = NULL;
197  if ( outp == NULL ) {
198  int index = 0;
199  fpath = g_build_filename ( xdg_pict_dir, filename, NULL );
200  while ( g_file_test ( fpath, G_FILE_TEST_EXISTS ) && index < 99 ) {
201  g_free ( fpath );
202  g_free ( filename );
203  // Try the next index.
204  index++;
205  // Format filename.
206  filename = g_strdup_printf ( "%s-%05d.png", timestmp, index );
207  // Build full path
208  fpath = g_build_filename ( xdg_pict_dir, filename, NULL );
209  }
210  }
211  else {
212  fpath = g_strdup ( outp );
213  }
214  fprintf ( stderr, color_green "Storing screenshot %s\n"color_reset, fpath );
215  cairo_status_t status = cairo_surface_write_to_png ( CacheState.edit_surf, fpath );
216  if ( status != CAIRO_STATUS_SUCCESS ) {
217  g_warning ( "Failed to produce screenshot '%s', got error: '%s'", fpath,
218  cairo_status_to_string ( status ) );
219  }
220  g_free ( fpath );
221  g_free ( filename );
222  g_free ( timestmp );
223  g_date_time_unref ( now );
224 }
225 
229 gboolean do_bench = TRUE;
230 struct
231 {
232  GTimer *time;
233  uint64_t draws;
234  double last_ts;
235  double min;
236 } BenchMark = {
237  .time = NULL,
238  .draws = 0,
239  .last_ts = 0.0,
240  .min = G_MAXDOUBLE
241 };
242 
243 static gboolean bench_update ( void )
244 {
245  if ( !config.benchmark_ui ) {
246  return FALSE;
247  }
248  BenchMark.draws++;
249  if ( BenchMark.time == NULL ) {
250  BenchMark.time = g_timer_new ();
251  }
252 
253  if ( ( BenchMark.draws & 1023 ) == 0 ) {
254  double ts = g_timer_elapsed ( BenchMark.time, NULL );
255  double fps = 1024 / ( ts - BenchMark.last_ts );
256 
257  if ( fps < BenchMark.min ) {
258  BenchMark.min = fps;
259  }
260  printf ( "current: %.2f fps, avg: %.2f fps, min: %.2f fps, %lu draws\r\n", fps, BenchMark.draws / ts, BenchMark.min, BenchMark.draws );
261 
262  BenchMark.last_ts = ts;
263  }
264  return TRUE;
265 }
266 
267 static gboolean rofi_view_repaint ( G_GNUC_UNUSED void * data )
268 {
269  if ( current_active_menu ) {
270  // Repaint the view (if needed).
271  // After a resize the edit_pixmap surface might not contain anything anymore.
272  // If we already re-painted, this does nothing.
274  g_debug ( "expose event" );
275  TICK_N ( "Expose" );
276  xcb_copy_area ( xcb->connection, CacheState.edit_pixmap, CacheState.main_window, CacheState.gc,
278  xcb_flush ( xcb->connection );
279  TICK_N ( "flush" );
280  CacheState.repaint_source = 0;
281  }
282  return ( bench_update () == TRUE ) ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
283 }
284 
286 {
287  if ( state->prompt ) {
288  const char *str = mode_get_display_name ( state->sw );
289  textbox_text ( state->prompt, str );
290  }
291 }
292 
305 static const int loc_transtable[9] = {
306  WL_CENTER,
307  WL_NORTH | WL_WEST,
308  WL_NORTH,
309  WL_NORTH | WL_EAST,
310  WL_EAST,
311  WL_SOUTH | WL_EAST,
312  WL_SOUTH,
313  WL_SOUTH | WL_WEST,
314  WL_WEST
315 };
317 {
318  int location = rofi_theme_get_position ( WIDGET ( state->main_window ), "location", loc_transtable[config.location] );
319  int anchor = location;
320  if ( !listview_get_fixed_num_lines ( state->list_view ) ) {
321  anchor = location;
322  if ( location == WL_CENTER ) {
323  anchor = WL_NORTH;
324  }
325  else if ( location == WL_EAST ) {
326  anchor = WL_NORTH_EAST;
327  }
328  else if ( location == WL_WEST ) {
329  anchor = WL_NORTH_WEST;
330  }
331  }
332  anchor = rofi_theme_get_position ( WIDGET ( state->main_window ), "anchor", anchor );
333 
334  if ( CacheState.fullscreen ) {
335  state->x = CacheState.mon.x;
336  state->y = CacheState.mon.y;
337  return;
338  }
339  state->y = CacheState.mon.y + ( CacheState.mon.h ) / 2;
340  state->x = CacheState.mon.x + ( CacheState.mon.w ) / 2;
341  // Determine window location
342  switch ( location )
343  {
344  case WL_NORTH_WEST:
345  state->x = CacheState.mon.x;
346  /* FALLTHRU */
347  case WL_NORTH:
348  state->y = CacheState.mon.y;
349  break;
350  case WL_NORTH_EAST:
351  state->y = CacheState.mon.y;
352  /* FALLTHRU */
353  case WL_EAST:
354  state->x = CacheState.mon.x + CacheState.mon.w;
355  break;
356  case WL_SOUTH_EAST:
357  state->x = CacheState.mon.x + CacheState.mon.w;
358  /* FALLTHRU */
359  case WL_SOUTH:
360  state->y = CacheState.mon.y + CacheState.mon.h;
361  break;
362  case WL_SOUTH_WEST:
363  state->y = CacheState.mon.y + CacheState.mon.h;
364  /* FALLTHRU */
365  case WL_WEST:
366  state->x = CacheState.mon.x;
367  break;
368  case WL_CENTER:
369  ;
370  /* FALLTHRU */
371  default:
372  break;
373  }
374  switch ( anchor )
375  {
376  case WL_SOUTH_WEST:
377  state->y -= state->height;
378  break;
379  case WL_SOUTH:
380  state->x -= state->width / 2;
381  state->y -= state->height;
382  break;
383  case WL_SOUTH_EAST:
384  state->x -= state->width;
385  state->y -= state->height;
386  break;
387  case WL_NORTH_EAST:
388  state->x -= state->width;
389  break;
390  case WL_NORTH_WEST:
391  break;
392  case WL_NORTH:
393  state->x -= state->width / 2;
394  break;
395  case WL_EAST:
396  state->x -= state->width;
397  state->y -= state->height / 2;
398  break;
399  case WL_WEST:
400  state->y -= state->height / 2;
401  break;
402  case WL_CENTER:
403  state->y -= state->height / 2;
404  state->x -= state->width / 2;
405  break;
406  default:
407  break;
408  }
409  // Apply offset.
410  RofiDistance x = rofi_theme_get_distance ( WIDGET ( state->main_window ), "x-offset", config.x_offset );
411  RofiDistance y = rofi_theme_get_distance ( WIDGET ( state->main_window ), "y-offset", config.y_offset );
414 }
415 
417 {
418  if ( state == NULL ) {
419  return;
420  }
421  uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
422  uint32_t vals[] = { state->x, state->y, state->width, state->height };
423 
424  // Display it.
425  xcb_configure_window ( xcb->connection, CacheState.main_window, mask, vals );
426  cairo_destroy ( CacheState.edit_draw );
427  cairo_surface_destroy ( CacheState.edit_surf );
428 
429  xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap );
430  CacheState.edit_pixmap = xcb_generate_id ( xcb->connection );
431  xcb_create_pixmap ( xcb->connection, depth->depth,
432  CacheState.edit_pixmap, CacheState.main_window, state->width, state->height );
433 
434  CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height );
435  CacheState.edit_draw = cairo_create ( CacheState.edit_surf );
436 
437  g_debug ( "Re-size window based internal request: %dx%d.", state->width, state->height );
438  // Should wrap main window in a widget.
439  widget_resize ( WIDGET ( state->main_window ), state->width, state->height );
440 }
441 
443 {
444  if ( state->mesg_box == NULL ) {
445  return;
446  }
447  char *msg = mode_get_message ( state->sw );
448  if ( msg ) {
449  textbox_text ( state->mesg_tb, msg );
450  widget_enable ( WIDGET ( state->mesg_box ) );
451  g_free ( msg );
452  }
453  else {
454  widget_disable ( WIDGET ( state->mesg_box ) );
455  }
456 }
457 
458 static gboolean rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data )
459 {
460  if ( current_active_menu ) {
461  current_active_menu->reload = TRUE;
464  }
465  CacheState.idle_timeout = 0;
466  return G_SOURCE_REMOVE;
467 }
468 
469 void rofi_view_reload ( void )
470 {
471  // @TODO add check if current view is equal to the callee
472  if ( CacheState.idle_timeout == 0 ) {
473  CacheState.idle_timeout = g_timeout_add ( 1000 / 10, rofi_view_reload_idle, NULL );
474  }
475 }
477 {
478  if ( current_active_menu && CacheState.repaint_source == 0 ) {
479  CacheState.count++;
480  g_debug ( "redraw %llu", CacheState.count );
481  CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, rofi_view_repaint, NULL, NULL );
482  }
483 }
484 
486 {
487  state->quit = FALSE;
488  state->retv = MENU_CANCEL;
489 }
490 
492 {
493  return current_active_menu;
494 }
495 
497 {
498  if ( state == current_active_menu ) {
499  rofi_view_set_active ( NULL );
500  }
501  else if ( state ) {
502  g_queue_remove ( &( CacheState.views ), state );
503  }
504 }
506 {
507  if ( current_active_menu != NULL && state != NULL ) {
508  g_queue_push_head ( &( CacheState.views ), current_active_menu );
509  // TODO check.
510  current_active_menu = state;
511  g_debug ( "stack view." );
514  return;
515  }
516  else if ( state == NULL && !g_queue_is_empty ( &( CacheState.views ) ) ) {
517  g_debug ( "pop view." );
518  current_active_menu = g_queue_pop_head ( &( CacheState.views ) );
521  return;
522  }
523  g_assert ( ( current_active_menu == NULL && state != NULL ) || ( current_active_menu != NULL && state == NULL ) );
524  current_active_menu = state;
526 }
527 
528 void rofi_view_set_selected_line ( RofiViewState *state, unsigned int selected_line )
529 {
530  state->selected_line = selected_line;
531  // Find the line.
532  unsigned int selected = 0;
533  for ( unsigned int i = 0; ( ( state->selected_line ) ) < UINT32_MAX && !selected && i < state->filtered_lines; i++ ) {
534  if ( state->line_map[i] == ( state->selected_line ) ) {
535  selected = i;
536  break;
537  }
538  }
539  listview_set_selected ( state->list_view, selected );
540  xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 );
541  xcb_flush ( xcb->connection );
542 }
543 
545 {
546  if ( state->tokens ) {
547  helper_tokenize_free ( state->tokens );
548  state->tokens = NULL;
549  }
550  // Do this here?
551  // Wait for final release?
552  widget_free ( WIDGET ( state->main_window ) );
553 
554  g_free ( state->line_map );
555  g_free ( state->distance );
556  // Free the switcher boxes.
557  // When state is free'ed we should no longer need these.
558  g_free ( state->modi );
559  state->num_modi = 0;
560  g_free ( state );
561 }
562 
564 {
565  return state->retv;
566 }
567 
568 unsigned int rofi_view_get_selected_line ( const RofiViewState *state )
569 {
570  return state->selected_line;
571 }
572 
573 unsigned int rofi_view_get_next_position ( const RofiViewState *state )
574 {
575  unsigned int next_pos = state->selected_line;
576  unsigned int selected = listview_get_selected ( state->list_view );
577  if ( ( selected + 1 ) < state->num_lines ) {
578  ( next_pos ) = state->line_map[selected + 1];
579  }
580  return next_pos;
581 }
582 
583 unsigned int rofi_view_get_completed ( const RofiViewState *state )
584 {
585  return state->quit;
586 }
587 
588 const char * rofi_view_get_user_input ( const RofiViewState *state )
589 {
590  if ( state->text ) {
591  return state->text->text;
592  }
593  return NULL;
594 }
595 
602 {
603  return g_malloc0 ( sizeof ( RofiViewState ) );
604 }
605 
609 typedef struct _thread_state_view
610 {
613 
615  GCond *cond;
617  GMutex *mutex;
619  unsigned int *acount;
620 
624  unsigned int start;
626  unsigned int stop;
628  unsigned int count;
629 
631  const char *pattern;
633  glong plen;
641 static void rofi_view_call_thread ( gpointer data, gpointer user_data )
642 {
643  thread_state *t = (thread_state *) data;
644  t->callback ( t, user_data );
645 }
646 
647 static void filter_elements ( thread_state *ts, G_GNUC_UNUSED gpointer user_data )
648 {
650  for ( unsigned int i = t->start; i < t->stop; i++ ) {
651  int match = mode_token_match ( t->state->sw, t->state->tokens, i );
652  // If each token was matched, add it to list.
653  if ( match ) {
654  t->state->line_map[t->start + t->count] = i;
655  if ( config.sort ) {
656  // This is inefficient, need to fix it.
657  char * str = mode_get_completion ( t->state->sw, i );
658  glong slen = g_utf8_strlen ( str, -1 );
659  switch ( config.sorting_method_enum )
660  {
661  case SORT_FZF:
662  t->state->distance[i] = rofi_scorer_fuzzy_evaluate ( t->pattern, t->plen, str, slen );
663  break;
664  case SORT_NORMAL:
665  default:
666  t->state->distance[i] = levenshtein ( t->pattern, t->plen, str, slen );
667  break;
668  }
669  g_free ( str );
670  }
671  t->count++;
672  }
673  }
674  if ( t->acount != NULL ) {
675  g_mutex_lock ( t->mutex );
676  ( *( t->acount ) )--;
677  g_cond_signal ( t->cond );
678  g_mutex_unlock ( t->mutex );
679  }
680 }
681 static void rofi_view_setup_fake_transparency ( const char* const fake_background )
682 {
683  if ( CacheState.fake_bg == NULL ) {
684  cairo_surface_t *s = NULL;
689  TICK_N ( "Fake start" );
690  if ( g_strcmp0 ( fake_background, "real" ) == 0 ) {
691  return;
692  }
693  else if ( g_strcmp0 ( fake_background, "screenshot" ) == 0 ) {
695  }
696  else if ( g_strcmp0 ( fake_background, "background" ) == 0 ) {
698  }
699  else {
700  char *fpath = rofi_expand_path ( fake_background );
701  g_debug ( "Opening %s to use as background.", fpath );
702  s = cairo_image_surface_create_from_png ( fpath );
703  CacheState.fake_bgrel = TRUE;
704  g_free ( fpath );
705  }
706  TICK_N ( "Get surface." );
707  if ( s != NULL ) {
708  if ( cairo_surface_status ( s ) != CAIRO_STATUS_SUCCESS ) {
709  g_debug ( "Failed to open surface fake background: %s",
710  cairo_status_to_string ( cairo_surface_status ( s ) ) );
711  cairo_surface_destroy ( s );
712  s = NULL;
713  }
714  else {
715  CacheState.fake_bg = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, CacheState.mon.w, CacheState.mon.h );
716  cairo_t *dr = cairo_create ( CacheState.fake_bg );
717  if ( CacheState.fake_bgrel ) {
718  cairo_set_source_surface ( dr, s, 0, 0 );
719  }
720  else {
721  cairo_set_source_surface ( dr, s, -CacheState.mon.x, -CacheState.mon.y );
722  }
723  cairo_paint ( dr );
724  cairo_destroy ( dr );
725  cairo_surface_destroy ( s );
726  }
727  }
728  TICK_N ( "Fake transparency" );
729  }
730 }
731 void __create_window ( MenuFlags menu_flags )
732 {
733  uint32_t selmask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY | XCB_CW_BACKING_STORE | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
734  uint32_t selval[] = {
735  XCB_BACK_PIXMAP_NONE, 0,
736  XCB_GRAVITY_STATIC,
737  XCB_BACKING_STORE_NOT_USEFUL,
738  XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
739  XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE |
740  XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION,
741  map
742  };
743 
744  xcb_window_t box_window = xcb_generate_id ( xcb->connection );
745  xcb_void_cookie_t cc = xcb_create_window_checked ( xcb->connection, depth->depth, box_window, xcb_stuff_get_root_window ( ),
746  0, 0, 200, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
747  visual->visual_id, selmask, selval );
748  xcb_generic_error_t *error;
749  error = xcb_request_check ( xcb->connection, cc );
750  if ( error ) {
751  g_error ( "xcb_create_window() failed error=0x%x\n", error->error_code );
752  exit ( EXIT_FAILURE );
753  }
754  TICK_N ( "xcb create window" );
755  CacheState.gc = xcb_generate_id ( xcb->connection );
756  xcb_create_gc ( xcb->connection, CacheState.gc, box_window, 0, 0 );
757 
758  TICK_N ( "xcb create gc" );
759  // Create a drawable.
760  CacheState.edit_pixmap = xcb_generate_id ( xcb->connection );
761  xcb_create_pixmap ( xcb->connection, depth->depth,
762  CacheState.edit_pixmap, CacheState.main_window, 200, 100 );
763 
764  CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, 200, 100 );
765  CacheState.edit_draw = cairo_create ( CacheState.edit_surf );
766 
767  TICK_N ( "create cairo surface" );
768  // Set up pango context.
769  cairo_font_options_t *fo = cairo_font_options_create ();
770  // Take font description from xlib surface
771  cairo_surface_get_font_options ( CacheState.edit_surf, fo );
772  // TODO should we update the drawable each time?
773  PangoContext *p = pango_cairo_create_context ( CacheState.edit_draw );
774  // Set the font options from the xlib surface
775  pango_cairo_context_set_font_options ( p, fo );
776  TICK_N ( "pango cairo font setup" );
777 
778  CacheState.main_window = box_window;
779  CacheState.flags = menu_flags;
780  monitor_active ( &( CacheState.mon ) );
781  // Setup dpi
782  if ( config.dpi > 1 ) {
783  PangoFontMap *font_map = pango_cairo_font_map_get_default ();
784  pango_cairo_font_map_set_resolution ( (PangoCairoFontMap *) font_map, (double) config.dpi );
785  }
786  else if ( config.dpi == 0 || config.dpi == 1 ) {
787  // Auto-detect mode.
788  double dpi = 96;
789  if ( CacheState.mon.mh > 0 && config.dpi == 1 ) {
790  dpi = ( CacheState.mon.h * 25.4 ) / (double) ( CacheState.mon.mh );
791  }
792  else {
793  dpi = ( xcb->screen->height_in_pixels * 25.4 ) / (double) ( xcb->screen->height_in_millimeters );
794  }
795 
796  g_debug ( "Auto-detected DPI: %.2lf", dpi );
797  PangoFontMap *font_map = pango_cairo_font_map_get_default ();
798  pango_cairo_font_map_set_resolution ( (PangoCairoFontMap *) font_map, dpi );
799  config.dpi = dpi;
800  }
801  else {
802  // default pango is 96.
803  PangoFontMap *font_map = pango_cairo_font_map_get_default ();
804  config.dpi = pango_cairo_font_map_get_resolution ( (PangoCairoFontMap *) font_map );
805  }
806  // Setup font.
807  // Dummy widget.
808  box *win = box_create ( NULL, "window", ROFI_ORIENTATION_HORIZONTAL );
809  const char *font = rofi_theme_get_string ( WIDGET ( win ), "font", config.menu_font );
810  if ( font ) {
811  PangoFontDescription *pfd = pango_font_description_from_string ( font );
812  if ( helper_validate_font ( pfd, font ) ) {
813  pango_context_set_font_description ( p, pfd );
814  }
815  pango_font_description_free ( pfd );
816  }
817  PangoLanguage *l = pango_language_get_default ();
818  pango_context_set_language ( p, l );
819  TICK_N ( "configure font" );
820 
821  // Tell textbox to use this context.
822  textbox_set_pango_context ( font, p );
823  // cleanup
824  g_object_unref ( p );
825  cairo_font_options_destroy ( fo );
826 
827  TICK_N ( "textbox setup" );
828  // // make it an unmanaged window
829  if ( ( ( menu_flags & MENU_NORMAL_WINDOW ) == 0 ) ) {
830  window_set_atom_prop ( box_window, xcb->ewmh._NET_WM_STATE, &( xcb->ewmh._NET_WM_STATE_ABOVE ), 1 );
831  uint32_t values[] = { 1 };
832  xcb_change_window_attributes ( xcb->connection, box_window, XCB_CW_OVERRIDE_REDIRECT, values );
833  }
834  else{
835  window_set_atom_prop ( box_window, xcb->ewmh._NET_WM_WINDOW_TYPE, &( xcb->ewmh._NET_WM_WINDOW_TYPE_NORMAL ), 1 );
836  x11_disable_decoration ( box_window );
837  }
838 
839  TICK_N ( "setup window attributes" );
840  CacheState.fullscreen = rofi_theme_get_boolean ( WIDGET ( win ), "fullscreen", config.fullscreen );
841  if ( CacheState.fullscreen ) {
842  xcb_atom_t atoms[] = {
843  xcb->ewmh._NET_WM_STATE_FULLSCREEN,
844  xcb->ewmh._NET_WM_STATE_ABOVE
845  };
846  window_set_atom_prop ( box_window, xcb->ewmh._NET_WM_STATE, atoms, sizeof ( atoms ) / sizeof ( xcb_atom_t ) );
847  }
848 
849  TICK_N ( "setup window fullscreen" );
850  // Set the WM_NAME
851  rofi_view_set_window_title ( "rofi" );
852  const char wm_class_name[] = "rofi\0Rofi";
853  xcb_icccm_set_wm_class ( xcb->connection, box_window, sizeof ( wm_class_name ), wm_class_name );
854 
855  TICK_N ( "setup window name and class" );
856  const char *transparency = rofi_theme_get_string ( WIDGET ( win ), "transparency", NULL );
857  if ( transparency ) {
858  rofi_view_setup_fake_transparency ( transparency );
859  }
862  }
863  if ( xcb->sncontext != NULL ) {
864  sn_launchee_context_setup_window ( xcb->sncontext, CacheState.main_window );
865  }
866  TICK_N ( "setup startup notification" );
867  widget_free ( WIDGET ( win ) );
868  TICK_N ( "done" );
869 
870  // Set the PID.
871  pid_t pid = getpid ();
872  xcb_ewmh_set_wm_pid ( &( xcb->ewmh ), CacheState.main_window, pid );
873 
874  // Get hostname
875  const char *hostname = g_get_host_name ();
876  char *ahost = g_hostname_to_ascii ( hostname );
877  if ( ahost != NULL ) {
878  xcb_icccm_set_wm_client_machine ( xcb->connection,
879  CacheState.main_window,
880  XCB_ATOM_STRING, 8,
881  strlen ( ahost ), ahost );
882  g_free ( ahost );
883  }
884 }
885 
892 {
893  if ( CacheState.fullscreen ) {
894  state->width = CacheState.mon.w;
895  return;
896  }
897  if ( config.menu_width < 0 ) {
898  double fw = textbox_get_estimated_char_width ( );
899  state->width = -( fw * config.menu_width );
900  state->width += widget_padding_get_padding_width ( WIDGET ( state->main_window ) );
901  }
902  else{
903  // Calculate as float to stop silly, big rounding down errors.
904  state->width = config.menu_width < 101 ? ( CacheState.mon.w / 100.0f ) * ( float ) config.menu_width : config.menu_width;
905  }
906  // Use theme configured width, if set.
907  RofiDistance width = rofi_theme_get_distance ( WIDGET ( state->main_window ), "width", state->width );
909 }
910 
920 static void rofi_view_nav_row_tab ( RofiViewState *state )
921 {
922  if ( state->filtered_lines == 1 ) {
923  state->retv = MENU_OK;
924  ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )];
925  state->quit = 1;
926  return;
927  }
928 
929  // Double tab!
930  if ( state->filtered_lines == 0 && ROW_TAB == state->prev_action ) {
931  state->retv = MENU_NEXT;
932  ( state->selected_line ) = 0;
933  state->quit = TRUE;
934  }
935  else {
936  listview_nav_down ( state->list_view );
937  }
938  state->prev_action = ROW_TAB;
939 }
945 inline static void rofi_view_nav_row_select ( RofiViewState *state )
946 {
947  if ( state->list_view == NULL ) {
948  return;
949  }
950  unsigned int selected = listview_get_selected ( state->list_view );
951  // If a valid item is selected, return that..
952  if ( selected < state->filtered_lines ) {
953  char *str = mode_get_completion ( state->sw, state->line_map[selected] );
954  textbox_text ( state->text, str );
955  g_free ( str );
956  textbox_keybinding ( state->text, MOVE_END );
957  state->refilter = TRUE;
958  }
959 }
960 
966 inline static void rofi_view_nav_first ( RofiViewState * state )
967 {
968 // state->selected = 0;
969  listview_set_selected ( state->list_view, 0 );
970 }
971 
977 inline static void rofi_view_nav_last ( RofiViewState * state )
978 {
979  // If no lines, do nothing.
980  if ( state->filtered_lines == 0 ) {
981  return;
982  }
983  //state->selected = state->filtered_lines - 1;
984  listview_set_selected ( state->list_view, -1 );
985 }
986 
987 static void update_callback ( textbox *t, icon *ico, unsigned int index, void *udata, TextBoxFontType *type, gboolean full )
988 {
989  RofiViewState *state = (RofiViewState *) udata;
990  if ( full ) {
991  GList *add_list = NULL;
992  int fstate = 0;
993  char *text = mode_get_display_value ( state->sw, state->line_map[index], &fstate, &add_list, TRUE );
994  ( *type ) |= fstate;
995  // TODO needed for markup.
996  textbox_font ( t, *type );
997  // Move into list view.
998  textbox_text ( t, text );
999  PangoAttrList *list = textbox_get_pango_attributes ( t );
1000  if ( list != NULL ) {
1001  pango_attr_list_ref ( list );
1002  }
1003  else{
1004  list = pango_attr_list_new ();
1005  }
1006  if ( ico ) {
1007  int icon_height = widget_get_desired_height ( WIDGET ( ico ) );
1008  cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[index], icon_height );
1009  icon_set_surface ( ico, icon );
1010  }
1011 
1012  if ( state->tokens && config.show_match ) {
1013  RofiHighlightColorStyle th = { ROFI_HL_BOLD | ROFI_HL_UNDERLINE, { 0.0, 0.0, 0.0, 0.0 } };
1014  th = rofi_theme_get_highlight ( WIDGET ( t ), "highlight", th );
1016  }
1017  for ( GList *iter = g_list_first ( add_list ); iter != NULL; iter = g_list_next ( iter ) ) {
1018  pango_attr_list_insert ( list, (PangoAttribute *) ( iter->data ) );
1019  }
1020  textbox_set_pango_attributes ( t, list );
1021  pango_attr_list_unref ( list );
1022  g_list_free ( add_list );
1023  g_free ( text );
1024  }
1025  else {
1026  int fstate = 0;
1027  mode_get_display_value ( state->sw, state->line_map[index], &fstate, NULL, FALSE );
1028  ( *type ) |= fstate;
1029  // TODO needed for markup.
1030  textbox_font ( t, *type );
1031  }
1032 }
1033 
1034 void rofi_view_update ( RofiViewState *state, gboolean qr )
1035 {
1036  if ( !widget_need_redraw ( WIDGET ( state->main_window ) ) ) {
1037  return;
1038  }
1039  g_debug ( "Redraw view" );
1040  TICK ();
1041  cairo_t *d = CacheState.edit_draw;
1042  cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE );
1043  if ( CacheState.fake_bg != NULL ) {
1044  if ( CacheState.fake_bgrel ) {
1045  cairo_set_source_surface ( d, CacheState.fake_bg, 0.0, 0.0 );
1046  }
1047  else {
1048  cairo_set_source_surface ( d, CacheState.fake_bg,
1049  -(double) ( state->x - CacheState.mon.x ),
1050  -(double) ( state->y - CacheState.mon.y ) );
1051  }
1052  cairo_paint ( d );
1053  cairo_set_operator ( d, CAIRO_OPERATOR_OVER );
1054  }
1055  else {
1056  // Paint the background transparent.
1057  cairo_set_source_rgba ( d, 0, 0, 0, 0.0 );
1058  cairo_paint ( d );
1059  }
1060  TICK_N ( "Background" );
1061 
1062  // Always paint as overlay over the background.
1063  cairo_set_operator ( d, CAIRO_OPERATOR_OVER );
1064  widget_draw ( WIDGET ( state->main_window ), d );
1065 
1066  TICK_N ( "widgets" );
1067  cairo_surface_flush ( CacheState.edit_surf );
1068  if ( qr ) {
1070  }
1071 }
1072 
1073 static void _rofi_view_reload_row ( RofiViewState *state )
1074 {
1075  g_free ( state->line_map );
1076  g_free ( state->distance );
1077  state->num_lines = mode_get_num_entries ( state->sw );
1078  state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) );
1079  state->distance = g_malloc0_n ( state->num_lines, sizeof ( int ) );
1080  listview_set_max_lines ( state->list_view, state->num_lines );
1081  rofi_view_reload_message_bar ( state );
1082 }
1083 
1084 static void rofi_view_refilter ( RofiViewState *state )
1085 {
1086  TICK_N ( "Filter start" );
1087  if ( state->reload ) {
1088  _rofi_view_reload_row ( state );
1089  state->reload = FALSE;
1090  }
1091  TICK_N ( "Filter reload rows" );
1092  if ( state->tokens ) {
1093  helper_tokenize_free ( state->tokens );
1094  state->tokens = NULL;
1095  }
1096  TICK_N ( "Filter tokenize" );
1097  if ( state->text && strlen ( state->text->text ) > 0 ) {
1098  unsigned int j = 0;
1099  gchar *pattern = mode_preprocess_input ( state->sw, state->text->text );
1100  glong plen = pattern ? g_utf8_strlen ( pattern, -1 ) : 0;
1101  state->tokens = helper_tokenize ( pattern, config.case_sensitive );
1108  unsigned int nt = MAX ( 1, state->num_lines / 500 );
1109  thread_state_view states[nt];
1110  GCond cond;
1111  GMutex mutex;
1112  g_mutex_init ( &mutex );
1113  g_cond_init ( &cond );
1114  unsigned int count = nt;
1115  unsigned int steps = ( state->num_lines + nt ) / nt;
1116  for ( unsigned int i = 0; i < nt; i++ ) {
1117  states[i].state = state;
1118  states[i].start = i * steps;
1119  states[i].stop = MIN ( state->num_lines, ( i + 1 ) * steps );
1120  states[i].count = 0;
1121  states[i].cond = &cond;
1122  states[i].mutex = &mutex;
1123  states[i].acount = &count;
1124  states[i].plen = plen;
1125  states[i].pattern = pattern;
1126  states[i].st.callback = filter_elements;
1127  if ( i > 0 ) {
1128  g_thread_pool_push ( tpool, &states[i], NULL );
1129  }
1130  }
1131  // Run one in this thread.
1132  rofi_view_call_thread ( &states[0], NULL );
1133  // No need to do this with only one thread.
1134  if ( nt > 1 ) {
1135  g_mutex_lock ( &mutex );
1136  while ( count > 0 ) {
1137  g_cond_wait ( &cond, &mutex );
1138  }
1139  g_mutex_unlock ( &mutex );
1140  }
1141  g_cond_clear ( &cond );
1142  g_mutex_clear ( &mutex );
1143  for ( unsigned int i = 0; i < nt; i++ ) {
1144  if ( j != states[i].start ) {
1145  memmove ( &( state->line_map[j] ), &( state->line_map[states[i].start] ), sizeof ( unsigned int ) * ( states[i].count ) );
1146  }
1147  j += states[i].count;
1148  }
1149  if ( config.sort ) {
1150  g_qsort_with_data ( state->line_map, j, sizeof ( int ), lev_sort, state->distance );
1151  }
1152 
1153  // Cleanup + bookkeeping.
1154  state->filtered_lines = j;
1155  g_free ( pattern );
1156  }
1157  else{
1158  for ( unsigned int i = 0; i < state->num_lines; i++ ) {
1159  state->line_map[i] = i;
1160  }
1161  state->filtered_lines = state->num_lines;
1162  }
1163  TICK_N ( "Filter matching done" );
1165 
1166  if ( state->tb_filtered_rows ) {
1167  char *r = g_strdup_printf ( "%u", state->filtered_lines );
1168  textbox_text ( state->tb_filtered_rows, r );
1169  g_free ( r );
1170  }
1171  if ( state->tb_total_rows ) {
1172  char *r = g_strdup_printf ( "%u", state->num_lines );
1173  textbox_text ( state->tb_total_rows, r );
1174  g_free ( r );
1175  }
1176  TICK_N ( "Update filter lines" );
1177 
1178  if ( config.auto_select == TRUE && state->filtered_lines == 1 && state->num_lines > 1 ) {
1179  ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )];
1180  state->retv = MENU_OK;
1181  state->quit = TRUE;
1182  }
1183 
1184  // Size the window.
1185  int height = rofi_view_calculate_height ( state );
1186  if ( height != state->height ) {
1187  state->height = height;
1189  rofi_view_window_update_size ( state );
1190  g_debug ( "Resize based on re-filter" );
1191  }
1192  TICK_N ( "Filter resize window based on window " );
1193  state->refilter = FALSE;
1194  TICK_N ( "Filter done" );
1195 }
1201 void process_result ( RofiViewState *state );
1203 {
1204  if ( state && state->finalize != NULL ) {
1205  state->finalize ( state );
1206  }
1207 }
1208 
1210 {
1211  RofiViewState *state = rofi_view_get_active ();
1212  switch ( action )
1213  {
1214  // Handling of paste
1215  case PASTE_PRIMARY:
1216  xcb_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_PRIMARY,
1217  xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME );
1218  xcb_flush ( xcb->connection );
1219  break;
1220  case PASTE_SECONDARY:
1221  xcb_convert_selection ( xcb->connection, CacheState.main_window, netatoms[CLIPBOARD],
1222  xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME );
1223  xcb_flush ( xcb->connection );
1224  break;
1225  case SCREENSHOT:
1227  break;
1228  case CHANGE_ELLIPSIZE:
1229  if ( state->list_view ) {
1231  }
1232  break;
1233  case TOGGLE_SORT:
1234  if ( state->case_indicator != NULL ) {
1235  config.sort = !config.sort;
1236  state->refilter = TRUE;
1238  }
1239  break;
1240  case MODE_PREVIOUS:
1241  state->retv = MENU_PREVIOUS;
1242  ( state->selected_line ) = 0;
1243  state->quit = TRUE;
1244  break;
1245  // Menu navigation.
1246  case MODE_NEXT:
1247  state->retv = MENU_NEXT;
1248  ( state->selected_line ) = 0;
1249  state->quit = TRUE;
1250  break;
1251  // Toggle case sensitivity.
1253  if ( state->case_indicator != NULL ) {
1255  ( state->selected_line ) = 0;
1256  state->refilter = TRUE;
1258  }
1259  break;
1260  // Special delete entry command.
1261  case DELETE_ENTRY:
1262  {
1263  unsigned int selected = listview_get_selected ( state->list_view );
1264  if ( selected < state->filtered_lines ) {
1265  ( state->selected_line ) = state->line_map[selected];
1266  state->retv = MENU_ENTRY_DELETE;
1267  state->quit = TRUE;
1268  }
1269  break;
1270  }
1271  case SELECT_ELEMENT_1:
1272  case SELECT_ELEMENT_2:
1273  case SELECT_ELEMENT_3:
1274  case SELECT_ELEMENT_4:
1275  case SELECT_ELEMENT_5:
1276  case SELECT_ELEMENT_6:
1277  case SELECT_ELEMENT_7:
1278  case SELECT_ELEMENT_8:
1279  case SELECT_ELEMENT_9:
1280  case SELECT_ELEMENT_10:
1281  {
1282  unsigned int index = action - SELECT_ELEMENT_1;
1283  if ( index < state->filtered_lines ) {
1284  state->selected_line = state->line_map[index];
1285  state->retv = MENU_OK;
1286  state->quit = TRUE;
1287  }
1288  break;
1289  }
1290  case CUSTOM_1:
1291  case CUSTOM_2:
1292  case CUSTOM_3:
1293  case CUSTOM_4:
1294  case CUSTOM_5:
1295  case CUSTOM_6:
1296  case CUSTOM_7:
1297  case CUSTOM_8:
1298  case CUSTOM_9:
1299  case CUSTOM_10:
1300  case CUSTOM_11:
1301  case CUSTOM_12:
1302  case CUSTOM_13:
1303  case CUSTOM_14:
1304  case CUSTOM_15:
1305  case CUSTOM_16:
1306  case CUSTOM_17:
1307  case CUSTOM_18:
1308  case CUSTOM_19:
1309  {
1310  state->selected_line = UINT32_MAX;
1311  unsigned int selected = listview_get_selected ( state->list_view );
1312  if ( selected < state->filtered_lines ) {
1313  ( state->selected_line ) = state->line_map[selected];
1314  }
1315  state->retv = MENU_CUSTOM_COMMAND | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK );
1316  state->quit = TRUE;
1317  break;
1318  }
1319  // If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too
1320  case CANCEL:
1321  state->retv = MENU_CANCEL;
1322  state->quit = TRUE;
1323  break;
1324  case ROW_UP:
1325  listview_nav_up ( state->list_view );
1326  break;
1327  case ROW_TAB:
1328  rofi_view_nav_row_tab ( state );
1329  break;
1330  case ROW_DOWN:
1331  listview_nav_down ( state->list_view );
1332  break;
1333  case ROW_LEFT:
1334  listview_nav_left ( state->list_view );
1335  break;
1336  case ROW_RIGHT:
1337  listview_nav_right ( state->list_view );
1338  break;
1339  case PAGE_PREV:
1340  listview_nav_page_prev ( state->list_view );
1341  break;
1342  case PAGE_NEXT:
1343  listview_nav_page_next ( state->list_view );
1344  break;
1345  case ROW_FIRST:
1346  rofi_view_nav_first ( state );
1347  break;
1348  case ROW_LAST:
1349  rofi_view_nav_last ( state );
1350  break;
1351  case ROW_SELECT:
1352  rofi_view_nav_row_select ( state );
1353  break;
1354  // If you add a binding here, make sure to add it to textbox_keybinding too
1355  case MOVE_CHAR_BACK:
1356  {
1357  if ( textbox_keybinding ( state->text, action ) == 0 ) {
1358  listview_nav_left ( state->list_view );
1359  }
1360  break;
1361  }
1362  case MOVE_CHAR_FORWARD:
1363  {
1364  if ( textbox_keybinding ( state->text, action ) == 0 ) {
1365  listview_nav_right ( state->list_view );
1366  }
1367  break;
1368  }
1369  case CLEAR_LINE:
1370  case MOVE_FRONT:
1371  case MOVE_END:
1372  case REMOVE_TO_EOL:
1373  case REMOVE_TO_SOL:
1374  case REMOVE_WORD_BACK:
1375  case REMOVE_WORD_FORWARD:
1376  case REMOVE_CHAR_FORWARD:
1377  case MOVE_WORD_BACK:
1378  case MOVE_WORD_FORWARD:
1379  case REMOVE_CHAR_BACK:
1380  {
1381  int rc = textbox_keybinding ( state->text, action );
1382  if ( rc == 1 ) {
1383  // Entry changed.
1384  state->refilter = TRUE;
1385  }
1386  else if ( rc == 2 ) {
1387  // Movement.
1388  }
1389  break;
1390  }
1391  case ACCEPT_ALT:
1392  {
1393  unsigned int selected = listview_get_selected ( state->list_view );
1394  state->selected_line = UINT32_MAX;
1395  if ( selected < state->filtered_lines ) {
1396  ( state->selected_line ) = state->line_map[selected];
1397  state->retv = MENU_OK;
1398  }
1399  else {
1400  // Nothing entered and nothing selected.
1401  state->retv = MENU_CUSTOM_INPUT;
1402  }
1403  state->retv |= MENU_CUSTOM_ACTION;
1404  state->quit = TRUE;
1405  break;
1406  }
1407  case ACCEPT_CUSTOM:
1408  {
1409  state->selected_line = UINT32_MAX;
1410  state->retv = MENU_CUSTOM_INPUT;
1411  state->quit = TRUE;
1412  break;
1413  }
1414  case ACCEPT_ENTRY:
1415  {
1416  // If a valid item is selected, return that..
1417  unsigned int selected = listview_get_selected ( state->list_view );
1418  state->selected_line = UINT32_MAX;
1419  if ( selected < state->filtered_lines ) {
1420  ( state->selected_line ) = state->line_map[selected];
1421  state->retv = MENU_OK;
1422  }
1423  else {
1424  // Nothing entered and nothing selected.
1425  state->retv = MENU_CUSTOM_INPUT;
1426  }
1427 
1428  state->quit = TRUE;
1429  break;
1430  }
1431  }
1432 }
1433 
1434 gboolean rofi_view_trigger_action ( RofiViewState *state, BindingsScope scope, guint action )
1435 {
1436  switch ( scope )
1437  {
1438  case SCOPE_GLOBAL:
1440  return TRUE;
1441  case SCOPE_MOUSE_LISTVIEW:
1443  case SCOPE_MOUSE_EDITBOX:
1444  case SCOPE_MOUSE_SCROLLBAR:
1446  {
1447  gint x = state->mouse.x, y = state->mouse.y;
1448  widget *target = widget_find_mouse_target ( WIDGET ( state->main_window ), scope, x, y );
1449  if ( target == NULL ) {
1450  return FALSE;
1451  }
1452  widget_xy_to_relative ( target, &x, &y );
1453  switch ( widget_trigger_action ( target, action, x, y ) )
1454  {
1456  return FALSE;
1458  target = NULL;
1459  /* FALLTHRU */
1461  state->mouse.motion_target = target;
1462  /* FALLTHRU */
1464  return TRUE;
1465  }
1466  break;
1467  }
1468  }
1469  return FALSE;
1470 }
1471 
1472 void rofi_view_handle_text ( RofiViewState *state, char *text )
1473 {
1474  if ( textbox_append_text ( state->text, text, strlen ( text ) ) ) {
1475  state->refilter = TRUE;
1476  }
1477 }
1478 
1479 void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y )
1480 {
1481  state->mouse.x = x;
1482  state->mouse.y = y;
1483  if ( state->mouse.motion_target != NULL ) {
1484  widget_xy_to_relative ( state->mouse.motion_target, &x, &y );
1485  widget_motion_notify ( state->mouse.motion_target, x, y );
1486  }
1487 }
1488 
1490 {
1491  if ( rofi_view_get_completed ( state ) ) {
1492  // This menu is done.
1493  rofi_view_finalize ( state );
1494  // If there a state. (for example error) reload it.
1495  state = rofi_view_get_active ();
1496 
1497  // cleanup, if no more state to display.
1498  if ( state == NULL ) {
1499  // Quit main-loop.
1501  return;
1502  }
1503  }
1504 
1505  // Update if requested.
1506  if ( state->refilter ) {
1507  rofi_view_refilter ( state );
1508  }
1509  rofi_view_update ( state, TRUE );
1510 }
1511 
1516 void rofi_view_temp_configure_notify ( RofiViewState *state, xcb_configure_notify_event_t *xce )
1517 {
1518  if ( xce->window == CacheState.main_window ) {
1519  if ( state->x != xce->x || state->y != xce->y ) {
1520  state->x = xce->x;
1521  state->y = xce->y;
1522  widget_queue_redraw ( WIDGET ( state->main_window ) );
1523  }
1524  if ( state->width != xce->width || state->height != xce->height ) {
1525  state->width = xce->width;
1526  state->height = xce->height;
1527 
1528  cairo_destroy ( CacheState.edit_draw );
1529  cairo_surface_destroy ( CacheState.edit_surf );
1530 
1531  xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap );
1532  CacheState.edit_pixmap = xcb_generate_id ( xcb->connection );
1533  xcb_create_pixmap ( xcb->connection, depth->depth, CacheState.edit_pixmap, CacheState.main_window,
1534  state->width, state->height );
1535 
1536  CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height );
1537  CacheState.edit_draw = cairo_create ( CacheState.edit_surf );
1538  g_debug ( "Re-size window based external request: %d %d", state->width, state->height );
1539  widget_resize ( WIDGET ( state->main_window ), state->width, state->height );
1540  }
1541  }
1542 }
1543 
1547 void rofi_view_temp_click_to_exit ( RofiViewState *state, xcb_window_t target )
1548 {
1549  if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == 0 ) {
1550  if ( target != CacheState.main_window ) {
1551  state->quit = TRUE;
1552  state->retv = MENU_CANCEL;
1553  }
1554  }
1555 }
1556 
1558 {
1559  if ( CacheState.repaint_source == 0 ) {
1560  CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, rofi_view_repaint, NULL, NULL );
1561  }
1562 }
1563 
1565 {
1566  if ( CacheState.fullscreen == TRUE ) {
1567  return CacheState.mon.h;
1568  }
1569 
1570  RofiDistance h = rofi_theme_get_distance ( WIDGET ( state->main_window ), "height", 0 );
1571  unsigned int height = distance_get_pixel ( h, ROFI_ORIENTATION_VERTICAL );
1572  // If height is set, return it.
1573  if ( height > 0 ) {
1574  return height;
1575  }
1576  // Autosize based on widgets.
1577  widget *main_window = WIDGET ( state->main_window );
1579 }
1580 
1581 static WidgetTriggerActionResult textbox_button_trigger_action ( widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data )
1582 {
1583  RofiViewState *state = ( RofiViewState *) user_data;
1584  switch ( action )
1585  {
1586  case MOUSE_CLICK_DOWN:
1587  {
1588  const char * type = rofi_theme_get_string ( wid, "action", "ok" );
1589  ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )];
1590  if ( strcmp ( type, "ok" ) == 0 ) {
1591  state->retv = MENU_OK;
1592  }
1593  else if ( strcmp ( type, "ok|alternate" ) == 0 ) {
1594  state->retv = MENU_CUSTOM_ACTION | MENU_OK;
1595  }
1596  else if ( strcmp ( type, "custom" ) ) {
1597  state->retv = MENU_CUSTOM_INPUT;
1598  }
1599  else if ( strcmp ( type, "custom|alternate" ) == 0 ) {
1601  }
1602  else {
1603  g_warning ( "Invalid action specified." );
1605  }
1606  state->quit = TRUE;
1607  state->skip_absorb = TRUE;
1609  }
1610  case MOUSE_CLICK_UP:
1611  case MOUSE_DCLICK_DOWN:
1612  case MOUSE_DCLICK_UP:
1613  break;
1614  }
1616 }
1617 static WidgetTriggerActionResult textbox_sidebar_modi_trigger_action ( widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data )
1618 {
1619  RofiViewState *state = ( RofiViewState *) user_data;
1620  unsigned int i;
1621  for ( i = 0; i < state->num_modi; i++ ) {
1622  if ( WIDGET ( state->modi[i] ) == wid ) {
1623  break;
1624  }
1625  }
1626  if ( i == state->num_modi ) {
1628  }
1629 
1630  switch ( action )
1631  {
1632  case MOUSE_CLICK_DOWN:
1633  state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK );
1634  state->quit = TRUE;
1635  state->skip_absorb = TRUE;
1637  case MOUSE_CLICK_UP:
1638  case MOUSE_DCLICK_DOWN:
1639  case MOUSE_DCLICK_UP:
1640  break;
1641  }
1643 }
1644 
1645 // @TODO don't like this construction.
1646 static void rofi_view_listview_mouse_activated_cb ( listview *lv, gboolean custom, void *udata )
1647 {
1648  RofiViewState *state = (RofiViewState *) udata;
1649  state->retv = MENU_OK;
1650  if ( custom ) {
1651  state->retv |= MENU_CUSTOM_ACTION;
1652  }
1653  ( state->selected_line ) = state->line_map[listview_get_selected ( lv )];
1654  // Quit
1655  state->quit = TRUE;
1656  state->skip_absorb = TRUE;
1657 }
1658 
1659 static void rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, const char *name )
1660 {
1661  char *defaults = NULL;
1662  widget *wid = NULL;
1663 
1667  if ( strcmp ( name, "mainbox" ) == 0 ) {
1668  wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL );
1669  box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE );
1670  if ( config.sidebar_mode ) {
1671  defaults = "inputbar,message,listview,mode-switcher";
1672  }
1673  else {
1674  defaults = "inputbar,message,listview";
1675  }
1676  }
1680  else if ( strcmp ( name, "inputbar" ) == 0 ) {
1681  wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL );
1682  defaults = "prompt,entry,overlay,case-indicator";
1683  box_add ( (box *) parent_widget, WIDGET ( wid ), FALSE );
1684  }
1688  else if ( strcmp ( name, "prompt" ) == 0 ) {
1689  if ( state->prompt != NULL ) {
1690  g_error ( "Prompt widget can only be added once to the layout." );
1691  return;
1692  }
1693  // Prompt box.
1694  state->prompt = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 );
1695  rofi_view_update_prompt ( state );
1696  box_add ( (box *) parent_widget, WIDGET ( state->prompt ), FALSE );
1697  defaults = NULL;
1698  }
1699  else if ( strcmp ( name, "num-rows" ) == 0 ) {
1700  state->tb_total_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 );
1701  box_add ( (box *) parent_widget, WIDGET ( state->tb_total_rows ), FALSE );
1702  defaults = NULL;
1703  }
1704  else if ( strcmp ( name, "num-filtered-rows" ) == 0 ) {
1705  state->tb_filtered_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 );
1706  box_add ( (box *) parent_widget, WIDGET ( state->tb_filtered_rows ), FALSE );
1707  defaults = NULL;
1708  }
1712  else if ( strcmp ( name, "case-indicator" ) == 0 ) {
1713  if ( state->case_indicator != NULL ) {
1714  g_error ( "Case indicator widget can only be added once to the layout." );
1715  return;
1716  }
1717  state->case_indicator = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "*", 0, 0 );
1718  // Add small separator between case indicator and text box.
1719  box_add ( (box *) parent_widget, WIDGET ( state->case_indicator ), FALSE );
1721  }
1725  else if ( strcmp ( name, "entry" ) == 0 ) {
1726  if ( state->text != NULL ) {
1727  g_error ( "Entry textbox widget can only be added once to the layout." );
1728  return;
1729  }
1730  // Entry box
1731  TextboxFlags tfl = TB_EDITABLE;
1732  tfl |= ( ( state->menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0;
1733  state->text = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, tfl | TB_AUTOHEIGHT, NORMAL, NULL, 0, 0 );
1734  box_add ( (box *) parent_widget, WIDGET ( state->text ), TRUE );
1735  }
1739  else if ( strcmp ( name, "message" ) == 0 ) {
1740  if ( state->mesg_box != NULL ) {
1741  g_error ( "Message widget can only be added once to the layout." );
1742  return;
1743  }
1744  state->mesg_box = container_create ( parent_widget, name );
1745  state->mesg_tb = textbox_create ( WIDGET ( state->mesg_box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL, 0, 0 );
1746  container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) );
1747  rofi_view_reload_message_bar ( state );
1748  box_add ( (box *) parent_widget, WIDGET ( state->mesg_box ), FALSE );
1749  }
1753  else if ( strcmp ( name, "listview" ) == 0 ) {
1754  if ( state->list_view != NULL ) {
1755  g_error ( "Listview widget can only be added once to the layout." );
1756  return;
1757  }
1758  state->list_view = listview_create ( parent_widget, name, update_callback, state, config.element_height, 0 );
1759  box_add ( (box *) parent_widget, WIDGET ( state->list_view ), TRUE );
1760  // Set configuration
1764 
1765  int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines );
1766  listview_set_num_lines ( state->list_view, lines );
1767  listview_set_max_lines ( state->list_view, state->num_lines );
1768  }
1772  else if ( strcmp ( name, "mode-switcher" ) == 0 || strcmp ( name, "sidebar" ) == 0 ) {
1773  if ( state->sidebar_bar != NULL ) {
1774  g_error ( "Mode-switcher can only be added once to the layout." );
1775  return;
1776  }
1777  state->sidebar_bar = box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL );
1778  box_add ( (box *) parent_widget, WIDGET ( state->sidebar_bar ), FALSE );
1779  state->num_modi = rofi_get_num_enabled_modi ();
1780  state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) );
1781  for ( unsigned int j = 0; j < state->num_modi; j++ ) {
1782  const Mode * mode = rofi_get_mode ( j );
1783  state->modi[j] = textbox_create ( WIDGET ( state->sidebar_bar ), WIDGET_TYPE_MODE_SWITCHER, "button", TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL,
1784  mode_get_display_name ( mode ), 0.5, 0.5 );
1785  box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE );
1787  }
1788  }
1789  else if ( g_ascii_strcasecmp ( name, "overlay" ) == 0 ) {
1790  state->overlay = textbox_create ( WIDGET ( parent_widget ), WIDGET_TYPE_TEXTBOX_TEXT, "overlay", TB_AUTOWIDTH | TB_AUTOHEIGHT, URGENT, "blaat", 0.5, 0 );
1791  box_add ( (box *) parent_widget, WIDGET ( state->overlay ), FALSE );
1792  widget_disable ( WIDGET ( state->overlay ) );
1793  }
1794  else if ( g_ascii_strncasecmp ( name, "textbox", 7 ) == 0 ) {
1795  textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 );
1796  box_add ( (box *) parent_widget, WIDGET ( t ), TRUE );
1797  }
1798  else if ( g_ascii_strncasecmp ( name, "button", 6 ) == 0 ) {
1799  textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 );
1800  box_add ( (box *) parent_widget, WIDGET ( t ), TRUE );
1802  }
1803  else if ( g_ascii_strncasecmp ( name, "icon", 4 ) == 0 ) {
1804  icon *t = icon_create ( parent_widget, name );
1805  box_add ( (box *) parent_widget, WIDGET ( t ), TRUE );
1806  }
1807  else {
1808  wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL );
1809  box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE );
1810  //g_error("The widget %s does not exists. Invalid layout.", name);
1811  }
1812  if ( wid ) {
1813  GList *list = rofi_theme_get_list ( wid, "children", defaults );
1814  for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) {
1815  rofi_view_add_widget ( state, wid, (const char *) iter->data );
1816  }
1817  g_list_free_full ( list, g_free );
1818  }
1819 }
1820 
1822  const char *input,
1823  MenuFlags menu_flags,
1824  void ( *finalize )( RofiViewState * ) )
1825 {
1826  TICK ();
1828  state->menu_flags = menu_flags;
1829  state->sw = sw;
1830  state->selected_line = UINT32_MAX;
1831  state->retv = MENU_CANCEL;
1832  state->distance = NULL;
1833  state->quit = FALSE;
1834  state->skip_absorb = FALSE;
1835  //We want to filter on the first run.
1836  state->refilter = TRUE;
1837  state->finalize = finalize;
1838  state->mouse_seen = FALSE;
1839 
1840  // Request the lines to show.
1841  state->num_lines = mode_get_num_entries ( sw );
1842 
1843  if ( state->sw ) {
1844  char * title = g_strdup_printf ( "rofi - %s", mode_get_display_name ( state->sw ) );
1845  rofi_view_set_window_title ( title );
1846  g_free ( title );
1847  }
1848  else {
1849  rofi_view_set_window_title ( "rofi" );
1850  }
1851  TICK_N ( "Startup notification" );
1852 
1853  // Get active monitor size.
1854  TICK_N ( "Get active monitor" );
1855 
1856  state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL );
1857  // Get children.
1858  GList *list = rofi_theme_get_list ( WIDGET ( state->main_window ), "children", "mainbox" );
1859  for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) {
1860  rofi_view_add_widget ( state, WIDGET ( state->main_window ), (const char *) iter->data );
1861  }
1862  g_list_free_full ( list, g_free );
1863 
1864  if ( state->text && input ) {
1865  textbox_text ( state->text, input );
1866  textbox_cursor_end ( state->text );
1867  }
1868 
1869  // filtered list
1870  state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) );
1871  state->distance = (int *) g_malloc0_n ( state->num_lines, sizeof ( int ) );
1872 
1874  // Need to resize otherwise calculated desired height is wrong.
1875  widget_resize ( WIDGET ( state->main_window ), state->width, 100 );
1876  // Only needed when window is fixed size.
1877  if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) {
1879  }
1880 
1881  state->height = rofi_view_calculate_height ( state );
1882  // Move the window to the correct x,y position.
1884  rofi_view_window_update_size ( state );
1885 
1886  state->quit = FALSE;
1887  rofi_view_refilter ( state );
1888  rofi_view_update ( state, TRUE );
1889  xcb_map_window ( xcb->connection, CacheState.main_window );
1890  widget_queue_redraw ( WIDGET ( state->main_window ) );
1891  xcb_flush ( xcb->connection );
1892  if ( xcb->sncontext != NULL ) {
1893  sn_launchee_context_complete ( xcb->sncontext );
1894  }
1895  return state;
1896 }
1897 
1898 int rofi_view_error_dialog ( const char *msg, int markup )
1899 {
1901  state->retv = MENU_CANCEL;
1902  state->menu_flags = MENU_ERROR_DIALOG;
1903  state->finalize = process_result;
1904 
1905  state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL );
1906  box *box = box_create ( WIDGET ( state->main_window ), "error-message", ROFI_ORIENTATION_VERTICAL );
1907  box_add ( state->main_window, WIDGET ( box ), TRUE );
1908  state->text = textbox_create ( WIDGET ( box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ),
1909  NORMAL, ( msg != NULL ) ? msg : "", 0, 0 );
1910  box_add ( box, WIDGET ( state->text ), TRUE );
1911 
1912  // Make sure we enable fixed num lines when in normal window mode.
1913  if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) {
1915  }
1917  // Need to resize otherwise calculated desired height is wrong.
1918  widget_resize ( WIDGET ( state->main_window ), state->width, 100 );
1919  // resize window vertically to suit
1920  state->height = widget_get_desired_height ( WIDGET ( state->main_window ) );
1921 
1922  // Calculte window position.
1924 
1925  // Move the window to the correct x,y position.
1926  rofi_view_window_update_size ( state );
1927 
1928  // Display it.
1929  xcb_map_window ( xcb->connection, CacheState.main_window );
1930  widget_queue_redraw ( WIDGET ( state->main_window ) );
1931 
1932  if ( xcb->sncontext != NULL ) {
1933  sn_launchee_context_complete ( xcb->sncontext );
1934  }
1935 
1936  // Set it as current window.
1937  rofi_view_set_active ( state );
1938  return TRUE;
1939 }
1940 
1941 void rofi_view_hide ( void )
1942 {
1943  if ( CacheState.main_window != XCB_WINDOW_NONE ) {
1944  xcb_unmap_window ( xcb->connection, CacheState.main_window );
1946  }
1947 }
1948 
1950 {
1951  g_debug ( "Cleanup." );
1952  if ( CacheState.idle_timeout > 0 ) {
1953  g_source_remove ( CacheState.idle_timeout );
1954  CacheState.idle_timeout = 0;
1955  }
1956  if ( CacheState.repaint_source > 0 ) {
1957  g_source_remove ( CacheState.repaint_source );
1958  CacheState.repaint_source = 0;
1959  }
1960  if ( CacheState.fake_bg ) {
1961  cairo_surface_destroy ( CacheState.fake_bg );
1962  CacheState.fake_bg = NULL;
1963  }
1964  if ( CacheState.edit_draw ) {
1965  cairo_destroy ( CacheState.edit_draw );
1966  CacheState.edit_draw = NULL;
1967  }
1968  if ( CacheState.edit_surf ) {
1969  cairo_surface_destroy ( CacheState.edit_surf );
1970  CacheState.edit_surf = NULL;
1971  }
1972  if ( CacheState.main_window != XCB_WINDOW_NONE ) {
1973  g_debug ( "Unmapping and free'ing window" );
1974  xcb_unmap_window ( xcb->connection, CacheState.main_window );
1975  xcb_free_gc ( xcb->connection, CacheState.gc );
1976  xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap );
1977  xcb_destroy_window ( xcb->connection, CacheState.main_window );
1978  CacheState.main_window = XCB_WINDOW_NONE;
1979  }
1980  if ( map != XCB_COLORMAP_NONE ) {
1981  xcb_free_colormap ( xcb->connection, map );
1982  map = XCB_COLORMAP_NONE;
1983  }
1984  xcb_flush ( xcb->connection );
1985  g_assert ( g_queue_is_empty ( &( CacheState.views ) ) );
1986 }
1988 {
1989  TICK_N ( "Setup Threadpool, start" );
1990  if ( config.threads == 0 ) {
1991  config.threads = 1;
1992  long procs = sysconf ( _SC_NPROCESSORS_CONF );
1993  if ( procs > 0 ) {
1994  config.threads = MIN ( procs, 128l );
1995  }
1996  }
1997  // Create thread pool
1998  GError *error = NULL;
1999  tpool = g_thread_pool_new ( rofi_view_call_thread, NULL, config.threads, FALSE, &error );
2000  if ( error == NULL ) {
2001  // Idle threads should stick around for a max of 60 seconds.
2002  g_thread_pool_set_max_idle_time ( 60000 );
2003  // We are allowed to have
2004  g_thread_pool_set_max_threads ( tpool, config.threads, &error );
2005  }
2006  // If error occurred during setup of pool, tell user and exit.
2007  if ( error != NULL ) {
2008  g_warning ( "Failed to setup thread pool: '%s'", error->message );
2009  g_error_free ( error );
2010  exit ( EXIT_FAILURE );
2011  }
2012  TICK_N ( "Setup Threadpool, done" );
2013 }
2015 {
2016  if ( tpool ) {
2017  g_thread_pool_free ( tpool, TRUE, TRUE );
2018  tpool = NULL;
2019  }
2020 }
2022 {
2023  return state->sw;
2024 }
2025 
2026 void rofi_view_set_overlay ( RofiViewState *state, const char *text )
2027 {
2028  if ( state->overlay == NULL || state->list_view == NULL ) {
2029  return;
2030  }
2031  if ( text == NULL ) {
2032  widget_disable ( WIDGET ( state->overlay ) );
2033  return;
2034  }
2035  widget_enable ( WIDGET ( state->overlay ) );
2036  textbox_text ( state->overlay, text );
2037  // We want to queue a repaint.
2039 }
2040 
2042 {
2043  if ( state->text ) {
2044  textbox_text ( state->text, "" );
2045  rofi_view_set_selected_line ( state, 0 );
2046  }
2047 }
2048 
2050 {
2052 }
2053 
2055 {
2056  state->sw = mode;
2057  // Update prompt;
2058  if ( state->prompt ) {
2059  rofi_view_update_prompt ( state );
2060  }
2061  if ( state->sw ) {
2062  char * title = g_strdup_printf ( "rofi - %s", mode_get_display_name ( state->sw ) );
2063  rofi_view_set_window_title ( title );
2064  g_free ( title );
2065  }
2066  else {
2067  rofi_view_set_window_title ( "rofi" );
2068  }
2069  if ( state->sidebar_bar ) {
2070  for ( unsigned int j = 0; j < state->num_modi; j++ ) {
2071  const Mode * mode = rofi_get_mode ( j );
2072  textbox_font ( state->modi[j], ( mode == state->sw ) ? HIGHLIGHT : NORMAL );
2073  }
2074  }
2075  rofi_view_restart ( state );
2076  state->reload = TRUE;
2077  state->refilter = TRUE;
2078  rofi_view_refilter ( state );
2079  rofi_view_update ( state, TRUE );
2080 }
2081 
2082 xcb_window_t rofi_view_get_window ( void )
2083 {
2084  return CacheState.main_window;
2085 }
2086 
2087 void rofi_view_set_window_title ( const char * title )
2088 {
2089  ssize_t len = strlen ( title );
2090  xcb_change_property ( xcb->connection, XCB_PROP_MODE_REPLACE, CacheState.main_window, xcb->ewmh._NET_WM_NAME, xcb->ewmh.UTF8_STRING, 8, len, title );
2091  xcb_change_property ( xcb->connection, XCB_PROP_MODE_REPLACE, CacheState.main_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, len, title );
2092 }
void rofi_view_cleanup()
Definition: view.c:1949
xcb_depth_t * depth
Definition: xcb.c:91
unsigned int * line_map
Definition: view-internal.h:75
void listview_set_selected(listview *lv, unsigned int selected)
Definition: listview.c:536
unsigned int levenshtein(const char *needle, const glong needlelen, const char *haystack, const glong haystacklen)
Definition: helper.c:744
static const int loc_transtable[9]
Definition: view.c:305
Definition: box.c:41
static gboolean bench_update(void)
Definition: view.c:243
guint idle_timeout
Definition: view.c:114
MenuReturn
Definition: mode.h:66
void textbox_cursor_end(textbox *tb)
Definition: textbox.c:582
GList * rofi_theme_get_list(const widget *widget, const char *property, const char *defaults)
Definition: theme.c:906
void rofi_view_get_current_monitor(int *width, int *height)
Definition: view.c:135
textbox ** modi
cairo_surface_t * fake_bg
Definition: view.c:96
MouseBindingMouseDefaultAction
Definition: keyb.h:167
unsigned int auto_select
Definition: settings.h:145
void rofi_quit_main_loop(void)
Definition: rofi.c:664
unsigned int fake_transparency
Definition: settings.h:169
void rofi_view_finalize(RofiViewState *state)
Definition: view.c:1202
gboolean widget_need_redraw(widget *wid)
Definition: widget.c:422
BindingsScope
Definition: keyb.h:42
listview * listview_create(widget *parent, const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse)
Definition: listview.c:643
static WidgetTriggerActionResult textbox_button_trigger_action(widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data)
Definition: view.c:1581
void display_early_cleanup(void)
Definition: xcb.c:1396
int mode_token_match(const Mode *mode, rofi_int_matcher **tokens, unsigned int selected_line)
Definition: mode.c:116
void rofi_view_clear_input(RofiViewState *state)
Definition: view.c:2041
WindowLocation location
Definition: settings.h:103
unsigned int case_sensitive
Definition: settings.h:135
static void rofi_view_nav_last(RofiViewState *state)
Definition: view.c:977
Definition: mode.h:69
void x11_disable_decoration(xcb_window_t window)
Definition: xcb.c:1431
void textbox_text(textbox *tb, const char *text)
Definition: textbox.c:312
struct _thread_state_view thread_state_view
const char * pattern
Definition: view.c:631
uint64_t draws
Definition: view.c:233
void rofi_view_reload(void)
Definition: view.c:469
unsigned int sidebar_mode
Definition: settings.h:141
PangoAttrList * helper_token_match_get_pango_attr(RofiHighlightColorStyle th, rofi_int_matcher **tokens, const char *input, PangoAttrList *retv)
Definition: helper.c:414
Definition: keyb.h:106
void rofi_view_temp_configure_notify(RofiViewState *state, xcb_configure_notify_event_t *xce)
Definition: view.c:1516
void listview_nav_up(listview *lv)
Definition: listview.c:729
Definition: keyb.h:99
Definition: xcb.h:99
void listview_nav_page_prev(listview *lv)
Definition: listview.c:844
void rofi_view_workers_finalize(void)
Definition: view.c:2014
void rofi_view_queue_redraw(void)
Definition: view.c:476
unsigned int menu_lines
Definition: settings.h:68
xcb_window_t xcb_stuff_get_root_window(void)
Definition: xcb.c:1391
Mode * rofi_view_get_mode(RofiViewState *state)
Definition: view.c:2021
double textbox_get_estimated_char_width(void)
Definition: textbox.c:895
void listview_set_num_elements(listview *lv, unsigned int rows)
Definition: listview.c:513
KeyBindingAction prev_action
Definition: view-internal.h:83
int widget_padding_get_padding_width(const widget *wid)
Definition: widget.c:544
cairo_surface_t * mode_get_icon(const Mode *mode, unsigned int selected_line, int height)
Definition: mode.c:72
unsigned int start
Definition: view.c:624
Definition: keyb.h:108
static void rofi_view_update_prompt(RofiViewState *state)
Definition: view.c:285
xcb_stuff * xcb
Definition: xcb.c:86
char * mode_preprocess_input(Mode *mode, const char *input)
Definition: mode.c:168
const char * rofi_view_get_user_input(const RofiViewState *state)
Definition: view.c:588
void __create_window(MenuFlags menu_flags)
Definition: view.c:731
const char * rofi_theme_get_string(const widget *widget, const char *property, const char *def)
Definition: theme.c:818
unsigned int sort
Definition: settings.h:117
static void rofi_view_setup_fake_transparency(const char *const fake_background)
Definition: view.c:681
unsigned int scroll_method
Definition: settings.h:174
void icon_set_surface(icon *icon, cairo_surface_t *surf)
Definition: icon.c:125
void listview_set_scroll_type(listview *lv, ScrollType type)
Definition: listview.c:913
void widget_free(widget *wid)
Definition: widget.c:339
Definition: keyb.h:102
GMutex * mutex
Definition: view.c:617
Definition: keyb.h:98
thread_state st
Definition: view.c:612
Definition: keyb.h:101
static void rofi_view_nav_first(RofiViewState *state)
Definition: view.c:966
static void update_callback(textbox *t, icon *ico, unsigned int index, void *udata, TextBoxFontType *type, gboolean full)
Definition: view.c:987
static WidgetTriggerActionResult textbox_sidebar_modi_trigger_action(widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data)
Definition: view.c:1617
void rofi_view_set_selected_line(RofiViewState *state, unsigned int selected_line)
Definition: view.c:528
#define color_green
Definition: rofi.h:96
void rofi_view_remove_active(RofiViewState *state)
Definition: view.c:496
Definition: keyb.h:117
unsigned int num_lines
Definition: view-internal.h:77
void rofi_view_temp_click_to_exit(RofiViewState *state, xcb_window_t target)
Definition: view.c:1547
void textbox_font(textbox *tb, TextBoxFontType tbft)
Definition: textbox.c:232
textbox * prompt
Definition: view-internal.h:57
Definition: keyb.h:109
char * text
Definition: textbox.h:66
int rofi_theme_get_boolean(const widget *widget, const char *property, int def)
Definition: theme.c:785
RofiViewState * rofi_view_get_active(void)
Definition: view.c:491
static void rofi_view_refilter(RofiViewState *state)
Definition: view.c:1084
void widget_set_trigger_action_handler(widget *wid, widget_trigger_action_cb cb, void *cb_data)
Definition: widget.c:462
RofiDistance rofi_theme_get_distance(const widget *widget, const char *property, int def)
Definition: theme.c:763
void widget_draw(widget *widget, cairo_t *d)
Definition: widget.c:185
unsigned int rofi_get_num_enabled_modi(void)
Definition: rofi.c:131
textbox * textbox_create(widget *parent, WidgetType type, const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text, double xalign, double yalign)
Definition: textbox.c:162
cairo_surface_t * x11_helper_get_bg_surface(void)
Definition: xcb.c:210
static void filter_elements(thread_state *ts, G_GNUC_UNUSED gpointer user_data)
Definition: view.c:647
MenuReturn rofi_view_get_return_value(const RofiViewState *state)
Definition: view.c:563
int fake_bgrel
Definition: view.c:106
void listview_nav_right(listview *lv)
Definition: listview.c:768
textbox * tb_total_rows
xcb_ewmh_connection_t ewmh
Definition: xcb-internal.h:48
char * fake_background
Definition: settings.h:177
void rofi_view_workers_initialize(void)
Definition: view.c:1987
void listview_set_mouse_activated_cb(listview *lv, listview_mouse_activated_cb cb, void *udata)
Definition: listview.c:920
void listview_set_num_lines(listview *lv, unsigned int num_lines)
Definition: listview.c:933
int dpi
Definition: settings.h:171
void rofi_view_frame_callback(void)
Definition: view.c:1557
rofi_int_matcher ** helper_tokenize(const char *input, int case_sensitive)
Definition: helper.c:260
xcb_pixmap_t edit_pixmap
Definition: view.c:100
void rofi_view_free(RofiViewState *state)
Definition: view.c:544
Definition: keyb.h:69
int y_offset
Definition: settings.h:107
RofiViewState * current_active_menu
Definition: view.c:86
void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y)
Definition: view.c:1479
textbox * mesg_tb
Definition: view-internal.h:70
void listview_set_ellipsize_start(listview *lv)
Definition: listview.c:968
static void rofi_view_nav_row_select(RofiViewState *state)
Definition: view.c:945
RofiHighlightColorStyle rofi_theme_get_highlight(widget *widget, const char *property, RofiHighlightColorStyle th)
Definition: theme.c:932
Definition: icon.c:40
static void widget_enable(widget *widget)
Definition: widget.h:185
TextboxFlags
Definition: textbox.h:90
void widget_xy_to_relative(widget *widget, gint *x, gint *y)
Definition: widget.c:387
void rofi_view_hide(void)
Definition: view.c:1941
xcb_window_t rofi_view_get_window(void)
Definition: view.c:2082
void widget_resize(widget *widget, short w, short h)
Definition: widget.c:123
unsigned long long count
Definition: view.c:116
xcb_window_t main_window
Definition: view.c:94
void rofi_view_update(RofiViewState *state, gboolean qr)
Definition: view.c:1034
WidgetTriggerActionResult widget_trigger_action(widget *wid, guint action, gint x, gint y)
Definition: widget.c:451
double min
Definition: view.c:235
unsigned int rofi_view_get_next_position(const RofiViewState *state)
Definition: view.c:573
int menu_width
Definition: settings.h:66
static void rofi_view_listview_mouse_activated_cb(listview *lv, gboolean custom, void *udata)
Definition: view.c:1646
void widget_queue_redraw(widget *wid)
Definition: widget.c:408
int x_offset
Definition: settings.h:109
Definition: keyb.h:111
Definition: keyb.h:113
MenuReturn retv
Definition: view-internal.h:94
Definition: keyb.h:115
unsigned int listview_get_selected(listview *lv)
Definition: listview.c:528
unsigned int * acount
Definition: view.c:619
int textbox_keybinding(textbox *tb, KeyBindingAction action)
Definition: textbox.c:719
struct @1 BenchMark
icon * icon_create(widget *parent, const char *name)
Definition: icon.c:139
void rofi_view_handle_text(RofiViewState *state, char *text)
Definition: view.c:1472
const char * textbox_get_visible_text(const textbox *tb)
Definition: textbox.c:289
unsigned int rofi_view_get_completed(const RofiViewState *state)
Definition: view.c:583
void rofi_view_set_active(RofiViewState *state)
Definition: view.c:505
guint repaint_source
Definition: view.c:118
container * mesg_box
Definition: view-internal.h:68
int rofi_view_error_dialog(const char *msg, int markup)
Definition: view.c:1898
textbox * text
Definition: view-internal.h:59
#define TICK()
Definition: timings.h:64
void rofi_view_set_window_title(const char *title)
Definition: view.c:2087
box * box_create(widget *parent, const char *name, RofiOrientation type)
Definition: box.c:343
gboolean rofi_view_trigger_action(RofiViewState *state, BindingsScope scope, guint action)
Definition: view.c:1434
static void rofi_view_nav_row_tab(RofiViewState *state)
Definition: view.c:920
TextBoxFontType
Definition: textbox.h:103
static char * get_matching_state(void)
Definition: view.c:144
static void rofi_view_window_update_size(RofiViewState *state)
Definition: view.c:416
char * menu_font
Definition: settings.h:72
cairo_t * edit_draw
Definition: view.c:104
GQueue views
Definition: view.c:110
void listview_set_multi_select(listview *lv, gboolean enable)
Definition: listview.c:927
GThreadPool * tpool
Definition: view.c:83
static void rofi_view_calculate_window_position(RofiViewState *state)
Definition: view.c:316
char * rofi_expand_path(const char *input)
Definition: helper.c:711
gboolean show_match
Definition: settings.h:182
xcb_connection_t * connection
Definition: xcb-internal.h:47
KeyBindingAction
Definition: keyb.h:58
xcb_colormap_t map
Definition: xcb.c:93
void process_result(RofiViewState *state)
Definition: rofi.c:208
void listview_set_fixed_num_lines(listview *lv)
Definition: listview.c:961
SortingMethod sorting_method_enum
Definition: settings.h:119
Definition: keyb.h:94
double last_ts
Definition: view.c:234
MenuFlags flags
Definition: view.c:108
void rofi_view_switch_mode(RofiViewState *state, Mode *mode)
Definition: view.c:2054
unsigned int mode_get_num_entries(const Mode *mode)
Definition: mode.c:56
void(* finalize)(struct RofiViewState *state)
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
Definition: theme.c:1026
static int rofi_view_calculate_height(RofiViewState *state)
Definition: view.c:1564
char * mode_get_display_value(const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry)
Definition: mode.c:63
textbox * case_indicator
Definition: view-internal.h:61
int monitor_active(workarea *mon)
Definition: xcb.c:750
gboolean fullscreen
Definition: view.c:120
xcb_visualtype_t * visual
Definition: xcb.c:92
unsigned int filtered_lines
Definition: view-internal.h:80
int element_height
Definition: settings.h:139
void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms, int count)
Definition: xcb.c:247
WidgetTriggerActionResult
Definition: widget.h:77
void listview_nav_page_next(listview *lv)
Definition: listview.c:856
GCond * cond
Definition: view.c:615
PangoAttrList * textbox_get_pango_attributes(textbox *tb)
Definition: textbox.c:296
void rofi_view_ellipsize_start(RofiViewState *state)
Definition: view.c:2049
static void rofi_view_add_widget(RofiViewState *state, widget *parent_widget, const char *name)
Definition: view.c:1659
void rofi_view_restart(RofiViewState *state)
Definition: view.c:485
static void rofi_view_reload_message_bar(RofiViewState *state)
Definition: view.c:442
xcb_screen_t * screen
Definition: xcb-internal.h:49
#define WIDGET(a)
Definition: widget.h:115
void helper_tokenize_free(rofi_int_matcher **tokens)
Definition: helper.c:125
void(* callback)(struct _thread_state *t, gpointer data)
Definition: rofi-types.h:280
void rofi_view_maybe_update(RofiViewState *state)
Definition: view.c:1489
listview * list_view
Definition: view-internal.h:64
int rofi_theme_get_position(const widget *widget, const char *property, int def)
Definition: theme.c:730
gboolean do_bench
Definition: view.c:229
textbox * overlay
Definition: view-internal.h:66
Definition: mode.h:73
static gboolean rofi_view_reload_idle(G_GNUC_UNUSED gpointer data)
Definition: view.c:458
SnLauncheeContext * sncontext
Definition: xcb-internal.h:52
const Mode * rofi_get_mode(unsigned int index)
Definition: rofi.c:136
void listview_set_max_lines(listview *lv, unsigned int max_lines)
Definition: listview.c:947
char * mode_get_message(const Mode *mode)
Definition: mode.c:175
gboolean benchmark_ui
Definition: settings.h:205
struct RofiViewState::@5 mouse
const char * mode_get_display_name(const Mode *mode)
Definition: mode.c:154
rofi_int_matcher ** tokens
unsigned int rofi_view_get_selected_line(const RofiViewState *state)
Definition: view.c:568
Settings config
unsigned int num_modi
static void rofi_view_calculate_window_width(RofiViewState *state)
Definition: view.c:891
void rofi_view_set_overlay(RofiViewState *state, const char *text)
Definition: view.c:2026
gboolean widget_motion_notify(widget *wid, gint x, gint y)
Definition: widget.c:471
int widget_get_desired_height(widget *wid)
Definition: widget.c:552
unsigned int threads
Definition: settings.h:173
widget * widget_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition: widget.c:433
gboolean listview_get_fixed_num_lines(listview *lv)
Definition: listview.c:954
void listview_nav_left(listview *lv)
Definition: listview.c:754
#define TICK_N(a)
Definition: timings.h:69
Definition: keyb.h:110
unsigned int count
Definition: view.c:628
static gboolean rofi_view_repaint(G_GNUC_UNUSED void *data)
Definition: view.c:267
xcb_gcontext_t gc
Definition: view.c:98
static void rofi_view_call_thread(gpointer data, gpointer user_data)
Definition: view.c:641
void container_add(container *container, widget *child)
Definition: container.c:72
unsigned int selected_line
Definition: view-internal.h:92
#define color_reset
Definition: rofi.h:90
static RofiViewState * __rofi_view_state_create(void)
Definition: view.c:601
void listview_nav_down(listview *lv)
Definition: listview.c:741
cairo_surface_t * edit_surf
Definition: view.c:102
void textbox_set_pango_attributes(textbox *tb, PangoAttrList *list)
Definition: textbox.c:303
Definition: keyb.h:100
int rofi_theme_get_integer(const widget *widget, const char *property, int def)
Definition: theme.c:747
Definition: keyb.h:114
unsigned int fullscreen
Definition: settings.h:167
textbox * tb_filtered_rows
void rofi_capture_screenshot(void)
Definition: view.c:177
Definition: keyb.h:112
static void rofi_view_trigger_global_action(KeyBindingAction action)
Definition: view.c:1209
Definition: keyb.h:116
void box_add(box *box, widget *child, gboolean expand)
Definition: box.c:283
MenuFlags menu_flags
RofiViewState * rofi_view_create(Mode *sw, const char *input, MenuFlags menu_flags, void(*finalize)(RofiViewState *))
Definition: view.c:1821
MenuFlags
Definition: view.h:43
char * mode_get_completion(const Mode *mode, unsigned int selected_line)
Definition: mode.c:84
RofiViewState * state
Definition: view.c:622
unsigned int stop
Definition: view.c:626
workarea mon
Definition: view.c:112
xcb_atom_t netatoms[NUM_NETATOMS]
Definition: xcb.c:98
static void _rofi_view_reload_row(RofiViewState *state)
Definition: view.c:1073
widget * motion_target
static int lev_sort(const void *p1, const void *p2, void *arg)
Definition: view.c:165
gboolean textbox_append_text(textbox *tb, const char *pad, const int pad_len)
Definition: textbox.c:783
int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str, glong slen)
Definition: helper.c:933
container * container_create(widget *parent, const char *name)
Definition: container.c:110
GTimer * time
Definition: view.c:232
static void widget_disable(widget *widget)
Definition: widget.h:175
void listview_toggle_ellipsizing(listview *lv)
Definition: listview.c:978
cairo_surface_t * x11_helper_get_screenshot_surface(void)
Definition: xcb.c:175
gboolean helper_validate_font(PangoFontDescription *pfd, const char *font)
Definition: helper.c:581
void textbox_set_pango_context(const char *font, PangoContext *p)
Definition: textbox.c:822
struct @0 CacheState