29 #define G_LOG_DOMAIN "View" 41 #include <xkbcommon/xkbcommon-x11.h> 43 #include <xcb/xcb_ewmh.h> 44 #include <xcb/xcb_icccm.h> 47 #include <cairo-xcb.h> 49 #define SN_API_NOT_YET_FROZEN 121 .main_window = XCB_WINDOW_NONE,
127 .views = G_QUEUE_INIT,
164 static int lev_sort (
const void *p1,
const void *p2,
void *arg )
168 int *distances = arg;
170 return distances[*a] - distances[*b];
178 const char *outp = g_getenv (
"ROFI_PNG_OUTPUT" );
181 g_warning (
"There is no rofi surface to store" );
184 const char *xdg_pict_dir = g_get_user_special_dir ( G_USER_DIRECTORY_PICTURES );
185 if ( outp == NULL && xdg_pict_dir == NULL ) {
186 g_warning (
"XDG user picture directory or ROFI_PNG_OUTPUT is not set. Cannot store screenshot." );
190 GDateTime *now = g_date_time_new_now_local ();
192 char *timestmp = g_date_time_format ( now,
"rofi-%Y-%m-%d-%H%M" );
193 char *filename = g_strdup_printf (
"%s-%05d.png", timestmp, 0 );
196 if ( outp == NULL ) {
198 fpath = g_build_filename ( xdg_pict_dir, filename, NULL );
199 while ( g_file_test ( fpath, G_FILE_TEST_EXISTS ) && index < 99 ) {
205 filename = g_strdup_printf (
"%s-%05d.png", timestmp, index );
207 fpath = g_build_filename ( xdg_pict_dir, filename, NULL );
211 fpath = g_strdup ( outp );
214 cairo_status_t status = cairo_surface_write_to_png (
CacheState.edit_surf, fpath );
215 if ( status != CAIRO_STATUS_SUCCESS ) {
216 g_warning (
"Failed to produce screenshot '%s', got error: '%s'", fpath,
217 cairo_status_to_string ( status ) );
222 g_date_time_unref ( now );
232 g_debug (
"expose event" );
240 return G_SOURCE_REMOVE;
277 int anchor = location;
283 else if ( location ==
WL_EAST ) {
286 else if ( location ==
WL_WEST ) {
338 state->
x -= state->
width / 2;
351 state->
x -= state->
width / 2;
362 state->
x -= state->
width / 2;
376 uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
377 uint32_t vals[] = { state->
x, state->
y, state->
width, state->
height };
382 cairo_surface_destroy (
CacheState.edit_surf );
392 g_debug (
"Re-size window based internal request: %dx%d.", state->
width, state->
height );
421 return G_SOURCE_REMOVE;
457 g_debug (
"stack view." );
462 else if ( state == NULL && !g_queue_is_empty ( &(
CacheState.views ) ) ) {
463 g_debug (
"pop view." );
478 unsigned int selected = 0;
506 g_free ( state->
modi );
526 if ( ( selected + 1 ) < state->
num_lines ) {
527 ( next_pos ) = state->
line_map[selected + 1];
581 g_mutex_lock ( t->
mutex );
583 g_cond_signal ( t->
cond );
584 g_mutex_unlock ( t->
mutex );
589 for (
unsigned int i = t->
start; i < t->
stop; i++ ) {
597 glong slen = g_utf8_strlen ( str, -1 );
613 cairo_surface_t *s = NULL;
619 if ( g_strcmp0 ( fake_background,
"real" ) == 0 ) {
622 else if ( g_strcmp0 ( fake_background,
"screenshot" ) == 0 ) {
625 else if ( g_strcmp0 ( fake_background,
"background" ) == 0 ) {
630 g_debug (
"Opening %s to use as background.", fpath );
631 s = cairo_image_surface_create_from_png ( fpath );
635 TICK_N (
"Get surface." );
637 if ( cairo_surface_status ( s ) != CAIRO_STATUS_SUCCESS ) {
638 g_debug (
"Failed to open surface fake background: %s",
639 cairo_status_to_string ( cairo_surface_status ( s ) ) );
640 cairo_surface_destroy ( s );
645 cairo_t *dr = cairo_create (
CacheState.fake_bg );
647 cairo_set_source_surface ( dr, s, 0, 0 );
653 cairo_destroy ( dr );
654 cairo_surface_destroy ( s );
657 TICK_N (
"Fake transparency" );
662 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;
663 uint32_t selval[] = {
664 XCB_BACK_PIXMAP_NONE, 0,
666 XCB_BACKING_STORE_NOT_USEFUL,
667 XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
668 XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE |
669 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION,
673 xcb_window_t box_window = xcb_generate_id (
xcb->
connection );
675 0, 0, 200, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
676 visual->visual_id, selmask, selval );
677 xcb_generic_error_t *error;
680 g_error (
"xcb_create_window() failed error=0x%x\n", error->error_code );
681 exit ( EXIT_FAILURE );
683 TICK_N (
"xcb create window" );
687 TICK_N (
"xcb create gc" );
696 TICK_N (
"create cairo surface" );
698 cairo_font_options_t *fo = cairo_font_options_create ();
700 cairo_surface_get_font_options (
CacheState.edit_surf, fo );
702 PangoContext *p = pango_cairo_create_context (
CacheState.edit_draw );
704 pango_cairo_context_set_font_options ( p, fo );
705 TICK_N (
"pango cairo font setup" );
712 PangoFontMap *font_map = pango_cairo_font_map_get_default ();
713 pango_cairo_font_map_set_resolution ( (PangoCairoFontMap *) font_map, (
double)
config.
dpi );
722 dpi = (
xcb->
screen->height_in_pixels * 25.4 ) / (
double) (
xcb->
screen->height_in_millimeters );
725 g_debug (
"Auto-detected DPI: %.2lf", dpi );
726 PangoFontMap *font_map = pango_cairo_font_map_get_default ();
727 pango_cairo_font_map_set_resolution ( (PangoCairoFontMap *) font_map, dpi );
735 PangoFontDescription *pfd = pango_font_description_from_string ( font );
737 pango_context_set_font_description ( p, pfd );
739 pango_font_description_free ( pfd );
741 PangoLanguage *l = pango_language_get_default ();
742 pango_context_set_language ( p, l );
743 TICK_N (
"configure font" );
748 g_object_unref ( p );
749 cairo_font_options_destroy ( fo );
751 TICK_N (
"textbox setup" );
755 uint32_t values[] = { 1 };
756 xcb_change_window_attributes (
xcb->
connection, box_window, XCB_CW_OVERRIDE_REDIRECT, values );
763 TICK_N (
"setup window attributes" );
766 xcb_atom_t atoms[] = {
767 xcb->
ewmh._NET_WM_STATE_FULLSCREEN,
773 TICK_N (
"setup window fullscreen" );
775 xcb_change_property (
xcb->
connection, XCB_PROP_MODE_REPLACE, box_window,
xcb->
ewmh._NET_WM_NAME,
xcb->
ewmh.UTF8_STRING, 8, 4,
"rofi" );
776 xcb_change_property (
xcb->
connection, XCB_PROP_MODE_REPLACE, box_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 4,
"rofi" );
778 const char wm_class_name[] =
"rofi\0Rofi";
779 xcb_icccm_set_wm_class (
xcb->
connection, box_window, sizeof ( wm_class_name ), wm_class_name );
781 TICK_N (
"setup window name and class" );
783 if ( transparency ) {
791 TICK_N (
"setup startup notification" );
796 pid_t pid = getpid ();
800 const char *hostname = g_get_host_name ();
801 char *ahost = g_hostname_to_ascii ( hostname );
802 if ( ahost != NULL ) {
806 strlen ( ahost ), ahost );
877 if ( selected < state->filtered_lines ) {
916 GList *add_list = NULL;
924 if ( list != NULL ) {
925 pango_attr_list_ref ( list );
928 list = pango_attr_list_new ();
940 for ( GList *iter = g_list_first ( add_list ); iter != NULL; iter = g_list_next ( iter ) ) {
941 pango_attr_list_insert ( list, (PangoAttribute *) ( iter->data ) );
944 pango_attr_list_unref ( list );
945 g_list_free ( add_list );
961 g_debug (
"Redraw view" );
964 cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE );
967 cairo_set_source_surface ( d,
CacheState.fake_bg, 0.0, 0.0 );
970 cairo_set_source_surface ( d,
CacheState.fake_bg,
975 cairo_set_operator ( d, CAIRO_OPERATOR_OVER );
979 cairo_set_source_rgba ( d, 0, 0, 0, 0.0 );
985 cairo_set_operator ( d, CAIRO_OPERATOR_OVER );
1011 TICK_N (
"Filter start" );
1035 g_mutex_init ( &
mutex );
1036 g_cond_init ( &
cond );
1037 unsigned int count = nt;
1039 for (
unsigned int i = 0; i < nt; i++ ) {
1041 states[i].
start = i * steps;
1043 states[i].
count = 0;
1051 g_thread_pool_push (
tpool, &states[i], NULL );
1058 g_mutex_lock ( &
mutex );
1059 while (
count > 0 ) {
1062 g_mutex_unlock ( &
mutex );
1064 g_cond_clear ( &
cond );
1065 g_mutex_clear ( &
mutex );
1066 for (
unsigned int i = 0; i < nt; i++ ) {
1067 if ( j != states[i].
start ) {
1070 j += states[i].
count;
1099 g_debug (
"Resize based on re-filter" );
1102 TICK_N (
"Filter done" );
1125 xcb->
ewmh.UTF8_STRING,
xcb->
ewmh.UTF8_STRING, XCB_CURRENT_TIME );
1130 xcb->
ewmh.UTF8_STRING,
xcb->
ewmh.UTF8_STRING, XCB_CURRENT_TIME );
1167 if ( selected < state->filtered_lines ) {
1186 if ( index < state->filtered_lines ) {
1215 if ( selected < state->filtered_lines ) {
1289 else if ( rc == 2 ) {
1298 if ( selected < state->filtered_lines ) {
1322 if ( selected < state->filtered_lines ) {
1352 if ( target == NULL ) {
1401 if (
state == NULL ) {
1417 if ( xce->window ==
CacheState.main_window ) {
1428 cairo_surface_destroy (
CacheState.edit_surf );
1521 char *defaults = NULL;
1527 if ( strcmp ( name,
"mainbox" ) == 0 ) {
1530 defaults =
"inputbar,message,listview,sidebar";
1535 else if ( strcmp ( name,
"inputbar" ) == 0 ) {
1537 defaults =
"prompt,entry,case-indicator";
1543 else if ( strcmp ( name,
"prompt" ) == 0 ) {
1545 g_error (
"Prompt widget can only be added once to the layout." );
1557 else if ( strcmp ( name,
"case-indicator" ) == 0 ) {
1559 g_error (
"Case indicator widget can only be added once to the layout." );
1570 else if ( strcmp ( name,
"entry" ) == 0 ) {
1572 g_error (
"Entry textbox widget can only be added once to the layout." );
1584 else if ( strcmp ( name,
"message" ) == 0 ) {
1586 g_error (
"Message widget can only be added once to the layout." );
1598 else if ( strcmp ( name,
"listview" ) == 0 ) {
1600 g_error (
"Listview widget can only be added once to the layout." );
1617 else if ( strcmp ( name,
"sidebar" ) == 0 ) {
1619 g_error (
"Sidebar widget can only be added once to the layout." );
1636 else if ( g_ascii_strncasecmp ( name,
"textbox", 7 ) == 0 ) {
1647 for (
const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) {
1650 g_list_free_full ( list, g_free );
1676 TICK_N (
"Startup notification" );
1679 TICK_N (
"Get active monitor" );
1684 for (
const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) {
1687 g_list_free_full ( list, g_free );
1737 NORMAL, ( msg != NULL ) ? msg :
"", 0, 0 );
1771 if (
CacheState.main_window != XCB_WINDOW_NONE ) {
1779 g_debug (
"Cleanup." );
1785 g_source_remove (
CacheState.repaint_source );
1789 cairo_surface_destroy (
CacheState.fake_bg );
1797 cairo_surface_destroy (
CacheState.edit_surf );
1800 if (
CacheState.main_window != XCB_WINDOW_NONE ) {
1801 g_debug (
"Unmapping and free'ing window" );
1808 if (
map != XCB_COLORMAP_NONE ) {
1810 map = XCB_COLORMAP_NONE;
1813 g_assert ( g_queue_is_empty ( &(
CacheState.views ) ) );
1817 TICK_N (
"Setup Threadpool, start" );
1820 long procs = sysconf ( _SC_NPROCESSORS_CONF );
1826 GError *error = NULL;
1828 if ( error == NULL ) {
1830 g_thread_pool_set_max_idle_time ( 60000 );
1835 if ( error != NULL ) {
1836 g_warning (
"Failed to setup thread pool: '%s'", error->message );
1837 g_error_free ( error );
1838 exit ( EXIT_FAILURE );
1840 TICK_N (
"Setup Threadpool, done" );
1845 g_thread_pool_free (
tpool, TRUE, TRUE );
1859 if ( text == NULL ) {
void listview_set_selected(listview *lv, unsigned int selected)
unsigned int levenshtein(const char *needle, const glong needlelen, const char *haystack, const glong haystacklen)
static const int loc_transtable[9]
void textbox_cursor_end(textbox *tb)
GList * rofi_theme_get_list(const widget *widget, const char *property, const char *defaults)
void rofi_view_get_current_monitor(int *width, int *height)
cairo_surface_t * fake_bg
MouseBindingMouseDefaultAction
void rofi_quit_main_loop(void)
unsigned int fake_transparency
void rofi_view_finalize(RofiViewState *state)
listview * listview_create(widget *parent, const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse)
void display_early_cleanup(void)
int mode_token_match(const Mode *mode, rofi_int_matcher **tokens, unsigned int selected_line)
void rofi_view_clear_input(RofiViewState *state)
unsigned int case_sensitive
static void rofi_view_nav_last(RofiViewState *state)
void x11_disable_decoration(xcb_window_t window)
void textbox_text(textbox *tb, const char *text)
static void update_callback(textbox *t, unsigned int index, void *udata, TextBoxFontType type, gboolean full)
void rofi_view_reload(void)
unsigned int sidebar_mode
PangoAttrList * helper_token_match_get_pango_attr(RofiHighlightColorStyle th, rofi_int_matcher **tokens, const char *input, PangoAttrList *retv)
void rofi_view_temp_configure_notify(RofiViewState *state, xcb_configure_notify_event_t *xce)
void listview_nav_up(listview *lv)
void listview_nav_page_prev(listview *lv)
void rofi_view_workers_finalize(void)
void textbox_icon(textbox *tb, cairo_surface_t *icon)
void rofi_view_queue_redraw(void)
xcb_window_t xcb_stuff_get_root_window(void)
Mode * rofi_view_get_mode(RofiViewState *state)
static void filter_elements(thread_state *t, G_GNUC_UNUSED gpointer user_data)
double textbox_get_estimated_char_width(void)
void listview_set_num_elements(listview *lv, unsigned int rows)
KeyBindingAction prev_action
cairo_surface_t * mode_get_icon(const Mode *mode, unsigned int selected_line, int height)
static void rofi_view_update_prompt(RofiViewState *state)
char * mode_preprocess_input(Mode *mode, const char *input)
const char * rofi_view_get_user_input(const RofiViewState *state)
void __create_window(MenuFlags menu_flags)
const char * rofi_theme_get_string(const widget *widget, const char *property, const char *def)
static void rofi_view_setup_fake_transparency(const char *const fake_background)
unsigned int scroll_method
void listview_set_scroll_type(listview *lv, ScrollType type)
static void rofi_view_nav_first(RofiViewState *state)
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)
void rofi_view_set_selected_line(RofiViewState *state, unsigned int selected_line)
struct _thread_state thread_state
void rofi_view_temp_click_to_exit(RofiViewState *state, xcb_window_t target)
void textbox_font(textbox *tb, TextBoxFontType tbft)
int rofi_theme_get_boolean(const widget *widget, const char *property, int def)
RofiViewState * rofi_view_get_active(void)
static void rofi_view_refilter(RofiViewState *state)
RofiDistance rofi_theme_get_distance(const widget *widget, const char *property, int def)
unsigned int rofi_get_num_enabled_modi(void)
textbox * textbox_create(widget *parent, WidgetType type, const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text, double xalign, double yalign)
cairo_surface_t * x11_helper_get_bg_surface(void)
MenuReturn rofi_view_get_return_value(const RofiViewState *state)
void listview_nav_right(listview *lv)
xcb_ewmh_connection_t ewmh
void rofi_view_workers_initialize(void)
void listview_set_mouse_activated_cb(listview *lv, listview_mouse_activated_cb cb, void *udata)
void listview_set_num_lines(listview *lv, unsigned int num_lines)
void rofi_view_frame_callback(void)
rofi_int_matcher ** helper_tokenize(const char *input, int case_sensitive)
void rofi_view_free(RofiViewState *state)
RofiViewState * current_active_menu
void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y)
static void rofi_view_nav_row_select(RofiViewState *state)
RofiHighlightColorStyle rofi_theme_get_highlight(widget *widget, const char *property, RofiHighlightColorStyle th)
void rofi_view_hide(void)
xcb_window_t rofi_view_get_window(void)
void rofi_view_update(RofiViewState *state, gboolean qr)
struct RofiViewState::@4 mouse
unsigned int rofi_view_get_next_position(const RofiViewState *state)
static void rofi_view_listview_mouse_activated_cb(listview *lv, gboolean custom, void *udata)
unsigned int listview_get_selected(listview *lv)
int textbox_keybinding(textbox *tb, KeyBindingAction action)
void rofi_view_handle_text(RofiViewState *state, char *text)
const char * textbox_get_visible_text(const textbox *tb)
unsigned int rofi_view_get_completed(const RofiViewState *state)
void rofi_view_set_active(RofiViewState *state)
int rofi_view_error_dialog(const char *msg, int markup)
box * box_create(widget *parent, const char *name, RofiOrientation type)
gboolean rofi_view_trigger_action(RofiViewState *state, BindingsScope scope, guint action)
static void rofi_view_nav_row_tab(RofiViewState *state)
static char * get_matching_state(void)
static void rofi_view_window_update_size(RofiViewState *state)
void listview_set_multi_select(listview *lv, gboolean enable)
static void rofi_view_calculate_window_position(RofiViewState *state)
MatchingMethod matching_method
char * rofi_expand_path(const char *input)
xcb_connection_t * connection
void process_result(RofiViewState *state)
void listview_set_fixed_num_lines(listview *lv)
void rofi_view_switch_mode(RofiViewState *state, Mode *mode)
unsigned int mode_get_num_entries(const Mode *mode)
void(* finalize)(struct RofiViewState *state)
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
static int rofi_view_calculate_height(RofiViewState *state)
char * mode_get_display_value(const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry)
int monitor_active(workarea *mon)
xcb_visualtype_t * visual
unsigned int filtered_lines
void window_set_atom_prop(xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms, int count)
void listview_nav_page_next(listview *lv)
PangoAttrList * textbox_get_pango_attributes(textbox *tb)
static void rofi_view_add_widget(RofiViewState *state, widget *parent_widget, const char *name)
void rofi_view_restart(RofiViewState *state)
static void rofi_view_reload_message_bar(RofiViewState *state)
unsigned int levenshtein_sort
void helper_tokenize_free(rofi_int_matcher **tokens)
void(* callback)(struct _thread_state *t, gpointer data)
void rofi_view_maybe_update(RofiViewState *state)
int rofi_theme_get_position(const widget *widget, const char *property, int def)
static gboolean rofi_view_reload_idle(G_GNUC_UNUSED gpointer data)
SnLauncheeContext * sncontext
const Mode * rofi_get_mode(unsigned int index)
void listview_set_max_lines(listview *lv, unsigned int max_lines)
char * mode_get_message(const Mode *mode)
const char * mode_get_display_name(const Mode *mode)
rofi_int_matcher ** tokens
unsigned int rofi_view_get_selected_line(const RofiViewState *state)
static void rofi_view_calculate_window_width(RofiViewState *state)
void rofi_view_set_overlay(RofiViewState *state, const char *text)
int textbox_get_font_height(const textbox *tb)
gboolean listview_get_fixed_num_lines(listview *lv)
void listview_nav_left(listview *lv)
static gboolean rofi_view_repaint(G_GNUC_UNUSED void *data)
static void rofi_view_call_thread(gpointer data, gpointer user_data)
void container_add(container *container, widget *child)
unsigned int selected_line
static RofiViewState * __rofi_view_state_create(void)
void listview_nav_down(listview *lv)
cairo_surface_t * edit_surf
void textbox_set_pango_attributes(textbox *tb, PangoAttrList *list)
int rofi_theme_get_integer(const widget *widget, const char *property, int def)
void rofi_capture_screenshot(void)
static void rofi_view_trigger_global_action(KeyBindingAction action)
void box_add(box *box, widget *child, gboolean expand)
RofiViewState * rofi_view_create(Mode *sw, const char *input, MenuFlags menu_flags, void(*finalize)(RofiViewState *))
char * mode_get_completion(const Mode *mode, unsigned int selected_line)
xcb_atom_t netatoms[NUM_NETATOMS]
static void _rofi_view_reload_row(RofiViewState *state)
static int lev_sort(const void *p1, const void *p2, void *arg)
gboolean textbox_append_text(textbox *tb, const char *pad, const int pad_len)
int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str, glong slen)
container * container_create(widget *parent, const char *name)
cairo_surface_t * x11_helper_get_screenshot_surface(void)
gboolean helper_validate_font(PangoFontDescription *pfd, const char *font)
void textbox_set_pango_context(const char *font, PangoContext *p)