From dc28a974373b5d462a734e35b8b47afce0782c77 Mon Sep 17 00:00:00 2001 From: TonCherAmi Date: Sun, 23 May 2021 01:17:27 +0300 Subject: [PATCH] Add cursor property (#1313) * Change mouse cursor on widget hover Currently only listview element and editbox are supported. * Add cursor property --- .travis.yml | 1 + INSTALL.md | 1 + configure.ac | 2 +- doc/default_theme.rasi | 5 +++ doc/rofi-theme.5 | 15 +++++++ doc/rofi-theme.5.markdown | 9 ++++ include/rofi-types.h | 12 ++++++ include/theme.h | 11 +++++ include/widgets/widget-internal.h | 3 ++ include/xcb.h | 23 ++++++++++ lexer/theme-lexer.l | 10 +++++ lexer/theme-parser.y | 15 +++++++ meson.build | 1 + source/rofi-types.c | 2 + source/theme.c | 26 +++++++++++ source/view.c | 71 ++++++++++++++++++++++++++++--- source/widgets/widget.c | 8 +++- source/xcb.c | 49 ++++++++++++++++++++- test/theme-parser-test.c | 29 +++++++++++++ 19 files changed, 284 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ddc0b6f..2dee9340 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,7 @@ addons: - libxcb-xinerama0-dev - libxcb-xkb-dev - libxcb-xrm-dev + - libxcb-cursor-dev - libxkbcommon-dev - libxkbcommon-dev - libxkbcommon-x11-dev diff --git a/INSTALL.md b/INSTALL.md index eb60573a..2df73997 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -40,6 +40,7 @@ You can also use [Meson](https://mesonbuild.com/) as an alternative. * xcb-util-wm (sometimes split as libxcb-ewmh and libxcb-icccm) * xcb-util-xrm [new module might not be available in your distribution. The source can be found here](https://github.com/Airblader/xcb-util-xrm/) +* xcb-util-cursor On debian based systems, the developer packages are in the form of: `-dev` on rpm based `-devel`. diff --git a/configure.ac b/configure.ac index 03dc1d2f..e561c900 100644 --- a/configure.ac +++ b/configure.ac @@ -144,7 +144,7 @@ NK_INIT([bindings xdg-theme]) PKG_CHECK_MODULES([glib], [glib-2.0 >= ${glib_min_version} gio-unix-2.0 gmodule-2.0]) AC_DEFINE_UNQUOTED([GLIB_VERSION_MIN_REQUIRED], [(G_ENCODE_VERSION(${glib_min_major},${glib_min_minor}))], [The lower GLib version supported]) AC_DEFINE_UNQUOTED([GLIB_VERSION_MAX_ALLOWED], [(G_ENCODE_VERSION(${glib_min_major},${glib_min_minor}))], [The highest GLib version supported]) -GW_CHECK_XCB([xcb-aux xcb-xkb xkbcommon xkbcommon-x11 xcb-ewmh xcb-icccm xcb-xrm xcb-randr xcb-xinerama]) +GW_CHECK_XCB([xcb-aux xcb-xkb xkbcommon xkbcommon-x11 xcb-ewmh xcb-icccm xcb-xrm xcb-cursor xcb-randr xcb-xinerama]) PKG_CHECK_MODULES([pango], [pango pangocairo]) PKG_CHECK_MODULES([cairo], [cairo cairo-xcb]) PKG_CHECK_MODULES([libsn], [libstartup-notification-1.0 ]) diff --git a/doc/default_theme.rasi b/doc/default_theme.rasi index 4c2b39a5..5c6e3bb5 100644 --- a/doc/default_theme.rasi +++ b/doc/default_theme.rasi @@ -35,6 +35,7 @@ element { padding: 1px ; spacing: 5px ; border: 0; + cursor: pointer; } element normal.normal { background-color: var(normal-background); @@ -76,11 +77,13 @@ element-text { background-color: rgba ( 0, 0, 0, 0 % ); text-color: inherit; highlight: inherit; + cursor: inherit; } element-icon { background-color: rgba ( 0, 0, 0, 0 % ); size: 1.0000em ; text-color: inherit; + cursor: inherit; } window { padding: 5; @@ -121,6 +124,7 @@ sidebar { button { spacing: 0; text-color: var(normal-foreground); + cursor: pointer; } button selected { background-color: var(selected-normal-background); @@ -151,6 +155,7 @@ entry { text-color: var(normal-foreground); placeholder-color: grey; placeholder: "Type to filter"; + cursor: text; } prompt { spacing: 0; diff --git a/doc/rofi-theme.5 b/doc/rofi-theme.5 index 3a555fdb..ad4d61dd 100644 --- a/doc/rofi-theme.5 +++ b/doc/rofi-theme.5 @@ -275,6 +275,8 @@ a reference .IP \(bu 2 an orientation .IP \(bu 2 +a cursor +.IP \(bu 2 a list of keywords .IP \(bu 2 an environment variable @@ -754,6 +756,16 @@ Format: \fB\fC(horizontal|vertical)\fR .PP Specify the orientation of the widget. +.SH Cursor +.RS +.IP \(bu 2 +Format: \fB\fC(default|pointer|text)\fR + +.RE + +.PP +Specify the type of mouse cursor that is set when the mouse pointer is over the widget. + .SH List of keywords .RS .IP \(bu 2 @@ -1012,6 +1024,9 @@ Background color .IP \(bu 2 \fBborder\-color\fP: color Color of the border +.IP \(bu 2 +\fBcursor\fP: cursor +Type of mouse cursor that is set when the mouse pointer is hovered over the widget. .RE diff --git a/doc/rofi-theme.5.markdown b/doc/rofi-theme.5.markdown index 4dd20b24..0f9dc6dc 100644 --- a/doc/rofi-theme.5.markdown +++ b/doc/rofi-theme.5.markdown @@ -189,6 +189,7 @@ The current theme format supports different types: * a position * a reference * an orientation + * a cursor * a list of keywords * an environment variable * Inherit @@ -462,6 +463,12 @@ window { Specify the orientation of the widget. +## Cursor + + * Format: `(default|pointer|text)` + +Specify the type of mouse cursor that is set when the mouse pointer is over the widget. + ## List of keywords * Format: `[ keyword, keyword ]` @@ -620,6 +627,8 @@ The following properties are currently supported: Background color * **border-color**: color Color of the border +* **cursor**: cursor + Type of mouse cursor that is set when the mouse pointer is hovered over the widget. ### window: diff --git a/include/rofi-types.h b/include/rofi-types.h index bdda445f..9bf8869f 100644 --- a/include/rofi-types.h +++ b/include/rofi-types.h @@ -33,6 +33,8 @@ typedef enum P_LIST, /** Orientation */ P_ORIENTATION, + /** Cursor */ + P_CURSOR, /** Inherit */ P_INHERIT, /** Number of types. */ @@ -140,6 +142,16 @@ typedef enum ROFI_ORIENTATION_HORIZONTAL } RofiOrientation; +/** + * Cursor type. + */ +typedef enum +{ + ROFI_CURSOR_DEFAULT, + ROFI_CURSOR_POINTER, + ROFI_CURSOR_TEXT +} RofiCursorType; + /** * Represent the color in theme. */ diff --git a/include/theme.h b/include/theme.h index 151446bc..ced26183 100644 --- a/include/theme.h +++ b/include/theme.h @@ -217,6 +217,17 @@ int rofi_theme_get_boolean ( const widget *widget, const char *property, int * @returns The orientation of this property for this widget or %def not found. */ RofiOrientation rofi_theme_get_orientation ( const widget *widget, const char *property, RofiOrientation def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the cursor indicated by %property of the widget. + * + * @returns The cursor for this widget or %def if not found. + */ +RofiCursorType rofi_theme_get_cursor_type ( const widget *widget, const char *property, RofiCursorType def ); /** * @param widget The widget to query * @param property The property to query. diff --git a/include/widgets/widget-internal.h b/include/widgets/widget-internal.h index ed028f3c..3106d063 100644 --- a/include/widgets/widget-internal.h +++ b/include/widgets/widget-internal.h @@ -54,6 +54,9 @@ struct _widget RofiPadding border; RofiPadding border_radius; + /** Cursor that is set when the widget is hovered */ + RofiCursorType cursor_type; + /** enabled or not */ gboolean enabled; /** Expand the widget when packed */ diff --git a/include/xcb.h b/include/xcb.h index b342e94e..9a71dc38 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -175,6 +175,28 @@ cairo_surface_t *x11_helper_get_screenshot_surface ( void ); */ void x11_disable_decoration ( xcb_window_t window ); +/** + * List of cursor types. + */ +typedef enum +{ + /** Default arrow cursor */ + CURSOR_DEFAULT = 0, + /** Cursor denoting a clickable area */ + CURSOR_POINTER, + /** Cursor denoting an input field / selectable text */ + CURSOR_TEXT, + NUM_CURSORS +} X11CursorType; + +/** + * @param window + * @param type + * + * Change mouse cursor + */ +void x11_set_cursor ( xcb_window_t window, X11CursorType type ); + /** * List of window managers that need different behaviour to functioning. */ @@ -212,4 +234,5 @@ cairo_surface_t *x11_helper_get_screenshot_surface_window ( xcb_window_t window, * Blur the content of the surface with radius and deviation. */ void cairo_image_surface_blur(cairo_surface_t* surface, double radius, double deviation); + #endif diff --git a/lexer/theme-lexer.l b/lexer/theme-lexer.l index c3bc99d0..48d043a8 100644 --- a/lexer/theme-lexer.l +++ b/lexer/theme-lexer.l @@ -227,6 +227,12 @@ LS_SOLID (?i:solid) ORIENTATION_HORI (?i:horizontal) ORIENTATION_VERT (?i:vertical) +/* Cursor */ + +CURSOR_DEF (?i:default) +CURSOR_PTR (?i:pointer) +CURSOR_TXT (?i:text) + /* Color schema */ RGBA (?i:rgb[a]?) HWB (?i:hwb) @@ -598,6 +604,10 @@ if ( queue == NULL ){ {ORIENTATION_HORI} { return ORIENTATION_HORI; } {ORIENTATION_VERT} { return ORIENTATION_VERT; } +{CURSOR_DEF} { return CURSOR_DEF; } +{CURSOR_PTR} { return CURSOR_PTR; } +{CURSOR_TXT} { return CURSOR_TXT; } + {COLOR_TRANSPARENT} { return T_COLOR_TRANSPARENT; } diff --git a/lexer/theme-parser.y b/lexer/theme-parser.y index bb1496db..8efcdc58 100644 --- a/lexer/theme-parser.y +++ b/lexer/theme-parser.y @@ -200,6 +200,10 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b) %token ORIENTATION_HORI "Horizontal" %token ORIENTATION_VERT "Vertical" +%token CURSOR_DEF "Default" +%token CURSOR_PTR "Pointer" +%token CURSOR_TXT "Text" + %token T_COL_RGBA "rgb[a] colorscheme" %token T_COL_HSL "hsl colorscheme" %token T_COL_HWB "hwb colorscheme" @@ -276,6 +280,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b) %type t_property_element_list %type t_property_element_list_optional %type t_property_orientation +%type t_property_cursor %type t_name_prefix_optional %start t_main @@ -512,6 +517,10 @@ t_property_element $$ = rofi_theme_property_create ( P_ORIENTATION ); $$->value.i = $1; } +| t_property_cursor { + $$ = rofi_theme_property_create ( P_CURSOR ); + $$->value.i = $1; +} ; /** List of elements */ @@ -846,6 +855,12 @@ t_property_orientation | ORIENTATION_VERT { $$ = ROFI_ORIENTATION_VERTICAL; } ; +t_property_cursor +: CURSOR_DEF { $$ = ROFI_CURSOR_DEFAULT; } +| CURSOR_PTR { $$ = ROFI_CURSOR_POINTER; } +| CURSOR_TXT { $$ = ROFI_CURSOR_TEXT; } +; + /** Property name */ t_property_name : T_PROP_NAME { $$ = $1; } diff --git a/meson.build b/meson.build index 67f35a60..98c9de7b 100644 --- a/meson.build +++ b/meson.build @@ -66,6 +66,7 @@ deps += [ dependency('xcb-icccm'), dependency('xcb-xrm'), dependency('xcb-randr'), + dependency('xcb-cursor'), dependency('xcb-xinerama'), dependency('cairo-xcb'), dependency('libstartup-notification-1.0'), diff --git a/source/rofi-types.c b/source/rofi-types.c index 690a18c4..2ff4e3e8 100644 --- a/source/rofi-types.c +++ b/source/rofi-types.c @@ -28,6 +28,8 @@ const char * const PropertyTypeName[P_NUM_TYPES] = { "List", /** Orientation */ "Orientation", + /** Cursor */ + "Cursor", /** Inherit */ "Inherit", }; diff --git a/source/theme.c b/source/theme.c index 26a4b854..2aaa3762 100644 --- a/source/theme.c +++ b/source/theme.c @@ -359,6 +359,13 @@ const char * const WindowLocationStr[9] = { "west" }; +/** Textual representation of RofiCursorType */ +const char *const RofiCursorTypeStr[3] = { + "default", + "pointer", + "text", +}; + static void int_rofi_theme_print_property ( Property *p ) { switch ( p->type ) @@ -376,6 +383,9 @@ static void int_rofi_theme_print_property ( Property *p ) case P_ORIENTATION: printf ( "%s", ( p->value.i == ROFI_ORIENTATION_HORIZONTAL ) ? "horizontal" : "vertical" ); break; + case P_CURSOR: + printf ( "%s", RofiCursorTypeStr[p->value.i] ); + break; case P_HIGHLIGHT: if ( p->value.highlight.style & ROFI_HL_BOLD ) { printf ( "bold " ); @@ -814,6 +824,22 @@ RofiOrientation rofi_theme_get_orientation ( const widget *widget, const char *p g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property ); return def; } +RofiCursorType rofi_theme_get_cursor_type ( const widget *widget, const char *property, RofiCursorType def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_CURSOR, property, FALSE ); + if ( p ) { + if ( p->type == P_INHERIT ) { + if ( widget->parent ) { + return rofi_theme_get_cursor_type ( widget->parent, property, def ); + } + return def; + } + return p->value.i; + } + g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property ); + return def; +} const char *rofi_theme_get_string ( const widget *widget, const char *property, const char *def ) { diff --git a/source/view.c b/source/view.c index 1b2edd76..43b56ab8 100644 --- a/source/view.c +++ b/source/view.c @@ -118,6 +118,8 @@ struct guint repaint_source; /** Window fullscreen */ gboolean fullscreen; + /** Cursor type */ + X11CursorType cursor_type; } CacheState = { .main_window = XCB_WINDOW_NONE, .fake_bg = NULL, @@ -722,7 +724,7 @@ static void rofi_view_setup_fake_transparency ( widget *win, const char* const f } else { CacheState.fake_bg = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, CacheState.mon.w, CacheState.mon.h ); - + int blur = rofi_theme_get_integer ( WIDGET ( win ), "blur", 0 ); cairo_t *dr = cairo_create ( CacheState.fake_bg ); if ( CacheState.fake_bgrel ) { @@ -748,10 +750,8 @@ void __create_window ( MenuFlags menu_flags ) uint32_t selmask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY | XCB_CW_BACKING_STORE | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; uint32_t xcb_event_masks = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION; - if ( config.hover_select == TRUE ) { - xcb_event_masks |= XCB_EVENT_MASK_POINTER_MOTION; - } + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION | XCB_EVENT_MASK_POINTER_MOTION; + uint32_t selval[] = { XCB_BACK_PIXMAP_NONE, 0, XCB_GRAVITY_STATIC, @@ -1500,19 +1500,65 @@ void rofi_view_handle_text ( RofiViewState *state, char *text ) } } +static X11CursorType rofi_cursor_type_to_x11_cursor_type ( RofiCursorType type ) +{ + switch ( type ) + { + case ROFI_CURSOR_DEFAULT: + return CURSOR_DEFAULT; + + case ROFI_CURSOR_POINTER: + return CURSOR_POINTER; + + case ROFI_CURSOR_TEXT: + return CURSOR_TEXT; + } + + return CURSOR_DEFAULT; +} + +static RofiCursorType rofi_view_resolve_cursor ( RofiViewState *state, gint x, gint y ) { + widget *target = widget_find_mouse_target ( WIDGET ( state->main_window ), WIDGET_TYPE_UNKNOWN, x, y ); + + return target != NULL + ? target->cursor_type + : ROFI_CURSOR_DEFAULT; +} + +static void rofi_view_set_cursor ( RofiCursorType type ) +{ + X11CursorType x11_type = rofi_cursor_type_to_x11_cursor_type ( type ); + + if ( x11_type == CacheState.cursor_type ) { + return; + } + + CacheState.cursor_type = x11_type; + + x11_set_cursor ( CacheState.main_window, x11_type ); +} + void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y, gboolean find_mouse_target ) { state->mouse.x = x; state->mouse.y = y; + + RofiCursorType cursor_type = rofi_view_resolve_cursor ( state, x, y ); + + rofi_view_set_cursor ( cursor_type ); + if ( find_mouse_target ) { widget *target = widget_find_mouse_target ( WIDGET ( state->main_window ), SCOPE_MOUSE_LISTVIEW_ELEMENT, x, y ); + if ( target != NULL ) { state->mouse.motion_target = target; } } + if ( state->mouse.motion_target != NULL ) { widget_xy_to_relative ( state->mouse.motion_target, &x, &y ); widget_motion_notify ( state->mouse.motion_target, x, y ); + if ( find_mouse_target ) { state->mouse.motion_target = NULL; } @@ -1851,6 +1897,20 @@ static void rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, } } +static void rofi_view_ping_mouse ( RofiViewState *state ) +{ + xcb_query_pointer_cookie_t pointer_cookie = xcb_query_pointer ( xcb->connection, CacheState.main_window ); + xcb_query_pointer_reply_t *pointer_reply = xcb_query_pointer_reply ( xcb->connection, pointer_cookie, NULL ); + + if ( pointer_reply == NULL ) { + return; + } + + rofi_view_handle_mouse_motion ( state, pointer_reply->win_x, pointer_reply->win_y, config.hover_select ); + + free ( pointer_reply ); +} + RofiViewState *rofi_view_create ( Mode *sw, const char *input, MenuFlags menu_flags, @@ -1921,6 +1981,7 @@ RofiViewState *rofi_view_create ( Mode *sw, rofi_view_update ( state, TRUE ); xcb_map_window ( xcb->connection, CacheState.main_window ); widget_queue_redraw ( WIDGET ( state->main_window ) ); + rofi_view_ping_mouse ( state ); xcb_flush ( xcb->connection ); /* When Override Redirect, the WM will not let us know we can take focus, so just steal it */ diff --git a/source/widgets/widget.c b/source/widgets/widget.c index 99bb8736..95ddd52a 100644 --- a/source/widgets/widget.c +++ b/source/widgets/widget.c @@ -87,7 +87,9 @@ void widget_init ( widget *wid, widget *parent, WidgetType type, const char *nam wid->border_radius = rofi_theme_get_padding ( wid, "border-radius", wid->def_border_radius ); wid->margin = rofi_theme_get_padding ( wid, "margin", wid->def_margin ); - // bled by default + wid->cursor_type = rofi_theme_get_cursor_type ( wid, "cursor", ROFI_CURSOR_DEFAULT ); + + // enabled by default wid->enabled = rofi_theme_get_boolean ( wid, "enabled", TRUE ); } @@ -443,9 +445,11 @@ widget *widget_find_mouse_target ( widget *wid, WidgetType type, gint x, gint y return target; } } - if ( wid->type == type ) { + + if ( wid->type == type || type == WIDGET_TYPE_UNKNOWN ) { return wid; } + return NULL; } diff --git a/source/xcb.c b/source/xcb.c index c3b9e3ad..ce5722d0 100644 --- a/source/xcb.c +++ b/source/xcb.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ /** Checks if the if x and y is inside rectangle. */ #define INTERSECT( x, y, x1, y1, w1, h1 ) ( ( ( ( x ) >= ( x1 ) ) && ( ( x ) < ( x1 + w1 ) ) ) && ( ( ( y ) >= ( y1 ) ) && ( ( y ) < ( y1 + h1 ) ) ) ) + WindowManagerQuirk current_window_manager = WM_EWHM; /** @@ -98,6 +100,18 @@ static xcb_visualtype_t *root_visual = NULL; xcb_atom_t netatoms[NUM_NETATOMS]; const char *netatom_names[] = { EWMH_ATOMS ( ATOM_CHAR ) }; +xcb_cursor_t cursors[NUM_CURSORS] = { XCB_CURSOR_NONE, XCB_CURSOR_NONE, XCB_CURSOR_NONE }; + +const struct +{ + const char *css_name; + const char *traditional_name; +} cursor_names[] = { + { "default", "left_ptr" }, + { "pointer", "hand" }, + { "text", "xterm" } +}; + static xcb_visualtype_t * lookup_visual ( xcb_screen_t *s, xcb_visualid_t visual ) { xcb_depth_iterator_t d; @@ -1057,7 +1071,7 @@ static void main_loop_x11_event_handler_view ( xcb_generic_event_t *event ) if ( button_mask && config.click_to_exit == TRUE ) { xcb->mouse_seen = TRUE; } - rofi_view_handle_mouse_motion ( state, xme->event_x, xme->event_y, !button_mask ); + rofi_view_handle_mouse_motion ( state, xme->event_x, xme->event_y, !button_mask && config.hover_select ); break; } case XCB_BUTTON_PRESS: @@ -1504,6 +1518,24 @@ static void x11_create_visual_and_colormap ( void ) } } +static void x11_lookup_cursors ( void ) { + xcb_cursor_context_t *ctx; + + if ( xcb_cursor_context_new( xcb->connection, xcb->screen, &ctx ) < 0 ) { + return; + } + + for ( int i = 0; i < NUM_CURSORS; ++i ) { + cursors[i] = xcb_cursor_load_cursor ( ctx, cursor_names[i].css_name ); + + if ( cursors[i] == XCB_CURSOR_NONE ) { + cursors[i] = xcb_cursor_load_cursor ( ctx, cursor_names[i].traditional_name ); + } + } + + xcb_cursor_context_free ( ctx ); +} + /** Retry count of grabbing keyboard. */ unsigned int lazy_grab_retry_count_kb = 0; /** Retry count of grabbing pointer. */ @@ -1540,6 +1572,8 @@ gboolean display_late_setup ( void ) { x11_create_visual_and_colormap (); + x11_lookup_cursors (); + /** * Create window (without showing) */ @@ -1633,3 +1667,16 @@ void x11_disable_decoration ( xcb_window_t window ) xcb_atom_t ha = netatoms[_MOTIF_WM_HINTS]; xcb_change_property ( xcb->connection, XCB_PROP_MODE_REPLACE, window, ha, ha, 32, 5, &hints ); } + +void x11_set_cursor ( xcb_window_t window, X11CursorType type ) +{ + if ( type < 0 || type >= NUM_CURSORS ) { + return; + } + + if ( cursors[type] == XCB_CURSOR_NONE ) { + return; + } + + xcb_change_window_attributes ( xcb->connection, window, XCB_CW_CURSOR, &( cursors[type] ) ); +} diff --git a/test/theme-parser-test.c b/test/theme-parser-test.c index dd722468..ab0800bd 100644 --- a/test/theme-parser-test.c +++ b/test/theme-parser-test.c @@ -1106,6 +1106,28 @@ START_TEST ( test_properties_orientation_case ) } END_TEST +START_TEST ( test_properties_cursor ) +{ + widget wid; + wid.name = "blaat"; + wid.state = NULL; + rofi_theme_parse_string ( "* { def: default; ptr: pointer; txt: text; }"); + ck_assert_int_eq ( rofi_theme_get_cursor_type( &wid, "def", ROFI_CURSOR_TEXT), ROFI_CURSOR_DEFAULT); + ck_assert_int_eq ( rofi_theme_get_cursor_type( &wid, "ptr", ROFI_CURSOR_DEFAULT), ROFI_CURSOR_POINTER); + ck_assert_int_eq ( rofi_theme_get_cursor_type( &wid, "txt", ROFI_CURSOR_DEFAULT), ROFI_CURSOR_TEXT); +} +END_TEST +START_TEST ( test_properties_cursor_case ) +{ + widget wid; + wid.name = "blaat"; + wid.state = NULL; + rofi_theme_parse_string ( "* { def: dEfault; ptr: POINter; txt: tExt; }"); + ck_assert_int_eq ( rofi_theme_get_cursor_type( &wid, "def", ROFI_CURSOR_TEXT), ROFI_CURSOR_DEFAULT); + ck_assert_int_eq ( rofi_theme_get_cursor_type( &wid, "ptr", ROFI_CURSOR_DEFAULT), ROFI_CURSOR_POINTER); + ck_assert_int_eq ( rofi_theme_get_cursor_type( &wid, "txt", ROFI_CURSOR_DEFAULT), ROFI_CURSOR_TEXT); +} +END_TEST START_TEST ( test_properties_list ) { widget wid; @@ -1375,6 +1397,13 @@ static Suite * theme_parser_suite (void) tcase_add_test ( tc_prop_orientation, test_properties_orientation_case ); suite_add_tcase(s, tc_prop_orientation ); } + { + TCase *tc_prop_cursor = tcase_create("Propertiescursor"); + tcase_add_checked_fixture(tc_prop_cursor, theme_parser_setup, theme_parser_teardown); + tcase_add_test ( tc_prop_cursor, test_properties_cursor); + tcase_add_test ( tc_prop_cursor, test_properties_cursor_case ); + suite_add_tcase(s, tc_prop_cursor ); + } { TCase *tc_prop_list = tcase_create("Propertieslist"); tcase_add_checked_fixture(tc_prop_list, theme_parser_setup, theme_parser_teardown);