2016-02-06 12:06:58 +00:00
/**
* rofi
*
* MIT / X11 License
2017-01-03 17:02:21 +00:00
* Modified 2016 - 2017 Qball Cow < qball @ gmpclient . org >
2016-02-06 12:06:58 +00:00
*
* Permission is hereby granted , free of charge , to any person obtaining
* a copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sublicense , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
* OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT ,
* TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
# include <config.h>
2017-01-09 08:15:19 +00:00
# include <limits.h>
2016-02-06 12:06:58 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <stdint.h>
# include <signal.h>
# include <errno.h>
# include <time.h>
# include <locale.h>
2016-02-21 12:10:32 +00:00
# include <xkbcommon/xkbcommon-x11.h>
# include <xcb/xkb.h>
2016-02-27 16:42:50 +00:00
# include <xcb/xcb_ewmh.h>
2016-02-06 12:06:58 +00:00
# include <cairo.h>
2016-02-21 18:42:32 +00:00
# include <cairo-xcb.h>
2016-02-06 12:06:58 +00:00
# define SN_API_NOT_YET_FROZEN
# include <libsn/sn.h>
# include "settings.h"
2016-02-07 19:38:34 +00:00
# include "rofi.h"
2016-02-06 12:06:58 +00:00
# include "mode.h"
2016-03-01 17:11:55 +00:00
# include "xcb-internal.h"
2016-02-21 12:10:32 +00:00
# include "xkb-internal.h"
2016-02-06 12:06:58 +00:00
# include "helper.h"
# include "x11-helper.h"
# include "xrmoptions.h"
# include "dialogs/dialogs.h"
2016-02-07 11:31:17 +00:00
# include "view.h"
# include "view-internal.h"
2016-12-09 21:16:31 +00:00
# include "theme.h"
2016-10-25 20:45:11 +00:00
/** The Rofi View log domain */
2016-09-17 16:35:03 +00:00
# define LOG_DOMAIN "View"
2016-09-17 15:49:45 +00:00
2017-01-09 08:15:19 +00:00
# define FUZZY_SCORER_MAX_LENGTH 256
# define MIN_SCORE (INT_MIN / 2)
# define LEADING_GAP_SCORE -4
# define GAP_SCORE -5
# define WORD_START_SCORE 50
# define NON_WORD_SCORE 40
# define CAMEL_SCORE (WORD_START_SCORE + GAP_SCORE - 1)
# define CONSECUTIVE_SCORE (WORD_START_SCORE + GAP_SCORE)
# define PATTERN_NON_START_MULTIPLIER 1
# define PATTERN_START_MULTIPLIER 2
2016-03-01 17:11:55 +00:00
# include "xcb.h"
2017-01-01 01:57:04 +00:00
/**
* @ param state The handle to the view
2017-01-01 17:40:49 +00:00
* @ param qr Indicate if queue_redraw should be called on changes .
2017-01-01 01:57:04 +00:00
*
* Update the state of the view . This involves filter state .
*/
void rofi_view_update ( RofiViewState * state , gboolean qr ) ;
2016-02-10 18:40:19 +00:00
2016-10-08 16:57:59 +00:00
static int rofi_view_calculate_height ( RofiViewState * state ) ;
2016-03-19 12:29:04 +00:00
2016-11-15 07:24:06 +00:00
/** Thread pool used for filtering */
2016-11-15 07:24:27 +00:00
GThreadPool * tpool = NULL ;
2016-02-08 17:16:16 +00:00
2016-11-15 07:24:06 +00:00
/** Global pointer to the currently active RofiViewState */
2016-09-17 15:49:45 +00:00
RofiViewState * current_active_menu = NULL ;
2016-03-05 10:18:23 +00:00
2016-11-15 07:24:06 +00:00
/**
* Structure holding cached state .
*/
2016-03-05 10:18:23 +00:00
struct
{
2016-11-15 07:24:06 +00:00
/** main x11 windows */
2017-01-08 20:36:06 +00:00
xcb_window_t main_window ;
2016-11-15 07:24:06 +00:00
/** surface containing the fake background. */
2017-01-08 20:36:06 +00:00
cairo_surface_t * fake_bg ;
2016-11-15 07:24:06 +00:00
/** Draw context for main window */
2017-01-08 20:36:06 +00:00
xcb_gcontext_t gc ;
2016-11-15 07:24:06 +00:00
/** Main X11 side pixmap to draw on. */
2017-01-08 20:36:06 +00:00
xcb_pixmap_t edit_pixmap ;
2016-11-15 07:24:06 +00:00
/** Cairo Surface for edit_pixmap */
2017-01-08 20:36:06 +00:00
cairo_surface_t * edit_surf ;
2016-11-15 07:24:06 +00:00
/** Drawable context for edit_surf */
2017-01-08 20:36:06 +00:00
cairo_t * edit_draw ;
2016-11-15 07:24:06 +00:00
/** Indicate that fake background should be drawn relative to the window */
2017-01-08 20:36:06 +00:00
int fake_bgrel ;
2016-11-15 07:24:06 +00:00
/** Main flags */
2017-01-08 20:36:06 +00:00
MenuFlags flags ;
2016-11-15 07:24:06 +00:00
/** List of stacked views */
2017-01-08 20:36:06 +00:00
GQueue views ;
2016-11-15 07:24:06 +00:00
/** Current work area */
2017-01-08 20:36:06 +00:00
workarea mon ;
2016-11-15 07:24:06 +00:00
/** timeout for reloading */
2017-01-08 20:36:06 +00:00
guint idle_timeout ;
2016-11-15 07:24:06 +00:00
/** debug counter for redraws */
2017-01-03 11:19:52 +00:00
unsigned long long count ;
2016-11-15 07:24:06 +00:00
/** redraw idle time. */
2017-01-08 20:36:06 +00:00
guint repaint_source ;
2017-01-08 16:38:21 +00:00
/** Window fullscreen */
2017-01-08 20:36:06 +00:00
gboolean fullscreen ;
2016-03-05 10:18:23 +00:00
} CacheState = {
2016-11-15 07:24:27 +00:00
. main_window = XCB_WINDOW_NONE ,
. fake_bg = NULL ,
. edit_surf = NULL ,
. edit_draw = NULL ,
. fake_bgrel = FALSE ,
. flags = MENU_NORMAL ,
. views = G_QUEUE_INIT ,
. idle_timeout = 0 ,
. count = 0L ,
2016-11-15 07:24:06 +00:00
. repaint_source = 0 ,
2017-01-08 20:36:06 +00:00
. fullscreen = FALSE ,
2016-03-05 10:18:23 +00:00
} ;
2016-02-06 13:27:36 +00:00
2017-01-08 20:36:06 +00:00
void rofi_view_get_current_monitor ( int * width , int * height )
2017-01-03 18:23:09 +00:00
{
2017-01-08 20:36:06 +00:00
if ( width ) {
2017-01-03 18:23:09 +00:00
* width = CacheState . mon . w ;
}
2017-01-08 20:36:06 +00:00
if ( height ) {
2017-01-03 18:23:09 +00:00
* height = CacheState . mon . h ;
}
}
2016-02-06 13:27:36 +00:00
static char * get_matching_state ( void )
{
if ( config . case_sensitive ) {
if ( config . levenshtein_sort ) {
return " ± " ;
}
else {
return " - " ;
}
}
else {
if ( config . levenshtein_sort ) {
return " + " ;
}
}
return " " ;
}
2016-02-07 19:38:34 +00:00
2016-02-06 13:27:36 +00:00
/**
* Levenshtein Sorting .
*/
static int lev_sort ( const void * p1 , const void * p2 , void * arg )
{
const int * a = p1 ;
const int * b = p2 ;
int * distances = arg ;
return distances [ * a ] - distances [ * b ] ;
}
/**
* Stores a screenshot of Rofi at that point in time .
*/
static void menu_capture_screenshot ( void )
{
const char * outp = g_getenv ( " ROFI_PNG_OUTPUT " ) ;
2016-11-11 15:29:59 +00:00
if ( CacheState . edit_surf = = NULL ) {
2016-02-06 13:27:36 +00:00
// Nothing to store.
fprintf ( stderr , " There is no rofi surface to store \n " ) ;
return ;
}
const char * xdg_pict_dir = g_get_user_special_dir ( G_USER_DIRECTORY_PICTURES ) ;
if ( outp = = NULL & & xdg_pict_dir = = NULL ) {
fprintf ( stderr , " XDG user picture directory or ROFI_PNG_OUTPUT is not set. Cannot store screenshot. \n " ) ;
return ;
}
// Get current time.
GDateTime * now = g_date_time_new_now_local ( ) ;
// Format filename.
char * timestmp = g_date_time_format ( now , " rofi-%Y-%m-%d-%H%M " ) ;
char * filename = g_strdup_printf ( " %s.png " , timestmp ) ;
// Build full path
char * fpath = NULL ;
if ( outp = = NULL ) {
int index = 0 ;
fpath = g_build_filename ( xdg_pict_dir , filename , NULL ) ;
while ( g_file_test ( fpath , G_FILE_TEST_EXISTS ) & & index < 99 ) {
g_free ( fpath ) ;
g_free ( filename ) ;
// Try the next index.
index + + ;
// Format filename.
filename = g_strdup_printf ( " %s-%d.png " , timestmp , index ) ;
// Build full path
fpath = g_build_filename ( xdg_pict_dir , filename , NULL ) ;
}
}
else {
fpath = g_strdup ( outp ) ;
}
fprintf ( stderr , color_green " Storing screenshot %s \n " color_reset , fpath ) ;
2016-11-11 15:29:59 +00:00
cairo_status_t status = cairo_surface_write_to_png ( CacheState . edit_surf , fpath ) ;
2016-02-06 13:27:36 +00:00
if ( status ! = CAIRO_STATUS_SUCCESS ) {
fprintf ( stderr , " Failed to produce screenshot '%s', got error: '%s' \n " , filename ,
cairo_status_to_string ( status ) ) ;
}
g_free ( fpath ) ;
g_free ( filename ) ;
g_free ( timestmp ) ;
g_date_time_unref ( now ) ;
}
2016-02-07 19:38:34 +00:00
2016-11-11 15:29:59 +00:00
static gboolean rofi_view_repaint ( G_GNUC_UNUSED void * data )
{
if ( current_active_menu ) {
2017-01-01 01:57:04 +00:00
// Repaint the view (if needed).
2017-01-01 11:16:04 +00:00
// After a resize the edit_pixmap surface might not contain anything anymore.
// If we already re-painted, this does nothing.
2017-01-08 20:36:06 +00:00
rofi_view_update ( current_active_menu , FALSE ) ;
2016-12-15 08:09:14 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " expose event " ) ;
2016-11-11 15:29:59 +00:00
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 " ) ;
2016-11-15 07:24:06 +00:00
CacheState . repaint_source = 0 ;
2016-11-11 15:29:59 +00:00
}
return G_SOURCE_REMOVE ;
}
2016-10-08 16:57:59 +00:00
static void rofi_view_update_prompt ( RofiViewState * state )
{
if ( state - > prompt ) {
2016-10-28 19:52:22 +00:00
const char * str = mode_get_display_name ( state - > sw ) ;
2016-11-11 15:29:59 +00:00
if ( ( state - > menu_flags & MENU_PROMPT_COLON ) ! = 0 ) {
char * pr = g_strconcat ( str , " : " , NULL ) ;
2016-11-07 07:51:23 +00:00
textbox_text ( state - > prompt , pr ) ;
2016-11-11 15:29:59 +00:00
g_free ( pr ) ;
2016-11-07 07:51:23 +00:00
}
else {
textbox_text ( state - > prompt , str ) ;
}
2016-10-08 16:57:59 +00:00
}
}
2016-02-06 13:27:36 +00:00
/**
2016-06-01 05:34:41 +00:00
* Calculates the window position
2016-02-06 13:27:36 +00:00
*/
2016-09-17 15:49:45 +00:00
static void rofi_view_calculate_window_position ( RofiViewState * state )
2016-02-06 13:27:36 +00:00
{
2017-01-06 18:04:25 +00:00
int location = rofi_theme_get_position ( WIDGET ( state - > main_window ) , " location " , config . location ) ;
2017-01-08 20:36:06 +00:00
int anchor = location ;
2017-01-08 16:18:49 +00:00
if ( ! listview_get_fixed_num_lines ( state - > list_view ) ) {
2017-01-06 18:04:25 +00:00
anchor = location ;
if ( location = = WL_CENTER ) {
anchor = WL_NORTH ;
2017-01-08 20:36:06 +00:00
}
else if ( location = = WL_EAST ) {
2017-01-06 18:04:25 +00:00
anchor = WL_NORTH_EAST ;
2017-01-08 20:36:06 +00:00
}
else if ( location = = WL_WEST ) {
2017-01-06 18:04:25 +00:00
anchor = WL_NORTH_WEST ;
}
}
anchor = rofi_theme_get_position ( WIDGET ( state - > main_window ) , " anchor " , anchor ) ;
2017-01-08 16:38:21 +00:00
if ( CacheState . fullscreen ) {
2016-09-17 15:49:45 +00:00
state - > x = CacheState . mon . x ;
state - > y = CacheState . mon . y ;
2016-03-16 17:53:33 +00:00
return ;
}
2017-01-06 18:04:25 +00:00
state - > y = CacheState . mon . y + ( CacheState . mon . h ) / 2 ;
state - > x = CacheState . mon . x + ( CacheState . mon . w ) / 2 ;
2016-02-06 13:27:36 +00:00
// Determine window location
2017-01-06 18:04:25 +00:00
switch ( location )
2016-02-06 13:27:36 +00:00
{
case WL_NORTH_WEST :
2016-09-17 15:49:45 +00:00
state - > x = CacheState . mon . x ;
2016-02-06 13:27:36 +00:00
case WL_NORTH :
2016-09-17 15:49:45 +00:00
state - > y = CacheState . mon . y ;
2016-02-06 13:27:36 +00:00
break ;
case WL_NORTH_EAST :
2016-09-17 15:49:45 +00:00
state - > y = CacheState . mon . y ;
2016-02-06 13:27:36 +00:00
case WL_EAST :
2017-01-06 18:04:25 +00:00
state - > x = CacheState . mon . x + CacheState . mon . w ;
2016-02-06 13:27:36 +00:00
break ;
2017-01-06 18:04:25 +00:00
case WL_SOUTH_EAST :
state - > x = CacheState . mon . x + CacheState . mon . w ;
2016-02-06 13:27:36 +00:00
case WL_SOUTH :
2017-01-06 18:04:25 +00:00
state - > y = CacheState . mon . y + CacheState . mon . h ;
2016-02-06 13:27:36 +00:00
break ;
case WL_SOUTH_WEST :
2017-01-06 18:04:25 +00:00
state - > y = CacheState . mon . y + CacheState . mon . h ;
2016-02-06 13:27:36 +00:00
case WL_WEST :
2016-09-17 15:49:45 +00:00
state - > x = CacheState . mon . x ;
2016-02-06 13:27:36 +00:00
break ;
case WL_CENTER :
default :
break ;
}
2017-01-06 18:04:25 +00:00
switch ( anchor )
{
2017-01-08 20:36:06 +00:00
case WL_SOUTH_WEST :
state - > y - = state - > height ;
break ;
case WL_SOUTH :
state - > x - = state - > width / 2 ;
state - > y - = state - > height ;
break ;
case WL_SOUTH_EAST :
state - > x - = state - > width ;
state - > y - = state - > height ;
break ;
case WL_NORTH_EAST :
state - > x - = state - > width ;
break ;
case WL_NORTH_WEST :
break ;
case WL_NORTH :
state - > x - = state - > width / 2 ;
break ;
case WL_EAST :
state - > x - = state - > width ;
state - > y - = state - > height / 2 ;
break ;
case WL_WEST :
state - > y - = state - > height / 2 ;
break ;
case WL_CENTER :
state - > y - = state - > height / 2 ;
state - > x - = state - > width / 2 ;
break ;
default :
break ;
2017-01-06 18:04:25 +00:00
}
2016-02-06 13:27:36 +00:00
// Apply offset.
2016-09-17 15:49:45 +00:00
state - > x + = config . x_offset ;
state - > y + = config . y_offset ;
2016-02-06 13:27:36 +00:00
}
2016-02-07 19:38:34 +00:00
2016-09-17 15:49:45 +00:00
static void rofi_view_window_update_size ( RofiViewState * state )
2016-09-16 18:28:59 +00:00
{
uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT ;
2016-09-17 15:49:45 +00:00
uint32_t vals [ ] = { state - > x , state - > y , state - > width , state - > height } ;
2016-09-16 18:28:59 +00:00
// Display it.
xcb_configure_window ( xcb - > connection , CacheState . main_window , mask , vals ) ;
2016-11-11 15:29:59 +00:00
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 ) ;
2016-12-27 21:19:15 +00:00
2017-01-02 20:19:27 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Re-size window based internal request: %dx%d. " , state - > width , state - > height ) ;
2016-12-27 21:19:15 +00:00
// Should wrap main window in a widget.
2016-12-29 20:58:32 +00:00
widget_resize ( WIDGET ( state - > main_window ) , state - > width , state - > height ) ;
2016-09-17 15:49:45 +00:00
}
2016-09-16 18:28:59 +00:00
2016-09-17 15:49:45 +00:00
static gboolean rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data )
{
if ( current_active_menu ) {
current_active_menu - > reload = TRUE ;
current_active_menu - > refilter = TRUE ;
2016-11-11 15:29:59 +00:00
rofi_view_queue_redraw ( ) ;
2016-09-17 15:49:45 +00:00
}
CacheState . idle_timeout = 0 ;
return G_SOURCE_REMOVE ;
2016-09-16 18:28:59 +00:00
}
2016-09-17 15:49:45 +00:00
void rofi_view_reload ( void )
{
2016-09-17 16:57:44 +00:00
// @TODO add check if current view is equal to the callee
2016-09-17 15:49:45 +00:00
if ( CacheState . idle_timeout = = 0 ) {
CacheState . idle_timeout = g_timeout_add ( 1000 / 10 , rofi_view_reload_idle , NULL ) ;
}
}
2016-03-01 17:11:55 +00:00
void rofi_view_queue_redraw ( void )
2016-02-06 12:06:58 +00:00
{
2016-11-15 07:24:06 +00:00
if ( current_active_menu & & CacheState . repaint_source = = 0 ) {
2016-11-11 15:29:59 +00:00
CacheState . count + + ;
2017-01-03 11:19:52 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " redraw %llu " , CacheState . count ) ;
2016-11-15 07:24:06 +00:00
CacheState . repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE , rofi_view_repaint , NULL , NULL ) ;
2016-02-06 12:06:58 +00:00
}
}
void rofi_view_restart ( RofiViewState * state )
{
state - > quit = FALSE ;
state - > retv = MENU_CANCEL ;
}
2016-02-07 19:38:34 +00:00
2016-02-08 17:16:16 +00:00
RofiViewState * rofi_view_get_active ( void )
{
return current_active_menu ;
}
2016-02-06 12:06:58 +00:00
void rofi_view_set_active ( RofiViewState * state )
{
2016-04-19 20:10:34 +00:00
if ( current_active_menu ! = NULL & & state ! = NULL ) {
2016-11-02 17:20:17 +00:00
g_queue_push_head ( & ( CacheState . views ) , current_active_menu ) ;
2016-10-08 16:57:59 +00:00
// TODO check.
current_active_menu = state ;
2016-09-17 15:49:45 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " stack view. " ) ;
rofi_view_window_update_size ( current_active_menu ) ;
2016-04-19 20:10:34 +00:00
rofi_view_queue_redraw ( ) ;
return ;
}
else if ( state = = NULL & & ! g_queue_is_empty ( & ( CacheState . views ) ) ) {
2016-09-17 15:49:45 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " pop view. " ) ;
2016-04-19 20:10:34 +00:00
current_active_menu = g_queue_pop_head ( & ( CacheState . views ) ) ;
2016-09-17 15:49:45 +00:00
rofi_view_window_update_size ( current_active_menu ) ;
2016-04-19 20:10:34 +00:00
rofi_view_queue_redraw ( ) ;
return ;
}
2016-02-06 12:06:58 +00:00
g_assert ( ( current_active_menu = = NULL & & state ! = NULL ) | | ( current_active_menu ! = NULL & & state = = NULL ) ) ;
current_active_menu = state ;
2016-11-14 15:31:26 +00:00
rofi_view_queue_redraw ( ) ;
2016-02-06 12:06:58 +00:00
}
void rofi_view_set_selected_line ( RofiViewState * state , unsigned int selected_line )
{
state - > selected_line = selected_line ;
// Find the line.
2016-10-08 16:57:59 +00:00
unsigned int selected = 0 ;
for ( unsigned int i = 0 ; ( ( state - > selected_line ) ) < UINT32_MAX & & ! selected & & i < state - > filtered_lines ; i + + ) {
2016-02-06 12:06:58 +00:00
if ( state - > line_map [ i ] = = ( state - > selected_line ) ) {
2016-10-08 16:57:59 +00:00
selected = i ;
2016-02-06 12:06:58 +00:00
break ;
}
}
2016-10-08 16:57:59 +00:00
listview_set_selected ( state - > list_view , selected ) ;
2016-04-19 20:10:34 +00:00
xcb_clear_area ( xcb - > connection , CacheState . main_window , 1 , 0 , 0 , 1 , 1 ) ;
2016-03-01 17:11:55 +00:00
xcb_flush ( xcb - > connection ) ;
2016-02-06 12:06:58 +00:00
}
void rofi_view_free ( RofiViewState * state )
{
2016-10-11 06:18:34 +00:00
if ( state - > tokens ) {
tokenize_free ( state - > tokens ) ;
state - > tokens = NULL ;
}
2016-02-06 12:06:58 +00:00
// Do this here?
// Wait for final release?
2016-12-28 18:42:14 +00:00
widget_free ( WIDGET ( state - > main_window ) ) ;
2016-09-27 20:38:26 +00:00
widget_free ( WIDGET ( state - > overlay ) ) ;
2016-02-06 12:06:58 +00:00
g_free ( state - > line_map ) ;
g_free ( state - > distance ) ;
// Free the switcher boxes.
// When state is free'ed we should no longer need these.
if ( config . sidebar_mode = = TRUE ) {
g_free ( state - > modi ) ;
state - > num_modi = 0 ;
}
2016-03-03 11:46:12 +00:00
g_free ( state ) ;
2016-02-06 12:06:58 +00:00
}
MenuReturn rofi_view_get_return_value ( const RofiViewState * state )
{
return state - > retv ;
}
2016-02-07 19:38:34 +00:00
2016-02-06 12:06:58 +00:00
unsigned int rofi_view_get_selected_line ( const RofiViewState * state )
{
return state - > selected_line ;
}
unsigned int rofi_view_get_next_position ( const RofiViewState * state )
{
unsigned int next_pos = state - > selected_line ;
2016-10-08 16:57:59 +00:00
unsigned int selected = listview_get_selected ( state - > list_view ) ;
if ( ( selected + 1 ) < state - > num_lines ) {
( next_pos ) = state - > line_map [ selected + 1 ] ;
2016-02-06 12:06:58 +00:00
}
return next_pos ;
}
unsigned int rofi_view_get_completed ( const RofiViewState * state )
{
return state - > quit ;
}
2016-02-07 19:38:34 +00:00
2016-02-06 12:06:58 +00:00
const char * rofi_view_get_user_input ( const RofiViewState * state )
{
if ( state - > text ) {
return state - > text - > text ;
}
return NULL ;
}
/**
* Create a new , 0 initialized RofiViewState structure .
*
* @ returns a new 0 initialized RofiViewState
*/
2016-02-08 17:16:16 +00:00
static RofiViewState * __rofi_view_state_create ( void )
2016-02-06 12:06:58 +00:00
{
return g_malloc0 ( sizeof ( RofiViewState ) ) ;
}
2016-11-15 20:54:31 +00:00
/**
* Structure with data to process by each worker thread .
*/
2016-02-06 13:27:36 +00:00
typedef struct _thread_state
{
RofiViewState * state ;
unsigned int start ;
unsigned int stop ;
unsigned int count ;
GCond * cond ;
GMutex * mutex ;
unsigned int * acount ;
void ( * callback ) ( struct _thread_state * t , gpointer data ) ;
} thread_state ;
2016-04-20 15:03:34 +00:00
/**
* @ param data A thread_state object .
* @ param user_data User data to pass to thread_state callback
*
* Small wrapper function that is internally used to pass a job to a worker .
*/
static void rofi_view_call_thread ( gpointer data , gpointer user_data )
{
thread_state * t = ( thread_state * ) data ;
t - > callback ( t , user_data ) ;
g_mutex_lock ( t - > mutex ) ;
( * ( t - > acount ) ) - - ;
g_cond_signal ( t - > cond ) ;
g_mutex_unlock ( t - > mutex ) ;
}
2016-02-06 13:27:36 +00:00
2017-01-09 08:15:19 +00:00
enum CharClass { LOWER , UPPER , DIGIT , NON_WORD } ;
static enum CharClass rofi_scorer_get_character_class ( gunichar c )
{
if ( g_unichar_islower ( c ) )
return LOWER ;
if ( g_unichar_isupper ( c ) )
return UPPER ;
if ( g_unichar_isdigit ( c ) )
return DIGIT ;
return NON_WORD ;
}
static int rofi_scorer_get_score_for ( enum CharClass prev , enum CharClass curr )
{
if ( prev = = NON_WORD & & curr ! = NON_WORD )
return WORD_START_SCORE ;
if ( ( prev = = LOWER & & curr = = UPPER ) | |
( prev ! = DIGIT & & curr = = DIGIT ) )
return CAMEL_SCORE ;
if ( curr = = NON_WORD )
return NON_WORD_SCORE ;
return 0 ;
}
/*
rofi_scorer_fuzzy_evaluate implements a global sequence alignment algorithm to find the maximum accumulated score by aligning ` pattern ` to ` str ` . It applies when ` pattern ` is a subsequence of ` str ` .
Scoring criteria
- Prefer matches at the start of a word , or the start of subwords in CamelCase / camelCase / camel123 words . See WORD_START_SCORE / CAMEL_SCORE .
- Non - word characters matter . See NON_WORD_SCORE .
- The first characters of words of ` pattern ` receive bonus because they usually have more significance than the rest . See PATTERN_START_MULTIPLIER / PATTERN_NON_START_MULTIPLIER .
- Superfluous characters in ` str ` will reduce the score ( gap penalty ) . See GAP_SCORE .
- Prefer early occurrence of the first character . See LEADING_GAP_SCORE / GAP_SCORE .
The recurrence of the dynamic programming :
dp [ i ] [ j ] : maximum accumulated score by aligning pattern [ 0. . i ] to str [ 0. . j ]
dp [ 0 ] [ j ] = leading_gap_penalty ( 0 , j ) + score [ j ]
dp [ i ] [ j ] = max ( dp [ i - 1 ] [ j - 1 ] + CONSECUTIVE_SCORE , max ( dp [ i - 1 ] [ k ] + gap_penalty ( k + 1 , j ) + score [ j ] : k < j ) )
The first dimension can be suppressed since we do not need a matching scheme , which reduces the space complexity from O ( N * M ) to O ( M )
*/
static int rofi_scorer_fuzzy_evaluate ( const char * pattern , glong plen , const char * str , glong slen )
{
if ( plen = = 5 )
plen = plen ;
glong pi , si ;
gboolean pfirst = TRUE , // whether we are aligning the first character of pattern
pstart = TRUE ; // whether the start of a word in pattern
int * score = g_malloc_n ( slen , sizeof ( int ) ) , // score for each position
* dp = g_malloc_n ( slen , sizeof ( int ) ) , // dp[i]: maximum value by aligning pattern[0..pi] to str[0..si]
uleft = 0 , ulefts = 0 , // uleft: value of the upper left cell; ulefts: maximum value of uleft and cells on the left. The arbitrary initial values suppress warnings.
left , lefts ; // uleft & ulefts for the next row
const gchar * pit = pattern , * sit ;
enum CharClass prev = NON_WORD , cur ;
for ( si = 0 , sit = str ; si < slen ; si + + , sit = g_utf8_next_char ( sit ) ) {
cur = rofi_scorer_get_character_class ( g_utf8_get_char ( sit ) ) ;
score [ si ] = rofi_scorer_get_score_for ( prev , cur ) ;
prev = cur ;
dp [ si ] = MIN_SCORE ;
}
for ( pi = 0 ; pi < plen ; pi + + , pit = g_utf8_next_char ( pit ) ) {
gunichar pc = g_utf8_get_char ( pit ) , sc ;
if ( g_unichar_isspace ( pc ) ) {
pstart = TRUE ;
continue ;
}
lefts = MIN_SCORE ;
for ( si = 0 , sit = str ; si < slen ; si + + , sit = g_utf8_next_char ( sit ) ) {
left = dp [ si ] ;
lefts = MAX ( lefts + GAP_SCORE , left ) ;
sc = g_utf8_get_char ( sit ) ;
if ( config . case_sensitive
? pc = = sc
: g_unichar_tolower ( pc ) = = g_unichar_tolower ( sc ) ) {
int t = score [ si ] * ( pstart ? PATTERN_START_MULTIPLIER : PATTERN_NON_START_MULTIPLIER ) ;
dp [ si ] = pfirst
? LEADING_GAP_SCORE * si + t
: MAX ( uleft + CONSECUTIVE_SCORE , ulefts + t ) ;
} else {
dp [ si ] = MIN_SCORE ;
}
uleft = left ;
ulefts = lefts ;
}
pfirst = pstart = FALSE ;
}
lefts = MIN_SCORE ;
for ( si = 0 ; si < slen ; si + + )
lefts = MAX ( lefts + GAP_SCORE , dp [ si ] ) ;
g_free ( score ) ;
g_free ( dp ) ;
return lefts ;
}
2016-02-06 13:27:36 +00:00
static void filter_elements ( thread_state * t , G_GNUC_UNUSED gpointer user_data )
{
2017-01-09 08:15:19 +00:00
char * pattern = NULL ;
glong plen ;
if ( config . matching_method = = MM_FUZZY | | config . levenshtein_sort ) {
pattern = mode_preprocess_input ( t - > state - > sw , t - > state - > text - > text ) ;
plen = g_utf8_strlen ( pattern , - 1 ) ;
}
2016-02-06 13:27:36 +00:00
for ( unsigned int i = t - > start ; i < t - > stop ; i + + ) {
2016-10-11 06:18:34 +00:00
int match = mode_token_match ( t - > state - > sw , t - > state - > tokens , i ) ;
2016-02-06 13:27:36 +00:00
// If each token was matched, add it to list.
if ( match ) {
t - > state - > line_map [ t - > start + t - > count ] = i ;
2017-01-09 08:15:19 +00:00
if ( config . matching_method = = MM_FUZZY ) {
char * str = mode_get_completion ( t - > state - > sw , i ) ;
glong slen = g_utf8_strlen ( str , - 1 ) ;
t - > state - > distance [ i ] = slen > FUZZY_SCORER_MAX_LENGTH
? - MIN_SCORE
: - rofi_scorer_fuzzy_evaluate ( pattern , plen , str , slen ) ;
g_free ( str ) ;
} else if ( config . levenshtein_sort ) {
2016-02-06 13:27:36 +00:00
// This is inefficient, need to fix it.
2016-06-16 20:23:55 +00:00
char * str = mode_get_completion ( t - > state - > sw , i ) ;
2017-01-09 08:15:19 +00:00
t - > state - > distance [ i ] = levenshtein ( pattern , str ) ;
2016-02-06 13:27:36 +00:00
g_free ( str ) ;
}
t - > count + + ;
}
}
2017-01-09 08:15:19 +00:00
if ( pattern ) {
g_free ( pattern ) ;
}
2016-02-06 13:27:36 +00:00
}
2016-12-19 16:49:52 +00:00
static void rofi_view_setup_fake_transparency ( const char const * fake_background )
2016-04-19 20:10:34 +00:00
{
if ( CacheState . fake_bg = = NULL ) {
2016-05-06 10:40:28 +00:00
cairo_surface_t * s = NULL ;
/**
* Select Background to use for fake transparency .
2016-12-19 16:49:52 +00:00
* Current options : ' real ' , ' screenshot ' , ' background '
2016-05-06 10:40:28 +00:00
*/
2016-11-12 10:28:40 +00:00
TICK_N ( " Fake start " ) ;
2017-01-08 20:36:06 +00:00
if ( g_strcmp0 ( fake_background , " real " ) = = 0 ) {
2016-12-19 16:49:52 +00:00
return ;
}
else if ( g_strcmp0 ( fake_background , " screenshot " ) = = 0 ) {
2016-11-15 07:24:06 +00:00
s = x11_helper_get_screenshot_surface ( ) ;
2016-05-06 10:40:28 +00:00
}
2016-12-19 16:49:52 +00:00
else if ( g_strcmp0 ( fake_background , " background " ) = = 0 ) {
2016-05-06 10:40:28 +00:00
s = x11_helper_get_bg_surface ( ) ;
}
2016-05-08 09:14:34 +00:00
else {
2016-12-19 16:49:52 +00:00
char * fpath = rofi_expand_path ( fake_background ) ;
2016-11-12 10:28:40 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Opening %s to use as background. " , fpath ) ;
2016-05-08 09:14:34 +00:00
s = cairo_image_surface_create_from_png ( fpath ) ;
CacheState . fake_bgrel = TRUE ;
g_free ( fpath ) ;
}
2016-11-12 10:28:40 +00:00
TICK_N ( " Get surface. " ) ;
2016-05-06 10:40:28 +00:00
if ( s ! = NULL ) {
2016-05-08 09:14:34 +00:00
if ( cairo_surface_status ( s ) ! = CAIRO_STATUS_SUCCESS ) {
2016-11-12 10:28:40 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Failed to open surface fake background: %s " ,
cairo_status_to_string ( cairo_surface_status ( s ) ) ) ;
2016-05-08 09:14:34 +00:00
cairo_surface_destroy ( s ) ;
s = NULL ;
}
else {
CacheState . fake_bg = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32 , CacheState . mon . w , CacheState . mon . h ) ;
cairo_t * dr = cairo_create ( CacheState . fake_bg ) ;
if ( CacheState . fake_bgrel ) {
cairo_set_source_surface ( dr , s , 0 , 0 ) ;
}
else {
cairo_set_source_surface ( dr , s , - CacheState . mon . x , - CacheState . mon . y ) ;
}
cairo_paint ( dr ) ;
cairo_destroy ( dr ) ;
cairo_surface_destroy ( s ) ;
}
2016-05-06 10:40:28 +00:00
}
2016-04-19 20:10:34 +00:00
TICK_N ( " Fake transparency " ) ;
}
}
void __create_window ( MenuFlags menu_flags )
2016-02-06 13:27:36 +00:00
{
2017-01-08 20:36:06 +00:00
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 ;
uint32_t selval [ ] = {
XCB_BACK_PIXMAP_NONE , 0 ,
2016-11-29 10:20:52 +00:00
XCB_GRAVITY_STATIC ,
XCB_BACKING_STORE_NOT_USEFUL ,
2016-03-05 10:34:44 +00:00
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
2016-04-07 18:28:40 +00:00
XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE |
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION ,
2016-03-05 10:34:44 +00:00
map
} ;
2016-02-21 18:42:32 +00:00
2017-01-08 20:36:06 +00:00
xcb_window_t box = xcb_generate_id ( xcb - > connection ) ;
xcb_void_cookie_t cc = xcb_create_window_checked ( xcb - > connection , depth - > depth , box , xcb_stuff_get_root_window ( xcb ) ,
0 , 0 , 200 , 100 , 0 , XCB_WINDOW_CLASS_INPUT_OUTPUT ,
visual - > visual_id , selmask , selval ) ;
2016-11-29 10:20:52 +00:00
xcb_generic_error_t * error ;
2017-01-08 20:36:06 +00:00
error = xcb_request_check ( xcb - > connection , cc ) ;
if ( error ) {
printf ( " xcb_create_window() failed error=0x%x \n " , error - > error_code ) ;
2016-11-29 10:20:52 +00:00
exit ( EXIT_FAILURE ) ;
}
2016-11-11 15:29:59 +00:00
CacheState . gc = xcb_generate_id ( xcb - > connection ) ;
xcb_create_gc ( xcb - > connection , CacheState . gc , box , 0 , 0 ) ;
2016-02-06 13:27:36 +00:00
// Create a drawable.
2016-11-11 15:29:59 +00:00
CacheState . edit_pixmap = xcb_generate_id ( xcb - > connection ) ;
xcb_create_pixmap ( xcb - > connection , depth - > depth ,
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 ) ;
2016-02-06 13:27:36 +00:00
// Set up pango context.
cairo_font_options_t * fo = cairo_font_options_create ( ) ;
// Take font description from xlib surface
2016-11-11 15:29:59 +00:00
cairo_surface_get_font_options ( CacheState . edit_surf , fo ) ;
// TODO should we update the drawable each time?
PangoContext * p = pango_cairo_create_context ( CacheState . edit_draw ) ;
2016-02-06 13:27:36 +00:00
// Set the font options from the xlib surface
pango_cairo_context_set_font_options ( p , fo ) ;
// Setup dpi
if ( config . dpi > 0 ) {
PangoFontMap * font_map = pango_cairo_font_map_get_default ( ) ;
pango_cairo_font_map_set_resolution ( ( PangoCairoFontMap * ) font_map , ( double ) config . dpi ) ;
}
// Setup font.
2017-01-04 21:27:27 +00:00
// Dummy widget.
2017-01-10 10:20:38 +00:00
container * win = container_create ( " window.box " ) ;
2017-01-08 20:36:06 +00:00
char * font = rofi_theme_get_string ( WIDGET ( win ) , " font " , config . menu_font ) ;
2016-12-11 16:06:31 +00:00
if ( font ) {
PangoFontDescription * pfd = pango_font_description_from_string ( font ) ;
2016-03-01 18:48:18 +00:00
pango_context_set_font_description ( p , pfd ) ;
pango_font_description_free ( pfd ) ;
}
2016-02-06 13:27:36 +00:00
// Tell textbox to use this context.
textbox_set_pango_context ( p ) ;
// cleanup
g_object_unref ( p ) ;
cairo_font_options_destroy ( fo ) ;
// // make it an unmanaged window
2016-03-16 17:53:33 +00:00
if ( ( ( menu_flags & MENU_NORMAL_WINDOW ) = = 0 ) ) {
2016-03-01 18:48:18 +00:00
window_set_atom_prop ( box , xcb - > ewmh . _NET_WM_STATE , & ( xcb - > ewmh . _NET_WM_STATE_ABOVE ) , 1 ) ;
2016-02-28 14:32:53 +00:00
uint32_t values [ ] = { 1 } ;
2016-03-01 17:11:55 +00:00
xcb_change_window_attributes ( xcb - > connection , box , XCB_CW_OVERRIDE_REDIRECT , values ) ;
2016-02-06 13:27:36 +00:00
}
else {
2016-03-01 18:48:18 +00:00
window_set_atom_prop ( box , xcb - > ewmh . _NET_WM_WINDOW_TYPE , & ( xcb - > ewmh . _NET_WM_WINDOW_TYPE_NORMAL ) , 1 ) ;
2016-09-08 06:53:14 +00:00
x11_disable_decoration ( box ) ;
2016-02-06 13:27:36 +00:00
}
2017-01-08 16:38:21 +00:00
2017-01-08 20:36:06 +00:00
CacheState . fullscreen = rofi_theme_get_boolean ( WIDGET ( win ) , " fullscreen " , config . fullscreen ) ;
2017-01-08 16:38:21 +00:00
if ( CacheState . fullscreen ) {
2016-02-27 16:42:50 +00:00
xcb_atom_t atoms [ ] = {
2016-03-01 17:11:55 +00:00
xcb - > ewmh . _NET_WM_STATE_FULLSCREEN ,
xcb - > ewmh . _NET_WM_STATE_ABOVE
2016-02-06 13:27:36 +00:00
} ;
2016-03-01 17:11:55 +00:00
window_set_atom_prop ( box , xcb - > ewmh . _NET_WM_STATE , atoms , sizeof ( atoms ) / sizeof ( xcb_atom_t ) ) ;
2016-02-06 13:27:36 +00:00
}
// Set the WM_NAME
2016-03-01 17:11:55 +00:00
xcb_change_property ( xcb - > connection , XCB_PROP_MODE_REPLACE , box , xcb - > ewmh . _NET_WM_NAME , xcb - > ewmh . UTF8_STRING , 8 , 4 , " rofi " ) ;
xcb_change_property ( xcb - > connection , XCB_PROP_MODE_REPLACE , box , XCB_ATOM_WM_NAME , XCB_ATOM_STRING , 8 , 4 , " rofi " ) ;
2016-02-06 13:27:36 +00:00
2016-04-19 20:10:34 +00:00
CacheState . main_window = box ;
CacheState . flags = menu_flags ;
monitor_active ( & ( CacheState . mon ) ) ;
2016-12-19 16:49:52 +00:00
2017-01-08 20:36:06 +00:00
char * transparency = rofi_theme_get_string ( WIDGET ( win ) , " transparency " , NULL ) ;
if ( transparency = = NULL & & config . fake_transparency ) {
2016-12-19 16:49:52 +00:00
transparency = config . fake_background ;
}
if ( transparency ) {
rofi_view_setup_fake_transparency ( transparency ) ;
2016-04-19 20:10:34 +00:00
}
if ( xcb - > sncontext ! = NULL ) {
sn_launchee_context_setup_window ( xcb - > sncontext , CacheState . main_window ) ;
}
2017-01-04 21:27:27 +00:00
widget_free ( WIDGET ( win ) ) ;
2016-02-06 13:27:36 +00:00
}
2016-02-07 19:38:34 +00:00
2016-02-06 13:27:36 +00:00
/**
* @ param state Internal state of the menu .
*
* Calculate the width of the window and the width of an element .
*/
2017-01-01 18:33:16 +00:00
static void rofi_view_calculate_window_width ( RofiViewState * state )
2016-02-06 13:27:36 +00:00
{
2017-01-08 16:38:21 +00:00
if ( CacheState . fullscreen ) {
2016-09-17 15:49:45 +00:00
state - > width = CacheState . mon . w ;
2017-01-06 16:21:16 +00:00
return ;
2016-03-16 17:53:33 +00:00
}
2017-01-06 16:21:16 +00:00
if ( config . menu_width < 0 ) {
2016-02-06 13:27:36 +00:00
double fw = textbox_get_estimated_char_width ( ) ;
2016-09-17 15:49:45 +00:00
state - > width = - ( fw * config . menu_width ) ;
2017-01-03 14:57:40 +00:00
state - > width + = widget_padding_get_padding_width ( WIDGET ( state - > main_window ) ) ;
2016-02-06 13:27:36 +00:00
}
else {
// Calculate as float to stop silly, big rounding down errors.
2016-09-17 15:49:45 +00:00
state - > width = config . menu_width < 101 ? ( CacheState . mon . w / 100.0f ) * ( float ) config . menu_width : config . menu_width ;
2016-02-06 13:27:36 +00:00
}
2017-01-06 16:21:16 +00:00
// Use theme configured width, if set.
Distance width = rofi_theme_get_distance ( WIDGET ( state - > main_window ) , " width " , state - > width ) ;
state - > width = distance_get_pixel ( width , ORIENTATION_HORIZONTAL ) ;
2016-02-06 13:27:36 +00:00
}
/**
* Nav helper functions , to avoid duplicate code .
*/
2016-02-07 19:38:34 +00:00
2016-07-05 07:21:50 +00:00
/**
* @ param state The current RofiViewState
*
* Tab handling .
*/
static void rofi_view_nav_row_tab ( RofiViewState * state )
{
if ( state - > filtered_lines = = 1 ) {
state - > retv = MENU_OK ;
2016-10-08 16:57:59 +00:00
( state - > selected_line ) = state - > line_map [ listview_get_selected ( state - > list_view ) ] ;
2016-07-05 07:21:50 +00:00
state - > quit = 1 ;
return ;
}
// Double tab!
if ( state - > filtered_lines = = 0 & & ROW_TAB = = state - > prev_action ) {
state - > retv = MENU_NEXT ;
( state - > selected_line ) = 0 ;
state - > quit = TRUE ;
}
else {
2016-10-08 16:57:59 +00:00
listview_nav_down ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
}
state - > prev_action = ROW_TAB ;
}
/**
* @ param state The current RofiViewState
*
* complete current row .
*/
2016-07-05 16:13:53 +00:00
inline static void rofi_view_nav_row_select ( RofiViewState * state )
2016-07-05 07:21:50 +00:00
{
2016-11-26 15:57:13 +00:00
if ( state - > list_view = = NULL ) {
2016-10-08 16:57:59 +00:00
return ;
}
unsigned int selected = listview_get_selected ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
// If a valid item is selected, return that..
2016-10-08 16:57:59 +00:00
if ( selected < state - > filtered_lines ) {
char * str = mode_get_completion ( state - > sw , state - > line_map [ selected ] ) ;
2016-07-05 07:21:50 +00:00
textbox_text ( state - > text , str ) ;
g_free ( str ) ;
2016-10-20 07:41:32 +00:00
textbox_keybinding ( state - > text , MOVE_END ) ;
2016-07-05 07:21:50 +00:00
state - > refilter = TRUE ;
}
}
2016-02-06 13:27:36 +00:00
/**
* @ param state The current RofiViewState
*
* Move the selection to first row .
*/
2016-02-07 13:09:00 +00:00
inline static void rofi_view_nav_first ( RofiViewState * state )
2016-02-06 13:27:36 +00:00
{
2016-10-08 16:57:59 +00:00
// state->selected = 0;
listview_set_selected ( state - > list_view , 0 ) ;
2016-02-06 13:27:36 +00:00
}
2016-02-07 19:38:34 +00:00
2016-02-06 13:27:36 +00:00
/**
* @ param state The current RofiViewState
*
* Move the selection to last row .
*/
2016-02-07 13:09:00 +00:00
inline static void rofi_view_nav_last ( RofiViewState * state )
2016-02-06 13:27:36 +00:00
{
// If no lines, do nothing.
if ( state - > filtered_lines = = 0 ) {
return ;
}
2016-10-08 16:57:59 +00:00
//state->selected = state->filtered_lines - 1;
listview_set_selected ( state - > list_view , - 1 ) ;
2016-02-17 19:29:23 +00:00
}
2016-10-08 16:57:59 +00:00
static void update_callback ( textbox * t , unsigned int index , void * udata , TextBoxFontType type , gboolean full )
2016-02-17 19:29:23 +00:00
{
2016-10-08 16:57:59 +00:00
RofiViewState * state = ( RofiViewState * ) udata ;
if ( full ) {
2016-10-11 06:18:34 +00:00
int fstate = 0 ;
char * text = mode_get_display_value ( state - > sw , state - > line_map [ index ] , & fstate , TRUE ) ;
2016-10-08 16:57:59 +00:00
type | = fstate ;
textbox_font ( t , type ) ;
// Move into list view.
textbox_text ( t , text ) ;
2016-10-25 19:19:39 +00:00
if ( state - > tokens & & config . show_match ) {
2016-10-11 06:18:34 +00:00
PangoAttrList * list = textbox_get_pango_attributes ( t ) ;
if ( list ! = NULL ) {
pango_attr_list_ref ( list ) ;
}
else {
list = pango_attr_list_new ( ) ;
}
2017-01-08 23:09:02 +00:00
ThemeHighlight th = { HL_BOLD | HL_UNDERLINE , { 0.0 , 0.0 , 0.0 , 0.0 } } ;
th = rofi_theme_get_highlight ( WIDGET ( t ) , " highlight " , th ) ;
token_match_get_pango_attr ( th , state - > tokens , textbox_get_visible_text ( t ) , list ) ;
2016-10-11 06:18:34 +00:00
textbox_set_pango_attributes ( t , list ) ;
pango_attr_list_unref ( list ) ;
2016-10-08 16:57:59 +00:00
}
g_free ( text ) ;
2016-02-06 13:27:36 +00:00
}
2016-10-08 16:57:59 +00:00
else {
int fstate = 0 ;
mode_get_display_value ( state - > sw , state - > line_map [ index ] , & fstate , FALSE ) ;
type | = fstate ;
textbox_font ( t , type ) ;
2016-02-06 13:27:36 +00:00
}
}
2017-01-01 01:57:04 +00:00
void rofi_view_update ( RofiViewState * state , gboolean qr )
2016-02-06 13:27:36 +00:00
{
2016-12-28 18:42:14 +00:00
if ( ! widget_need_redraw ( WIDGET ( state - > main_window ) ) & & ! widget_need_redraw ( WIDGET ( state - > overlay ) ) ) {
2016-02-07 19:38:34 +00:00
return ;
}
2017-01-04 09:47:37 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Redraw view " ) ;
2016-02-06 13:27:36 +00:00
TICK ( ) ;
2016-11-11 15:29:59 +00:00
cairo_t * d = CacheState . edit_draw ;
2016-02-06 13:27:36 +00:00
cairo_set_operator ( d , CAIRO_OPERATOR_SOURCE ) ;
2016-12-19 16:49:52 +00:00
if ( CacheState . fake_bg ! = NULL ) {
2016-11-11 21:11:32 +00:00
if ( CacheState . fake_bgrel ) {
cairo_set_source_surface ( d , CacheState . fake_bg , 0.0 , 0.0 ) ;
2016-02-06 13:27:36 +00:00
}
2016-11-11 21:11:32 +00:00
else {
cairo_set_source_surface ( d , CacheState . fake_bg ,
2016-11-12 10:28:40 +00:00
- ( double ) ( state - > x - CacheState . mon . x ) ,
- ( double ) ( state - > y - CacheState . mon . y ) ) ;
2016-11-11 21:11:32 +00:00
}
cairo_paint ( d ) ;
cairo_set_operator ( d , CAIRO_OPERATOR_OVER ) ;
2016-02-06 13:27:36 +00:00
}
else {
2016-12-28 18:42:14 +00:00
// Paint the background transparent.
2017-01-08 20:36:06 +00:00
cairo_set_source_rgba ( d , 0 , 0 , 0 , 0.0 ) ;
2016-02-06 13:27:36 +00:00
cairo_paint ( d ) ;
}
TICK_N ( " Background " ) ;
// Always paint as overlay over the background.
cairo_set_operator ( d , CAIRO_OPERATOR_OVER ) ;
2016-12-28 18:42:14 +00:00
widget_draw ( WIDGET ( state - > main_window ) , d ) ;
2016-02-06 13:27:36 +00:00
2016-06-26 13:48:12 +00:00
if ( state - > overlay ) {
2016-09-27 06:51:17 +00:00
widget_draw ( WIDGET ( state - > overlay ) , d ) ;
2016-06-26 13:48:12 +00:00
}
2016-11-11 15:29:59 +00:00
TICK_N ( " widgets " ) ;
cairo_surface_flush ( CacheState . edit_surf ) ;
2017-01-01 01:57:04 +00:00
if ( qr ) {
rofi_view_queue_redraw ( ) ;
}
2016-02-06 13:27:36 +00:00
}
/**
* @ param state Internal state of the menu .
* @ param xse X selection event .
*
* Handle paste event .
*/
2016-02-19 21:43:53 +00:00
static void rofi_view_paste ( RofiViewState * state , xcb_selection_notify_event_t * xse )
2016-02-06 13:27:36 +00:00
{
2016-02-28 14:32:53 +00:00
if ( xse - > property = = XCB_ATOM_NONE ) {
2016-02-27 16:57:43 +00:00
fprintf ( stderr , " Failed to convert selection \n " ) ;
2016-02-28 14:32:53 +00:00
}
2016-03-01 17:11:55 +00:00
else if ( xse - > property = = xcb - > ewmh . UTF8_STRING ) {
2016-04-19 20:10:34 +00:00
gchar * text = window_get_text_prop ( CacheState . main_window , xcb - > ewmh . UTF8_STRING ) ;
2016-02-06 13:27:36 +00:00
if ( text ! = NULL & & text [ 0 ] ! = ' \0 ' ) {
unsigned int dl = strlen ( text ) ;
// Strip new line
2017-01-08 20:36:06 +00:00
for ( unsigned int i = 0 ; i < dl ; i + + ) {
if ( text [ i ] = = ' \n ' ) {
2017-01-03 11:20:21 +00:00
dl = i ;
}
2016-02-06 13:27:36 +00:00
}
// Insert string move cursor.
textbox_insert ( state - > text , state - > text - > cursor , text , dl ) ;
2016-08-19 08:46:38 +00:00
textbox_cursor ( state - > text , state - > text - > cursor + g_utf8_strlen ( text , - 1 ) ) ;
2016-02-06 13:27:36 +00:00
// Force a redraw and refiltering of the text.
state - > refilter = TRUE ;
}
g_free ( text ) ;
2016-02-28 14:32:53 +00:00
}
else {
2016-02-27 16:57:43 +00:00
fprintf ( stderr , " Failed \n " ) ;
2016-02-06 13:27:36 +00:00
}
}
2016-02-19 21:43:53 +00:00
static void rofi_view_mouse_navigation ( RofiViewState * state , xcb_button_press_event_t * xbe )
2016-02-06 13:27:36 +00:00
{
// Scroll event
2016-02-19 21:43:53 +00:00
if ( xbe - > detail > 3 ) {
if ( xbe - > detail = = 4 ) {
2016-10-08 16:57:59 +00:00
listview_nav_up ( state - > list_view ) ;
2016-02-06 13:27:36 +00:00
}
2016-02-19 21:43:53 +00:00
else if ( xbe - > detail = = 5 ) {
2016-10-08 16:57:59 +00:00
listview_nav_down ( state - > list_view ) ;
2016-02-06 13:27:36 +00:00
}
2016-02-19 21:43:53 +00:00
else if ( xbe - > detail = = 6 ) {
2016-10-08 16:57:59 +00:00
listview_nav_left ( state - > list_view ) ;
2016-02-06 13:27:36 +00:00
}
2016-02-19 21:43:53 +00:00
else if ( xbe - > detail = = 7 ) {
2016-10-08 16:57:59 +00:00
listview_nav_right ( state - > list_view ) ;
2016-02-06 13:27:36 +00:00
}
return ;
}
else {
2016-10-08 16:57:59 +00:00
xcb_button_press_event_t rel = * xbe ;
2016-12-28 18:42:14 +00:00
if ( widget_clicked ( WIDGET ( state - > main_window ) , & rel ) ) {
2016-02-06 13:27:36 +00:00
return ;
}
}
}
2016-09-17 15:49:45 +00:00
static void _rofi_view_reload_row ( RofiViewState * state )
{
g_free ( state - > line_map ) ;
g_free ( state - > distance ) ;
state - > num_lines = mode_get_num_entries ( state - > sw ) ;
state - > line_map = g_malloc0_n ( state - > num_lines , sizeof ( unsigned int ) ) ;
state - > distance = g_malloc0_n ( state - > num_lines , sizeof ( int ) ) ;
2017-01-02 20:19:27 +00:00
listview_set_max_lines ( state - > list_view , state - > num_lines ) ;
2016-09-17 15:49:45 +00:00
}
2016-02-07 13:09:00 +00:00
static void rofi_view_refilter ( RofiViewState * state )
2016-02-06 13:27:36 +00:00
{
TICK_N ( " Filter start " ) ;
2016-09-17 15:49:45 +00:00
if ( state - > reload ) {
_rofi_view_reload_row ( state ) ;
state - > reload = FALSE ;
}
2016-10-11 06:18:34 +00:00
if ( state - > tokens ) {
tokenize_free ( state - > tokens ) ;
state - > tokens = NULL ;
}
2016-02-06 13:27:36 +00:00
if ( strlen ( state - > text - > text ) > 0 ) {
2016-10-11 06:18:34 +00:00
unsigned int j = 0 ;
gchar * input = mode_preprocess_input ( state - > sw , state - > text - > text ) ;
state - > tokens = tokenize ( input , config . case_sensitive ) ;
2016-05-26 06:39:33 +00:00
g_free ( input ) ;
2016-02-06 13:27:36 +00:00
/**
* On long lists it can be beneficial to parallelize .
* If number of threads is 1 , no thread is spawn .
* If number of threads > 1 and there are enough ( > 1000 ) items , spawn jobs for the thread pool .
* For large lists with 8 threads I see a factor three speedup of the whole function .
*/
unsigned int nt = MAX ( 1 , state - > num_lines / 500 ) ;
thread_state states [ nt ] ;
GCond cond ;
GMutex mutex ;
g_mutex_init ( & mutex ) ;
g_cond_init ( & cond ) ;
unsigned int count = nt ;
unsigned int steps = ( state - > num_lines + nt ) / nt ;
for ( unsigned int i = 0 ; i < nt ; i + + ) {
states [ i ] . state = state ;
states [ i ] . start = i * steps ;
states [ i ] . stop = MIN ( state - > num_lines , ( i + 1 ) * steps ) ;
states [ i ] . count = 0 ;
states [ i ] . cond = & cond ;
states [ i ] . mutex = & mutex ;
states [ i ] . acount = & count ;
states [ i ] . callback = filter_elements ;
if ( i > 0 ) {
g_thread_pool_push ( tpool , & states [ i ] , NULL ) ;
}
}
// Run one in this thread.
2016-04-20 15:03:34 +00:00
rofi_view_call_thread ( & states [ 0 ] , NULL ) ;
2016-02-06 13:27:36 +00:00
// No need to do this with only one thread.
if ( nt > 1 ) {
g_mutex_lock ( & mutex ) ;
while ( count > 0 ) {
g_cond_wait ( & cond , & mutex ) ;
}
g_mutex_unlock ( & mutex ) ;
}
g_cond_clear ( & cond ) ;
g_mutex_clear ( & mutex ) ;
for ( unsigned int i = 0 ; i < nt ; i + + ) {
if ( j ! = states [ i ] . start ) {
memmove ( & ( state - > line_map [ j ] ) , & ( state - > line_map [ states [ i ] . start ] ) , sizeof ( unsigned int ) * ( states [ i ] . count ) ) ;
}
j + = states [ i ] . count ;
}
2017-01-09 08:15:19 +00:00
if ( config . matching_method = = MM_FUZZY | | config . levenshtein_sort ) {
2016-02-06 13:27:36 +00:00
g_qsort_with_data ( state - > line_map , j , sizeof ( int ) , lev_sort , state - > distance ) ;
}
// Cleanup + bookkeeping.
state - > filtered_lines = j ;
}
else {
for ( unsigned int i = 0 ; i < state - > num_lines ; i + + ) {
state - > line_map [ i ] = i ;
}
state - > filtered_lines = state - > num_lines ;
}
2016-10-08 16:57:59 +00:00
listview_set_num_elements ( state - > list_view , state - > filtered_lines ) ;
2016-02-06 13:27:36 +00:00
if ( config . auto_select = = TRUE & & state - > filtered_lines = = 1 & & state - > num_lines > 1 ) {
2016-10-08 16:57:59 +00:00
( state - > selected_line ) = state - > line_map [ listview_get_selected ( state - > list_view ) ] ;
2016-02-06 13:27:36 +00:00
state - > retv = MENU_OK ;
state - > quit = TRUE ;
}
2017-01-02 20:19:27 +00:00
// Make sure we enable fixed num lines when in normal window mode.
2017-01-08 20:36:06 +00:00
if ( ( CacheState . flags & MENU_NORMAL_WINDOW ) = = 0 ) {
2016-10-08 16:57:59 +00:00
int height = rofi_view_calculate_height ( state ) ;
if ( height ! = state - > height ) {
state - > height = height ;
2016-09-17 15:49:45 +00:00
rofi_view_calculate_window_position ( state ) ;
rofi_view_window_update_size ( state ) ;
2016-09-17 16:35:03 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Resize based on re-filter " ) ;
2016-09-17 15:49:45 +00:00
}
}
2016-02-06 13:27:36 +00:00
state - > refilter = FALSE ;
TICK_N ( " Filter done " ) ;
}
2016-02-06 12:06:58 +00:00
/**
* @ param state The Menu Handle
*
* Check if a finalize function is set , and if sets executes it .
*/
2016-04-19 20:10:34 +00:00
void process_result ( RofiViewState * state ) ;
2016-02-06 12:06:58 +00:00
void rofi_view_finalize ( RofiViewState * state )
{
2016-04-19 20:10:34 +00:00
if ( state & & state - > finalize ! = NULL ) {
2016-02-06 12:06:58 +00:00
state - > finalize ( state ) ;
}
}
2016-02-06 13:27:36 +00:00
2016-05-07 09:31:00 +00:00
gboolean rofi_view_trigger_action ( RofiViewState * state , KeyBindingAction action )
{
gboolean ret = TRUE ;
switch ( action )
{
// Handling of paste
case PASTE_PRIMARY :
xcb_convert_selection ( xcb - > connection , CacheState . main_window , XCB_ATOM_PRIMARY ,
xcb - > ewmh . UTF8_STRING , xcb - > ewmh . UTF8_STRING , XCB_CURRENT_TIME ) ;
xcb_flush ( xcb - > connection ) ;
break ;
case PASTE_SECONDARY :
2016-12-24 13:12:54 +00:00
xcb_convert_selection ( xcb - > connection , CacheState . main_window , netatoms [ CLIPBOARD ] ,
2016-05-07 09:31:00 +00:00
xcb - > ewmh . UTF8_STRING , xcb - > ewmh . UTF8_STRING , XCB_CURRENT_TIME ) ;
xcb_flush ( xcb - > connection ) ;
break ;
case SCREENSHOT :
menu_capture_screenshot ( ) ;
break ;
case TOGGLE_SORT :
config . levenshtein_sort = ! config . levenshtein_sort ;
state - > refilter = TRUE ;
textbox_text ( state - > case_indicator , get_matching_state ( ) ) ;
break ;
case MODE_PREVIOUS :
state - > retv = MENU_PREVIOUS ;
( state - > selected_line ) = 0 ;
state - > quit = TRUE ;
break ;
// Menu navigation.
case MODE_NEXT :
state - > retv = MENU_NEXT ;
( state - > selected_line ) = 0 ;
state - > quit = TRUE ;
break ;
// Toggle case sensitivity.
case TOGGLE_CASE_SENSITIVITY :
config . case_sensitive = ! config . case_sensitive ;
( state - > selected_line ) = 0 ;
state - > refilter = TRUE ;
textbox_text ( state - > case_indicator , get_matching_state ( ) ) ;
break ;
// Special delete entry command.
case DELETE_ENTRY :
2016-10-08 16:57:59 +00:00
{
unsigned int selected = listview_get_selected ( state - > list_view ) ;
if ( selected < state - > filtered_lines ) {
( state - > selected_line ) = state - > line_map [ selected ] ;
2016-05-07 09:31:00 +00:00
state - > retv = MENU_ENTRY_DELETE ;
state - > quit = TRUE ;
}
else {
ret = FALSE ;
}
break ;
2016-10-08 16:57:59 +00:00
}
2017-01-10 06:41:37 +00:00
case SELECT_ELEMENT_1 :
case SELECT_ELEMENT_2 :
case SELECT_ELEMENT_3 :
case SELECT_ELEMENT_4 :
case SELECT_ELEMENT_5 :
case SELECT_ELEMENT_6 :
case SELECT_ELEMENT_7 :
case SELECT_ELEMENT_8 :
case SELECT_ELEMENT_9 :
case SELECT_ELEMENT_10 :
{
unsigned int index = action - SELECT_ELEMENT_1 ;
if ( index < state - > filtered_lines ) {
state - > selected_line = state - > line_map [ index ] ;
state - > retv = MENU_OK ;
state - > quit = TRUE ;
}
break ;
}
2016-05-07 09:31:00 +00:00
case CUSTOM_1 :
case CUSTOM_2 :
case CUSTOM_3 :
case CUSTOM_4 :
case CUSTOM_5 :
case CUSTOM_6 :
case CUSTOM_7 :
case CUSTOM_8 :
case CUSTOM_9 :
case CUSTOM_10 :
case CUSTOM_11 :
case CUSTOM_12 :
case CUSTOM_13 :
case CUSTOM_14 :
case CUSTOM_15 :
case CUSTOM_16 :
case CUSTOM_17 :
case CUSTOM_18 :
case CUSTOM_19 :
2016-10-08 16:57:59 +00:00
{
2016-05-07 09:31:00 +00:00
state - > selected_line = UINT32_MAX ;
2016-10-08 16:57:59 +00:00
unsigned int selected = listview_get_selected ( state - > list_view ) ;
if ( selected < state - > filtered_lines ) {
( state - > selected_line ) = state - > line_map [ selected ] ;
2016-05-07 09:31:00 +00:00
}
state - > retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK ) ;
state - > quit = TRUE ;
break ;
2016-10-08 16:57:59 +00:00
}
2016-05-07 09:31:00 +00:00
// If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too
case CANCEL :
2016-07-05 07:21:50 +00:00
state - > retv = MENU_CANCEL ;
state - > quit = TRUE ;
break ;
2016-05-07 09:31:00 +00:00
case ROW_UP :
2016-10-08 16:57:59 +00:00
listview_nav_up ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
break ;
2016-05-07 09:31:00 +00:00
case ROW_TAB :
2016-07-05 07:21:50 +00:00
rofi_view_nav_row_tab ( state ) ;
break ;
2016-05-07 09:31:00 +00:00
case ROW_DOWN :
2016-10-08 16:57:59 +00:00
listview_nav_down ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
break ;
2016-05-07 09:31:00 +00:00
case ROW_LEFT :
2016-10-08 16:57:59 +00:00
listview_nav_left ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
break ;
2016-05-07 09:31:00 +00:00
case ROW_RIGHT :
2016-10-08 16:57:59 +00:00
listview_nav_right ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
break ;
2016-05-07 09:31:00 +00:00
case PAGE_PREV :
2016-10-08 16:57:59 +00:00
listview_nav_page_prev ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
break ;
2016-05-07 09:31:00 +00:00
case PAGE_NEXT :
2016-10-08 16:57:59 +00:00
listview_nav_page_next ( state - > list_view ) ;
2016-07-05 07:21:50 +00:00
break ;
2016-05-07 09:31:00 +00:00
case ROW_FIRST :
2016-07-05 07:21:50 +00:00
rofi_view_nav_first ( state ) ;
break ;
2016-05-07 09:31:00 +00:00
case ROW_LAST :
2016-07-05 07:21:50 +00:00
rofi_view_nav_last ( state ) ;
break ;
2016-05-07 09:31:00 +00:00
case ROW_SELECT :
2016-07-05 07:21:50 +00:00
rofi_view_nav_row_select ( state ) ;
2016-05-07 09:31:00 +00:00
break ;
// If you add a binding here, make sure to add it to textbox_keybinding too
case MOVE_CHAR_BACK :
case MOVE_CHAR_FORWARD :
case CLEAR_LINE :
case MOVE_FRONT :
case MOVE_END :
2016-07-25 09:32:30 +00:00
case REMOVE_TO_EOL :
2016-07-27 06:10:55 +00:00
case REMOVE_TO_SOL :
2016-05-07 09:31:00 +00:00
case REMOVE_WORD_BACK :
case REMOVE_WORD_FORWARD :
case REMOVE_CHAR_FORWARD :
case MOVE_WORD_BACK :
case MOVE_WORD_FORWARD :
case REMOVE_CHAR_BACK :
{
int rc = textbox_keybinding ( state - > text , action ) ;
2016-06-19 16:02:49 +00:00
if ( rc = = 1 ) {
// Entry changed.
2016-05-07 09:31:00 +00:00
state - > refilter = TRUE ;
}
2016-06-19 16:02:49 +00:00
else if ( rc = = 2 ) {
// Movement.
2016-05-07 09:31:00 +00:00
}
break ;
}
2016-06-19 16:02:49 +00:00
case ACCEPT_ALT :
{
2016-10-08 16:57:59 +00:00
unsigned int selected = listview_get_selected ( state - > list_view ) ;
2016-06-19 16:02:49 +00:00
state - > selected_line = UINT32_MAX ;
2016-10-08 16:57:59 +00:00
if ( selected < state - > filtered_lines ) {
( state - > selected_line ) = state - > line_map [ selected ] ;
2016-06-19 16:02:49 +00:00
state - > retv = MENU_OK ;
}
else {
// Nothing entered and nothing selected.
state - > retv = MENU_CUSTOM_INPUT ;
}
state - > retv | = MENU_CUSTOM_ACTION ;
state - > quit = TRUE ;
break ;
}
case ACCEPT_CUSTOM :
{
state - > selected_line = UINT32_MAX ;
state - > retv = MENU_CUSTOM_INPUT ;
state - > quit = TRUE ;
break ;
}
case ACCEPT_ENTRY :
{
// If a valid item is selected, return that..
2016-10-08 16:57:59 +00:00
unsigned int selected = listview_get_selected ( state - > list_view ) ;
2016-06-19 16:02:49 +00:00
state - > selected_line = UINT32_MAX ;
2016-10-08 16:57:59 +00:00
if ( selected < state - > filtered_lines ) {
( state - > selected_line ) = state - > line_map [ selected ] ;
2016-06-19 16:02:49 +00:00
state - > retv = MENU_OK ;
}
else {
// Nothing entered and nothing selected.
state - > retv = MENU_CUSTOM_INPUT ;
}
state - > quit = TRUE ;
break ;
}
2016-05-07 09:31:00 +00:00
case NUM_ABE :
ret = FALSE ;
break ;
}
return ret ;
}
2016-04-07 12:22:18 +00:00
static void rofi_view_handle_keypress ( RofiViewState * state , xkb_stuff * xkb , xcb_key_press_event_t * xkpe )
2016-02-06 13:27:36 +00:00
{
2016-05-08 09:13:11 +00:00
xcb_keysym_t key ;
char pad [ 32 ] ;
int len = 0 ;
2016-02-21 12:10:32 +00:00
2016-04-07 12:24:34 +00:00
key = xkb_state_key_get_one_sym ( xkb - > state , xkpe - > detail ) ;
2016-02-23 11:52:35 +00:00
2016-04-07 12:24:34 +00:00
if ( xkb - > compose . state ! = NULL ) {
if ( ( key ! = XKB_KEY_NoSymbol ) & & ( xkb_compose_state_feed ( xkb - > compose . state , key ) = = XKB_COMPOSE_FEED_ACCEPTED ) ) {
switch ( xkb_compose_state_get_status ( xkb - > compose . state ) )
{
case XKB_COMPOSE_CANCELLED :
/* Eat the keysym that cancelled the compose sequence.
* This is default behaviour with Xlib */
case XKB_COMPOSE_COMPOSING :
key = XKB_KEY_NoSymbol ;
break ;
case XKB_COMPOSE_COMPOSED :
key = xkb_compose_state_get_one_sym ( xkb - > compose . state ) ;
len = xkb_compose_state_get_utf8 ( xkb - > compose . state , pad , sizeof ( pad ) ) ;
break ;
case XKB_COMPOSE_NOTHING :
break ;
2016-02-19 21:43:53 +00:00
}
2016-04-07 12:24:34 +00:00
if ( ( key = = XKB_KEY_NoSymbol ) & & ( len = = 0 ) ) {
2016-04-07 12:22:18 +00:00
return ;
2016-02-19 21:43:53 +00:00
}
2016-04-07 12:24:34 +00:00
}
}
if ( len = = 0 ) {
len = xkb_state_key_get_utf8 ( xkb - > state , xkpe - > detail , pad , sizeof ( pad ) ) ;
}
2016-11-14 07:28:54 +00:00
xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods ( xkb - > state , xkpe - > detail ) ;
2016-11-14 15:25:04 +00:00
unsigned int modstate = x11_canonalize_mask ( xkpe - > state & ( ~ consumed ) ) ;
2016-04-07 12:24:34 +00:00
if ( key ! = XKB_KEY_NoSymbol ) {
2016-04-07 13:32:22 +00:00
KeyBindingAction action ;
action = abe_find_action ( modstate , key ) ;
2016-05-07 09:31:00 +00:00
if ( rofi_view_trigger_action ( state , action ) ) {
2016-04-07 13:32:22 +00:00
return ;
2016-04-07 12:24:34 +00:00
}
}
2016-04-07 13:01:14 +00:00
2016-05-22 17:41:52 +00:00
if ( ( len > 0 ) & & ( textbox_append_char ( state - > text , pad , len ) ) ) {
2016-04-07 12:24:34 +00:00
state - > refilter = TRUE ;
2016-04-07 13:01:14 +00:00
return ;
2016-04-07 12:24:34 +00:00
}
2016-04-07 12:22:18 +00:00
}
2016-10-28 06:51:53 +00:00
void rofi_view_itterrate ( RofiViewState * state , xcb_generic_event_t * ev , xkb_stuff * xkb )
2016-04-07 12:22:18 +00:00
{
switch ( ev - > response_type & ~ 0x80 )
{
2016-10-28 06:51:53 +00:00
case XCB_CONFIGURE_NOTIFY :
{
xcb_configure_notify_event_t * xce = ( xcb_configure_notify_event_t * ) ev ;
if ( xce - > window = = CacheState . main_window ) {
if ( state - > x ! = xce - > x | | state - > y ! = xce - > y ) {
state - > x = xce - > x ;
state - > y = xce - > y ;
2016-12-29 21:06:31 +00:00
widget_queue_redraw ( WIDGET ( state - > main_window ) ) ;
2016-10-28 06:51:53 +00:00
}
if ( state - > width ! = xce - > width | | state - > height ! = xce - > height ) {
state - > width = xce - > width ;
state - > height = xce - > height ;
2016-11-11 15:29:59 +00:00
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 ) ;
2017-01-08 20:36:06 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Re-size window based external request: %d %d \n " , state - > width , state - > height ) ;
2016-12-29 20:58:32 +00:00
widget_resize ( WIDGET ( state - > main_window ) , state - > width , state - > height ) ;
2016-10-28 06:51:53 +00:00
}
}
break ;
}
2016-04-07 12:22:18 +00:00
case XCB_FOCUS_IN :
2016-09-06 07:05:30 +00:00
if ( ( CacheState . flags & MENU_NORMAL_WINDOW ) = = 0 ) {
2017-01-08 20:36:06 +00:00
take_keyboard ( CacheState . main_window , 1 ) ;
2016-04-07 12:22:18 +00:00
}
break ;
case XCB_FOCUS_OUT :
2016-09-06 07:05:30 +00:00
if ( ( CacheState . flags & MENU_NORMAL_WINDOW ) = = 0 ) {
2016-04-07 12:22:18 +00:00
release_keyboard ( ) ;
}
break ;
case XCB_MOTION_NOTIFY :
{
2016-09-08 20:42:41 +00:00
if ( config . click_to_exit = = TRUE ) {
state - > mouse_seen = TRUE ;
}
2016-10-25 19:19:39 +00:00
xcb_motion_notify_event_t xme = * ( ( xcb_motion_notify_event_t * ) ev ) ;
2016-12-28 18:42:14 +00:00
if ( widget_motion_notify ( WIDGET ( state - > main_window ) , & xme ) ) {
2016-10-25 19:19:39 +00:00
return ;
}
2016-02-19 21:43:53 +00:00
break ;
}
2016-04-07 12:22:18 +00:00
case XCB_BUTTON_PRESS :
rofi_view_mouse_navigation ( state , ( xcb_button_press_event_t * ) ev ) ;
break ;
2016-09-08 20:42:41 +00:00
case XCB_BUTTON_RELEASE :
if ( config . click_to_exit = = TRUE ) {
if ( ( CacheState . flags & MENU_NORMAL_WINDOW ) = = 0 ) {
xcb_button_release_event_t * bre = ( xcb_button_release_event_t * ) ev ;
if ( ( state - > mouse_seen = = FALSE ) & & ( bre - > event ! = CacheState . main_window ) ) {
state - > quit = TRUE ;
state - > retv = MENU_CANCEL ;
}
}
state - > mouse_seen = FALSE ;
}
break ;
2016-04-07 12:22:18 +00:00
// Paste event.
case XCB_SELECTION_NOTIFY :
rofi_view_paste ( state , ( xcb_selection_notify_event_t * ) ev ) ;
break ;
2016-04-07 18:28:40 +00:00
case XCB_KEYMAP_NOTIFY :
{
2016-05-08 09:13:11 +00:00
xcb_keymap_notify_event_t * kne = ( xcb_keymap_notify_event_t * ) ev ;
guint modstate = x11_get_current_mask ( xkb ) ;
2016-10-17 18:54:41 +00:00
for ( gint32 by = 0 ; by < 31 ; + + by ) {
2016-05-08 09:13:11 +00:00
for ( gint8 bi = 0 ; bi < 7 ; + + bi ) {
if ( kne - > keys [ by ] & ( 1 < < bi ) ) {
2016-04-07 18:28:40 +00:00
// X11 keycodes starts at 8
xkb_keysym_t key = xkb_state_key_get_one_sym ( xkb - > state , ( 8 * by + bi ) + 8 ) ;
abe_find_action ( modstate , key ) ;
}
}
}
break ;
}
2016-04-07 12:22:18 +00:00
case XCB_KEY_PRESS :
rofi_view_handle_keypress ( state , xkb , ( xcb_key_press_event_t * ) ev ) ;
break ;
2016-04-07 18:28:40 +00:00
case XCB_KEY_RELEASE :
{
2016-05-08 09:13:11 +00:00
xcb_key_release_event_t * xkre = ( xcb_key_release_event_t * ) ev ;
unsigned int modstate = x11_canonalize_mask ( xkre - > state ) ;
2016-04-07 18:28:40 +00:00
if ( modstate = = 0 ) {
abe_trigger_release ( ) ;
}
break ;
}
2016-10-28 06:51:53 +00:00
default :
2016-10-28 16:57:12 +00:00
break ;
2016-02-06 13:27:36 +00:00
}
// Update if requested.
if ( state - > refilter ) {
2016-02-07 13:09:00 +00:00
rofi_view_refilter ( state ) ;
2016-02-06 13:27:36 +00:00
}
2017-01-01 01:57:04 +00:00
rofi_view_update ( state , TRUE ) ;
2016-11-11 15:29:59 +00:00
2016-11-15 07:24:06 +00:00
if ( ( ev - > response_type & ~ 0x80 ) = = XCB_EXPOSE & & CacheState . repaint_source = = 0 ) {
CacheState . repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE , rofi_view_repaint , NULL , NULL ) ;
2016-11-11 15:29:59 +00:00
}
2016-02-06 13:27:36 +00:00
}
2016-09-17 15:49:45 +00:00
2016-10-08 16:57:59 +00:00
static int rofi_view_calculate_height ( RofiViewState * state )
2016-09-17 15:49:45 +00:00
{
2016-10-08 16:57:59 +00:00
unsigned int height = 0 ;
2017-01-08 16:38:21 +00:00
if ( listview_get_num_lines ( state - > list_view ) = = 0 | | CacheState . fullscreen = = TRUE ) {
2016-10-08 16:57:59 +00:00
height = CacheState . mon . h ;
return height ;
}
2016-12-30 17:31:30 +00:00
2016-12-28 18:42:14 +00:00
widget * main_window = WIDGET ( state - > main_window ) ;
2016-12-30 17:31:30 +00:00
height = widget_get_desired_height ( main_window ) ;
2016-10-08 16:57:59 +00:00
return height ;
}
2016-09-17 15:49:45 +00:00
2016-10-08 16:57:59 +00:00
static gboolean rofi_view_modi_clicked_cb ( widget * textbox , G_GNUC_UNUSED xcb_button_press_event_t * xbe , void * udata )
{
RofiViewState * state = ( RofiViewState * ) udata ;
for ( unsigned int i = 0 ; i < state - > num_modi ; i + + ) {
if ( WIDGET ( state - > modi [ i ] ) = = textbox ) {
state - > retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK ) ;
state - > quit = TRUE ;
state - > skip_absorb = TRUE ;
return TRUE ;
}
}
return FALSE ;
}
// @TODO don't like this construction.
static void rofi_view_listview_mouse_activated_cb ( listview * lv , xcb_button_press_event_t * xce , void * udata )
{
RofiViewState * state = ( RofiViewState * ) udata ;
int control = x11_modifier_active ( xce - > state , X11MOD_CONTROL ) ;
state - > retv = MENU_OK ;
if ( control ) {
state - > retv | = MENU_CUSTOM_ACTION ;
2016-09-17 15:49:45 +00:00
}
2016-10-08 16:57:59 +00:00
( state - > selected_line ) = state - > line_map [ listview_get_selected ( lv ) ] ;
// Quit
state - > quit = TRUE ;
state - > skip_absorb = TRUE ;
2016-09-17 15:49:45 +00:00
}
2016-02-06 13:27:36 +00:00
RofiViewState * rofi_view_create ( Mode * sw ,
2016-02-07 11:31:17 +00:00
const char * input ,
2016-02-06 13:27:36 +00:00
const char * message ,
2016-02-19 18:29:06 +00:00
MenuFlags menu_flags ,
2016-04-19 20:10:34 +00:00
void ( * finalize ) ( RofiViewState * ) )
2016-02-06 13:27:36 +00:00
{
TICK ( ) ;
2016-02-08 17:16:16 +00:00
RofiViewState * state = __rofi_view_state_create ( ) ;
2016-02-10 18:40:19 +00:00
state - > menu_flags = menu_flags ;
2016-02-06 13:27:36 +00:00
state - > sw = sw ;
state - > selected_line = UINT32_MAX ;
state - > retv = MENU_CANCEL ;
state - > distance = NULL ;
state - > quit = FALSE ;
state - > skip_absorb = FALSE ;
//We want to filter on the first run.
2016-10-28 16:57:12 +00:00
state - > refilter = TRUE ;
state - > finalize = finalize ;
state - > mouse_seen = FALSE ;
2016-02-06 13:27:36 +00:00
// Request the lines to show.
2016-05-26 06:39:33 +00:00
state - > num_lines = mode_get_num_entries ( sw ) ;
2016-02-06 13:27:36 +00:00
2016-02-27 23:09:05 +00:00
TICK_N ( " Startup notification " ) ;
2016-02-06 13:27:36 +00:00
// Get active monitor size.
TICK_N ( " Get active monitor " ) ;
2017-01-10 10:20:38 +00:00
state - > main_window = container_create ( " window.box " ) ;
2017-01-08 20:36:06 +00:00
state - > main_box = box_create ( " window.mainbox.box " , BOX_VERTICAL ) ;
2017-01-05 17:33:57 +00:00
container_add ( state - > main_window , WIDGET ( state - > main_box ) ) ;
2016-10-08 16:57:59 +00:00
2017-01-04 21:27:27 +00:00
state - > input_bar = box_create ( " window.mainbox.inputbar.box " , BOX_HORIZONTAL ) ;
2016-12-10 17:27:36 +00:00
// Only enable widget when sidebar is enabled.
if ( config . sidebar_mode ) {
2017-01-04 21:27:27 +00:00
state - > sidebar_bar = box_create ( " window.mainbox.sidebar.box " , BOX_HORIZONTAL ) ;
2017-01-06 15:41:23 +00:00
box_add ( state - > main_box , WIDGET ( state - > sidebar_bar ) , FALSE , 10 ) ;
2016-12-10 17:27:36 +00:00
state - > num_modi = rofi_get_num_enabled_modi ( ) ;
state - > modi = g_malloc0 ( state - > num_modi * sizeof ( textbox * ) ) ;
for ( unsigned int j = 0 ; j < state - > num_modi ; j + + ) {
const Mode * mode = rofi_get_mode ( j ) ;
2017-01-08 20:36:06 +00:00
state - > modi [ j ] = textbox_create ( " window.mainbox.sidebar.button " , TB_CENTER | TB_AUTOHEIGHT , ( mode = = state - > sw ) ? HIGHLIGHT : NORMAL ,
2016-12-10 17:27:36 +00:00
mode_get_display_name ( mode ) ) ;
2017-01-06 15:41:23 +00:00
box_add ( state - > sidebar_bar , WIDGET ( state - > modi [ j ] ) , TRUE , j ) ;
2016-12-10 17:27:36 +00:00
widget_set_clicked_handler ( WIDGET ( state - > modi [ j ] ) , rofi_view_modi_clicked_cb , state ) ;
}
}
2016-10-08 16:57:59 +00:00
2017-01-06 18:04:25 +00:00
int location = rofi_theme_get_position ( WIDGET ( state - > main_window ) , " location " , config . location ) ;
2017-01-08 20:36:06 +00:00
int end = ( location = = WL_SOUTH_EAST | | location = = WL_SOUTH | | location = = WL_SOUTH_WEST ) ;
box_add ( state - > main_box , WIDGET ( state - > input_bar ) , FALSE , end ? 9 : 0 ) ;
2016-10-08 16:57:59 +00:00
2017-01-08 20:36:06 +00:00
state - > case_indicator = textbox_create ( " window.mainbox.inputbar.case-indicator " , TB_AUTOWIDTH | TB_AUTOHEIGHT , NORMAL , " * " ) ;
2016-10-09 08:07:32 +00:00
// Add small separator between case indicator and text box.
2017-01-06 15:41:23 +00:00
box_add ( state - > input_bar , WIDGET ( state - > case_indicator ) , FALSE , 3 ) ;
2016-02-06 13:27:36 +00:00
// Prompt box.
2017-01-08 20:36:06 +00:00
state - > prompt = textbox_create ( " window.mainbox.inputbar.prompt " , TB_AUTOWIDTH | TB_AUTOHEIGHT , NORMAL , " " ) ;
2016-10-08 16:57:59 +00:00
rofi_view_update_prompt ( state ) ;
2017-01-06 15:41:23 +00:00
box_add ( state - > input_bar , WIDGET ( state - > prompt ) , FALSE , 1 ) ;
2016-10-08 16:57:59 +00:00
2016-02-06 13:27:36 +00:00
// Entry box
TextboxFlags tfl = TB_EDITABLE ;
tfl | = ( ( menu_flags & MENU_PASSWORD ) = = MENU_PASSWORD ) ? TB_PASSWORD : 0 ;
2017-01-08 20:36:06 +00:00
state - > text = textbox_create ( " window.mainbox.inputbar.entry " , tfl | TB_AUTOHEIGHT , NORMAL , input ) ;
2016-02-06 13:27:36 +00:00
2017-01-06 15:41:23 +00:00
box_add ( state - > input_bar , WIDGET ( state - > text ) , TRUE , 2 ) ;
2016-02-06 13:27:36 +00:00
textbox_text ( state - > case_indicator , get_matching_state ( ) ) ;
if ( message ) {
2017-01-08 20:36:06 +00:00
container * box = container_create ( " window.mainbox.message.box " ) ;
textbox * message_tb = textbox_create ( " window.mainbox.message.textbox " , TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP , NORMAL , message ) ;
container_add ( box , WIDGET ( message_tb ) ) ;
box_add ( state - > main_box , WIDGET ( box ) , FALSE , end ? 8 : 2 ) ;
2016-02-06 13:27:36 +00:00
}
2017-01-08 20:36:06 +00:00
state - > overlay = textbox_create ( " window.overlay " , TB_AUTOWIDTH | TB_AUTOHEIGHT , URGENT , " blaat " ) ;
2016-06-26 13:48:12 +00:00
widget_disable ( WIDGET ( state - > overlay ) ) ;
2016-02-06 13:27:36 +00:00
2017-01-04 21:27:27 +00:00
state - > list_view = listview_create ( " window.mainbox.listview " , update_callback , state , config . element_height , end ) ;
2016-10-08 16:57:59 +00:00
// Set configuration
listview_set_multi_select ( state - > list_view , ( state - > menu_flags & MENU_INDICATOR ) = = MENU_INDICATOR ) ;
listview_set_scroll_type ( state - > list_view , config . scroll_method ) ;
listview_set_mouse_activated_cb ( state - > list_view , rofi_view_listview_mouse_activated_cb , state ) ;
2017-01-08 16:18:49 +00:00
2017-01-08 20:36:06 +00:00
int lines = rofi_theme_get_integer ( WIDGET ( state - > list_view ) , " lines " , config . menu_lines ) ;
2017-01-08 16:18:49 +00:00
listview_set_num_lines ( state - > list_view , lines ) ;
2017-01-02 20:19:27 +00:00
listview_set_max_lines ( state - > list_view , state - > num_lines ) ;
2016-10-08 16:57:59 +00:00
2017-01-08 20:36:06 +00:00
box_add ( state - > main_box , WIDGET ( state - > list_view ) , TRUE , 3 ) ;
2016-10-08 16:57:59 +00:00
2016-02-06 13:27:36 +00:00
// filtered list
state - > line_map = g_malloc0_n ( state - > num_lines , sizeof ( unsigned int ) ) ;
state - > distance = ( int * ) g_malloc0_n ( state - > num_lines , sizeof ( int ) ) ;
2017-01-02 20:19:27 +00:00
rofi_view_calculate_window_width ( state ) ;
2017-01-03 11:19:07 +00:00
// Need to resize otherwise calculated desired height is wrong.
2017-01-08 20:36:06 +00:00
widget_resize ( WIDGET ( state - > main_window ) , state - > width , 100 ) ;
2017-01-02 20:19:27 +00:00
// Only needed when window is fixed size.
2017-01-08 20:36:06 +00:00
if ( ( CacheState . flags & MENU_NORMAL_WINDOW ) = = MENU_NORMAL_WINDOW ) {
2017-01-08 16:18:49 +00:00
listview_set_fixed_num_lines ( state - > list_view ) ;
2017-01-02 20:19:27 +00:00
rofi_view_window_update_size ( state ) ;
2016-12-29 20:58:32 +00:00
}
2016-02-06 13:27:36 +00:00
// Move the window to the correct x,y position.
2016-09-17 15:49:45 +00:00
rofi_view_calculate_window_position ( state ) ;
2016-02-06 13:27:36 +00:00
2016-10-08 16:57:59 +00:00
state - > quit = FALSE ;
2016-02-07 13:09:00 +00:00
rofi_view_refilter ( state ) ;
2017-01-01 01:57:04 +00:00
rofi_view_update ( state , TRUE ) ;
2016-04-19 20:10:34 +00:00
xcb_map_window ( xcb - > connection , CacheState . main_window ) ;
2016-12-28 18:42:14 +00:00
widget_queue_redraw ( WIDGET ( state - > main_window ) ) ;
2016-03-01 17:11:55 +00:00
xcb_flush ( xcb - > connection ) ;
if ( xcb - > sncontext ! = NULL ) {
sn_launchee_context_complete ( xcb - > sncontext ) ;
2016-02-06 13:27:36 +00:00
}
return state ;
}
2016-09-16 18:28:59 +00:00
2016-03-03 07:21:28 +00:00
int rofi_view_error_dialog ( const char * msg , int markup )
2016-02-06 13:27:36 +00:00
{
2016-02-08 17:16:16 +00:00
RofiViewState * state = __rofi_view_state_create ( ) ;
2016-10-28 16:57:12 +00:00
state - > retv = MENU_CANCEL ;
state - > menu_flags = MENU_ERROR_DIALOG ;
state - > finalize = process_result ;
2016-02-27 23:09:05 +00:00
2017-01-10 10:20:38 +00:00
state - > main_window = container_create ( " window box " ) ;
2017-01-08 20:36:06 +00:00
state - > main_box = box_create ( " window.mainbox.message.box " , BOX_VERTICAL ) ;
2017-01-05 17:33:57 +00:00
container_add ( state - > main_window , WIDGET ( state - > main_box ) ) ;
2017-01-08 15:49:46 +00:00
state - > text = textbox_create ( " window.mainbox.message.textbox " , ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ) ,
2017-01-08 20:36:06 +00:00
NORMAL , ( msg ! = NULL ) ? msg : " " ) ;
2017-01-06 15:41:23 +00:00
box_add ( state - > main_box , WIDGET ( state - > text ) , TRUE , 1 ) ;
2017-01-03 11:19:07 +00:00
2016-12-29 20:58:32 +00:00
// Make sure we enable fixed num lines when in normal window mode.
2017-01-08 20:36:06 +00:00
if ( ( CacheState . flags & MENU_NORMAL_WINDOW ) = = MENU_NORMAL_WINDOW ) {
2017-01-08 16:18:49 +00:00
listview_set_fixed_num_lines ( state - > list_view ) ;
2016-12-29 20:58:32 +00:00
}
2017-01-03 11:19:07 +00:00
rofi_view_calculate_window_width ( state ) ;
// Need to resize otherwise calculated desired height is wrong.
2017-01-08 20:36:06 +00:00
widget_resize ( WIDGET ( state - > main_window ) , state - > width , 100 ) ;
2017-01-03 11:19:07 +00:00
unsigned int line_height = textbox_get_height ( state - > text ) ;
2016-02-06 13:27:36 +00:00
// resize window vertically to suit
2017-01-08 20:36:06 +00:00
state - > height = line_height + widget_padding_get_padding_height ( WIDGET ( state - > main_window ) ) ;
2016-02-06 13:27:36 +00:00
2016-03-05 10:08:32 +00:00
// Calculte window position.
2016-09-17 15:49:45 +00:00
rofi_view_calculate_window_position ( state ) ;
2016-03-05 10:08:32 +00:00
2016-02-06 13:27:36 +00:00
// Move the window to the correct x,y position.
2016-09-17 15:49:45 +00:00
rofi_view_window_update_size ( state ) ;
2016-02-21 18:42:32 +00:00
2016-02-06 13:27:36 +00:00
// Display it.
2016-04-19 20:10:34 +00:00
xcb_map_window ( xcb - > connection , CacheState . main_window ) ;
2016-12-28 18:42:14 +00:00
widget_queue_redraw ( WIDGET ( state - > main_window ) ) ;
2016-02-06 13:27:36 +00:00
2016-03-01 17:11:55 +00:00
if ( xcb - > sncontext ! = NULL ) {
sn_launchee_context_complete ( xcb - > sncontext ) ;
2016-02-06 13:27:36 +00:00
}
2016-03-05 10:08:32 +00:00
2016-11-14 15:31:26 +00:00
// Set it as current window.
2016-02-06 13:27:36 +00:00
rofi_view_set_active ( state ) ;
2016-03-03 07:21:28 +00:00
return TRUE ;
2016-02-06 13:27:36 +00:00
}
2016-08-30 06:14:53 +00:00
void rofi_view_hide ( void )
{
if ( CacheState . main_window ! = XCB_WINDOW_NONE ) {
xcb_unmap_window ( xcb - > connection , CacheState . main_window ) ;
2016-09-02 07:38:51 +00:00
release_keyboard ( ) ;
2016-09-02 18:19:37 +00:00
release_pointer ( ) ;
2016-09-02 07:38:51 +00:00
xcb_flush ( xcb - > connection ) ;
2016-08-30 06:14:53 +00:00
}
}
2016-02-06 13:27:36 +00:00
void rofi_view_cleanup ( )
{
2016-12-15 08:09:14 +00:00
g_log ( LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Cleanup. " ) ;
2016-09-17 16:35:03 +00:00
if ( CacheState . idle_timeout > 0 ) {
2016-09-17 15:49:45 +00:00
g_source_remove ( CacheState . idle_timeout ) ;
CacheState . idle_timeout = 0 ;
}
2016-11-15 07:24:06 +00:00
if ( CacheState . repaint_source > 0 ) {
g_source_remove ( CacheState . repaint_source ) ;
2016-11-30 07:41:47 +00:00
CacheState . repaint_source = 0 ;
2016-11-11 15:29:59 +00:00
}
2016-03-05 10:18:23 +00:00
if ( CacheState . fake_bg ) {
cairo_surface_destroy ( CacheState . fake_bg ) ;
CacheState . fake_bg = NULL ;
}
2016-11-11 15:29:59 +00:00
if ( CacheState . edit_draw ) {
cairo_destroy ( CacheState . edit_draw ) ;
CacheState . edit_draw = NULL ;
2016-03-05 10:18:23 +00:00
}
2016-11-11 15:29:59 +00:00
if ( CacheState . edit_surf ) {
cairo_surface_destroy ( CacheState . edit_surf ) ;
CacheState . edit_surf = NULL ;
2016-03-05 10:18:23 +00:00
}
if ( CacheState . main_window ! = XCB_WINDOW_NONE ) {
xcb_unmap_window ( xcb - > connection , CacheState . main_window ) ;
2016-11-11 15:29:59 +00:00
xcb_free_gc ( xcb - > connection , CacheState . gc ) ;
xcb_free_pixmap ( xcb - > connection , CacheState . edit_pixmap ) ;
2016-03-05 10:18:23 +00:00
xcb_destroy_window ( xcb - > connection , CacheState . main_window ) ;
CacheState . main_window = XCB_WINDOW_NONE ;
2016-02-06 13:27:36 +00:00
}
2016-02-21 18:42:32 +00:00
if ( map ! = XCB_COLORMAP_NONE ) {
2016-03-01 17:11:55 +00:00
xcb_free_colormap ( xcb - > connection , map ) ;
2016-02-21 18:42:32 +00:00
map = XCB_COLORMAP_NONE ;
2016-02-06 13:27:36 +00:00
}
2016-04-19 20:10:34 +00:00
g_assert ( g_queue_is_empty ( & ( CacheState . views ) ) ) ;
2016-02-06 13:27:36 +00:00
}
2016-02-10 18:40:19 +00:00
void rofi_view_workers_initialize ( void )
{
TICK_N ( " Setup Threadpool, start " ) ;
2016-02-19 19:08:14 +00:00
if ( config . threads = = 0 ) {
config . threads = 1 ;
long procs = sysconf ( _SC_NPROCESSORS_CONF ) ;
if ( procs > 0 ) {
config . threads = MIN ( procs , 128l ) ;
}
}
2016-02-10 18:40:19 +00:00
// Create thread pool
GError * error = NULL ;
tpool = g_thread_pool_new ( rofi_view_call_thread , NULL , config . threads , FALSE , & error ) ;
if ( error = = NULL ) {
// Idle threads should stick around for a max of 60 seconds.
g_thread_pool_set_max_idle_time ( 60000 ) ;
// We are allowed to have
g_thread_pool_set_max_threads ( tpool , config . threads , & error ) ;
}
// If error occured during setup of pool, tell user and exit.
if ( error ! = NULL ) {
2016-05-06 10:40:28 +00:00
fprintf ( stderr , " Failed to setup thread pool: '%s' " , error - > message ) ;
2016-02-10 18:40:19 +00:00
g_error_free ( error ) ;
exit ( EXIT_FAILURE ) ;
}
TICK_N ( " Setup Threadpool, done " ) ;
}
void rofi_view_workers_finalize ( void )
{
if ( tpool ) {
g_thread_pool_free ( tpool , TRUE , FALSE ) ;
tpool = NULL ;
}
}
2016-02-19 18:29:06 +00:00
Mode * rofi_view_get_mode ( RofiViewState * state )
{
return state - > sw ;
}
2016-02-10 18:40:19 +00:00
2016-06-26 13:48:12 +00:00
void rofi_view_set_overlay ( RofiViewState * state , const char * text )
{
if ( state - > overlay = = NULL ) {
return ;
}
if ( text = = NULL ) {
widget_disable ( WIDGET ( state - > overlay ) ) ;
return ;
}
widget_enable ( WIDGET ( state - > overlay ) ) ;
textbox_text ( state - > overlay , text ) ;
2017-01-08 20:36:06 +00:00
int x_offset = widget_get_width ( WIDGET ( state - > main_window ) ) ;
2017-01-01 11:16:04 +00:00
// Within padding of window.
2017-01-08 20:36:06 +00:00
x_offset - = widget_padding_get_right ( WIDGET ( state - > main_window ) ) ;
2017-01-01 11:16:04 +00:00
// Within the border of widget.
2017-01-08 20:36:06 +00:00
x_offset - = widget_padding_get_right ( WIDGET ( state - > main_box ) ) ;
x_offset - = widget_padding_get_right ( WIDGET ( state - > input_bar ) ) ;
2017-01-01 11:16:04 +00:00
x_offset - = widget_get_width ( WIDGET ( state - > case_indicator ) ) ;
2016-10-08 16:57:59 +00:00
x_offset - = widget_get_width ( WIDGET ( state - > overlay ) ) ;
2017-01-08 20:36:06 +00:00
int top_offset = widget_padding_get_top ( WIDGET ( state - > main_window ) ) ;
top_offset + = widget_padding_get_top ( WIDGET ( state - > main_box ) ) ;
2017-01-01 11:16:04 +00:00
widget_move ( WIDGET ( state - > overlay ) , x_offset , top_offset ) ;
2016-12-26 19:57:33 +00:00
// We want to queue a repaint.
rofi_view_queue_redraw ( ) ;
2016-10-08 16:57:59 +00:00
}
2016-12-12 07:52:27 +00:00
void rofi_view_clear_input ( RofiViewState * state )
{
2017-01-08 20:36:06 +00:00
if ( state - > text ) {
textbox_text ( state - > text , " " ) ;
2016-12-12 07:52:27 +00:00
rofi_view_set_selected_line ( state , 0 ) ;
}
}
2016-10-08 16:57:59 +00:00
void rofi_view_switch_mode ( RofiViewState * state , Mode * mode )
{
state - > sw = mode ;
// Update prompt;
if ( state - > prompt ) {
rofi_view_update_prompt ( state ) ;
if ( config . sidebar_mode ) {
for ( unsigned int j = 0 ; j < state - > num_modi ; j + + ) {
const Mode * mode = rofi_get_mode ( j ) ;
textbox_font ( state - > modi [ j ] , ( mode = = state - > sw ) ? HIGHLIGHT : NORMAL ) ;
}
}
}
rofi_view_restart ( state ) ;
state - > reload = TRUE ;
state - > refilter = TRUE ;
2016-10-11 06:11:14 +00:00
rofi_view_refilter ( state ) ;
2017-01-01 01:57:04 +00:00
rofi_view_update ( state , TRUE ) ;
2016-06-26 13:48:12 +00:00
}
2016-11-07 08:15:04 +00:00
xcb_window_t rofi_view_get_window ( void )
{
return CacheState . main_window ;
}