summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Wong <gtw@gnu.org>2009-09-01 20:51:40 -0600
committerGary Wong <gtw@gnu.org>2009-09-01 20:51:40 -0600
commitdb6024a4eb1f55605e344d9cb640424038a239be (patch)
treee453835cc097c67441d2de2dba60dd0c87b13a8d
parent86c8dfa5b8017f7a940ed2e7333ee27a4af199a8 (diff)
Add simple menu handling.
-rw-r--r--ChangeLog20
-rw-r--r--Makefile.am1
-rw-r--r--actions.c51
-rw-r--r--actions.h19
-rw-r--r--decorate-core.c41
-rw-r--r--decorate-core.h2
-rw-r--r--decorate-render.c75
-rw-r--r--decorate-render.h2
-rw-r--r--frame.c27
-rw-r--r--gwm.c24
-rw-r--r--gwm.h37
-rw-r--r--keyboard.h3
-rw-r--r--menu.c325
-rw-r--r--menu.h15
-rw-r--r--root.c18
15 files changed, 608 insertions, 52 deletions
diff --git a/ChangeLog b/ChangeLog
index ec8610d..41f9c28 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/actions.c b/actions.c
index 81c0b86..8c49b6b 100644
--- a/actions.c
+++ b/actions.c
@@ -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 */
diff --git a/actions.h b/actions.h
index e143f41..9a74525 100644
--- a/actions.h
+++ b/actions.h
@@ -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 );
diff --git a/frame.c b/frame.c
index 793bace..1b6c453 100644
--- a/frame.c
+++ b/frame.c
@@ -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 ) ==
diff --git a/gwm.c b/gwm.c
index 4b699e2..bb846f6 100644
--- a/gwm.c
+++ b/gwm.c
@@ -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). */
diff --git a/gwm.h b/gwm.h
index d1d17ca..63e7534 100644
--- a/gwm.h
+++ b/gwm.h
@@ -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
diff --git a/keyboard.h b/keyboard.h
index 6171bae..06e100b 100644
--- a/keyboard.h
+++ b/keyboard.h
@@ -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 );
diff --git a/menu.c b/menu.c
new file mode 100644
index 0000000..3497103
--- /dev/null
+++ b/menu.c
@@ -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 */
+};
diff --git a/menu.h b/menu.h
new file mode 100644
index 0000000..0857a9e
--- /dev/null
+++ b/menu.h
@@ -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
diff --git a/root.c b/root.c
index 68c6463..fa64df8 100644
--- a/root.c
+++ b/root.c
@@ -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;
}