1
0
Fork 0
mirror of https://github.com/davatorium/rofi.git synced 2024-11-25 13:55:34 -05:00

Re-work the expose, redraw system. Use XServer side pixmaps to do drawing and flipping.

- By server side drawing and flipping buffer time to draw buffer to window speed up by factor 1000+.
   Flipping is now one xcb_copy_area call.
 - Don't abuse Xserver expose events to singal internal drawings.
 - Queue redraws in idle time.
 - Avoid unneeded redraws.
This commit is contained in:
Dave Davenport 2016-11-11 16:29:59 +01:00
parent e3af1efaef
commit 1d40782515
2 changed files with 97 additions and 52 deletions

View file

@ -8,6 +8,8 @@ v1.3.0: Dan vs. Greg: The never ending story. (unreleased)
- Dynamically sizing window with content. - Dynamically sizing window with content.
- When placed at bottom of screen re-order screen to have entry at bottom. - When placed at bottom of screen re-order screen to have entry at bottom.
Improvements Improvements
- Speedup drawing of screen. Works well now for 4k and 8k screens. (factor 1000+ speedup for the flipping of
buffer) (#496)
- DRun mode more compatible with specification. - DRun mode more compatible with specification.
- Debug output via g_log. - Debug output via g_log.
- Fix password entry cursor position. - Fix password entry cursor position.

View file

@ -72,23 +72,29 @@ RofiViewState *current_active_menu = NULL;
struct struct
{ {
xcb_window_t main_window; xcb_window_t main_window;
cairo_surface_t *surface;
cairo_surface_t *fake_bg; cairo_surface_t *fake_bg;
xcb_gcontext_t gc;
xcb_pixmap_t edit_pixmap;
cairo_surface_t *edit_surf;
cairo_t *edit_draw;
int fake_bgrel; int fake_bgrel;
cairo_t *draw;
MenuFlags flags; MenuFlags flags;
GQueue views; GQueue views;
workarea mon; workarea mon;
guint idle_timeout; guint idle_timeout;
uint64_t count;
guint repaint_timeout;
} CacheState = { } CacheState = {
.main_window = XCB_WINDOW_NONE, .main_window = XCB_WINDOW_NONE,
.surface = NULL,
.fake_bg = NULL, .fake_bg = NULL,
.edit_surf = NULL,
.edit_draw = NULL,
.fake_bgrel = FALSE, .fake_bgrel = FALSE,
.draw = NULL,
.flags = MENU_NORMAL, .flags = MENU_NORMAL,
.views = G_QUEUE_INIT, .views = G_QUEUE_INIT,
.idle_timeout = 0, .idle_timeout = 0,
.count = 0L,
.repaint_timeout = 0,
}; };
static char * get_matching_state ( void ) static char * get_matching_state ( void )
@ -127,7 +133,7 @@ static int lev_sort ( const void *p1, const void *p2, void *arg )
static void menu_capture_screenshot ( void ) static void menu_capture_screenshot ( void )
{ {
const char *outp = g_getenv ( "ROFI_PNG_OUTPUT" ); const char *outp = g_getenv ( "ROFI_PNG_OUTPUT" );
if ( CacheState.surface == NULL ) { if ( CacheState.edit_surf == NULL ) {
// Nothing to store. // Nothing to store.
fprintf ( stderr, "There is no rofi surface to store\n" ); fprintf ( stderr, "There is no rofi surface to store\n" );
return; return;
@ -162,7 +168,7 @@ static void menu_capture_screenshot ( void )
fpath = g_strdup ( outp ); fpath = g_strdup ( outp );
} }
fprintf ( stderr, color_green "Storing screenshot %s\n"color_reset, fpath ); fprintf ( stderr, color_green "Storing screenshot %s\n"color_reset, fpath );
cairo_status_t status = cairo_surface_write_to_png ( CacheState.surface, fpath ); cairo_status_t status = cairo_surface_write_to_png ( CacheState.edit_surf, fpath );
if ( status != CAIRO_STATUS_SUCCESS ) { if ( status != CAIRO_STATUS_SUCCESS ) {
fprintf ( stderr, "Failed to produce screenshot '%s', got error: '%s'\n", filename, fprintf ( stderr, "Failed to produce screenshot '%s', got error: '%s'\n", filename,
cairo_status_to_string ( status ) ); cairo_status_to_string ( status ) );
@ -173,6 +179,20 @@ static void menu_capture_screenshot ( void )
g_date_time_unref ( now ); g_date_time_unref ( now );
} }
static gboolean rofi_view_repaint ( G_GNUC_UNUSED void * data )
{
if ( current_active_menu ) {
g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "expose event\n" );
TICK_N ( "Expose" );
xcb_copy_area ( xcb->connection, CacheState.edit_pixmap, CacheState.main_window, CacheState.gc,
0, 0, 0, 0, current_active_menu->width, current_active_menu->height );
xcb_flush ( xcb->connection );
TICK_N ( "flush" );
CacheState.repaint_timeout = 0;
}
return G_SOURCE_REMOVE;
}
static void rofi_view_update_prompt ( RofiViewState *state ) static void rofi_view_update_prompt ( RofiViewState *state )
{ {
if ( state->prompt ) { if ( state->prompt ) {
@ -246,7 +266,16 @@ static void rofi_view_window_update_size ( RofiViewState * state )
// Display it. // Display it.
xcb_configure_window ( xcb->connection, CacheState.main_window, mask, vals ); xcb_configure_window ( xcb->connection, CacheState.main_window, mask, vals );
cairo_xcb_surface_set_size ( CacheState.surface, state->width, state->height ); cairo_destroy ( CacheState.edit_draw );
cairo_surface_destroy ( CacheState.edit_surf );
xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap );
CacheState.edit_pixmap = xcb_generate_id ( xcb->connection );
xcb_create_pixmap ( xcb->connection, depth->depth,
CacheState.edit_pixmap, CacheState.main_window, state->width, state->height );
CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height );
CacheState.edit_draw = cairo_create ( CacheState.edit_surf );
widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border ); widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border );
} }
@ -255,8 +284,7 @@ static gboolean rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data )
if ( current_active_menu ) { if ( current_active_menu ) {
current_active_menu->reload = TRUE; current_active_menu->reload = TRUE;
current_active_menu->refilter = TRUE; current_active_menu->refilter = TRUE;
xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 ); rofi_view_queue_redraw ();
xcb_flush ( xcb->connection );
} }
CacheState.idle_timeout = 0; CacheState.idle_timeout = 0;
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
@ -271,9 +299,10 @@ void rofi_view_reload ( void )
} }
void rofi_view_queue_redraw ( void ) void rofi_view_queue_redraw ( void )
{ {
if ( current_active_menu ) { if ( current_active_menu && CacheState.repaint_timeout == 0 ) {
xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 ); CacheState.count++;
xcb_flush ( xcb->connection ); g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "redraw %lu\n", CacheState.count );
CacheState.repaint_timeout = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, rofi_view_repaint, NULL, NULL );
} }
} }
@ -502,17 +531,23 @@ void __create_window ( MenuFlags menu_flags )
0, 0, 200, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, 0, 0, 200, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
visual->visual_id, selmask, selval ); visual->visual_id, selmask, selval );
CacheState.surface = cairo_xcb_surface_create ( xcb->connection, box, visual, 200, 100 ); CacheState.gc = xcb_generate_id ( xcb->connection );
xcb_create_gc ( xcb->connection, CacheState.gc, box, 0, 0 );
// Create a drawable. // Create a drawable.
CacheState.draw = cairo_create ( CacheState.surface ); CacheState.edit_pixmap = xcb_generate_id ( xcb->connection );
g_assert ( CacheState.draw != NULL ); xcb_create_pixmap ( xcb->connection, depth->depth,
cairo_set_operator ( CacheState.draw, CAIRO_OPERATOR_SOURCE ); CacheState.edit_pixmap, CacheState.main_window, 200, 100 );
CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, 200, 100 );
CacheState.edit_draw = cairo_create ( CacheState.edit_surf );
// Set up pango context. // Set up pango context.
cairo_font_options_t *fo = cairo_font_options_create (); cairo_font_options_t *fo = cairo_font_options_create ();
// Take font description from xlib surface // Take font description from xlib surface
cairo_surface_get_font_options ( CacheState.surface, fo ); cairo_surface_get_font_options ( CacheState.edit_surf, fo );
PangoContext *p = pango_cairo_create_context ( CacheState.draw ); // TODO should we update the drawable each time?
PangoContext *p = pango_cairo_create_context ( CacheState.edit_draw );
// Set the font options from the xlib surface // Set the font options from the xlib surface
pango_cairo_context_set_font_options ( p, fo ); pango_cairo_context_set_font_options ( p, fo );
// Setup dpi // Setup dpi
@ -701,8 +736,7 @@ void rofi_view_update ( RofiViewState *state )
return; return;
} }
TICK (); TICK ();
cairo_surface_t * surf = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, state->width, state->height ); cairo_t *d = CacheState.edit_draw;
cairo_t *d = cairo_create ( surf );
cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE ); cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE );
if ( config.fake_transparency ) { if ( config.fake_transparency ) {
if ( CacheState.fake_bg != NULL ) { if ( CacheState.fake_bg != NULL ) {
@ -747,18 +781,9 @@ void rofi_view_update ( RofiViewState *state )
if ( state->overlay ) { if ( state->overlay ) {
widget_draw ( WIDGET ( state->overlay ), d ); widget_draw ( WIDGET ( state->overlay ), d );
} }
TICK_N ( "widgets" );
// Draw to actual window. cairo_surface_flush ( CacheState.edit_surf );
cairo_set_source_surface ( CacheState.draw, surf, 0, 0 ); rofi_view_queue_redraw ();
cairo_paint ( CacheState.draw );
// Cleanup
cairo_destroy ( d );
cairo_surface_destroy ( surf );
// Flush the surface.
cairo_surface_flush ( CacheState.surface );
xcb_flush ( xcb->connection );
TICK ();
} }
/** /**
@ -1184,9 +1209,6 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st
{ {
switch ( ev->response_type & ~0x80 ) switch ( ev->response_type & ~0x80 )
{ {
case XCB_EXPOSE:
widget_queue_redraw ( WIDGET ( state->main_box ) );
break;
case XCB_CONFIGURE_NOTIFY: case XCB_CONFIGURE_NOTIFY:
{ {
xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) ev; xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) ev;
@ -1198,7 +1220,17 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st
if ( state->width != xce->width || state->height != xce->height ) { if ( state->width != xce->width || state->height != xce->height ) {
state->width = xce->width; state->width = xce->width;
state->height = xce->height; state->height = xce->height;
cairo_xcb_surface_set_size ( CacheState.surface, state->width, state->height );
cairo_destroy ( CacheState.edit_draw );
cairo_surface_destroy ( CacheState.edit_surf );
xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap );
CacheState.edit_pixmap = xcb_generate_id ( xcb->connection );
xcb_create_pixmap ( xcb->connection, depth->depth, CacheState.edit_pixmap, CacheState.main_window,
state->width, state->height );
CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height );
CacheState.edit_draw = cairo_create ( CacheState.edit_surf );
widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border ); widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border );
} }
} }
@ -1281,6 +1313,10 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st
rofi_view_refilter ( state ); rofi_view_refilter ( state );
} }
rofi_view_update ( state ); rofi_view_update ( state );
if ( ( ev->response_type & ~0x80 ) == XCB_EXPOSE && CacheState.repaint_timeout == 0 ) {
CacheState.repaint_timeout = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, rofi_view_repaint, NULL, NULL );
}
} }
static int rofi_view_calculate_height ( RofiViewState *state ) static int rofi_view_calculate_height ( RofiViewState *state )
@ -1471,6 +1507,7 @@ RofiViewState *rofi_view_create ( Mode *sw,
rofi_view_update ( state ); rofi_view_update ( state );
xcb_map_window ( xcb->connection, CacheState.main_window ); xcb_map_window ( xcb->connection, CacheState.main_window );
widget_queue_redraw ( WIDGET ( state->main_box ) );
xcb_flush ( xcb->connection ); xcb_flush ( xcb->connection );
if ( xcb->sncontext != NULL ) { if ( xcb->sncontext != NULL ) {
sn_launchee_context_complete ( xcb->sncontext ); sn_launchee_context_complete ( xcb->sncontext );
@ -1486,7 +1523,6 @@ int rofi_view_error_dialog ( const char *msg, int markup )
state->menu_flags = MENU_ERROR_DIALOG; state->menu_flags = MENU_ERROR_DIALOG;
state->finalize = process_result; state->finalize = process_result;
rofi_view_calculate_window_and_element_width ( state ); rofi_view_calculate_window_and_element_width ( state );
state->main_box = box_create ( BOX_VERTICAL, state->main_box = box_create ( BOX_VERTICAL,
state->border, state->border, state->border, state->border,
@ -1508,6 +1544,7 @@ int rofi_view_error_dialog ( const char *msg, int markup )
// Display it. // Display it.
xcb_map_window ( xcb->connection, CacheState.main_window ); xcb_map_window ( xcb->connection, CacheState.main_window );
widget_queue_redraw ( WIDGET ( state->main_box ) );
if ( xcb->sncontext != NULL ) { if ( xcb->sncontext != NULL ) {
sn_launchee_context_complete ( xcb->sncontext ); sn_launchee_context_complete ( xcb->sncontext );
@ -1534,20 +1571,26 @@ void rofi_view_cleanup ()
g_source_remove ( CacheState.idle_timeout ); g_source_remove ( CacheState.idle_timeout );
CacheState.idle_timeout = 0; CacheState.idle_timeout = 0;
} }
if ( CacheState.repaint_timeout > 0 ) {
g_source_remove ( CacheState.repaint_timeout );
CacheState.idle_timeout = 0;
}
if ( CacheState.fake_bg ) { if ( CacheState.fake_bg ) {
cairo_surface_destroy ( CacheState.fake_bg ); cairo_surface_destroy ( CacheState.fake_bg );
CacheState.fake_bg = NULL; CacheState.fake_bg = NULL;
} }
if ( CacheState.draw ) { if ( CacheState.edit_draw ) {
cairo_destroy ( CacheState.draw ); cairo_destroy ( CacheState.edit_draw );
CacheState.draw = NULL; CacheState.edit_draw = NULL;
} }
if ( CacheState.surface ) { if ( CacheState.edit_surf ) {
cairo_surface_destroy ( CacheState.surface ); cairo_surface_destroy ( CacheState.edit_surf );
CacheState.surface = NULL; CacheState.edit_surf = NULL;
} }
if ( CacheState.main_window != XCB_WINDOW_NONE ) { if ( CacheState.main_window != XCB_WINDOW_NONE ) {
xcb_unmap_window ( xcb->connection, CacheState.main_window ); xcb_unmap_window ( xcb->connection, CacheState.main_window );
xcb_free_gc ( xcb->connection, CacheState.gc );
xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap );
xcb_destroy_window ( xcb->connection, CacheState.main_window ); xcb_destroy_window ( xcb->connection, CacheState.main_window );
CacheState.main_window = XCB_WINDOW_NONE; CacheState.main_window = XCB_WINDOW_NONE;
} }