diff --git a/Makefile.am b/Makefile.am index 2543b38b..b01ed5aa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,19 +9,19 @@ bin_PROGRAMS=rofi dist_bin_SCRIPTS=script/rofi-sensible-terminal LIBS=\ - @xft_LIBS@\ @x11_LIBS@\ @xinerama_LIBS@\ @libsn_LIBS@\ - @pango_LIBS@ + @pango_LIBS@\ + @cairo_LIBS@ AM_CFLAGS=\ @EXTRA_CFLAGS@\ - @xft_CFLAGS@\ @x11_CFLAGS@\ @xinerama_CFLAGS@\ @pango_CFLAGS@\ @libsn_CFLAGS@\ + @cairo_CFLAGS@\ -DMANPAGE_PATH="\"$(mandir)/\""\ -I$(top_srcdir)/include/\ -I$(top_srcdir)/config/\ diff --git a/configure.ac b/configure.ac index a94eb514..6b89a73c 100644 --- a/configure.ac +++ b/configure.ac @@ -48,10 +48,10 @@ AS_IF([test "x$enable_i3support" != xno && test "x$enable_windowmode" != "xno"], ## PKG_PROG_PKG_CONFIG -PKG_CHECK_MODULES([xft], [xft]) PKG_CHECK_MODULES([x11], [x11]) PKG_CHECK_MODULES([xinerama], [xinerama]) -PKG_CHECK_MODULES([pango], [pango pangoxft]) +PKG_CHECK_MODULES([pango], [pango pangocairo]) +PKG_CHECK_MODULES([cairo], [cairo cairo-xlib]) PKG_CHECK_MODULES([libsn], [libstartup-notification-1.0]) AC_SUBST([EXTRA_CFLAGS], ["-Wall -Wextra -Wparentheses -Winline -pedantic"]) diff --git a/include/rofi.h b/include/rofi.h index d44ddbf7..23401841 100644 --- a/include/rofi.h +++ b/include/rofi.h @@ -2,6 +2,8 @@ #define ROFI_MAIN_H #include #include +#include +#include #include #include "keyb.h" diff --git a/include/scrollbar.h b/include/scrollbar.h index 2e07468d..0b535ea8 100644 --- a/include/scrollbar.h +++ b/include/scrollbar.h @@ -1,14 +1,12 @@ #ifndef ROFI_SCROLLBAR_H #define ROFI_SCROLLBAR_H - +#include /** * Internal structure for the scrollbar. */ typedef struct _scrollbar { - Window window, parent; short x, y, w, h; - GC gc; unsigned int length; unsigned int pos; unsigned int pos_length; @@ -27,22 +25,7 @@ typedef struct _scrollbar * * @returns the scrollbar object. */ -scrollbar *scrollbar_create ( Window parent, XVisualInfo *vinfo, Colormap map, - short x, short y, short w, short h ); - -/** - * @param sb scrollbar object - * - * Hide (unmap) the scrollbar. - */ -void scrollbar_hide ( scrollbar *sb ); - -/** - * @param sb scrollbar object - * - * Show (map) the scrollbar. - */ -void scrollbar_show ( scrollbar *sb ); +scrollbar *scrollbar_create ( short x, short y, short w, short h ); /** * @param sb scrollbar object @@ -80,7 +63,7 @@ void scrollbar_set_max_value ( scrollbar *sb, unsigned int max ); * * Draw the scrollbar, used after expose event or update */ -void scrollbar_draw ( scrollbar *sb ); +void scrollbar_draw ( scrollbar *sb, cairo_t *draw ); /** * @param sb scrollbar object diff --git a/include/textbox.h b/include/textbox.h index 17a5c519..b4e0c5d7 100644 --- a/include/textbox.h +++ b/include/textbox.h @@ -1,24 +1,34 @@ #ifndef ROFI_TEXTBOX_H #define ROFI_TEXTBOX_H +#include #include -#include #include +#include typedef struct { - unsigned long flags; - Window window, parent; - short x, y, w, h; - short cursor; - XftColor color_fg, color_bg; - char *text; - XIM xim; - XIC xic; - PangoLayout *layout; - int tbft; - int markup; - int changed; + double red, green, blue, alpha; +} Color; + +typedef struct +{ + unsigned long flags; + short x, y, w, h; + short cursor; + Color color_fg, color_bg; + char *text; + XIM xim; + XIC xic; + PangoLayout *layout; + int tbft; + int markup; + int changed; + + cairo_surface_t *main_surface; + cairo_t *main_draw; + + int update; } textbox; typedef enum @@ -49,10 +59,7 @@ typedef enum STATE_MASK = ~( ALT | HIGHLIGHT ) } TextBoxFontType; -textbox* textbox_create ( Window parent, - XVisualInfo *vinfo, - Colormap map, - TextboxFlags flags, +textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h, TextBoxFontType tbft, const char *text ); @@ -79,19 +86,12 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft ); */ void textbox_text ( textbox *tb, const char *text ); -/** - * @param tb Handle to the textbox - * - * Show the textbox (map window) - */ -void textbox_show ( textbox *tb ); - /** * @param tb Handle to the textbox * * Render the textbox. */ -void textbox_draw ( textbox * tb ); +void textbox_draw ( textbox *tb, cairo_t *draw ); /** * @param tb Handle to the textbox @@ -101,7 +101,7 @@ void textbox_draw ( textbox * tb ); * * @returns if the key was handled (1), unhandled(0) or handled and return was pressed (-1) */ -int textbox_keypress ( textbox *tb, XEvent *ev ); +int textbox_keypress ( textbox *tb, XIC xic, XEvent *ev ); /** * @param tb Handle to the textbox @@ -137,21 +137,11 @@ void textbox_move ( textbox *tb, int x, int y ); void textbox_insert ( textbox *tb, int pos, char *str ); /** - * @param tb Handle to the textbox - * - * Unmap the textbox window. Effectively hiding it. - */ -void textbox_hide ( textbox *tb ); - -/** - * @param visual Information about the visual to target - * @param colormap The colormap to set the colors for. - * * Setup the cached fonts. This is required to do * before any of the textbox_ functions is called. * Clean with textbox_cleanup() */ -void textbox_setup ( XVisualInfo *visual, Colormap colormap ); +void textbox_setup ( void ); /** * Cleanup the allocated colors and fonts by textbox_setup(). diff --git a/source/rofi.c b/source/rofi.c index 372a9959..2da3f75e 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -44,6 +44,9 @@ #include #include +#include +#include + #define SN_API_NOT_YET_FROZEN #include @@ -70,14 +73,14 @@ typedef struct _Mode // Pidfile. extern Atom netatoms[NUM_NETATOMS]; -char *pidfile = NULL; -const char *cache_dir = NULL; -SnDisplay *sndisplay = NULL; -SnLauncheeContext *sncontext = NULL; -Display *display = NULL; -char *display_str = NULL; -Window main_window = None; -GC gc = NULL; +char *pidfile = NULL; +const char *cache_dir = NULL; +SnDisplay *sndisplay = NULL; +SnLauncheeContext *sncontext = NULL; +Display *display = NULL; +char *display_str = NULL; +Window main_window = None; +// GC gc = NULL; Colormap map = None; unsigned int normal_window_mode = FALSE; // Array of switchers. @@ -87,6 +90,11 @@ unsigned int num_switchers = 0; unsigned int curr_switcher = 0; XVisualInfo vinfo; +cairo_surface_t *surface = NULL; +cairo_t *draw = NULL; +XIM xim; +XIC xic; + /** * @param name Name of the switcher to lookup. * @@ -188,44 +196,6 @@ static int levenshtein ( const char *s, const char *t ) return dist ( s, t, d, ls, lt, 0, 0 ); } -static Window create_window ( Display *display ) -{ - XSetWindowAttributes attr; - attr.colormap = map; - attr.border_pixel = color_border ( display ); - attr.background_pixel = color_background ( display ); - - Window box = XCreateWindow ( display, DefaultRootWindow ( display ), 0, 0, 200, 100, config.menu_bw, vinfo.depth, InputOutput, - vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr ); - XSelectInput ( display, box, KeyReleaseMask | KeyPressMask | ExposureMask | ButtonPressMask | StructureNotifyMask | FocusChangeMask ); - - gc = XCreateGC ( display, box, 0, 0 ); - int line_style = LineOnOffDash; - if ( strcasecmp ( config.separator_style, "dash" ) == 0 ) { - line_style = LineOnOffDash; - } - else if ( strcasecmp ( config.separator_style, "solid" ) == 0 ) { - line_style = LineSolid; - } - XSetLineAttributes ( display, gc, 2, line_style, CapButt, JoinMiter ); - XSetForeground ( display, gc, color_separator ( display ) ); - // make it an unmanaged window - if ( !normal_window_mode ) { - window_set_atom_prop ( display, box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1 ); - XSetWindowAttributes sattr = { .override_redirect = True }; - XChangeWindowAttributes ( display, box, CWOverrideRedirect, &sattr ); - } - else{ - window_set_atom_prop ( display, box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_NORMAL], 1 ); - } - - // Set the WM_NAME - XStoreName ( display, box, "rofi" ); - - x11_set_window_opacity ( display, box, config.window_opacity ); - return box; -} - // State of the menu. typedef struct MenuState @@ -278,6 +248,47 @@ typedef struct MenuState int line_height; }MenuState; +static Window create_window ( Display *display ) +{ + XSetWindowAttributes attr; + attr.colormap = map; + attr.border_pixel = color_border ( display ); + attr.background_pixel = color_background ( display ); + + Window box = XCreateWindow ( display, DefaultRootWindow ( display ), 0, 0, 200, 100, config.menu_bw, vinfo.depth, InputOutput, + vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr ); + XSelectInput ( + display, + box, + KeyReleaseMask | KeyPressMask | ExposureMask | ButtonPressMask | StructureNotifyMask | FocusChangeMask | + Button1MotionMask ); + + surface = cairo_xlib_surface_create ( display, box, vinfo.visual, 200, 100 ); + // Create a drawable. + draw = cairo_create ( surface ); + cairo_set_operator ( draw, CAIRO_OPERATOR_SOURCE ); + + // // make it an unmanaged window + if ( !normal_window_mode ) { + window_set_atom_prop ( display, box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1 ); + XSetWindowAttributes sattr = { .override_redirect = True }; + XChangeWindowAttributes ( display, box, CWOverrideRedirect, &sattr ); + } + else{ + window_set_atom_prop ( display, box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_NORMAL], 1 ); + } + + xim = XOpenIM ( display, NULL, NULL, NULL ); + xic = XCreateIC ( xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, + box, XNFocusWindow, box, NULL ); + + // Set the WM_NAME + XStoreName ( display, box, "rofi" ); + + x11_set_window_opacity ( display, box, config.window_opacity ); + return box; +} + /** * @param state Internal state of the menu. * @@ -632,6 +643,26 @@ static void menu_keyboard_navigation ( MenuState *state, KeySym key, unsigned in * mouse navigation through the elements. * */ +static int intersect ( textbox *tb, int x, int y ) +{ + if ( x >= ( tb->x ) && x < ( tb->x + tb->w ) ) { + if ( y >= ( tb->y ) && y < ( tb->y + tb->h ) ) { + return TRUE; + } + } + + return FALSE; +} +static int sb_intersect ( scrollbar *tb, int x, int y ) +{ + if ( x >= ( tb->x ) && x < ( tb->x + tb->w ) ) { + if ( y >= ( tb->y ) && y < ( tb->y + tb->h ) ) { + return TRUE; + } + } + + return FALSE; +} static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe ) { // Scroll event @@ -651,13 +682,13 @@ static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe ) return; } else { - if ( state->scrollbar && state->scrollbar->window == xbe->window ) { + if ( state->scrollbar && sb_intersect ( state->scrollbar, xbe->x, xbe->y ) ) { state->selected = scrollbar_clicked ( state->scrollbar, xbe->y ); state->update = TRUE; return; } for ( unsigned int i = 0; config.sidebar_mode == TRUE && i < num_switchers; i++ ) { - if ( switchers[i].tb->window == ( xbe->window ) ) { + if ( intersect ( switchers[i].tb, xbe->x, xbe->y ) ) { *( state->selected_line ) = 0; state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK ); state->quit = TRUE; @@ -666,7 +697,7 @@ static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe ) } } for ( unsigned int i = 0; i < state->max_elements; i++ ) { - if ( ( xbe->window ) == ( state->boxes[i]->window ) ) { + if ( intersect ( state->boxes[i], xbe->x, xbe->y ) ) { // Only allow items that are visible to be selected. if ( ( state->last_offset + i ) >= state->filtered_lines ) { break; @@ -734,10 +765,20 @@ static void menu_refilter ( MenuState *state ) state->rchanged = TRUE; } -static void menu_draw ( MenuState *state ) +static void menu_draw ( MenuState *state, cairo_t *draw ) { unsigned int i, offset = 0; + unsigned pixel = color_background ( display ); + + cairo_set_source_rgba ( draw, + ( ( pixel & 0x00FF0000 ) >> 16 ) / 256.0, + ( ( pixel & 0x0000FF00 ) >> 8 ) / 256.0, + ( ( pixel & 0x000000FF ) >> 0 ) / 256.0, + ( ( pixel & 0xFF000000 ) >> 24 ) / 256.0 + ); + cairo_paint ( draw ); + // selected row is always visible. // If selected is visible do not scroll. if ( ( ( state->selected - ( state->last_offset ) ) < ( state->max_elements ) ) && ( state->selected >= ( state->last_offset ) ) ) { @@ -752,7 +793,7 @@ static void menu_draw ( MenuState *state ) state->cur_page = page; state->rchanged = TRUE; } - // Set the position. + // Set the position scrollbar_set_handle ( state->scrollbar, page * state->max_elements ); } // Re calculate the boxes and sizes, see if we can move this in the menu_calc*rowscolumns @@ -765,7 +806,7 @@ static void menu_draw ( MenuState *state ) // Update the handle length. scrollbar_set_handle_length ( state->scrollbar, columns * state->max_rows ); - scrollbar_draw ( state->scrollbar ); + scrollbar_draw ( state->scrollbar, draw ); // Element width. unsigned int element_width = state->w - ( 2 * ( config.padding ) ); if ( state->scrollbar != NULL ) { @@ -781,10 +822,6 @@ static void menu_draw ( MenuState *state ) // Calculate number of visible rows. unsigned int max_elements = MIN ( a_lines, state->max_rows * columns ); - // Hide now invisible boxes. - for ( i = max_elements; i < state->max_elements; i++ ) { - textbox_hide ( state->boxes[i] ); - } if ( state->rchanged ) { // Move, resize visible boxes and show them. for ( i = 0; i < max_elements; i++ ) { @@ -800,8 +837,7 @@ static void menu_draw ( MenuState *state ) textbox_font ( state->boxes[i], tbft ); textbox_text ( state->boxes[i], text ); } - textbox_show ( state->boxes[i] ); - textbox_draw ( state->boxes[i] ); + textbox_draw ( state->boxes[i], draw ); } state->rchanged = FALSE; } @@ -813,37 +849,63 @@ static void menu_draw ( MenuState *state ) state->sw->mgrv ( state->line_map[i + offset], state->sw, &fstate ); TextBoxFontType tbft = fstate | ( ( i + offset ) == state->selected ? HIGHLIGHT : type ); textbox_font ( state->boxes[i], tbft ); - textbox_draw ( state->boxes[i] ); + textbox_draw ( state->boxes[i], draw ); } } + cairo_show_page ( draw ); } static void menu_update ( MenuState *state ) { - textbox_draw ( state->case_indicator ); - textbox_draw ( state->prompt_tb ); - textbox_draw ( state->text ); + cairo_surface_t *surf = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, state->w, state->h ); + cairo_t *d = cairo_create ( surf ); + cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE ); + + menu_draw ( state, d ); + textbox_draw ( state->prompt_tb, d ); + textbox_draw ( state->text, d ); + textbox_draw ( state->case_indicator, d ); if ( state->message_tb ) { - textbox_draw ( state->message_tb ); + textbox_draw ( state->message_tb, d ); } - menu_draw ( state ); - // Why do we need the special -1? - XDrawLine ( display, main_window, gc, 0, state->line_height + ( config.padding ) * 1 + config.line_margin + 1, state->w, - state->line_height + ( config.padding ) * 1 + config.line_margin + 1 ); + unsigned pixel = color_separator ( display ); + + cairo_set_source_rgba ( d, + ( ( pixel & 0x00FF0000 ) >> 16 ) / 256.0, + ( ( pixel & 0x0000FF00 ) >> 8 ) / 256.0, + ( ( pixel & 0x000000FF ) >> 0 ) / 256.0, + ( ( pixel & 0xFF000000 ) >> 24 ) / 256.0 + ); + if ( strcmp ( config.separator_style, "dash" ) == 0 ) { + const double dashes[1] = { 4 }; + cairo_set_dash ( d, dashes, 1, 0.0 ); + } + cairo_move_to ( d, 0, state->line_height + ( config.padding ) * 1 + config.line_margin + 1 ); + cairo_line_to ( d, state->w, state->line_height + ( config.padding ) * 1 + config.line_margin + 1 ); + cairo_stroke ( d ); if ( state->message_tb ) { - XDrawLine ( display, main_window, gc, 0, state->top_offset - ( config.line_margin ) - 1, - state->w, state->top_offset - ( config.line_margin ) - 1 ); + cairo_move_to ( d, 0, state->top_offset - ( config.line_margin ) - 1 ); + cairo_line_to ( d, state->w, state->top_offset - ( config.line_margin ) - 1 ); + cairo_stroke ( d ); } if ( config.sidebar_mode == TRUE ) { - XDrawLine ( display, main_window, gc, 0, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin, - state->w, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin ); + cairo_move_to ( d, 0, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin ); + cairo_line_to ( d, state->w, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin ); + cairo_stroke ( d ); + for ( unsigned int j = 0; j < num_switchers; j++ ) { - textbox_draw ( switchers[j].tb ); + textbox_draw ( switchers[j].tb, d ); } } state->update = FALSE; + + cairo_set_source_surface ( draw, surf, 0, 0 ); + cairo_paint ( draw ); + + cairo_destroy ( d ); + cairo_surface_destroy ( surf ); } /** @@ -883,8 +945,7 @@ static void menu_resize ( MenuState *state ) for ( unsigned int j = 0; j < num_switchers; j++ ) { textbox_moveresize ( switchers[j].tb, config.padding + j * ( width + config.line_margin ), state->h - state->line_height - config.padding, width, state->line_height ); - textbox_show ( switchers[j].tb ); - textbox_draw ( switchers[j].tb ); + textbox_draw ( switchers[j].tb, draw ); } } /** @@ -915,7 +976,7 @@ static void menu_resize ( MenuState *state ) } // Add newly added boxes. for ( unsigned int i = last_length; i < state->max_elements; i++ ) { - state->boxes[i] = textbox_create ( main_window, &vinfo, map, rstate, x_offset, y_offset, + state->boxes[i] = textbox_create ( rstate, x_offset, y_offset, state->element_width, element_height, NORMAL, "" ); } scrollbar_resize ( state->scrollbar, -1, ( state->max_rows ) * ( element_height ) - config.line_margin ); @@ -977,7 +1038,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select // we need this at this point so we can get height. state.line_height = textbox_get_estimated_char_height (); - state.case_indicator = textbox_create ( main_window, &vinfo, map, TB_AUTOWIDTH, ( config.padding ), ( config.padding ), + state.case_indicator = textbox_create ( TB_AUTOWIDTH, ( config.padding ), ( config.padding ), 0, state.line_height, NORMAL, "*" ); // Height of a row. if ( config.menu_lines == 0 ) { @@ -993,13 +1054,13 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select menu_calculate_window_and_element_width ( &state, &mon ); // Prompt box. - state.prompt_tb = textbox_create ( main_window, &vinfo, map, TB_AUTOWIDTH, ( config.padding ), ( config.padding ), + state.prompt_tb = textbox_create ( TB_AUTOWIDTH, ( config.padding ), ( config.padding ), 0, state.line_height, NORMAL, prompt ); // Entry box int entrybox_width = state.w - ( 2 * ( config.padding ) ) - textbox_get_width ( state.prompt_tb ) - textbox_get_width ( state.case_indicator ); - state.text = textbox_create ( main_window, &vinfo, map, TB_EDITABLE, + state.text = textbox_create ( TB_EDITABLE, ( config.padding ) + textbox_get_width ( state.prompt_tb ), ( config.padding ), entrybox_width, state.line_height, NORMAL, *input ); @@ -1008,19 +1069,17 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select // Move indicator to end. textbox_move ( state.case_indicator, config.padding + textbox_get_width ( state.prompt_tb ) + entrybox_width, config.padding ); - textbox_show ( state.text ); - textbox_show ( state.prompt_tb ); - if ( config.case_sensitive ) { - textbox_show ( state.case_indicator ); + textbox_text ( state.case_indicator, "*" ); + } + else{ + textbox_text ( state.case_indicator, "" ); } - state.message_tb = NULL; if ( message ) { - state.message_tb = textbox_create ( main_window, &vinfo, map, TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, + state.message_tb = textbox_create ( TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, ( config.padding ), state.top_offset, state.w - ( 2 * ( config.padding ) ), -1, NORMAL, message ); - textbox_show ( state.message_tb ); state.top_offset += textbox_get_height ( state.message_tb ); state.top_offset += config.line_margin * 2 + 2; } @@ -1037,12 +1096,12 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select rstate = TB_MARKUP; } for ( unsigned int i = 0; i < state.max_elements; i++ ) { - state.boxes[i] = textbox_create ( main_window, &vinfo, map, rstate, x_offset, y_offset, + state.boxes[i] = textbox_create ( rstate, x_offset, y_offset, state.element_width, element_height, NORMAL, "" ); } if ( !config.hide_scrollbar ) { unsigned int sbw = config.line_margin + 8; - state.scrollbar = scrollbar_create ( main_window, &vinfo, map, state.w - config.padding - sbw, state.top_offset, + state.scrollbar = scrollbar_create ( state.w - config.padding - sbw, state.top_offset, sbw, ( state.max_rows - 1 ) * ( element_height + config.line_margin ) + element_height ); } @@ -1074,22 +1133,22 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select if ( config.sidebar_mode == TRUE ) { int width = ( state.w - ( 2 * ( config.padding ) + ( num_switchers - 1 ) * config.line_margin ) ) / num_switchers; for ( unsigned int j = 0; j < num_switchers; j++ ) { - switchers[j].tb = textbox_create ( main_window, &vinfo, map, TB_CENTER, config.padding + j * ( width + config.line_margin ), + switchers[j].tb = textbox_create ( TB_CENTER, config.padding + j * ( width + config.line_margin ), state.h - state.line_height - config.padding, width, state.line_height, ( j == curr_switcher ) ? HIGHLIGHT : NORMAL, switchers[j].sw->name ); - textbox_show ( switchers[j].tb ); } } - scrollbar_show ( state.scrollbar ); // Display it. XMoveResizeWindow ( display, main_window, state.x, state.y, state.w, state.h ); + cairo_xlib_surface_set_size ( surface, state.w, state.h ); XMapRaised ( display, main_window ); // if grabbing keyboard failed, fall through state.selected = 0; - state.quit = FALSE; + state.quit = FALSE; + state.update = TRUE; menu_refilter ( &state ); for ( unsigned int i = 0; ( *( state.selected_line ) ) < UINT32_MAX && !state.selected && i < state.filtered_lines; i++ ) { @@ -1151,6 +1210,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select 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 ); menu_resize ( &state ); } } @@ -1173,8 +1233,10 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select ; } XMotionEvent xme = ev.xmotion; - state.selected = scrollbar_clicked ( state.scrollbar, xme.y ); - state.update = TRUE; + if ( xme.x >= state.scrollbar->x && xme.x < ( state.scrollbar->x + state.scrollbar->w ) ) { + state.selected = scrollbar_clicked ( state.scrollbar, xme.y ); + state.update = TRUE; + } } // Button press event. else if ( ev.type == ButtonPress ) { @@ -1222,10 +1284,10 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select state.refilter = TRUE; state.update = TRUE; if ( config.case_sensitive ) { - textbox_show ( state.case_indicator ); + textbox_text ( state.case_indicator, "*" ); } else { - textbox_hide ( state.case_indicator ); + textbox_text ( state.case_indicator, "" ); } } // Special delete entry command. @@ -1253,7 +1315,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select continue; } - int rc = textbox_keypress ( state.text, &ev ); + int rc = textbox_keypress ( state.text, xic, &ev ); // Row is accepted. if ( rc < 0 ) { shift = ( ( ev.xkey.state & ShiftMask ) == ShiftMask ); @@ -1370,10 +1432,9 @@ void error_dialog ( const char *msg, int markup ) menu_calculate_window_and_element_width ( &state, &mon ); state.max_elements = 0; - state.text = textbox_create ( main_window, &vinfo, map, TB_AUTOHEIGHT|TB_WRAP + ( ( markup ) ? TB_MARKUP : 0 ), + state.text = textbox_create ( TB_AUTOHEIGHT | TB_WRAP + ( ( markup ) ? TB_MARKUP : 0 ), ( config.padding ), ( config.padding ), ( state.w - ( 2 * ( config.padding ) ) ), 1, NORMAL, ( msg != NULL ) ? msg : "" ); - textbox_show ( state.text ); state.line_height = textbox_get_height ( state.text ); // resize window vertically to suit @@ -1382,7 +1443,7 @@ void error_dialog ( const char *msg, int markup ) // Move the window to the correct x,y position. calculate_window_position ( &state, &mon ); XMoveResizeWindow ( display, main_window, state.x, state.y, state.w, state.h ); - + cairo_xlib_surface_set_size ( surface, state.w, state.h ); // Display it. XMapRaised ( display, main_window ); @@ -1392,7 +1453,7 @@ void error_dialog ( const char *msg, int markup ) while ( !state.quit ) { // Update if requested. if ( state.update ) { - textbox_draw ( state.text ); + textbox_draw ( state.text, draw ); state.update = FALSE; } // Wait for event. @@ -1430,7 +1491,7 @@ static int setup () if ( pfd >= 0 ) { // Request truecolor visual. create_visual_and_colormap ( display ); - textbox_setup ( &vinfo, map ); + textbox_setup ( ); } return pfd; } @@ -1448,11 +1509,18 @@ static void teardown ( int pfd ) XUnmapWindow ( display, main_window ); XDestroyWindow ( display, main_window ); main_window = None; + XDestroyIC ( xic ); + XCloseIM ( xim ); } - if ( gc != NULL ) { - XFreeGC ( display, gc ); - gc = NULL; + if ( draw ) { + cairo_destroy ( draw ); + draw = NULL; } + if ( surface ) { + cairo_surface_destroy ( surface ); + surface = NULL; + } + if ( map != None ) { XFreeColormap ( display, map ); map = None; @@ -1580,11 +1648,10 @@ static void cleanup () release_keyboard ( display ); XDestroyWindow ( display, main_window ); main_window = None; + XDestroyIC ( xic ); + XCloseIM ( xim ); } - if ( gc != NULL ) { - XFreeGC ( display, gc ); - gc = NULL; - } + if ( sncontext != NULL ) { sn_launchee_context_unref ( sncontext ); sncontext = NULL; diff --git a/source/scrollbar.c b/source/scrollbar.c index c95aaf59..f7b1466f 100644 --- a/source/scrollbar.c +++ b/source/scrollbar.c @@ -37,53 +37,26 @@ extern Display *display; -scrollbar *scrollbar_create ( Window parent, XVisualInfo *vinfo, Colormap map, short x, short y, short w, short h ) +scrollbar *scrollbar_create ( short x, short y, short w, short h ) { scrollbar *sb = g_malloc0 ( sizeof ( scrollbar ) ); - sb->parent = parent; - sb->x = x; - sb->y = y; - sb->w = MAX ( 1, w ); - sb->h = MAX ( 1, h ); + sb->x = x; + sb->y = y; + sb->w = MAX ( 1, w ); + sb->h = MAX ( 1, h ); sb->length = 10; sb->pos = 0; sb->pos_length = 4; - XSetWindowAttributes attr; - attr.colormap = map; - attr.border_pixel = color_border ( display ); - attr.background_pixel = color_background ( display ); - sb->window = XCreateWindow ( display, sb->parent, sb->x, sb->y, sb->w, sb->h, 0, vinfo->depth, InputOutput, vinfo->visual, - CWColormap | CWBorderPixel | CWBackPixel, &attr ); - - XSelectInput ( display, sb->window, ExposureMask | ButtonPressMask | Button1MotionMask ); - sb->gc = XCreateGC ( display, sb->window, 0, 0 ); - XSetForeground ( display, sb->gc, color_separator ( display ) ); - // Create GC. return sb; } -void scrollbar_show ( scrollbar *sb ) -{ - if ( sb != NULL ) { - XMapWindow ( display, sb->window ); - } -} -void scrollbar_hide ( scrollbar *sb ) -{ - if ( sb != NULL ) { - XUnmapWindow ( display, sb->window ); - } -} - void scrollbar_free ( scrollbar *sb ) { if ( sb != NULL ) { - XFreeGC ( display, sb->gc ); - XDestroyWindow ( display, sb->window ); g_free ( sb ); } } @@ -109,7 +82,7 @@ void scrollbar_set_handle_length ( scrollbar *sb, unsigned int pos_length ) } } -void scrollbar_draw ( scrollbar *sb ) +void scrollbar_draw ( scrollbar *sb, cairo_t *draw ) { if ( sb != NULL ) { // Calculate position and size. @@ -124,9 +97,16 @@ void scrollbar_draw ( scrollbar *sb ) // Cap length; height = MIN ( bh - y + 1, ( height ) ); // Redraw base window - XClearWindow ( display, sb->window ); - // Paint the handle. - XFillRectangle ( display, sb->window, sb->gc, config.line_margin, y, sb->w - config.line_margin, height ); + unsigned pixel = color_separator ( display ); + + cairo_set_source_rgba ( draw, + ( ( pixel & 0x00FF0000 ) >> 16 ) / 256.0, + ( ( pixel & 0x0000FF00 ) >> 8 ) / 256.0, + ( ( pixel & 0x000000FF ) >> 0 ) / 256.0, + ( ( pixel & 0xFF000000 ) >> 24 ) / 256.0 + ); + cairo_rectangle ( draw, sb->x + config.line_margin, sb->y + y, sb->w - config.line_margin, height ); + cairo_fill ( draw ); } } void scrollbar_resize ( scrollbar *sb, int w, int h ) @@ -138,7 +118,6 @@ void scrollbar_resize ( scrollbar *sb, int w, int h ) if ( w > 0 ) { sb->w = w; } - XResizeWindow ( display, sb->window, sb->w, sb->h ); } } void scrollbar_move ( scrollbar *sb, int x, int y ) @@ -146,18 +125,20 @@ void scrollbar_move ( scrollbar *sb, int x, int y ) if ( sb != NULL ) { sb->x = x; sb->y = y; - XMoveWindow ( display, sb->window, x, y ); } } unsigned int scrollbar_clicked ( scrollbar *sb, int y ) { if ( sb != NULL ) { - y = MIN ( MAX ( 1, y ), sb->h - 1 ) - 1; - const short bh = sb->h - 2; - float sec = ( ( bh ) / (float) sb->length ); - unsigned int sel = y / sec; - return MIN ( sel, sb->length - 1 ); + if ( y >= sb->y && y < ( sb->y + sb->h ) ) { + y -= sb->y; + y = MIN ( MAX ( 1, y ), sb->h - 1 ) - 1; + const short bh = sb->h - 2; + float sec = ( ( bh ) / (float) sb->length ); + unsigned int sel = y / sec; + return MIN ( sel, sb->length - 1 ); + } } return 0; } diff --git a/source/textbox.c b/source/textbox.c index 372ed047..aa61f136 100644 --- a/source/textbox.c +++ b/source/textbox.c @@ -32,9 +32,8 @@ #include #include #include -#include #include - +#include #include "rofi.h" #include "textbox.h" #include "keyb.h" @@ -47,16 +46,13 @@ extern Display *display; * Font + font color cache. * Avoid re-loading font on every change on every textbox. */ -XVisualInfo *visual_info; -Colormap target_colormap; - typedef struct _RowColor { - XftColor fg; - XftColor bg; - XftColor bgalt; - XftColor hlfg; - XftColor hlbg; + Color fg; + Color bg; + Color bgalt; + Color hlfg; + Color hlbg; } RowColor; #define num_states 3 @@ -64,45 +60,23 @@ RowColor colors[num_states]; PangoContext *p_context = NULL; -// Xft text box, optionally editable -textbox* textbox_create ( Window parent, XVisualInfo *vinfo, Colormap map, TextboxFlags flags, short x, short y, short w, short h, +textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h, TextBoxFontType tbft, const char *text ) { textbox *tb = g_malloc0 ( sizeof ( textbox ) ); - tb->flags = flags; - tb->parent = parent; + tb->flags = flags; tb->x = x; tb->y = y; tb->w = MAX ( 1, w ); tb->h = MAX ( 1, h ); - tb->layout = pango_layout_new ( p_context ); - tb->changed = FALSE; - unsigned int cp; - switch ( tbft ) - { - case HIGHLIGHT: - cp = colors[NORMAL].hlbg.pixel; - break; - case ALT: - cp = colors[NORMAL].bgalt.pixel; - break; - default: - cp = colors[NORMAL].bg.pixel; - break; - } - - XSetWindowAttributes attr; - attr.colormap = map; - attr.border_pixel = cp; - attr.background_pixel = cp; - tb->window = XCreateWindow ( display, tb->parent, tb->x, tb->y, tb->w, tb->h, 0, vinfo->depth, - InputOutput, vinfo->visual, CWColormap | CWBorderPixel | CWBackPixel, &attr ); - + tb->main_surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, tb->w, tb->h ); + tb->main_draw = cairo_create ( tb->main_surface ); + tb->layout = pango_cairo_create_layout ( tb->main_draw ); PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font ); pango_layout_set_font_description ( tb->layout, pfd ); pango_font_description_free ( pfd ); @@ -117,20 +91,9 @@ textbox* textbox_create ( Window parent, XVisualInfo *vinfo, Colormap map, Textb // auto height/width modes get handled here textbox_moveresize ( tb, tb->x, tb->y, tb->w, tb->h ); - // edit mode controls - if ( tb->flags & TB_EDITABLE ) { - tb->xim = XOpenIM ( display, NULL, NULL, NULL ); - tb->xic = XCreateIC ( tb->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, - tb->window, XNFocusWindow, tb->window, NULL ); - } - else { - XSelectInput ( display, tb->window, ButtonPressMask ); - } - return tb; } -// set an Xft font by name void textbox_font ( textbox *tb, TextBoxFontType tbft ) { RowColor *color = &( colors[tbft & STATE_MASK] ); @@ -149,14 +112,19 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft ) tb->color_fg = color->fg; break; } + if ( tb->tbft != tbft ) { + tb->update = TRUE; + } tb->tbft = tbft; } // set the default text to display void textbox_text ( textbox *tb, const char *text ) { + tb->update = TRUE; g_free ( tb->text ); const gchar *last_pointer = NULL; + if ( g_utf8_validate ( text, -1, &last_pointer ) ) { tb->text = g_strdup ( text ); } @@ -169,6 +137,7 @@ void textbox_text ( textbox *tb, const char *text ) tb->text = g_strdup ( "Invalid UTF-8 string." ); } } + if ( tb->flags & TB_MARKUP ) { pango_layout_set_markup ( tb->layout, tb->text, strlen ( tb->text ) ); } @@ -187,7 +156,6 @@ void textbox_move ( textbox *tb, int x, int y ) if ( x != tb->x || y != tb->y ) { tb->x = x; tb->y = y; - XMoveResizeWindow ( display, tb->window, tb->x, tb->y, tb->w, tb->h ); } } // within the parent handled auto width/height modes @@ -195,12 +163,7 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) { if ( tb->flags & TB_AUTOWIDTH ) { pango_layout_set_width ( tb->layout, -1 ); - if ( w > 1 ) { - w = MIN ( w, textbox_get_width ( tb ) ); - } - else{ - w = textbox_get_width ( tb ); - } + w = textbox_get_width ( tb ); } else { // set ellipsize @@ -224,106 +187,123 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) tb->y = y; tb->h = MAX ( 1, h ); tb->w = MAX ( 1, w ); - XMoveResizeWindow ( display, tb->window, tb->x, tb->y, tb->w, tb->h ); } + // We always want to update this pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tb->w - 2 * SIDE_MARGIN ) ); } -void textbox_show ( textbox *tb ) -{ - XMapWindow ( display, tb->window ); -} -void textbox_hide ( textbox *tb ) -{ - XUnmapWindow ( display, tb->window ); -} - // will also unmap the window if still displayed void textbox_free ( textbox *tb ) { if ( tb == NULL ) { return; } - if ( tb->flags & TB_EDITABLE ) { - XDestroyIC ( tb->xic ); - XCloseIM ( tb->xim ); - } g_free ( tb->text ); if ( tb->layout != NULL ) { g_object_unref ( tb->layout ); } + if ( tb->main_draw ) { + cairo_destroy ( tb->main_draw ); + tb->main_draw = NULL; + } + if ( tb->main_surface ) { + cairo_surface_destroy ( tb->main_surface ); + tb->main_surface = NULL; + } - XDestroyWindow ( display, tb->window ); g_free ( tb ); } -void textbox_draw ( textbox *tb ) +static void texbox_update ( textbox *tb ) { - GC context = XCreateGC ( display, tb->window, 0, 0 ); - Pixmap canvas = XCreatePixmap ( display, tb->window, tb->w, tb->h, visual_info->depth ); - XftDraw *draw = XftDrawCreate ( display, canvas, visual_info->visual, target_colormap ); - - // clear canvas - XftDrawRect ( draw, &tb->color_bg, 0, 0, tb->w, tb->h ); - - char *text = tb->text ? tb->text : ""; - int text_len = strlen ( text ); - int font_height = textbox_get_font_height ( tb ); - - int cursor_x = 0; - int cursor_width = MAX ( 2, font_height / 10 ); - - if ( tb->changed ) { - if ( tb->flags & TB_MARKUP ) { - pango_layout_set_markup ( tb->layout, text, text_len ); + if ( tb->update ) { + if ( tb->main_surface ) { + cairo_destroy ( tb->main_draw ); + cairo_surface_destroy ( tb->main_surface ); + tb->main_draw = NULL; + tb->main_surface = NULL; } - else{ - pango_layout_set_text ( tb->layout, text, text_len ); + tb->main_surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, tb->w, tb->h ); + tb->main_draw = cairo_create ( tb->main_surface ); + PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font ); + pango_font_description_free ( pfd ); + cairo_set_operator ( tb->main_draw, CAIRO_OPERATOR_SOURCE ); + + pango_cairo_update_layout ( tb->main_draw, tb->layout ); + char *text = tb->text ? tb->text : ""; + int text_len = strlen ( text ); + int font_height = textbox_get_font_height ( tb ); + + int cursor_x = 0; + int cursor_width = MAX ( 2, font_height / 10 ); + + if ( tb->changed ) { + if ( tb->flags & TB_MARKUP ) { + pango_layout_set_markup ( tb->layout, text, text_len ); + } + else{ + pango_layout_set_text ( tb->layout, text, text_len ); + } } + + if ( tb->flags & TB_EDITABLE ) { + PangoRectangle pos; + int cursor_offset = 0; + cursor_offset = MIN ( tb->cursor, text_len ); + pango_layout_get_cursor_pos ( tb->layout, cursor_offset, &pos, NULL ); + // Add a small 4px offset between cursor and last glyph. + cursor_x = pos.x / PANGO_SCALE; + } + + // Skip the side MARGIN on the X axis. + int x = SIDE_MARGIN; + int y = 0; + + if ( tb->flags & TB_RIGHT ) { + int line_width = 0; + // Get actual width. + pango_layout_get_pixel_size ( tb->layout, &line_width, NULL ); + x = ( tb->w - line_width - SIDE_MARGIN ); + } + else if ( tb->flags & TB_CENTER ) { + int tw = textbox_get_font_width ( tb ); + x = ( ( tb->w - tw - 2 * SIDE_MARGIN ) ) / 2; + } + y = ( ( tb->h - textbox_get_font_height ( tb ) ) ) / 2; + + // Set ARGB + Color col = tb->color_bg; + cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha ); + cairo_paint ( tb->main_draw ); + + // Set ARGB + col = tb->color_fg; + cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha ); + cairo_move_to ( tb->main_draw, x, y ); + pango_cairo_show_layout ( tb->main_draw, tb->layout ); + + //cairo_fill(tb->draw); + // draw the cursor + if ( tb->flags & TB_EDITABLE ) { + cairo_rectangle ( tb->main_draw, x + cursor_x, y, cursor_width, font_height ); + cairo_fill ( tb->main_draw ); + } + + tb->update = FALSE; } +} +void textbox_draw ( textbox *tb, cairo_t *draw ) +{ + texbox_update ( tb ); - if ( tb->flags & TB_EDITABLE ) { - PangoRectangle pos; - int cursor_offset = 0; - cursor_offset = MIN ( tb->cursor, text_len ); - pango_layout_get_cursor_pos ( tb->layout, cursor_offset, &pos, NULL ); - // Add a small 4px offset between cursor and last glyph. - cursor_x = pos.x / PANGO_SCALE; - } + /* Write buffer */ - // Skip the side MARGIN on the X axis. - int x = PANGO_SCALE * SIDE_MARGIN; - int y = 0; - - if ( tb->flags & TB_RIGHT ) { - int line_width = 0; - // Get actual width. - pango_layout_get_pixel_size ( tb->layout, &line_width, NULL ); - x = ( tb->w - line_width - SIDE_MARGIN ) * PANGO_SCALE; - } - else if ( tb->flags & TB_CENTER ) { - int tw = textbox_get_font_width ( tb ); - x = ( PANGO_SCALE * ( tb->w - tw - 2 * SIDE_MARGIN ) ) / 2; - } - y = ( PANGO_SCALE * ( tb->h - textbox_get_font_height ( tb ) ) ) / 2; - // Render the layout. - pango_xft_render_layout ( draw, &( tb->color_fg ), tb->layout, x, y ); - - // draw the cursor - if ( tb->flags & TB_EDITABLE ) { - XftDrawRect ( draw, &tb->color_fg, x / PANGO_SCALE + cursor_x, y / PANGO_SCALE, cursor_width, font_height ); - } - - // flip canvas to window - // XClearWindow ( display, tb->window); - XCopyArea ( display, canvas, tb->window, context, 0, 0, tb->w, tb->h, 0, 0 ); - - XFreeGC ( display, context ); - XftDrawDestroy ( draw ); - XFreePixmap ( display, canvas ); + cairo_set_source_surface ( draw, tb->main_surface, tb->x, tb->y ); + cairo_rectangle ( draw, tb->x, tb->y, tb->w, tb->h ); + cairo_fill ( draw ); } // cursor handling for edit mode @@ -331,6 +311,7 @@ void textbox_cursor ( textbox *tb, int pos ) { int length = ( tb->text == NULL ) ? 0 : strlen ( tb->text ); tb->cursor = MAX ( 0, MIN ( length, pos ) ); + tb->update = TRUE; } // move right @@ -413,6 +394,7 @@ static void textbox_cursor_dec_word ( textbox *tb ) void textbox_cursor_end ( textbox *tb ) { tb->cursor = ( int ) strlen ( tb->text ); + tb->update = TRUE; } // insert text @@ -430,6 +412,7 @@ void textbox_insert ( textbox *tb, int pos, char *str ) // Set modified, lay out need te be redrawn tb->changed = TRUE; + tb->update = TRUE; } // remove text @@ -449,6 +432,7 @@ void textbox_delete ( textbox *tb, int pos, int dlen ) } // Set modified, lay out need te be redrawn tb->changed = TRUE; + tb->update = TRUE; } // delete on character @@ -495,24 +479,18 @@ 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 ) +int textbox_keypress ( textbox *tb, XIC xic, XEvent *ev ) { KeySym key; Status stat; char pad[32]; int len; - // This is needed for letting the Input Method handle combined keys. - // E.g. `e into รจ - if ( XFilterEvent ( ev, tb->window ) ) { - return 0; - } - if ( !( tb->flags & TB_EDITABLE ) ) { return 0; } - len = Xutf8LookupString ( tb->xic, &ev->xkey, pad, sizeof ( pad ), &key, &stat ); + len = Xutf8LookupString ( xic, &ev->xkey, pad, sizeof ( pad ), &key, &stat ); pad[len] = 0; // Left or Ctrl-b if ( abe_test_action ( MOVE_CHAR_BACK, ev->xkey.state, key ) ) { @@ -590,41 +568,27 @@ int textbox_keypress ( textbox *tb, XEvent *ev ) /*** * Font setup. */ -static void parse_color ( Visual *visual, Colormap colormap, - const char *bg, XftColor *color, const char *def ) +static void parse_color ( char *bg, Color *col ) { if ( bg == NULL ) { return; } if ( strncmp ( bg, "argb:", 5 ) == 0 ) { - XRenderColor col; unsigned int val = strtoul ( &bg[5], NULL, 16 ); - col.alpha = ( ( val & 0xFF000000 ) >> 24 ) * 255; - col.red = ( ( val & 0x00FF0000 ) >> 16 ) * 255; - col.green = ( ( val & 0x0000FF00 ) >> 8 ) * 255; - col.blue = ( ( val & 0x000000FF ) ) * 255; - if ( !XftColorAllocValue ( display, visual, colormap, &col, color ) ) { - fprintf ( stderr, "Failed to parse color: '%s'\n", bg ); - // Go for default. - if ( !XftColorAllocName ( display, visual, colormap, def, color ) ) { - fprintf ( stderr, "Cannot allocate default color, giving up.\n" ); - exit ( EXIT_FAILURE ); - } - } + col->alpha = ( ( val & 0xFF000000 ) >> 24 ) / 256.0; + col->red = ( ( val & 0x00FF0000 ) >> 16 ) / 256.0; + col->green = ( ( val & 0x0000FF00 ) >> 8 ) / 256.0; + col->blue = ( ( val & 0x000000FF ) ) / 256.0; } else { - if ( !XftColorAllocName ( display, visual, colormap, bg, color ) ) { - fprintf ( stderr, "Failed to parse color: '%s'\n", bg ); - // Go for default. - if ( !XftColorAllocName ( display, visual, colormap, def, color ) ) { - fprintf ( stderr, "Cannot allocate default color, giving up.\n" ); - exit ( EXIT_FAILURE ); - } - } + unsigned int val = strtoul ( &bg[1], NULL, 16 ); + col->alpha = 1; + col->red = ( ( val & 0x00FF0000 ) >> 16 ) / 256.0; + col->green = ( ( val & 0x0000FF00 ) >> 8 ) / 256.0; + col->blue = ( ( val & 0x000000FF ) ) / 256.0; } } -static void textbox_parse_string ( XVisualInfo *visual, Colormap colormap, const char *str, - RowColor *color ) +static void textbox_parse_string ( const char *str, RowColor *color ) { if ( str == NULL ) { return; @@ -637,79 +601,61 @@ static void textbox_parse_string ( XVisualInfo *visual, Colormap colormap, const switch ( index ) { case 0: - parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->bg ), "black" ); + parse_color ( g_strstrip ( token ), &( color->bg ) ); break; case 1: - parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->fg ), "white" ); + parse_color ( g_strstrip ( token ), &( color->fg ) ); break; case 2: - parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->bgalt ), "black" ); + parse_color ( g_strstrip ( token ), &( color->bgalt ) ); break; case 3: - parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->hlbg ), "black" ); + parse_color ( g_strstrip ( token ), &( color->hlbg ) ); break; case 4: - parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->hlfg ), "white" ); + parse_color ( g_strstrip ( token ), &( color->hlfg ) ); break; } index++; } g_free ( cstr ); } -void textbox_setup ( XVisualInfo *visual, Colormap colormap ) +void textbox_setup ( void ) { - visual_info = visual; - target_colormap = colormap; - if ( config.color_enabled ) { - textbox_parse_string ( visual, target_colormap, config.color_normal, &( colors[NORMAL] ) ); - textbox_parse_string ( visual, target_colormap, config.color_urgent, &( colors[URGENT] ) ); - textbox_parse_string ( visual, target_colormap, config.color_active, &( colors[ACTIVE] ) ); + textbox_parse_string ( config.color_normal, &( colors[NORMAL] ) ); + textbox_parse_string ( config.color_urgent, &( colors[URGENT] ) ); + textbox_parse_string ( config.color_active, &( colors[ACTIVE] ) ); } else { - parse_color ( visual_info->visual, target_colormap, config.menu_bg, &( colors[NORMAL].bg ), "black" ); - parse_color ( visual_info->visual, target_colormap, config.menu_fg, &( colors[NORMAL].fg ), "white" ); - parse_color ( visual_info->visual, target_colormap, config.menu_bg_alt, &( colors[NORMAL].bgalt ), "black" ); - parse_color ( visual_info->visual, target_colormap, config.menu_hlfg, &( colors[NORMAL].hlfg ), "white" ); - parse_color ( visual_info->visual, target_colormap, config.menu_hlbg, &( colors[NORMAL].hlbg ), "black" ); + parse_color ( config.menu_bg, &( colors[NORMAL].bg ) ); + parse_color ( config.menu_fg, &( colors[NORMAL].fg ) ); + parse_color ( config.menu_bg_alt, &( colors[NORMAL].bgalt ) ); + parse_color ( config.menu_hlfg, &( colors[NORMAL].hlfg ) ); + parse_color ( config.menu_hlbg, &( colors[NORMAL].hlbg ) ); - parse_color ( visual_info->visual, target_colormap, config.menu_bg_urgent, &( colors[URGENT].bg ), "black" ); - parse_color ( visual_info->visual, target_colormap, config.menu_fg_urgent, &( colors[URGENT].fg ), "white" ); - parse_color ( visual_info->visual, target_colormap, config.menu_bg_alt, &( colors[URGENT].bgalt ), "black" ); - parse_color ( visual_info->visual, target_colormap, config.menu_hlfg_urgent, &( colors[URGENT].hlfg ), "white" ); - parse_color ( visual_info->visual, target_colormap, config.menu_hlbg_urgent, &( colors[URGENT].hlbg ), "black" ); + parse_color ( config.menu_bg_urgent, &( colors[URGENT].bg ) ); + parse_color ( config.menu_fg_urgent, &( colors[URGENT].fg ) ); + parse_color ( config.menu_bg_alt, &( colors[URGENT].bgalt ) ); + parse_color ( config.menu_hlfg_urgent, &( colors[URGENT].hlfg ) ); + parse_color ( config.menu_hlbg_urgent, &( colors[URGENT].hlbg ) ); - parse_color ( visual_info->visual, target_colormap, config.menu_bg_active, &( colors[ACTIVE].bg ), "black" ); - parse_color ( visual_info->visual, target_colormap, config.menu_fg_active, &( colors[ACTIVE].fg ), "white" ); - parse_color ( visual_info->visual, target_colormap, config.menu_bg_alt, &( colors[ACTIVE].bgalt ), "black" ); - parse_color ( visual_info->visual, target_colormap, config.menu_hlfg_active, &( colors[ACTIVE].hlfg ), "white" ); - parse_color ( visual_info->visual, target_colormap, config.menu_hlbg_active, &( colors[ACTIVE].hlbg ), "black" ); + parse_color ( config.menu_bg_active, &( colors[ACTIVE].bg ) ); + parse_color ( config.menu_fg_active, &( colors[ACTIVE].fg ) ); + parse_color ( config.menu_bg_alt, &( colors[ACTIVE].bgalt ) ); + parse_color ( config.menu_hlfg_active, &( colors[ACTIVE].hlfg ) ); + parse_color ( config.menu_hlbg_active, &( colors[ACTIVE].hlbg ) ); } - PangoFontMap *font_map = pango_xft_get_font_map ( display, DefaultScreen ( display ) ); + PangoFontMap *font_map = pango_cairo_font_map_new (); p_context = pango_font_map_create_context ( font_map ); -} - -static void textbox_clean_rowcolor ( RowColor * color ) -{ - XftColorFree ( display, visual_info->visual, target_colormap, &( color->fg ) ); - XftColorFree ( display, visual_info->visual, target_colormap, &( color->bg ) ); - XftColorFree ( display, visual_info->visual, target_colormap, &( color->bgalt ) ); - XftColorFree ( display, visual_info->visual, target_colormap, &( color->hlfg ) ); - XftColorFree ( display, visual_info->visual, target_colormap, &( color->hlbg ) ); + g_object_unref ( font_map ); } void textbox_cleanup ( void ) { if ( p_context ) { - for ( unsigned int st = 0; st < num_states; st++ ) { - textbox_clean_rowcolor ( &colors[st] ); - } - g_object_unref ( p_context ); - pango_xft_shutdown_display ( display, DefaultScreen ( display ) ); - p_context = NULL; - visual_info = NULL; - target_colormap = None; + p_context = NULL; } } diff --git a/test/textbox-test.c b/test/textbox-test.c index a4009053..3c4aade1 100644 --- a/test/textbox-test.c +++ b/test/textbox-test.c @@ -83,11 +83,16 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) 0, 0, 200, 100, config.menu_bw, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr ); TASSERT ( mw != None ); + + cairo_surface_t *surface = cairo_xlib_surface_create ( display, mw, vinfo.visual, 200, 100 ); + // Create a drawable. + cairo_t *draw = cairo_create ( surface ); + cairo_set_operator ( draw, CAIRO_OPERATOR_SOURCE ); // Set alternate row to normal row. config.menu_bg_alt = config.menu_bg; - textbox_setup ( &vinfo, map ); + textbox_setup ( ); textbox *box = - textbox_create ( mw, &vinfo, map, TB_EDITABLE | TB_AUTOWIDTH | TB_AUTOHEIGHT, 0, 0, -1, -1, + textbox_create ( TB_EDITABLE | TB_AUTOWIDTH | TB_AUTOHEIGHT, 0, 0, -1, -1, NORMAL, "test" ); TASSERT ( box != NULL ); @@ -158,19 +163,19 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) TASSERT ( box->cursor == 5 ); textbox_font ( box, HIGHLIGHT ); - textbox_draw ( box ); + textbox_draw ( box, draw ); - textbox_show ( box ); textbox_move ( box, 12, 13 ); TASSERT ( box->x == 12 ); TASSERT ( box->y == 13 ); - textbox_hide ( box ); textbox_free ( box ); textbox_cleanup ( ); cleanup_abe (); + cairo_destroy ( draw ); + cairo_surface_destroy ( surface ); XDestroyWindow ( display, mw ); XCloseDisplay ( display ); }