Use cairo to do drawing.

This commit is contained in:
QC 2015-09-26 20:34:34 +02:00
parent 29e81fdb07
commit 8f1f03e6d2
9 changed files with 398 additions and 424 deletions

View File

@ -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/\

View File

@ -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"])

View File

@ -2,6 +2,8 @@
#define ROFI_MAIN_H
#include <X11/X.h>
#include <glib.h>
#include <string.h>
#include <stdlib.h>
#include <textbox.h>
#include "keyb.h"

View File

@ -1,14 +1,12 @@
#ifndef ROFI_SCROLLBAR_H
#define ROFI_SCROLLBAR_H
#include <cairo.h>
/**
* 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

View File

@ -1,24 +1,34 @@
#ifndef ROFI_TEXTBOX_H
#define ROFI_TEXTBOX_H
#include <X11/Xutil.h>
#include <pango/pango.h>
#include <pango/pangoxft.h>
#include <pango/pango-fontmap.h>
#include <cairo.h>
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().

View File

@ -44,6 +44,9 @@
#include <sys/wait.h>
#include <sys/types.h>
#include <cairo.h>
#include <cairo-xlib.h>
#define SN_API_NOT_YET_FROZEN
#include <libsn/sn.h>
@ -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;

View File

@ -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;
}

View File

@ -32,9 +32,8 @@
#include <X11/Xproto.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/Xft/Xft.h>
#include <ctype.h>
#include <pango/pangocairo.h>
#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;
}
}

View File

@ -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 );
}