diff options
author | Gary Wong <gtw@gnu.org> | 2009-09-01 20:51:40 -0600 |
---|---|---|
committer | Gary Wong <gtw@gnu.org> | 2009-09-01 20:51:40 -0600 |
commit | db6024a4eb1f55605e344d9cb640424038a239be (patch) | |
tree | e453835cc097c67441d2de2dba60dd0c87b13a8d | |
parent | 86c8dfa5b8017f7a940ed2e7333ee27a4af199a8 (diff) |
Add simple menu handling.
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | actions.c | 51 | ||||
-rw-r--r-- | actions.h | 19 | ||||
-rw-r--r-- | decorate-core.c | 41 | ||||
-rw-r--r-- | decorate-core.h | 2 | ||||
-rw-r--r-- | decorate-render.c | 75 | ||||
-rw-r--r-- | decorate-render.h | 2 | ||||
-rw-r--r-- | frame.c | 27 | ||||
-rw-r--r-- | gwm.c | 24 | ||||
-rw-r--r-- | gwm.h | 37 | ||||
-rw-r--r-- | keyboard.h | 3 | ||||
-rw-r--r-- | menu.c | 325 | ||||
-rw-r--r-- | menu.h | 15 | ||||
-rw-r--r-- | root.c | 18 |
15 files changed, 608 insertions, 52 deletions
@@ -1,4 +1,22 @@ -Tue Sep 1 14:40:15 2009 Gary Wong <gtw@gnu.org> +2009-09-01 Gary Wong <gtw@gnu.org> + + * menu.c, menu.h: New files. + * actions.c: Added callback parameter to all actions. All callers + changed. + (action_root_menu, action_window_menu): New functions. + * decorate-core.c (core_update_window): Draw menu items. + (core_window_size): New function. + * decorate-render.c (render_update_window): Draw menu items. + (text_width, render_window_size): New functions. + * frame.c (recalc_size): Call window_size() to obtain feedback + dimensions. + (frame_button_press): Pop up a menu on button 2 presses. + * gwm.c (show_window): Annotate menu windows. + (setup_display): Initialise window_size. + (start_managing_window): Add OwnerGrabButton to frame windows. + (setup_display): Add OwnerGrabButton to the roots. + +2009-09-01 Gary Wong <gtw@gnu.org> * gwm.c (handle_events): Replace passive_grab with pointer_demux, which redirects events on the client side. diff --git a/Makefile.am b/Makefile.am index b67b80d..ed07c20 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,6 +10,7 @@ gwm_SOURCES = \ gwm.c gwm.h \ keyboard.c keyboard.h \ managed.c managed.h \ + menu.c menu.h \ root.c root.h \ utf8.c utf8.h \ window-table.c window-table.h @@ -34,6 +34,7 @@ #include "actions.h" #include "frame.h" +#include "menu.h" #include "window-table.h" static void external_command( char *name, ... ) { @@ -67,26 +68,42 @@ static void external_command( char *name, ... ) { } extern void action_raise_lowest( struct gwm_window *window, - xcb_generic_event_t *ev ) { + xcb_generic_event_t *ev, + union callback_param cp ) { - if( focus_frame ) - xcb_circulate_window( c, XCB_CIRCULATE_RAISE_LOWEST, - screens[ focus_frame->screen ]->root ); + xcb_circulate_window( c, XCB_CIRCULATE_RAISE_LOWEST, + screens[ window->screen ]->root ); } extern void action_stack_opposite( struct gwm_window *window, - xcb_generic_event_t *ev ) { + xcb_generic_event_t *ev, + union callback_param cp ) { - if( focus_frame ) { + if( window->type == WINDOW_FRAME ) { uint32_t n = XCB_STACK_MODE_OPPOSITE; - xcb_configure_window( c, focus_frame->w, XCB_CONFIG_WINDOW_STACK_MODE, + xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_STACK_MODE, &n ); } } +extern void action_root_menu( struct gwm_window *window, + xcb_generic_event_t *ev, + union callback_param cp ) { + + /* FIXME this should be configurable, of course */ + const struct menuitem root_menu[] = { + { "Map all icons", action_map_all_icons }, + { "Raise lowest window", action_raise_lowest }, + { "xterm", action_start_xterm } + }; + + popup_menu( window, ev, sizeof root_menu / sizeof *root_menu, root_menu ); +} + extern void action_map_all_icons( struct gwm_window *window, - xcb_generic_event_t *ev ) { + xcb_generic_event_t *ev, + union callback_param cp ) { int i; @@ -97,7 +114,23 @@ extern void action_map_all_icons( struct gwm_window *window, } extern void action_start_xterm( struct gwm_window *window, - xcb_generic_event_t *ev ) { + xcb_generic_event_t *ev, + union callback_param cp ) { external_command( "xterm", NULL ); } + +extern void action_window_menu( struct gwm_window *window, + xcb_generic_event_t *ev, + union callback_param cp ) { + + /* FIXME this should be configurable, of course */ + const struct menuitem window_menu[] = { + { "Raise/lower", action_stack_opposite } + }; + + popup_menu( window, ev, sizeof window_menu / sizeof *window_menu, + window_menu ); +} + +/* FIXME make the current frame bindings (move, resize, close?) actions too */ @@ -2,16 +2,27 @@ #define ACTIONS_H extern void action_raise_lowest( struct gwm_window *window, - xcb_generic_event_t *ev ); + xcb_generic_event_t *ev, + union callback_param cp ); extern void action_stack_opposite( struct gwm_window *window, - xcb_generic_event_t *ev ); + xcb_generic_event_t *ev, + union callback_param cp ); + +extern void action_root_menu( struct gwm_window *window, + xcb_generic_event_t *ev, + union callback_param cp ); extern void action_map_all_icons( struct gwm_window *window, - xcb_generic_event_t *ev ); + xcb_generic_event_t *ev, + union callback_param cp ); extern void action_start_xterm( struct gwm_window *window, - xcb_generic_event_t *ev ); + xcb_generic_event_t *ev, + union callback_param cp ); +extern void action_window_menu( struct gwm_window *window, + xcb_generic_event_t *ev, + union callback_param cp ); #endif diff --git a/decorate-core.c b/decorate-core.c index 8613d12..c21325a 100644 --- a/decorate-core.c +++ b/decorate-core.c @@ -40,6 +40,9 @@ #define STRING(x) STRING2(x) #define FONT_SIZE_STRING STRING( FONT_SIZE ) +#define FEEDBACK_WIDTH 96 /* width of size feedback window */ +#define FEEDBACK_HEIGHT 24 /* height of size feedback window */ + static xcb_font_t font; static xcb_gcontext_t *gcs; @@ -137,7 +140,7 @@ extern void core_update_window( struct gwm_window *window ) { xcb_set_clip_rectangles( c, XCB_CLIP_ORDERING_YX_BANDED, gcs[ window->screen ], 0, 0, 1, &window->update ); - + if( window->type == WINDOW_FRAME ) { char *name = window->u.frame.child->u.managed.name; @@ -145,6 +148,14 @@ extern void core_update_window( struct gwm_window *window ) { COL_TITLE_ACTIVE : COL_TITLE_INACTIVE, button_size( window->u.frame.button, TRUE ) + 4, FONT_SIZE, name ? name : "(Untitled)" ); + } else if( window->type == WINDOW_MENUITEM ) { + struct gwm_window *menu = window->u.menuitem.menu; + + core_text( window->w, window->screen, + menu->u.menu.active_item >= 0 && + menu->u.menu.items[ menu->u.menu.active_item ] == window ? + COL_MENU_ACTIVE_FORE : COL_MENU_INACTIVE_FORE, + 4, FONT_SIZE, window->u.menuitem.label ); } else if( window->type == WINDOW_FEEDBACK ) { char *p, text[ 32 ]; int width; @@ -168,6 +179,28 @@ extern void core_update_window( struct gwm_window *window ) { } } +extern void core_window_size( struct gwm_window *window, int *width, + int *height ) { + + switch( window->type ) { + case WINDOW_MENUITEM: + /* This is nasty. But then again, core fonts are fairly nasty... + this code is only here in case RENDER isn't available, so + hopefully it will never be used. */ + *width = FONT_SIZE << 4; + *height = FONT_SIZE + 4; + return; + + case WINDOW_FEEDBACK: + *width = FEEDBACK_WIDTH; + *height = FEEDBACK_HEIGHT; + return; + + default: + assert( FALSE ); + } +} + static void query_text_callback( unsigned int sequence, void *reply, xcb_generic_error_t *error, union callback_param p ) { @@ -196,7 +229,11 @@ static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = { { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */ { 0x0000, 0x0000, 0x0000 }, /* COL_TITLE_INACTIVE */ { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */ - { 0x0000, 0x0000, 0x0000 } /* COL_FEEDBACK_FORE */ + { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */ + { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */ + { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */ + { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */ }; static uint16_t luma( int col ) { diff --git a/decorate-core.h b/decorate-core.h index 0d768ad..6e39a36 100644 --- a/decorate-core.h +++ b/decorate-core.h @@ -2,6 +2,8 @@ #define DECORATE_CORE_H extern void core_update_window( struct gwm_window *window ); +extern void core_window_size( struct gwm_window *window, int *width, + int *height ); extern void decorate_core_init( void ); extern void decorate_core_done( void ); diff --git a/decorate-render.c b/decorate-render.c index 6012ca3..a04a3fc 100644 --- a/decorate-render.c +++ b/decorate-render.c @@ -45,13 +45,19 @@ enum style_id { }; #define TITLE_FONT_SIZE 12 -#define MENU_FONT_SIZE 10 +#define MENU_FONT_SIZE 14 #define STRING2(x) #x #define STRING(x) STRING2(x) #define TITLE_FONT_SIZE_STRING STRING( TITLE_FONT_SIZE ) #define MENU_FONT_SIZE_STRING STRING( MENU_FONT_SIZE ) +#define MENU_X_PAD 4 +#define MENU_Y_PAD 2 + +#define FEEDBACK_WIDTH 96 /* width of size feedback window */ +#define FEEDBACK_HEIGHT 24 /* height of size feedback window */ + static const FcChar8 *const style_names[ NUM_STYLES ] = { /* FIXME make this configurable */ (FcChar8 *) "sans:pixelsize=" TITLE_FONT_SIZE_STRING ":bold", @@ -83,7 +89,11 @@ static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = { { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */ { 0x3333, 0x3333, 0x3333 }, /* COL_TITLE_INACTIVE */ { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */ - { 0x0000, 0x0000, 0x0000 } /* COL_FEEDBACK_FORE */ + { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */ + { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */ + { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */ + { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */ }; #define METRIC_CACHE_SIZE_BITS 14 @@ -559,6 +569,21 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, return xcb_render_free_picture( c, dest ); } +static int text_width( enum style_id style, const char *text ) { + + const char *p; + int width; + + for( width = 0, p = text; *p; p++ ) { + int dx, dy; + + query_metrics( style, *p, &dx, &dy ); + width += dx; + } + + return width; +} + extern void render_update_window( struct gwm_window *window ) { if( !window->cleared ) @@ -573,23 +598,49 @@ extern void render_update_window( struct gwm_window *window ) { button_size( window->u.frame.button, TRUE ) + 4, TITLE_FONT_SIZE, name ? name : "(Untitled)", STYLE_TITLE, &window->update ); + } else if( window->type == WINDOW_MENUITEM ) { + struct gwm_window *menu = window->u.menuitem.menu; + + render_text( window->w, window->screen, + menu->u.menu.active_item >= 0 && + menu->u.menu.items[ menu->u.menu.active_item ] == window ? + COL_MENU_ACTIVE_FORE : COL_MENU_INACTIVE_FORE, + MENU_X_PAD, MENU_FONT_SIZE, + window->u.menuitem.label, STYLE_MENU, &window->update ); } else if( window->type == WINDOW_FEEDBACK ) { - char *p, text[ 32 ]; - int width; + char text[ 32 ]; sprintf( text, "%dx%d", window->u.feedback.fb_width, window->u.feedback.fb_height ); - for( width = 0, p = text; *p; p++ ) { - int dx, dy; + render_text( window->w, window->screen, COL_FEEDBACK_FORE, + ( FEEDBACK_WIDTH - text_width( STYLE_TITLE, text ) ) >> 1, + 16, text, STYLE_TITLE, &window->update ); + } +} - query_metrics( STYLE_TITLE, *p, &dx, &dy ); - width += dx; - } +extern void render_window_size( struct gwm_window *window, int *width, + int *height ) { - render_text( window->w, window->screen, COL_FEEDBACK_FORE, - ( FEEDBACK_WIDTH - width ) >> 1, 16, text, - STYLE_TITLE, &window->update ); + switch( window->type ) { + case WINDOW_MENUITEM: + if( window->u.menuitem.label ) { + *width = text_width( STYLE_MENU, window->u.menuitem.label ) + + ( MENU_X_PAD << 1 ); + *height = MENU_FONT_SIZE + ( MENU_Y_PAD << 1 ); + } else { + *width = MENU_X_PAD << 1; + *height = MENU_Y_PAD << 1; + } + return; + + case WINDOW_FEEDBACK: + *width = FEEDBACK_WIDTH; + *height = FEEDBACK_HEIGHT; + return; + + default: + assert( FALSE ); } } diff --git a/decorate-render.h b/decorate-render.h index e607544..bd60d3d 100644 --- a/decorate-render.h +++ b/decorate-render.h @@ -2,6 +2,8 @@ #define DECORATE_RENDER_H extern void render_update_window( struct gwm_window *window ); +extern void render_window_size( struct gwm_window *window, int *width, + int *height ); extern void decorate_render_init( void ); extern void decorate_render_done( void ); @@ -29,6 +29,7 @@ #include "gwm.h" +#include "actions.h" #include "frame.h" #include "managed.h" #include "window-table.h" @@ -662,6 +663,7 @@ static void recalc_size( struct gwm_window *window, int x, int y, if( new_l != window->u.frame.x || new_t != window->u.frame.y || new_width != window->u.frame.width || new_height != window->u.frame.height ) { + int disp_width, disp_height; int new_fb_width, new_fb_height; uint32_t values[ 4 ] = { new_l, new_t, new_width, new_height }; @@ -679,7 +681,9 @@ static void recalc_size( struct gwm_window *window, int x, int y, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values ); - if( !feedback ) { + if( feedback ) + window_size( feedback, &disp_width, &disp_height ); + else { uint32_t values[ 4 ]; feedback = add_window( xcb_generate_id( c ) ); @@ -687,7 +691,8 @@ static void recalc_size( struct gwm_window *window, int x, int y, feedback->type = WINDOW_FEEDBACK; feedback->u.feedback.fb_width = feedback->u.feedback.fb_height = -1; - + window_size( feedback, &disp_width, &disp_height ); + values[ 0 ] = gwm_screens[ window->screen ].pixels[ COL_FEEDBACK_BACK ]; /* background pixel */ values[ 1 ] = gwm_screens[ window->screen ].pixels[ @@ -697,10 +702,10 @@ static void recalc_size( struct gwm_window *window, int x, int y, xcb_create_window( c, XCB_COPY_FROM_PARENT, feedback->w, screens[ window->screen ]->root, ( screens[ window->screen ]->width_in_pixels - - FEEDBACK_WIDTH - 2 ) >> 1, + disp_width - 2 ) >> 1, ( screens[ window->screen ]->height_in_pixels - - FEEDBACK_HEIGHT - 2 ) >> 1, - FEEDBACK_WIDTH, FEEDBACK_HEIGHT, 1, + disp_height - 2 ) >> 1, + disp_width, disp_height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | @@ -722,8 +727,8 @@ static void recalc_size( struct gwm_window *window, int x, int y, feedback->u.feedback.fb_width = new_fb_width; feedback->u.feedback.fb_height = new_fb_height; - queue_window_update( feedback, 0, 0, FEEDBACK_WIDTH, - FEEDBACK_HEIGHT, FALSE ); + queue_window_update( feedback, 0, 0, disp_width, disp_height, + FALSE ); } } } @@ -734,6 +739,14 @@ static void frame_button_press( struct gwm_window *window, if( pointer_demux || ev->child == window->u.frame.child->w ) return; + if( ev->detail == 2 ) { + union callback_param cp; + + cp.p = NULL; + action_window_menu( window, (xcb_generic_event_t *) ev, cp ); + return; + } + pointer_demux = window->w; window_op = ( ev->detail > 1 ) == @@ -69,6 +69,7 @@ #include "frame.h" #include "keyboard.h" #include "managed.h" +#include "menu.h" #include "root.h" #include "utf8.h" #include "window-table.h" @@ -149,6 +150,7 @@ static int flag_debug; static volatile int signal_caught; static void ( *update_window )( struct gwm_window *window ); +void ( *window_size )( struct gwm_window *window, int *width, int *height ); extern FORMAT( printf, 1, 2 ) void warning( char *format, ... ) { @@ -321,6 +323,16 @@ static void show_window( char *label, xcb_window_t w ) { fputs( "button, ", stdout ); client = window->u.button.frame->u.frame.child; break; + + case WINDOW_MENU: + fputs( "menu", stdout ); + client = NULL; + break; + + case WINDOW_MENUITEM: + fputs( "menu item", stdout ); + client = NULL; + break; case WINDOW_FAKE: fputs( "fake", stdout ); @@ -1125,7 +1137,9 @@ static void start_managing_window( struct gwm_window *window, values[ 4 ] = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_POINTER_MOTION_HINT | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_EXPOSURE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; + XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | + XCB_EVENT_MASK_OWNER_GRAB_BUTTON; xcb_create_window( c, XCB_COPY_FROM_PARENT, frame->w, geom->root, frame->u.frame.x, frame->u.frame.y, @@ -1574,7 +1588,8 @@ static const event_handler fake_handlers[] = { static const event_handler *const handlers[] = { root_handlers, managed_handlers, frame_handlers, button_handlers, - fake_handlers, feedback_handlers, NULL, childless_handlers + menu_handlers, menuitem_handlers, fake_handlers, feedback_handlers, NULL, + childless_handlers }; static void setup_display( void ) { @@ -1805,11 +1820,13 @@ static void setup_display( void ) { if( have_extension[ EXT_RENDER ] ) { decorate_render_init(); update_window = render_update_window; + window_size = render_window_size; } else #endif { decorate_core_init(); update_window = core_update_window; + window_size = core_window_size; } /* Listen for a selection timestamp. */ @@ -2049,7 +2066,8 @@ static void setup_display( void ) { msg.data.data32[ 4 ] = 0; n = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | + XCB_EVENT_MASK_OWNER_GRAB_BUTTON; for( i = 0; i < num_screens; i++ ) { /* Send StructureNotify ClientMessage (ICCCM 2.0, section 2.8). */ @@ -141,6 +141,10 @@ enum decoration_col { COL_TITLE_INACTIVE, COL_FEEDBACK_BACK, COL_FEEDBACK_FORE, + COL_MENU_ACTIVE_BACK, + COL_MENU_ACTIVE_FORE, + COL_MENU_INACTIVE_BACK, + COL_MENU_INACTIVE_FORE, NUM_COLS }; @@ -188,6 +192,11 @@ enum gwm_cursor { }; extern xcb_cursor_t cursors[ NUM_CURSORS ]; +union callback_param { + long l; + void *p; +}; + #define HINT_ICONIC 0x1 /* otherwise normal */ #define HINT_INPUT 0x2 #define HINT_USER_POSITION 0x4 @@ -208,8 +217,9 @@ struct gwm_window { xcb_window_t w; int screen; enum _gwm_window_type { - WINDOW_ROOT, WINDOW_MANAGED, WINDOW_FRAME, WINDOW_BUTTON, WINDOW_FAKE, - WINDOW_FEEDBACK, WINDOW_INCOMPLETE, WINDOW_CHILDLESS + WINDOW_ROOT, WINDOW_MANAGED, WINDOW_FRAME, WINDOW_BUTTON, WINDOW_MENU, + WINDOW_MENUITEM, WINDOW_FAKE, WINDOW_FEEDBACK, WINDOW_INCOMPLETE, + WINDOW_CHILDLESS } type; xcb_rectangle_t update; /* the area which needs updating */ int cleared; /* TRUE if the update area has already been cleared */ @@ -246,6 +256,20 @@ struct gwm_window { struct _gwm_button { struct gwm_window *frame; } button; + struct _gwm_menu { + struct gwm_window *window_param; + int num_items, active_item; + int width; + struct gwm_window **items; /* must be free()d */ + } menu; + struct _gwm_menuitem { + struct gwm_window *menu; + char *label; /* must be free()d */ + void ( *action )( struct gwm_window *window, + xcb_generic_event_t *ev, + union callback_param cp ); + union callback_param cp; + } menuitem; struct _gwm_fake { xcb_timestamp_t timestamp; } fake; @@ -266,11 +290,6 @@ extern const char *argv0; extern FORMAT( printf, 1, 2 ) void warning( char *format, ... ); extern FORMAT( printf, 1, 2 ) NORETURN void fatal( char *format, ... ); -union callback_param { - long l; - void *p; -}; - /* Schedule a callback function to be invoked when a reply or error is available for handling, or when neither can arrive. */ extern void handle_async_reply( unsigned int sequence, @@ -336,7 +355,7 @@ extern void unmanage_window( struct gwm_window *window ); asynchronous callbacks scheduled for replies and errors. */ extern xcb_generic_event_t *wait_for_event( void ); -#define FEEDBACK_WIDTH 96 /* width of size feedback window */ -#define FEEDBACK_HEIGHT 24 /* height of size feedback window */ +extern void ( *window_size )( struct gwm_window *window, int *width, + int *height ); #endif @@ -11,7 +11,8 @@ extern xcb_keycode_t *modifier_map[ 8 ]; extern const struct key_action { xcb_keysym_t keysym; xcb_mod_mask_t modifiers; - void ( *handler )( struct gwm_window *window, xcb_generic_event_t *ev ); + void ( *handler )( struct gwm_window *window, xcb_generic_event_t *ev, + union callback_param cp ); } key_actions[ NUM_KEY_ACTIONS ]; extern void get_keyboard_mapping( int first, int count ); @@ -0,0 +1,325 @@ +/* + * menu.c + * + * Part of gwm, the Gratuitous Window Manager, + * by Gary Wong, <gtw@gnu.org>. + * + * Copyright (C) 2009 Gary Wong + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * $Id$ + */ + +#include <config.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <xcb/xcb.h> + +#include "gwm.h" + +#include "menu.h" + +#include "actions.h" +#include "frame.h" +#include "keyboard.h" +#include "managed.h" +#include "root.h" +#include "window-table.h" + +static void destroy_menu( struct gwm_window *menu ) { + + int i; + + for( i = 0; i < menu->u.menu.num_items; i++ ) { + free( menu->u.menu.items[ i ]->u.menuitem.label ); + forget_window( menu->u.menu.items[ i ] ); + } + + free( menu->u.menu.items ); + + xcb_destroy_window( c, menu->w ); + + forget_window( menu ); +} + +static void menu_button_release( struct gwm_window *window, + xcb_button_release_event_t *ev ) { + + struct gwm_window *window_param; + void ( *action )( struct gwm_window *window, xcb_generic_event_t *ev, + union callback_param cp ); + union callback_param cp; + + if( final_release( ev ) ) { + if( window->u.menu.active_item >= 0 ) { + window_param = window->u.menu.window_param; + action = window->u.menu.items[ window->u.menu.active_item ]-> + u.menuitem.action; + cp = window->u.menu.items[ window->u.menu.active_item ]-> + u.menuitem.cp; + } else + action = NULL; + + destroy_menu( window ); + + if( action ) + action( window_param, (xcb_generic_event_t *) ev, cp ); + } +} + +static void menu_enter_notify( struct gwm_window *window, + xcb_enter_notify_event_t *ev ) { + + struct gwm_window *child; + + if( !pointer_demux ) + return; + + if( ( child = lookup_window( ev->event ) ) && + child->type == WINDOW_MENUITEM ) { + uint32_t n; + int i, width, height; + + for( i = 0; i < window->u.menu.num_items; i++ ) + if( window->u.menu.items[ i ] == child ) + break; + + if( i == window->u.menu.num_items || i == window->u.menu.active_item ) + return; + + if( child->u.menuitem.label ) + window_size( child, &width, &height ); + else { + window_size( window->u.menu.items[ window->u.menu.active_item ], + &width, &height ); + i = -1; + } + + if( window->u.menu.active_item >= 0 ) { + n = gwm_screens[ window->screen ].pixels[ COL_MENU_INACTIVE_BACK ]; + xcb_change_window_attributes( c, window->u.menu.items[ + window->u.menu.active_item ]->w, + XCB_CW_BACK_PIXEL, &n ); + queue_window_update( window->u.menu.items[ + window->u.menu.active_item ], 0, 0, + window->u.menu.width, height, FALSE ); + } + + if( i >= 0 ) { + n = gwm_screens[ window->screen ].pixels[ COL_MENU_ACTIVE_BACK ]; + xcb_change_window_attributes( c, child->w, XCB_CW_BACK_PIXEL, &n ); + queue_window_update( child, 0, 0, + window->u.menu.width, height, FALSE ); + } + + window->u.menu.active_item = i; + } +} + +static void menu_leave_notify( struct gwm_window *window, + xcb_leave_notify_event_t *ev ) { + + uint32_t n; + int width, height; + + if( !pointer_demux || ev->detail == XCB_NOTIFY_DETAIL_INFERIOR ) + return; + + if( window->u.menu.active_item >= 0 ) { + window_size( window->u.menu.items[ 0 ], &width, &height ); + + n = gwm_screens[ window->screen ].pixels[ COL_MENU_INACTIVE_BACK ]; + xcb_change_window_attributes( c, window->u.menu.items[ + window->u.menu.active_item ]->w, + XCB_CW_BACK_PIXEL, &n ); + queue_window_update( window->u.menu.items[ + window->u.menu.active_item ], 0, 0, + window->u.menu.width, height, FALSE ); + + window->u.menu.active_item = -1; + } +} + +extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, + int num_items, const struct menuitem *items ) { + + struct gwm_window *menu, *item; + int i, y, height, *widths, *heights, ev_x, ev_y; + xcb_timestamp_t timestamp; + uint32_t values[ 4 ]; + xcb_button_press_event_t *bp = (xcb_button_press_event_t *) ev; + int screen = window->screen; + + if( num_items < 1 ) + return; + + switch( ev->response_type ) { + case XCB_BUTTON_PRESS: + ev_x = bp->root_x; + ev_y = bp->root_y; + timestamp = bp->time; + break; + + default: + assert( FALSE ); + } + + menu = add_window( xcb_generate_id( c ) ); + menu->screen = screen; + menu->type = WINDOW_MENU; + menu->u.menu.window_param = window; + menu->u.menu.num_items = num_items; + menu->u.menu.active_item = -1; + menu->u.menu.items = xmalloc( num_items * sizeof *menu->u.menu.items ); + + widths = alloca( num_items * sizeof *widths ); + heights = alloca( num_items * sizeof *widths ); + + menu->u.menu.width = height = 0; + for( i = 0; i < num_items; i++ ) { + menu->u.menu.items[ i ] = item = add_window( xcb_generate_id( c ) ); + item->screen = screen; + item->type = WINDOW_MENUITEM; + item->u.menuitem.menu = menu; + item->u.menuitem.label = strdup( items[ i ].label ); + item->u.menuitem.action = items[ i ].action; + item->u.menuitem.cp = items[ i ].cp; + window_size( item, widths + i, heights + i ); + + if( widths[ i ] > menu->u.menu.width ) + menu->u.menu.width = widths[ i ]; + + height += heights[ i ]; + } + + if( ev_x + menu->u.menu.width > screens[ screen ]->width_in_pixels ) + ev_x = screens[ screen ]->width_in_pixels - menu->u.menu.width; + if( ev_x < 0 ) + ev_x = 0; + + if( ev_y + height > screens[ screen ]->height_in_pixels ) + ev_y = screens[ screen ]->height_in_pixels - height; + if( ev_y < 0 ) + ev_y = 0; + + values[ 0 ] = gwm_screens[ screen ].pixels[ COL_BORDER ]; /* border pixel */ + values[ 1 ] = TRUE; /* override redirect */ + values[ 2 ] = TRUE; /* save under */ + values[ 3 ] = XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_ENTER_WINDOW | + XCB_EVENT_MASK_LEAVE_WINDOW; + xcb_create_window( c, XCB_COPY_FROM_PARENT, menu->w, + screens[ screen ]->root, ev_x, ev_y, menu->u.menu.width, + height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, + XCB_COPY_FROM_PARENT, XCB_CW_BORDER_PIXEL | + XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER | + XCB_CW_EVENT_MASK, values ); + + values[ 0 ] = gwm_screens[ screen ].pixels[ + COL_MENU_INACTIVE_BACK ]; /* background pixel */ + values[ 1 ] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_EXPOSURE; + y = 0; + for( i = 0; i < num_items; i++ ) { + xcb_create_window( c, XCB_COPY_FROM_PARENT, menu->u.menu.items[ i ]->w, + menu->w, 0, y, menu->u.menu.width, heights[ i ], 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, + XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, values ); + + y += heights[ i ]; + } + + xcb_map_subwindows( c, menu->w ); + xcb_map_window( c, menu->w ); + + pointer_demux = menu->w; +} + +event_handler menu_handlers[] = { + NULL, /* Error */ + NULL, /* Reply */ + NULL, /* KeyPress */ + NULL, /* KeyRelease */ + NULL, /* ButtonPress */ + (event_handler) menu_button_release, + NULL, /* MotionNotify */ + (event_handler) menu_enter_notify, + (event_handler) menu_leave_notify, + NULL, /* FocusIn */ + NULL, /* FocusOut */ + NULL, /* KeymapNotify */ + NULL, /* Expose */ + NULL, /* GraphicsExpose */ + NULL, /* NoExposure */ + NULL, /* VisibilityNotify */ + NULL, /* CreateNotify */ + NULL, /* DestroyNotify */ + NULL, /* UnmapNotify */ + NULL, /* MapNotify */ + NULL, /* MapRequest */ + NULL, /* ReparentNotify */ + NULL, /* ConfigureNotify */ + NULL, /* ConfigureRequest */ + NULL, /* GravityNotify */ + NULL, /* ResizeRequest */ + NULL, /* CirculateNotify */ + NULL, /* CirculateRequest */ + NULL, /* PropertyNotify */ + NULL, /* SelectionClear */ + NULL, /* SelectionRequest */ + NULL, /* SelectionNotify */ + NULL, /* ColormapNotify */ + NULL, /* ClientMessage */ + NULL, /* MappingNotify */ + NULL, /* (synthetic) */ + NULL /* ShapeNotify */ +}, menuitem_handlers[] = { + NULL, /* Error */ + NULL, /* Reply */ + NULL, /* KeyPress */ + NULL, /* KeyRelease */ + NULL, /* ButtonPress */ + NULL, /* ButtonRelease */ + NULL, /* MotionNotify */ + NULL, /* EnterNotify */ + NULL, /* LeaveNotify */ + NULL, /* FocusIn */ + NULL, /* FocusOut */ + NULL, /* KeymapNotify */ + (event_handler) generic_expose, + NULL, /* GraphicsExpose */ + NULL, /* NoExposure */ + NULL, /* VisibilityNotify */ + NULL, /* CreateNotify */ + NULL, /* DestroyNotify */ + NULL, /* UnmapNotify */ + NULL, /* MapNotify */ + NULL, /* MapRequest */ + NULL, /* ReparentNotify */ + NULL, /* ConfigureNotify */ + NULL, /* ConfigureRequest */ + NULL, /* GravityNotify */ + NULL, /* ResizeRequest */ + NULL, /* CirculateNotify */ + NULL, /* CirculateRequest */ + NULL, /* PropertyNotify */ + NULL, /* SelectionClear */ + NULL, /* SelectionRequest */ + NULL, /* SelectionNotify */ + NULL, /* ColormapNotify */ + NULL, /* ClientMessage */ + NULL, /* MappingNotify */ + NULL, /* (synthetic) */ + NULL /* ShapeNotify */ +}; @@ -0,0 +1,15 @@ +#ifndef MENU_H +#define MENU_H + +struct menuitem { + const char *label; + void ( *action )( struct gwm_window *window, xcb_generic_event_t *ev, + union callback_param cp ); + union callback_param cp; +}; +extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, + int num_items, const struct menuitem *items ); + +extern event_handler menu_handlers[], menuitem_handlers[]; + +#endif @@ -43,7 +43,11 @@ static void root_key_press( struct gwm_window *window, if( keyboard_map[ ev->detail ][ 0 ] == key_actions[ i ].keysym && ( ev->state & 0xFF & key_actions[ i ].modifiers ) == key_actions[ i ].modifiers ) { - key_actions[ i ].handler( window, (xcb_generic_event_t *) ev ); + union callback_param cp; + + cp.p = NULL; + key_actions[ i ].handler( focus_frame ? focus_frame : window, + (xcb_generic_event_t *) ev, cp ); break; } @@ -52,10 +56,12 @@ static void root_key_press( struct gwm_window *window, static const struct button_action { int button; xcb_mod_mask_t modifiers; - void ( *handler )( struct gwm_window *window, xcb_generic_event_t *ev ); + void ( *handler )( struct gwm_window *window, xcb_generic_event_t *ev, + union callback_param cp ); } button_actions[] = { /* FIXME This table should be configurable, of course. */ - { 1, 0, action_map_all_icons }, + { 1, 0, action_root_menu }, + { 2, 0, action_map_all_icons }, { 3, 0, action_start_xterm } }; @@ -73,7 +79,11 @@ static void root_button_press( struct gwm_window *window, if( ev->detail == button_actions[ i ].button && ( ev->state & 0xFF & button_actions[ i ].modifiers ) == button_actions[ i ].modifiers ) { - button_actions[ i ].handler( window, (xcb_generic_event_t *) ev ); + union callback_param cp; + + cp.p = NULL; + button_actions[ i ].handler( window, (xcb_generic_event_t *) ev, + cp ); break; } |