diff --git a/include/rofi.h b/include/rofi.h index 41072e07..8830b62d 100644 --- a/include/rofi.h +++ b/include/rofi.h @@ -77,6 +77,8 @@ void rofi_add_error_message ( GString *str ); */ void rofi_set_return_code ( int code ); +void rofi_quit_main_loop ( void ); + /** * @param name Search for mode with this name. * diff --git a/include/view.h b/include/view.h index e9a437c1..55ac0685 100644 --- a/include/view.h +++ b/include/view.h @@ -93,12 +93,33 @@ MenuReturn rofi_view_get_return_value ( const RofiViewState *state ); unsigned int rofi_view_get_next_position ( const RofiViewState *state ); /** * @param state the Menu handle - * @param event the event to handle - * @param xkb the keyboard handle + * @param text The text to add to the input box * - * Process an Xevent. + * Update the state if needed. */ -void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event, NkBindingsSeat *seat ); +void rofi_view_handle_text ( RofiViewState *state, char *text ); +/** + * @param state the Menu handle + * @param x The X coordinates of the motion + * @param x The Y coordinates of the motion + * + * Update the state if needed. + */ +void rofi_view_handle_mouse_motion( RofiViewState *state, gint x, gint y ); +/** + * @param state the Menu handle + * + * Update the state if needed. + */ +void rofi_view_maybe_update ( RofiViewState *state ); +void rofi_view_temp_configure_notify ( RofiViewState *state, xcb_configure_notify_event_t *xce ); +void rofi_view_temp_click_to_exit ( RofiViewState *state, xcb_window_t target ); +/** + * @param state the Menu handle + * + * Update the state if needed. + */ +void rofi_view_frame_callback ( void ); /** * @param state the Menu handle * diff --git a/include/xcb-internal.h b/include/xcb-internal.h index 53dc99a8..4189dc63 100644 --- a/include/xcb-internal.h +++ b/include/xcb-internal.h @@ -60,6 +60,7 @@ struct _xcb_stuff } xkb; NkBindings *bindings; NkBindingsSeat *bindings_seat; + gboolean mouse_seen; }; #endif diff --git a/source/rofi.c b/source/rofi.c index 3390a44b..9c52437c 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -591,6 +591,11 @@ static gboolean setup_modi ( void ) return FALSE; } +void rofi_quit_main_loop ( void ) +{ + g_main_loop_quit ( main_loop ); +} + static gboolean main_loop_signal_handler_int ( G_GNUC_UNUSED gpointer data ) { // Break out of loop. diff --git a/source/view.c b/source/view.c index 923cd714..5776d845 100644 --- a/source/view.c +++ b/source/view.c @@ -972,40 +972,6 @@ void rofi_view_update ( RofiViewState *state, gboolean qr ) } } -/** - * @param state Internal state of the menu. - * @param xse X selection event. - * - * Handle paste event. - */ -static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t *xse ) -{ - if ( xse->property == XCB_ATOM_NONE ) { - g_warning ( "Failed to convert selection" ); - } - else if ( xse->property == xcb->ewmh.UTF8_STRING ) { - gchar *text = window_get_text_prop ( CacheState.main_window, xcb->ewmh.UTF8_STRING ); - if ( text != NULL && text[0] != '\0' ) { - unsigned int dl = strlen ( text ); - // Strip new line - for ( unsigned int i = 0; i < dl; i++ ) { - if ( text[i] == '\n' ) { - dl = i; - } - } - // Insert string move cursor. - textbox_insert ( state->text, state->text->cursor, text, dl ); - textbox_cursor ( state->text, state->text->cursor + g_utf8_strlen ( text, -1 ) ); - // Force a redraw and refiltering of the text. - state->refilter = TRUE; - } - g_free ( text ); - } - else { - g_warning ( "Failed" ); - } -} - static void _rofi_view_reload_row ( RofiViewState *state ) { g_free ( state->line_map ); @@ -1382,122 +1348,83 @@ gboolean rofi_view_trigger_action ( RofiViewState *state, BindingsScope scope, g return FALSE; } -void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event, NkBindingsSeat *seat ) +void rofi_view_handle_text ( RofiViewState *state, char *text ) { - switch ( event->response_type & ~0x80 ) - { - case XCB_CONFIGURE_NOTIFY: - { - xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) event; - if ( xce->window == CacheState.main_window ) { - if ( state->x != xce->x || state->y != xce->y ) { - state->x = xce->x; - state->y = xce->y; - widget_queue_redraw ( WIDGET ( state->main_window ) ); - } - if ( state->width != xce->width || state->height != xce->height ) { - state->width = xce->width; - state->height = xce->height; + if ( textbox_append_text ( state->text, text, strlen ( text ) ) ) { + state->refilter = TRUE; + } +} - cairo_destroy ( CacheState.edit_draw ); - cairo_surface_destroy ( CacheState.edit_surf ); +void rofi_view_handle_mouse_motion( RofiViewState *state, gint x, gint y ) +{ + state->mouse.x = x; + state->mouse.y = y; + if ( state->mouse.motion_target != NULL ) { + widget_xy_to_relative ( state->mouse.motion_target, &x, &y ); + widget_motion_notify ( state->mouse.motion_target, x, y ); + } +} - 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 ); +void rofi_view_maybe_update ( RofiViewState *state ) +{ + if ( rofi_view_get_completed ( state ) ) { + // This menu is done. + rofi_view_finalize ( state ); + // cleanup + if ( rofi_view_get_active () == NULL ) { + rofi_quit_main_loop(); + return; + } + } - 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 ); - g_debug ( "Re-size window based external request: %d %d", state->width, state->height ); - widget_resize ( WIDGET ( state->main_window ), state->width, state->height ); - } - } - break; - } - case XCB_MOTION_NOTIFY: - { - if ( config.click_to_exit == TRUE ) { - state->mouse_seen = TRUE; - } - xcb_motion_notify_event_t xme = *( (xcb_motion_notify_event_t *) event ); - state->mouse.x = xme.event_x; - state->mouse.y = xme.event_y; - if ( state->mouse.motion_target != NULL ) { - gint x = state->mouse.x; - gint y = state->mouse.y; - widget_xy_to_relative ( state->mouse.motion_target, &x, &y ); - widget_motion_notify ( state->mouse.motion_target, x, y ); - } - break; - } - case XCB_BUTTON_PRESS: - { - xcb_button_press_event_t *bpe = (xcb_button_press_event_t *) event; - state->mouse.x = bpe->event_x; - state->mouse.y = bpe->event_y; - nk_bindings_seat_handle_button ( seat, bpe->detail, NK_BINDINGS_BUTTON_STATE_PRESS, bpe->time ); - break; - } - case XCB_BUTTON_RELEASE: - { - xcb_button_release_event_t *bre = (xcb_button_release_event_t *) event; - nk_bindings_seat_handle_button ( seat, bre->detail, NK_BINDINGS_BUTTON_STATE_RELEASE, bre->time ); - if ( config.click_to_exit == TRUE ) { - if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == 0 ) { - if ( ( state->mouse_seen == FALSE ) && ( bre->event != CacheState.main_window ) ) { - state->quit = TRUE; - state->retv = MENU_CANCEL; - } - } - state->mouse_seen = FALSE; - } - break; - } - // Paste event. - case XCB_SELECTION_NOTIFY: - rofi_view_paste ( state, (xcb_selection_notify_event_t *) event ); - break; - case XCB_KEYMAP_NOTIFY: - { - xcb_keymap_notify_event_t *kne = (xcb_keymap_notify_event_t *) event; - for ( gint32 by = 0; by < 31; ++by ) { - for ( gint8 bi = 0; bi < 7; ++bi ) { - if ( kne->keys[by] & ( 1 << bi ) ) { - // X11 keycodes starts at 8 - nk_bindings_seat_handle_key ( seat, ( 8 * by + bi ) + 8, NK_BINDINGS_KEY_STATE_PRESSED ); - } - } - } - break; - } - case XCB_KEY_PRESS: - { - xcb_key_press_event_t *xkpe = (xcb_key_press_event_t *) event; - gchar *text; - - text = nk_bindings_seat_handle_key ( seat, xkpe->detail, NK_BINDINGS_KEY_STATE_PRESS ); - if ( ( text != NULL ) && ( textbox_append_text ( state->text, text, strlen ( text ) ) ) ) { - state->refilter = TRUE; - } - break; - } - case XCB_KEY_RELEASE: - { - xcb_key_release_event_t *xkre = (xcb_key_release_event_t *) event; - nk_bindings_seat_handle_key ( seat, xkre->detail, NK_BINDINGS_KEY_STATE_RELEASE ); - break; - } - default: - break; - } // Update if requested. if ( state->refilter ) { rofi_view_refilter ( state ); } rofi_view_update ( state, TRUE ); +} - if ( ( event->response_type & ~0x80 ) == XCB_EXPOSE && CacheState.repaint_source == 0 ) { +void rofi_view_temp_configure_notify ( RofiViewState *state, xcb_configure_notify_event_t *xce ) +{ + if ( xce->window == CacheState.main_window ) { + if ( state->x != xce->x || state->y != xce->y ) { + state->x = xce->x; + state->y = xce->y; + widget_queue_redraw ( WIDGET ( state->main_window ) ); + } + if ( state->width != xce->width || state->height != xce->height ) { + state->width = xce->width; + state->height = xce->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 ); + g_debug ( "Re-size window based external request: %d %d", state->width, state->height ); + widget_resize ( WIDGET ( state->main_window ), state->width, state->height ); + } + } +} + +void rofi_view_temp_click_to_exit ( RofiViewState *state, xcb_window_t target ) +{ + if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == 0 ) { + if ( target != CacheState.main_window ) { + state->quit = TRUE; + state->retv = MENU_CANCEL; + } + } +} + +void rofi_view_frame_callback ( void ) +{ + if ( CacheState.repaint_source == 0 ) { CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, rofi_view_repaint, NULL, NULL ); } } diff --git a/source/xcb.c b/source/xcb.c index 14ccfe42..58fff7c0 100644 --- a/source/xcb.c +++ b/source/xcb.c @@ -557,23 +557,123 @@ int monitor_active ( workarea *mon ) return FALSE; } +/** + * @param state Internal state of the menu. + * @param xse X selection event. + * + * Handle paste event. + */ +static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t *xse ) +{ + if ( xse->property == XCB_ATOM_NONE ) { + g_warning ( "Failed to convert selection" ); + } + else if ( xse->property == xcb->ewmh.UTF8_STRING ) { + gchar *text = window_get_text_prop ( xse->requestor, xcb->ewmh.UTF8_STRING ); + if ( text != NULL && text[0] != '\0' ) { + unsigned int dl = strlen ( text ); + // Strip new line + for ( unsigned int i = 0; i < dl; i++ ) { + if ( text[i] == '\n' ) { + text[i] = '\0'; + } + } + rofi_view_handle_text ( state, text ); + } + g_free ( text ); + } + else { + g_warning ( "Failed" ); + } +} + /** * Process X11 events in the main-loop (gui-thread) of the application. */ -static void main_loop_x11_event_handler_view ( xcb_generic_event_t *ev ) +static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event ) { RofiViewState *state = rofi_view_get_active (); - if ( state != NULL ) { - rofi_view_itterrate ( state, ev, xcb->bindings_seat ); - if ( rofi_view_get_completed ( state ) ) { - // This menu is done. - rofi_view_finalize ( state ); - // cleanup - if ( rofi_view_get_active () == NULL ) { - g_main_loop_quit ( xcb->main_loop ); + if ( state == NULL ) { + return; + } + + switch ( event->response_type & ~0x80 ) + { + case XCB_EXPOSE: + rofi_view_frame_callback (); + break; + case XCB_CONFIGURE_NOTIFY: + { + xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) event; + rofi_view_temp_configure_notify ( state, xce ); + break; + } + case XCB_MOTION_NOTIFY: + { + if ( config.click_to_exit == TRUE ) { + xcb->mouse_seen = TRUE; + } + xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *) event; + rofi_view_handle_mouse_motion ( state, xme->event_x, xme->event_y ); + break; + } + case XCB_BUTTON_PRESS: + { + xcb_button_press_event_t *bpe = (xcb_button_press_event_t *) event; + rofi_view_handle_mouse_motion ( state, bpe->event_x, bpe->event_y ); + nk_bindings_seat_handle_button ( xcb->bindings_seat, bpe->detail, NK_BINDINGS_BUTTON_STATE_PRESS, bpe->time ); + break; + } + case XCB_BUTTON_RELEASE: + { + xcb_button_release_event_t *bre = (xcb_button_release_event_t *) event; + nk_bindings_seat_handle_button ( xcb->bindings_seat, bre->detail, NK_BINDINGS_BUTTON_STATE_RELEASE, bre->time ); + if ( config.click_to_exit == TRUE ) { + if ( ! xcb->mouse_seen ) { + rofi_view_temp_click_to_exit ( state, bre->event ); + } + xcb->mouse_seen = FALSE; + } + break; + } + // Paste event. + case XCB_SELECTION_NOTIFY: + rofi_view_paste ( state, (xcb_selection_notify_event_t *) event ); + break; + case XCB_KEYMAP_NOTIFY: + { + xcb_keymap_notify_event_t *kne = (xcb_keymap_notify_event_t *) event; + for ( gint32 by = 0; by < 31; ++by ) { + for ( gint8 bi = 0; bi < 7; ++bi ) { + if ( kne->keys[by] & ( 1 << bi ) ) { + // X11 keycodes starts at 8 + nk_bindings_seat_handle_key ( xcb->bindings_seat, ( 8 * by + bi ) + 8, NK_BINDINGS_KEY_STATE_PRESSED ); + } } } + break; } + case XCB_KEY_PRESS: + { + xcb_key_press_event_t *xkpe = (xcb_key_press_event_t *) event; + gchar *text; + + text = nk_bindings_seat_handle_key ( xcb->bindings_seat, xkpe->detail, NK_BINDINGS_KEY_STATE_PRESS ); + if ( text != NULL ) { + rofi_view_handle_text ( state, text ); + } + break; + } + case XCB_KEY_RELEASE: + { + xcb_key_release_event_t *xkre = (xcb_key_release_event_t *) event; + nk_bindings_seat_handle_key ( xcb->bindings_seat, xkre->detail, NK_BINDINGS_KEY_STATE_RELEASE ); + break; + } + default: + break; + } + rofi_view_maybe_update ( state ); } static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UNUSED gpointer user_data ) @@ -613,9 +713,7 @@ static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UN ksne->baseGroup, ksne->latchedGroup, ksne->lockedGroup ); - xcb_generic_event_t dev; - dev.response_type = 0; - main_loop_x11_event_handler_view ( &dev ); + rofi_view_maybe_update ( rofi_view_get_active () ); break; } }