rofi: Convert to XCB events

Signed-off-by: Quentin Glidic <sardemff7+git@sardemff7.net>
This commit is contained in:
Quentin Glidic 2016-02-19 22:43:53 +01:00
parent 2eb63456db
commit fe7ca2079d
12 changed files with 337 additions and 324 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "libgwater"]
path = libgwater
url = git://github.com/sardemff7/libgwater

View File

@ -1,6 +1,11 @@
# Specify automake version.
AUTOMAKE_OPTIONS = 1.11.3
ACLOCAL_AMFLAGS = -I libgwater ${ACLOCAL_FLAGS}
noinst_LIBRARIES =
include $(top_srcdir)/libgwater-xcb-nolibtool.mk
##
# Rofi the program
##
@ -60,6 +65,7 @@ rofi_SOURCES=\
rofi_CFLAGS=\
$(AM_CFLAGS)\
$(glib_CFLAGS)\
$(GW_XCB_CFLAGS)\
$(x11_CFLAGS)\
$(xinerama_CFLAGS)\
$(pango_CFLAGS)\
@ -73,6 +79,7 @@ rofi_CFLAGS=\
rofi_LDADD=\
$(glib_LIBS)\
$(GW_XCB_LIBS)\
$(x11_LIBS)\
$(xinerama_LIBS)\
$(libsn_LIBS)\

View File

@ -30,6 +30,12 @@ dnl System extensions
dnl ---------------------------------------------------------------------
AC_USE_SYSTEM_EXTENSIONS
dnl ---------------------------------------------------------------------
dnl Static libraries programs
dnl ---------------------------------------------------------------------
AC_PROG_RANLIB
AM_PROG_AR
dnl ---------------------------------------------------------------------
dnl Base CFLAGS
dnl ---------------------------------------------------------------------
@ -77,7 +83,8 @@ dnl ---------------------------------------------------------------------
dnl X11, Glib, Xinerama, Pango, Cairo, libstartup notification
dnl ---------------------------------------------------------------------
PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.40])
PKG_CHECK_MODULES([x11], [x11])
GW_CHECK_XCB([xcb-aux xkbcommon xkbcommon-x11])
PKG_CHECK_MODULES([x11], [x11 x11-xcb])
PKG_CHECK_MODULES([xinerama], [xinerama])
PKG_CHECK_MODULES([pango], [pango pangocairo])
PKG_CHECK_MODULES([cairo], [cairo cairo-xlib])

View File

@ -1,5 +1,6 @@
#ifndef ROFI_MAIN_H
#define ROFI_MAIN_H
#include <xcb/xcb.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <glib.h>

View File

@ -113,7 +113,7 @@ void textbox_draw ( textbox *tb, cairo_t *draw );
*
* @returns if the key was handled (1), unhandled(0) or handled and return was pressed (-1)
*/
int textbox_keypress ( textbox *tb, XEvent *ev, char *pad, int pad_len, KeySym key, Status stat );
int textbox_keypress ( textbox *tb, xcb_key_press_event_t *ev, char *pad, int pad_len, KeySym key, Status stat );
/**
* @param tb Handle to the textbox

View File

@ -69,7 +69,7 @@ struct RofiViewState
MenuFlags menu_flags;
// Handlers.
void ( *x11_event_loop )( struct RofiViewState *state, XEvent *ev );
void ( *x11_event_loop )( struct RofiViewState *state, xcb_generic_event_t *ev );
void ( *finalize )( struct RofiViewState *state );
};
/** @} */

View File

@ -47,7 +47,7 @@ void rofi_view_finalize ( RofiViewState *state );
MenuReturn rofi_view_get_return_value ( const RofiViewState *state );
unsigned int rofi_view_get_next_position ( const RofiViewState *state );
void rofi_view_itterrate ( RofiViewState *state, XEvent *event );
void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event );
unsigned int rofi_view_get_completed ( const RofiViewState *state );
const char * rofi_view_get_user_input ( const RofiViewState *state );

1
libgwater Submodule

@ -0,0 +1 @@
Subproject commit 50a0f8a4bc2d762c412b6dd1823a14949de583aa

View File

@ -0,0 +1,20 @@
noinst_LIBRARIES += \
libgwater/libgwater-xcb.a
libgwater_libgwater_xcb_a_SOURCES = \
libgwater/xcb/libgwater-xcb.c \
libgwater/xcb/libgwater-xcb.h
libgwater_libgwater_xcb_a_CFLAGS = \
$(AM_CFLAGS) \
$(GW_XCB_INTERNAL_CFLAGS)
GW_XCB_CFLAGS = \
-I$(srcdir)/libgwater/xcb \
$(GW_XCB_INTERNAL_CFLAGS)
GW_XCB_LIBS = \
libgwater/libgwater-xcb.a \
$(GW_XCB_INTERNAL_LIBS)

View File

@ -34,9 +34,13 @@
#include <errno.h>
#include <time.h>
#include <locale.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <xkbcommon/xkbcommon.h>
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/keysym.h>
@ -45,6 +49,8 @@
#include <glib-unix.h>
#include <libgwater-xcb.h>
#define SN_API_NOT_YET_FROZEN
#include <libsn/sn.h>
@ -63,13 +69,15 @@
gboolean daemon_mode = FALSE;
// Pidfile.
char *pidfile = NULL;
const char *cache_dir = NULL;
SnDisplay *sndisplay = NULL;
SnLauncheeContext *sncontext = NULL;
Display *display = NULL;
char *display_str = NULL;
char *config_path = NULL;
char *pidfile = NULL;
const char *cache_dir = NULL;
SnDisplay *sndisplay = NULL;
SnLauncheeContext *sncontext = NULL;
xcb_connection_t *xcb_connection = NULL;
xcb_screen_t *xcb_screen = NULL;
Display *display = NULL;
char *display_str = NULL;
char *config_path = NULL;
// Array of modi.
Mode **modi = NULL;
unsigned int num_modi = 0;
@ -77,7 +85,7 @@ unsigned int num_modi = 0;
unsigned int curr_switcher = 0;
GMainLoop *main_loop = NULL;
GSource *main_loop_source = NULL;
GWaterXcbSource *main_loop_source = NULL;
gboolean quiet = FALSE;
static int dmenu_mode = FALSE;
@ -277,19 +285,19 @@ int show_error_message ( const char *msg, int markup )
* Function that listens for global key-presses.
* This is only used when in daemon mode.
*/
static void handle_keypress ( XEvent *ev )
static void handle_keypress ( xcb_key_press_event_t *ev )
{
int index;
KeySym key = XkbKeycodeToKeysym ( display, ev->xkey.keycode, 0, 0 );
index = locate_switcher ( key, ev->xkey.state );
KeySym key = XkbKeycodeToKeysym ( display, ev->detail, 0, 0 );
index = locate_switcher ( key, ev->state );
if ( index >= 0 ) {
run_switcher ( index );
}
else {
fprintf ( stderr,
"Warning: Unhandled keypress in global keyhandler, keycode = %u mask = %u\n",
ev->xkey.keycode,
ev->xkey.state );
ev->detail,
ev->state );
}
}
@ -354,7 +362,7 @@ static void cleanup ()
rofi_view_workers_finalize ();
if ( main_loop != NULL ) {
if ( main_loop_source ) {
g_source_destroy ( main_loop_source );
g_water_xcb_source_unref ( main_loop_source );
}
g_main_loop_unref ( main_loop );
main_loop = NULL;
@ -542,19 +550,14 @@ static void reload_configuration ()
/**
* Process X11 events in the main-loop (gui-thread) of the application.
*/
static gboolean main_loop_x11_event_handler ( G_GNUC_UNUSED gpointer data )
static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UNUSED gpointer data )
{
RofiViewState *state = rofi_view_get_active ();
if ( sndisplay != NULL ) {
sn_xcb_display_process_event ( sndisplay, ev );
}
if ( state != NULL ) {
while ( XPending ( display ) ) {
XEvent ev;
// Read event, we know this won't block as we checked with XPending.
XNextEvent ( display, &ev );
if ( sndisplay != NULL ) {
sn_display_process_event ( sndisplay, &ev );
}
rofi_view_itterrate ( state, &ev );
}
rofi_view_itterrate ( state, ev );
if ( rofi_view_get_completed ( state ) ) {
// This menu is done.
rofi_view_finalize ( state );
@ -566,24 +569,14 @@ static gboolean main_loop_x11_event_handler ( G_GNUC_UNUSED gpointer data )
}
}
}
return G_SOURCE_CONTINUE;
}
// X11 produced an event. Consume them.
while ( XPending ( display ) ) {
XEvent ev;
// Read event, we know this won't block as we checked with XPending.
XNextEvent ( display, &ev );
if ( sndisplay != NULL ) {
sn_display_process_event ( sndisplay, &ev );
}
else {
// X11 produced an event. Consume them.
// If we get an event that does not belong to a window:
// Ignore it.
if ( ev.xany.window == None ) {
continue;
}
// If keypress, handle it.
if ( ev.type == KeyPress ) {
handle_keypress ( &ev );
if ( ( ev->response_type & ~0x80 ) == XCB_KEY_PRESS ) {
handle_keypress ( (xcb_key_press_event_t *) ev );
}
}
return G_SOURCE_CONTINUE;
@ -606,7 +599,7 @@ static gboolean main_loop_signal_handler_hup ( G_GNUC_UNUSED gpointer data )
// Grab the possibly new keybindings.
grab_global_keybindings ();
// We need to flush, otherwise the first key presses are not caught.
XFlush ( display );
xcb_flush ( xcb_connection );
return G_SOURCE_CONTINUE;
}
@ -722,8 +715,9 @@ static gboolean startup ( G_GNUC_UNUSED gpointer data )
sn_launchee_context_complete ( sncontext );
}
daemon_mode = TRUE;
XSelectInput ( display, DefaultRootWindow ( display ), KeyPressMask );
XFlush ( display );
uint32_t mask[] = { XCB_EVENT_MASK_KEY_PRESS };
xcb_change_window_attributes ( xcb_connection, xcb_screen->root, XCB_CW_EVENT_MASK, mask );
xcb_flush ( xcb_connection );
}
return G_SOURCE_REMOVE;
@ -807,6 +801,9 @@ int main ( int argc, char *argv[] )
}
TICK_N ( "Open Display" );
xcb_connection = XGetXCBConnection ( display );
xcb_screen = xcb_aux_get_screen ( xcb_connection, DefaultScreen ( display ) );
main_loop = g_main_loop_new ( NULL, FALSE );
TICK_N ( "Setup mainloop" );
@ -856,8 +853,7 @@ int main ( int argc, char *argv[] )
}
x11_setup ( display );
main_loop_source = x11_event_source_new ( display );
x11_event_source_set_callback ( main_loop_source, main_loop_x11_event_handler );
main_loop_source = g_water_xcb_source_new_for_connection ( NULL, xcb_connection, main_loop_x11_event_handler, NULL, NULL );
TICK_N ( "X11 Setup " );

View File

@ -24,6 +24,7 @@
*/
#include <config.h>
#include <xcb/xcb.h>
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
@ -532,7 +533,7 @@ static void textbox_cursor_del_word ( textbox *tb )
// 0 = unhandled
// 1 = handled
// -1 = handled and return pressed (finished)
int textbox_keypress ( textbox *tb, XEvent *ev, char *pad, int pad_len, KeySym key, Status stat )
int textbox_keypress ( textbox *tb, xcb_key_press_event_t *ev, char *pad, int pad_len, KeySym key, Status stat )
{
if ( !( tb->flags & TB_EDITABLE ) ) {
return 0;
@ -541,67 +542,67 @@ int textbox_keypress ( textbox *tb, XEvent *ev, char *pad, int pad_len, KeySym k
tb->blink = 2;
if ( stat == XLookupKeySym || stat == XLookupBoth ) {
// Left or Ctrl-b
if ( abe_test_action ( MOVE_CHAR_BACK, ev->xkey.state, key ) ) {
if ( abe_test_action ( MOVE_CHAR_BACK, ev->state, key ) ) {
textbox_cursor_dec ( tb );
return 2;
}
// Right or Ctrl-F
else if ( abe_test_action ( MOVE_CHAR_FORWARD, ev->xkey.state, key ) ) {
else if ( abe_test_action ( MOVE_CHAR_FORWARD, ev->state, key ) ) {
textbox_cursor_inc ( tb );
return 2;
}
// Ctrl-U: Kill from the beginning to the end of the line.
else if ( abe_test_action ( CLEAR_LINE, ev->xkey.state, key ) ) {
else if ( abe_test_action ( CLEAR_LINE, ev->state, key ) ) {
textbox_text ( tb, "" );
return 1;
}
// Ctrl-A
else if ( abe_test_action ( MOVE_FRONT, ev->xkey.state, key ) ) {
else if ( abe_test_action ( MOVE_FRONT, ev->state, key ) ) {
textbox_cursor ( tb, 0 );
return 2;
}
// Ctrl-E
else if ( abe_test_action ( MOVE_END, ev->xkey.state, key ) ) {
else if ( abe_test_action ( MOVE_END, ev->state, key ) ) {
textbox_cursor_end ( tb );
return 2;
}
// Ctrl-Alt-h
else if ( abe_test_action ( REMOVE_WORD_BACK, ev->xkey.state, key ) ) {
else if ( abe_test_action ( REMOVE_WORD_BACK, ev->state, key ) ) {
textbox_cursor_bkspc_word ( tb );
return 1;
}
// Ctrl-Alt-d
else if ( abe_test_action ( REMOVE_WORD_FORWARD, ev->xkey.state, key ) ) {
else if ( abe_test_action ( REMOVE_WORD_FORWARD, ev->state, key ) ) {
textbox_cursor_del_word ( tb );
return 1;
} // Delete or Ctrl-D
else if ( abe_test_action ( REMOVE_CHAR_FORWARD, ev->xkey.state, key ) ) {
else if ( abe_test_action ( REMOVE_CHAR_FORWARD, ev->state, key ) ) {
textbox_cursor_del ( tb );
return 1;
}
// Alt-B
else if ( abe_test_action ( MOVE_WORD_BACK, ev->xkey.state, key ) ) {
else if ( abe_test_action ( MOVE_WORD_BACK, ev->state, key ) ) {
textbox_cursor_dec_word ( tb );
return 2;
}
// Alt-F
else if ( abe_test_action ( MOVE_WORD_FORWARD, ev->xkey.state, key ) ) {
else if ( abe_test_action ( MOVE_WORD_FORWARD, ev->state, key ) ) {
textbox_cursor_inc_word ( tb );
return 2;
}
// BackSpace, Ctrl-h
else if ( abe_test_action ( REMOVE_CHAR_BACK, ev->xkey.state, key ) ) {
else if ( abe_test_action ( REMOVE_CHAR_BACK, ev->state, key ) ) {
textbox_cursor_bkspc ( tb );
return 1;
}
else if ( abe_test_action ( ACCEPT_CUSTOM, ev->xkey.state, key ) ) {
else if ( abe_test_action ( ACCEPT_CUSTOM, ev->state, key ) ) {
return -2;
}
else if ( abe_test_action ( ACCEPT_ENTRY_CONTINUE, ev->xkey.state, key ) ) {
else if ( abe_test_action ( ACCEPT_ENTRY_CONTINUE, ev->state, key ) ) {
return -3;
}
else if ( abe_test_action ( ACCEPT_ENTRY, ev->xkey.state, key ) ) {
else if ( abe_test_action ( ACCEPT_ENTRY, ev->state, key ) ) {
return -1;
}
}

View File

@ -308,9 +308,95 @@ unsigned int rofi_view_get_completed ( const RofiViewState *state )
return state->quit;
}
void rofi_view_itterrate ( RofiViewState *state, XEvent *event )
static void rofi_view_resize ( RofiViewState *state )
{
state->x11_event_loop ( state, event );
unsigned int sbw = config.line_margin + 8;
widget_move ( WIDGET ( state->scrollbar ), state->w - state->border - sbw, state->top_offset );
if ( config.sidebar_mode == TRUE ) {
int width = ( state->w - ( 2 * ( state->border ) + ( state->num_modi - 1 ) * config.line_margin ) ) / state->num_modi;
for ( unsigned int j = 0; j < state->num_modi; j++ ) {
textbox_moveresize ( state->modi[j],
state->border + j * ( width + config.line_margin ), state->h - state->line_height - state->border,
width, state->line_height );
textbox_draw ( state->modi[j], draw );
}
}
int entrybox_width = state->w - ( 2 * ( state->border ) ) - textbox_get_width ( state->prompt_tb )
- textbox_get_width ( state->case_indicator );
textbox_moveresize ( state->text, state->text->widget.x, state->text->widget.y, entrybox_width, state->line_height );
widget_move ( WIDGET ( state->case_indicator ), state->w - state->border - textbox_get_width ( state->case_indicator ), state->border );
/**
* Resize in Height
*/
{
unsigned int last_length = state->max_elements;
int element_height = state->line_height * config.element_height + config.line_margin;
// Calculated new number of boxes.
int h = ( state->h - state->top_offset - config.padding );
if ( config.sidebar_mode == TRUE ) {
h -= state->line_height + config.line_margin;
}
if ( h < 0 ) {
fprintf ( stderr, "Current padding %u (on each side) does not fit within visible window %u.\n", config.padding, state->h );
h = ( state->h - state->top_offset - state->h / 3 );
if ( config.sidebar_mode == TRUE ) {
h -= state->line_height + config.line_margin;
}
}
state->max_rows = MAX ( 1, ( h / element_height ) );
state->menu_lines = state->max_rows;
state->max_elements = state->max_rows * config.menu_columns;
// Free boxes no longer needed.
for ( unsigned int i = state->max_elements; i < last_length; i++ ) {
textbox_free ( state->boxes[i] );
}
// resize array.
state->boxes = g_realloc ( state->boxes, state->max_elements * sizeof ( textbox* ) );
int y_offset = state->top_offset;
int x_offset = state->border;
// Add newly added boxes.
for ( unsigned int i = last_length; i < state->max_elements; i++ ) {
state->boxes[i] = textbox_create ( 0, x_offset, y_offset,
state->element_width, element_height, NORMAL, "" );
}
scrollbar_resize ( state->scrollbar, -1, ( state->max_rows ) * ( element_height ) - config.line_margin );
}
state->rchanged = TRUE;
state->update = TRUE;
}
void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event )
{
uint8_t type = event->response_type & ~0x80;
switch ( type )
{
case XCB_EXPOSE:
state->update = TRUE;
break;
case XCB_CONFIGURE_NOTIFY:
{
xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) event;
if ( xce->window == main_window ) {
if ( state->x != xce->x || state->y != xce->y ) {
state->x = xce->x;
state->y = xce->y;
state->update = TRUE;
}
if ( state->w != xce->width || state->h != xce->height ) {
state->w = xce->width;
state->h = xce->height;
cairo_xlib_surface_set_size ( surface, state->w, state->h );
rofi_view_resize ( state );
}
}
}
break;
default:
state->x11_event_loop ( state, event, xkb );
}
rofi_view_update ( state );
}
const char * rofi_view_get_user_input ( const RofiViewState *state )
@ -881,7 +967,7 @@ void rofi_view_update ( RofiViewState *state )
*
* Handle paste event.
*/
static void rofi_view_paste ( RofiViewState *state, XSelectionEvent *xse )
static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t *xse )
{
if ( xse->property == netatoms[UTF8_STRING] ) {
gchar *text = window_get_text_prop ( display, main_window, netatoms[UTF8_STRING] );
@ -903,65 +989,6 @@ static void rofi_view_paste ( RofiViewState *state, XSelectionEvent *xse )
}
}
static void rofi_view_resize ( RofiViewState *state )
{
unsigned int sbw = config.line_margin + 8;
widget_move ( WIDGET ( state->scrollbar ), state->w - state->border - sbw, state->top_offset );
if ( config.sidebar_mode == TRUE ) {
int width = ( state->w - ( 2 * ( state->border ) + ( state->num_modi - 1 ) * config.line_margin ) ) / state->num_modi;
for ( unsigned int j = 0; j < state->num_modi; j++ ) {
textbox_moveresize ( state->modi[j],
state->border + j * ( width + config.line_margin ), state->h - state->line_height - state->border,
width, state->line_height );
textbox_draw ( state->modi[j], draw );
}
}
int entrybox_width = state->w - ( 2 * ( state->border ) ) - textbox_get_width ( state->prompt_tb )
- textbox_get_width ( state->case_indicator );
textbox_moveresize ( state->text, state->text->widget.x, state->text->widget.y, entrybox_width, state->line_height );
widget_move ( WIDGET ( state->case_indicator ), state->w - state->border - textbox_get_width ( state->case_indicator ), state->border );
/**
* Resize in Height
*/
{
unsigned int last_length = state->max_elements;
int element_height = state->line_height * config.element_height + config.line_margin;
// Calculated new number of boxes.
int h = ( state->h - state->top_offset - config.padding );
if ( config.sidebar_mode == TRUE ) {
h -= state->line_height + config.line_margin;
}
if ( h < 0 ) {
fprintf ( stderr, "Current padding %u (on each side) does not fit within visible window %u.\n", config.padding, state->h );
h = ( state->h - state->top_offset - state->h / 3 );
if ( config.sidebar_mode == TRUE ) {
h -= state->line_height + config.line_margin;
}
}
state->max_rows = MAX ( 1, ( h / element_height ) );
state->menu_lines = state->max_rows;
state->max_elements = state->max_rows * config.menu_columns;
// Free boxes no longer needed.
for ( unsigned int i = state->max_elements; i < last_length; i++ ) {
textbox_free ( state->boxes[i] );
}
// resize array.
state->boxes = g_realloc ( state->boxes, state->max_elements * sizeof ( textbox* ) );
int y_offset = state->top_offset;
int x_offset = state->border;
// Add newly added boxes.
for ( unsigned int i = last_length; i < state->max_elements; i++ ) {
state->boxes[i] = textbox_create ( 0, x_offset, y_offset,
state->element_width, element_height, NORMAL, "" );
}
scrollbar_resize ( state->scrollbar, -1, ( state->max_rows ) * ( element_height ) - config.line_margin );
}
state->rchanged = TRUE;
state->update = TRUE;
}
/**
* @param state Internal state of the menu.
* @param key the Key being pressed.
@ -1047,32 +1074,32 @@ static int rofi_view_keyboard_navigation ( RofiViewState *state, KeySym key, uns
return 0;
}
static void rofi_view_mouse_navigation ( RofiViewState *state, XButtonEvent *xbe )
static void rofi_view_mouse_navigation ( RofiViewState *state, xcb_button_press_event_t *xbe )
{
// Scroll event
if ( xbe->button > 3 ) {
if ( xbe->button == 4 ) {
if ( xbe->detail > 3 ) {
if ( xbe->detail == 4 ) {
rofi_view_nav_up ( state );
}
else if ( xbe->button == 5 ) {
else if ( xbe->detail == 5 ) {
rofi_view_nav_down ( state );
}
else if ( xbe->button == 6 ) {
else if ( xbe->detail == 6 ) {
rofi_view_nav_left ( state );
}
else if ( xbe->button == 7 ) {
else if ( xbe->detail == 7 ) {
rofi_view_nav_right ( state );
}
return;
}
else {
if ( state->scrollbar && widget_intersect ( &( state->scrollbar->widget ), xbe->x, xbe->y ) ) {
state->selected = scrollbar_clicked ( state->scrollbar, xbe->y );
if ( state->scrollbar && widget_intersect ( &( state->scrollbar->widget ), xbe->event_x, xbe->event_y ) ) {
state->selected = scrollbar_clicked ( state->scrollbar, xbe->event_y );
state->update = TRUE;
return;
}
for ( unsigned int i = 0; config.sidebar_mode == TRUE && i < state->num_modi; i++ ) {
if ( widget_intersect ( &( state->modi[i]->widget ), xbe->x, xbe->y ) ) {
if ( widget_intersect ( &( state->modi[i]->widget ), xbe->event_x, xbe->event_y ) ) {
( state->selected_line ) = 0;
state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK );
state->quit = TRUE;
@ -1081,7 +1108,7 @@ static void rofi_view_mouse_navigation ( RofiViewState *state, XButtonEvent *xbe
}
}
for ( unsigned int i = 0; i < state->max_elements; i++ ) {
if ( widget_intersect ( &( state->boxes[i]->widget ), xbe->x, xbe->y ) ) {
if ( widget_intersect ( &( state->boxes[i]->widget ), xbe->event_x, xbe->event_y ) ) {
// Only allow items that are visible to be selected.
if ( ( state->last_offset + i ) >= state->filtered_lines ) {
break;
@ -1219,192 +1246,165 @@ void rofi_view_setup_fake_transparency ( Display *display, RofiViewState *state
}
}
static void rofi_view_mainloop_iter ( RofiViewState *state, XEvent *ev )
static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t *ev )
{
if ( ev->type == KeymapNotify ) {
XRefreshKeyboardMapping ( &( ev->xmapping ) );
}
else if ( ev->type == ConfigureNotify ) {
XConfigureEvent xce = ev->xconfigure;
if ( xce.window == main_window ) {
if ( state->x != (int ) xce.x || state->y != (int) xce.y ) {
state->x = xce.x;
state->y = xce.y;
state->update = TRUE;
}
if ( state->w != (unsigned int) xce.width || state->h != (unsigned int ) xce.height ) {
state->w = xce.width;
state->h = xce.height;
cairo_xlib_surface_set_size ( surface, state->w, state->h );
rofi_view_resize ( state );
}
}
}
else if ( ev->type == FocusIn ) {
switch ( ev->response_type & ~0x80 )
{
case XCB_FOCUS_IN:
if ( ( state->menu_flags & MENU_NORMAL_WINDOW ) == 0 ) {
take_keyboard ( display, main_window );
}
}
else if ( ev->type == FocusOut ) {
break;
case XCB_FOCUS_OUT:
if ( ( state->menu_flags & MENU_NORMAL_WINDOW ) == 0 ) {
release_keyboard ( display );
}
}
// Handle event.
else if ( ev->type == Expose ) {
while ( XCheckTypedEvent ( display, Expose, ev ) ) {
;
}
state->update = TRUE;
}
else if ( ev->type == MotionNotify ) {
while ( XCheckTypedEvent ( display, MotionNotify, ev ) ) {
;
}
XMotionEvent xme = ev->xmotion;
if ( xme.x >= state->scrollbar->widget.x && xme.x < ( state->scrollbar->widget.x + state->scrollbar->widget.w ) ) {
state->selected = scrollbar_clicked ( state->scrollbar, xme.y );
break;
case XCB_MOTION_NOTIFY:
{
xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *) ev;
if ( xme->event_x >= state->scrollbar->widget.x && xme->event_x < ( state->scrollbar->widget.x + state->scrollbar->widget.w ) ) {
state->selected = scrollbar_clicked ( state->scrollbar, xme->event_y );
state->update = TRUE;
}
break;
}
// Button press event.
else if ( ev->type == ButtonPress ) {
while ( XCheckTypedEvent ( display, ButtonPress, ev ) ) {
;
}
rofi_view_mouse_navigation ( state, &( ev->xbutton ) );
}
case XCB_BUTTON_PRESS:
rofi_view_mouse_navigation ( state, (xcb_button_press_event_t *) ev );
break;
// Paste event.
else if ( ev->type == SelectionNotify ) {
do {
rofi_view_paste ( state, &( ev->xselection ) );
} while ( XCheckTypedEvent ( display, SelectionNotify, ev ) );
}
// Key press event.
else if ( ev->type == KeyPress ) {
do {
// This is needed for letting the Input Method handle combined keys.
// E.g. `e into è
if ( XFilterEvent ( ev, main_window ) ) {
continue;
case XCB_SELECTION_NOTIFY:
rofi_view_paste ( state, (xcb_selection_notify_event_t *) ev );
break;
case XCB_KEY_PRESS:
{
xcb_key_press_event_t *xkpe = (xcb_key_press_event_t *) ev;
XEvent fake_event;
fake_event.type = KeyPress;
fake_event.xany.display = display;
fake_event.xany.window = xkpe->event;
fake_event.xkey.state = xkpe->state;
fake_event.xkey.keycode = xkpe->detail;
// This is needed for letting the Input Method handle combined keys.
// E.g. `e into è
Status stat;
char pad[32];
KeySym key; // = XkbKeycodeToKeysym ( display, ev->xkey.keycode, 0, 0 );
int len = Xutf8LookupString ( xic, &( fake_event.xkey ), pad, sizeof ( pad ), &key, &stat );
pad[len] = 0;
if ( stat == XLookupKeySym || stat == XLookupBoth ) {
// Handling of paste
if ( abe_test_action ( PASTE_PRIMARY, xkpe->state, key ) ) {
XConvertSelection ( display, XA_PRIMARY, netatoms[UTF8_STRING], netatoms[UTF8_STRING], main_window, CurrentTime );
}
Status stat;
char pad[32];
KeySym key; // = XkbKeycodeToKeysym ( display, ev->xkey.keycode, 0, 0 );
int len = Xutf8LookupString ( xic, &( ev->xkey ), pad, sizeof ( pad ), &key, &stat );
pad[len] = 0;
if ( stat == XLookupKeySym || stat == XLookupBoth ) {
// Handling of paste
if ( abe_test_action ( PASTE_PRIMARY, ev->xkey.state, key ) ) {
XConvertSelection ( display, XA_PRIMARY, netatoms[UTF8_STRING], netatoms[UTF8_STRING], main_window, CurrentTime );
}
else if ( abe_test_action ( PASTE_SECONDARY, ev->xkey.state, key ) ) {
XConvertSelection ( display, netatoms[CLIPBOARD], netatoms[UTF8_STRING], netatoms[UTF8_STRING], main_window,
CurrentTime );
}
if ( abe_test_action ( SCREENSHOT, ev->xkey.state, key ) ) {
menu_capture_screenshot ( );
break;
}
if ( abe_test_action ( TOGGLE_SORT, ev->xkey.state, key ) ) {
config.levenshtein_sort = !config.levenshtein_sort;
state->refilter = TRUE;
state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () );
break;
}
else if ( abe_test_action ( MODE_PREVIOUS, ev->xkey.state, key ) ) {
state->retv = MENU_PREVIOUS;
( state->selected_line ) = 0;
else if ( abe_test_action ( PASTE_SECONDARY, xkpe->state, key ) ) {
XConvertSelection ( display, netatoms[CLIPBOARD], netatoms[UTF8_STRING], netatoms[UTF8_STRING], main_window,
CurrentTime );
}
if ( abe_test_action ( SCREENSHOT, xkpe->state, key ) ) {
menu_capture_screenshot ( );
break;
}
if ( abe_test_action ( TOGGLE_SORT, xkpe->state, key ) ) {
config.levenshtein_sort = !config.levenshtein_sort;
state->refilter = TRUE;
state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () );
break;
}
else if ( abe_test_action ( MODE_PREVIOUS, xkpe->state, key ) ) {
state->retv = MENU_PREVIOUS;
( state->selected_line ) = 0;
state->quit = TRUE;
break;
}
// Menu navigation.
else if ( abe_test_action ( MODE_NEXT, xkpe->state, key ) ) {
state->retv = MENU_NEXT;
( state->selected_line ) = 0;
state->quit = TRUE;
break;
}
// Toggle case sensitivity.
else if ( abe_test_action ( TOGGLE_CASE_SENSITIVITY, xkpe->state, key ) ) {
config.case_sensitive = !config.case_sensitive;
( state->selected_line ) = 0;
state->refilter = TRUE;
state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () );
break;
}
// Special delete entry command.
else if ( abe_test_action ( DELETE_ENTRY, xkpe->state, key ) ) {
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
state->retv = MENU_ENTRY_DELETE;
state->quit = TRUE;
break;
}
// Menu navigation.
else if ( abe_test_action ( MODE_NEXT, ev->xkey.state, key ) ) {
state->retv = MENU_NEXT;
( state->selected_line ) = 0;
state->quit = TRUE;
break;
}
// Toggle case sensitivity.
else if ( abe_test_action ( TOGGLE_CASE_SENSITIVITY, ev->xkey.state, key ) ) {
config.case_sensitive = !config.case_sensitive;
( state->selected_line ) = 0;
state->refilter = TRUE;
state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () );
break;
}
// Special delete entry command.
else if ( abe_test_action ( DELETE_ENTRY, ev->xkey.state, key ) ) {
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
state->retv = MENU_ENTRY_DELETE;
state->quit = TRUE;
break;
}
}
for ( unsigned int a = CUSTOM_1; a <= CUSTOM_19; a++ ) {
if ( abe_test_action ( a, ev->xkey.state, key ) ) {
state->selected_line = UINT32_MAX;
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
}
state->retv = MENU_QUICK_SWITCH | ( ( a - CUSTOM_1 ) & MENU_LOWER_MASK );
state->quit = TRUE;
break;
}
}
if ( rofi_view_keyboard_navigation ( state, key, ev->xkey.state ) ) {
continue;
}
}
{
// Skip if we detected key before.
if ( state->quit ) {
continue;
}
int rc = textbox_keypress ( state->text, ev, pad, len, key, stat );
// Row is accepted.
if ( rc < 0 ) {
int shift = ( ( ev->xkey.state & ShiftMask ) == ShiftMask );
// If a valid item is selected, return that..
for ( unsigned int a = CUSTOM_1; a <= CUSTOM_19; a++ ) {
if ( abe_test_action ( a, xkpe->state, key ) ) {
state->selected_line = UINT32_MAX;
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
if ( strlen ( state->text->text ) > 0 && rc == -2 ) {
state->retv = MENU_CUSTOM_INPUT;
}
else {
state->retv = MENU_OK;
}
}
else if ( strlen ( state->text->text ) > 0 ) {
state->retv = MENU_CUSTOM_INPUT;
}
else{
// Nothing entered and nothing selected.
state->retv = MENU_CUSTOM_INPUT;
}
if ( shift ) {
state->retv |= MENU_SHIFT;
}
state->retv = MENU_QUICK_SWITCH | ( ( a - CUSTOM_1 ) & MENU_LOWER_MASK );
state->quit = TRUE;
}
// Key press is handled by entry box.
else if ( rc == 1 ) {
state->refilter = TRUE;
state->update = TRUE;
}
else if ( rc == 2 ) {
// redraw.
state->update = TRUE;
break;
}
}
} while ( XCheckTypedEvent ( display, KeyPress, ev ) );
if ( rofi_view_keyboard_navigation ( state, key, xkpe->state ) ) {
break;
}
}
{
// Skip if we detected key before.
if ( state->quit ) {
break;
}
int rc = textbox_keypress ( state->text, xkpe, pad, len, key, stat );
// Row is accepted.
if ( rc < 0 ) {
int shift = ( ( xkpe->state & ShiftMask ) == ShiftMask );
// If a valid item is selected, return that..
state->selected_line = UINT32_MAX;
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
if ( strlen ( state->text->text ) > 0 && rc == -2 ) {
state->retv = MENU_CUSTOM_INPUT;
}
else {
state->retv = MENU_OK;
}
}
else if ( strlen ( state->text->text ) > 0 ) {
state->retv = MENU_CUSTOM_INPUT;
}
else{
// Nothing entered and nothing selected.
state->retv = MENU_CUSTOM_INPUT;
}
if ( shift ) {
state->retv |= MENU_SHIFT;
}
state->quit = TRUE;
}
// Key press is handled by entry box.
else if ( rc == 1 ) {
state->refilter = TRUE;
state->update = TRUE;
}
else if ( rc == 2 ) {
// redraw.
state->update = TRUE;
}
}
break;
}
}
// Update if requested.
if ( state->refilter ) {
@ -1625,38 +1625,15 @@ RofiViewState *rofi_view_create ( Mode *sw,
}
return state;
}
static void __error_dialog_event_loop ( RofiViewState *state, XEvent *ev )
static void __error_dialog_event_loop ( RofiViewState *state, xcb_generic_event_t *ev )
{
// Handle event.
if ( ev->type == Expose ) {
while ( XCheckTypedEvent ( display, Expose, ev ) ) {
;
}
state->update = TRUE;
}
else if ( ev->type == ConfigureNotify ) {
XConfigureEvent xce = ev->xconfigure;
if ( xce.window == main_window ) {
if ( state->x != (int ) xce.x || state->y != (int) xce.y ) {
state->x = xce.x;
state->y = xce.y;
state->update = TRUE;
}
if ( state->w != (unsigned int) xce.width || state->h != (unsigned int ) xce.height ) {
state->w = xce.width;
state->h = xce.height;
cairo_xlib_surface_set_size ( surface, state->w, state->h );
}
}
}
switch ( ev->response_type & ~0x80 )
{
// Key press event.
else if ( ev->type == KeyPress ) {
while ( XCheckTypedEvent ( display, KeyPress, ev ) ) {
;
}
case XCB_KEY_PRESS:
state->quit = TRUE;
}
rofi_view_update ( state );
}
void process_result_error ( RofiViewState *state );
void rofi_view_error_dialog ( const char *msg, int markup )