changeset 14343:0387a167f342

[gaim-migrate @ 17044] A WM can now act on keystrokes. As an example, the sample WM will toggle the buddylist on pressing Alt+b. Mouse clicking and scrolling is now supported in most/all widgets. To use a WM, you need to add "wm=/path/to/wm.so" under [general] in ~/.gntrc. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 26 Aug 2006 12:54:39 +0000
parents 12156328fb4f
children ed771d01239b
files console/libgnt/gntbox.c console/libgnt/gntbutton.c console/libgnt/gntcheckbox.c console/libgnt/gntcombobox.c console/libgnt/gntmain.c console/libgnt/gntmarshal.c console/libgnt/gntmarshal.h console/libgnt/gnttextview.c console/libgnt/gnttree.c console/libgnt/gntwidget.c console/libgnt/gntwidget.h console/libgnt/gntwm.h console/libgnt/wms/Makefile.am console/libgnt/wms/s.c
diffstat 14 files changed, 380 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/console/libgnt/gntbox.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntbox.c	Sat Aug 26 12:54:39 2006 +0000
@@ -454,6 +454,30 @@
 	reposition_children(widget);
 }
 
+static gboolean
+gnt_box_clicked(GntWidget *widget, GntMouseEvent event, int cx, int cy)
+{
+	GList *iter;
+	for (iter = GNT_BOX(widget)->list; iter; iter = iter->next) {
+		int x, y, w, h;
+		GntWidget *wid = iter->data;
+
+		gnt_widget_get_position(wid, &x, &y);
+		gnt_widget_get_size(wid, &w, &h);
+
+		if (cx >= x && cx < x + w && cy >= y && cy < y + h) {
+			if (event <= GNT_MIDDLE_MOUSE_DOWN &&
+				GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_CAN_TAKE_FOCUS)) {
+				while (widget->parent)
+					widget = widget->parent;
+				gnt_box_give_focus_to_child(GNT_BOX(widget), wid);
+			}
+			return gnt_widget_clicked(wid, event, cx, cy);
+		}
+	}
+	return FALSE;
+}
+
 static void
 gnt_box_class_init(GntBoxClass *klass)
 {
@@ -465,6 +489,7 @@
 	parent_class->size_request = gnt_box_size_request;
 	parent_class->set_position = gnt_box_set_position;
 	parent_class->key_pressed = gnt_box_key_pressed;
+	parent_class->clicked = gnt_box_clicked;
 	parent_class->lost_focus = gnt_box_lost_focus;
 	parent_class->gained_focus = gnt_box_gained_focus;
 	parent_class->confirm_size = gnt_box_confirm_size;
@@ -603,6 +628,7 @@
 
 		if (box->vertical)
 		{
+			x = pos;
 			if (box->alignment == GNT_ALIGN_RIGHT)
 				x += widget->priv.width - width;
 			else if (box->alignment == GNT_ALIGN_MID)
@@ -612,6 +638,7 @@
 		}
 		else
 		{
+			y = pos;
 			if (box->alignment == GNT_ALIGN_BOTTOM)
 				y += widget->priv.height - height;
 			else if (box->alignment == GNT_ALIGN_MID)
@@ -622,6 +649,7 @@
 
 		copywin(w->window, widget->window, 0, 0,
 				y, x, y + height - 1, x + width - 1, FALSE);
+		gnt_widget_set_position(w, x + widget->priv.x, y + widget->priv.y);
 	}
 }
 
--- a/console/libgnt/gntbutton.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntbutton.c	Sat Aug 26 12:54:39 2006 +0000
@@ -58,6 +58,16 @@
 	return FALSE;
 }
 
+static gboolean
+gnt_button_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
+{
+	if (event == GNT_LEFT_MOUSE_DOWN) {
+		gnt_widget_activate(widget);
+		return TRUE;
+	}
+	return FALSE;
+}
+
 static void
 gnt_button_class_init(GntWidgetClass *klass)
 {
@@ -66,6 +76,7 @@
 	parent_class->map = gnt_button_map;
 	parent_class->size_request = gnt_button_size_request;
 	parent_class->key_pressed = gnt_button_key_pressed;
+	parent_class->clicked = gnt_button_clicked;
 
 	DEBUG;
 }
--- a/console/libgnt/gntcheckbox.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntcheckbox.c	Sat Aug 26 12:54:39 2006 +0000
@@ -46,20 +46,36 @@
 	DEBUG;
 }
 
+static void
+toggle_selection(GntWidget *widget)
+{
+	GNT_CHECK_BOX(widget)->checked = !GNT_CHECK_BOX(widget)->checked;
+	g_signal_emit(widget, signals[SIG_TOGGLED], 0);
+	gnt_widget_draw(widget);
+}
+
 static gboolean
 gnt_check_box_key_pressed(GntWidget *widget, const char *text)
 {
 	if (text[0] == ' ' && text[1] == '\0')
 	{
-		GNT_CHECK_BOX(widget)->checked = !GNT_CHECK_BOX(widget)->checked;
-		g_signal_emit(widget, signals[SIG_TOGGLED], 0);
-		gnt_widget_draw(widget);
+		toggle_selection(widget);
 		return TRUE;
 	}
 
 	return FALSE;
 }
 
+static gboolean
+gnt_check_box_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
+{
+	if (event == GNT_LEFT_MOUSE_DOWN) {
+		toggle_selection(widget);
+		return TRUE;
+	}
+	return FALSE;
+}
+
 static void
 gnt_check_box_destroy(GntWidget *widget)
 {
@@ -76,6 +92,7 @@
 	/*parent_class->map = gnt_check_box_map;*/
 	/*parent_class->size_request = gnt_check_box_size_request;*/
 	wclass->key_pressed = gnt_check_box_key_pressed;
+	wclass->clicked = gnt_check_box_clicked;
 
 	signals[SIG_TOGGLED] = 
 		g_signal_new("toggled",
--- a/console/libgnt/gntcombobox.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntcombobox.c	Sat Aug 26 12:54:39 2006 +0000
@@ -91,6 +91,26 @@
 	DEBUG;
 }
 
+static void
+popup_dropdown(GntComboBox *box)
+{
+	GntWidget *widget = GNT_WIDGET(box);
+	GntWidget *parent = box->dropdown->parent;
+	int height = g_list_length(GNT_TREE(box->dropdown)->list);
+	int y = widget->priv.y + widget->priv.height - 1;
+	gnt_widget_set_size(box->dropdown, widget->priv.width, height + 2);
+
+	if (y + height + 2 >= getmaxy(stdscr))
+		y = widget->priv.y - height - 1;
+	gnt_widget_set_position(parent, widget->priv.x, y);
+	if (parent->window)
+	{
+		mvwin(parent->window, y, widget->priv.x);
+	}
+
+	gnt_widget_draw(parent);
+}
+
 static gboolean
 gnt_combo_box_key_pressed(GntWidget *widget, const char *text)
 {
@@ -121,20 +141,7 @@
 			if (strcmp(text + 1, GNT_KEY_UP) == 0 ||
 					strcmp(text + 1, GNT_KEY_DOWN) == 0)
 			{
-				GntWidget *parent = box->dropdown->parent;
-				int height = g_list_length(GNT_TREE(box->dropdown)->list);
-				int y = widget->priv.y + widget->priv.height - 1;
-				gnt_widget_set_size(box->dropdown, widget->priv.width, height + 2);
-
-				if (y + height + 2 >= getmaxy(stdscr))
-					y = widget->priv.y - height - 1;
-				gnt_widget_set_position(parent, widget->priv.x, y);
-				if (parent->window)
-				{
-					mvwin(parent->window, y, widget->priv.x);
-				}
-
-				gnt_widget_draw(parent);
+				popup_dropdown(box);
 				return TRUE;
 			}
 		}
@@ -158,6 +165,32 @@
 	widget_lost_focus(widget);
 }
 
+static gboolean
+gnt_combo_box_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
+{
+	GntComboBox *box = GNT_COMBO_BOX(widget);
+	gboolean dshowing = GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED);
+
+	if (event == GNT_MOUSE_SCROLL_UP) {
+		if (dshowing)
+			gnt_widget_key_pressed(box->dropdown, "\033" GNT_KEY_UP);
+	} else if (event == GNT_MOUSE_SCROLL_DOWN) {
+		if (dshowing)
+			gnt_widget_key_pressed(box->dropdown, "\033" GNT_KEY_DOWN);
+	} else if (event == GNT_LEFT_MOUSE_DOWN) {
+		if (dshowing) {
+			set_selection(box, gnt_tree_get_selection_data(GNT_TREE(box->dropdown)));
+			gnt_tree_set_selected(GNT_TREE(box->dropdown), box->selected);
+			gnt_widget_hide(box->dropdown->parent);
+		} else {
+			popup_dropdown(GNT_COMBO_BOX(widget));
+			return TRUE;
+		}
+	} else
+		return FALSE;
+	return TRUE;
+}
+
 static void
 gnt_combo_box_class_init(GntComboBoxClass *klass)
 {
@@ -168,6 +201,7 @@
 	parent_class->map = gnt_combo_box_map;
 	parent_class->size_request = gnt_combo_box_size_request;
 	parent_class->key_pressed = gnt_combo_box_key_pressed;
+	parent_class->clicked = gnt_combo_box_clicked;
 
 	widget_lost_focus = parent_class->lost_focus;
 	parent_class->lost_focus = gnt_combo_box_lost_focus;
--- a/console/libgnt/gntmain.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntmain.c	Sat Aug 26 12:54:39 2006 +0000
@@ -41,8 +41,6 @@
 static gboolean ascii_only;
 static gboolean mouse_enabled;
 
-static GntWM wm;
-
 static GMainLoop *loop;
 static struct
 {
@@ -73,6 +71,23 @@
 static void bring_on_top(GntWidget *widget);
 
 static gboolean refresh_screen();
+static const GList *list_all_windows();
+
+static GntWM wm = 
+{
+	NULL,   /* new_window */
+	NULL,   /* close_window */
+	NULL,   /* key_pressed */
+	NULL,   /* mouse clicked */
+	bring_on_top, /* give_focus */
+	NULL,   /* uninit */
+	list_all_windows,  /* window_list */
+};
+
+static const GList *list_all_windows()
+{
+	return focus_list;
+}
 
 static GList *
 g_list_bring_to_front(GList *list, gpointer data)
@@ -127,7 +142,7 @@
 
 	if (ordered)
 	{
-		bring_on_top(ordered->data);
+		wm.give_focus(ordered->data);
 	}
 	draw_taskbar(FALSE);
 }
@@ -139,6 +154,12 @@
 
 	if (!node)
 		return;
+	
+	if (ordered->data != widget) {
+		GntWidget *w = ordered->data;
+		ordered = g_list_bring_to_front(ordered, widget);
+		gnt_widget_set_focus(w, FALSE);
+	}
 
 	gnt_widget_set_focus(widget, TRUE);
 	gnt_widget_draw(widget);
@@ -247,7 +268,7 @@
 
 	ordered = g_list_bring_to_front(ordered, wid);
 
-	bring_on_top(ordered->data);
+	wm.give_focus(ordered->data);
 
 	if (w != wid)
 	{
@@ -269,7 +290,7 @@
 	if ((l = g_list_nth(focus_list, n)) != NULL)
 	{
 		ordered = g_list_bring_to_front(ordered, l->data);
-		bring_on_top(ordered->data);
+		wm.give_focus(ordered->data);
 	}
 
 	if (l && w != l->data)
@@ -289,7 +310,7 @@
 
 	old = ordered->data;
 	ordered = g_list_bring_to_front(ordered, widget);
-	bring_on_top(widget);
+	wm.give_focus(widget);
 
 	if (old != widget)
 	{
@@ -514,8 +535,9 @@
 	} button = MOUSE_NONE;
 	static GntWidget *remember = NULL;
 	static int offset = 0;
+	GntMouseEvent event;
 
-	if (buffer[0] != 27)
+	if (!ordered || buffer[0] != 27)
 		return FALSE;
 	
 	buffer++;
@@ -541,7 +563,7 @@
 					if (iter != ordered) {
 						GntWidget *w = ordered->data;
 						ordered = g_list_bring_to_front(ordered, iter->data);
-						bring_on_top(ordered->data);
+						wm.give_focus(ordered->data);
 						gnt_widget_set_focus(w, FALSE);
 					}
 					if (y == wid->priv.y) {
@@ -553,14 +575,19 @@
 				}
 			}
 		}
+		event = GNT_LEFT_MOUSE_DOWN;
 	} else if (strncmp(buffer, "[M\"", 3) == 0) {
 		/* right button down */
+		event = GNT_RIGHT_MOUSE_DOWN;
 	} else if (strncmp(buffer, "[M!", 3) == 0) {
 		/* middle button down */
+		event = GNT_MIDDLE_MOUSE_DOWN;
 	} else if (strncmp(buffer, "[M`", 3) == 0) {
 		/* wheel up*/
+		event = GNT_MOUSE_SCROLL_UP;
 	} else if (strncmp(buffer, "[Ma", 3) == 0) {
 		/* wheel down */
+		event = GNT_MOUSE_SCROLL_DOWN;
 	} else if (strncmp(buffer, "[M#", 3) == 0) {
 		/* button up */
 		if (button == MOUSE_NONE && y == getmaxy(stdscr) - 1) {
@@ -579,19 +606,23 @@
 		button = MOUSE_NONE;
 		remember = NULL;
 		offset = 0;
+		event = GNT_MOUSE_UP;
 	} else
 		return FALSE;
+
+	gnt_widget_clicked(ordered->data, event, x, y);
 	return FALSE; /* XXX: this should be TRUE */
 }
 
 static gboolean
 io_invoke(GIOChannel *source, GIOCondition cond, gpointer null)
 {
-	char buffer[256];
+	char keys[256];
 	gboolean ret = FALSE;
 	static GntKeyPressMode mode = GNT_KP_MODE_NORMAL;
+	const char *buffer;
 
-	int rd = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
+	int rd = read(STDIN_FILENO, keys, sizeof(keys) - 1);
 	if (rd < 0)
 	{
 		endwin();
@@ -605,18 +636,25 @@
 		exit(1);
 	}
 
-	buffer[rd] = 0;
+	keys[rd] = 0;
 
-	if (buffer[0] == 27 && buffer[1] == 'd' && buffer[2] == 0)
+	if (keys[0] == 27 && keys[1] == 'd' && keys[2] == 0)
 	{
 		/* This dumps the screen contents in an html file */
 		dump_screen();
 	}
 
-	gnt_keys_refine(buffer);
+	gnt_keys_refine(keys);
 
-	if (mouse_enabled && detect_mouse_action(buffer))
+	if (mouse_enabled && detect_mouse_action(keys))
 		return TRUE;
+	
+	if (wm.key_pressed) {
+		buffer = wm.key_pressed(keys);
+		if (buffer == NULL)
+			return TRUE;
+	} else
+		buffer = keys;
 
 	if (mode == GNT_KP_MODE_NORMAL)
 	{
--- a/console/libgnt/gntmarshal.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntmarshal.c	Sat Aug 26 12:54:39 2006 +0000
@@ -166,3 +166,40 @@
 }
 
 
+void gnt_closure_marshal_BOOLEAN__INT_INT_INT(GClosure *closure,
+										GValue *ret_value,
+										guint n_param_values,
+										const GValue *param_values,
+										gpointer invocation_hint,
+										gpointer marshal_data)
+{
+	typedef gboolean (*func) (gpointer data1, int, int, int, gpointer data2);
+	register func callback;
+	register GCClosure *cc = (GCClosure*)closure;
+	register gpointer data1, data2;
+	gboolean ret;
+
+	g_return_if_fail(ret_value != NULL);
+	g_return_if_fail(n_param_values == 4);
+
+	if (G_CCLOSURE_SWAP_DATA(closure))
+	{
+		data1 = closure->data;
+		data2 = g_value_peek_pointer(param_values + 0);
+	}
+	else
+	{
+		data1 = g_value_peek_pointer(param_values + 0);
+		data2 = closure->data;
+	}
+
+	callback = (func) (marshal_data ? marshal_data : cc->callback);
+	ret = callback(data1,
+			g_value_get_int(param_values + 1) ,
+			g_value_get_int(param_values + 2) ,
+			g_value_get_int(param_values + 3) ,
+			data2);
+	g_value_set_boolean(ret_value, ret);
+}
+
+
--- a/console/libgnt/gntmarshal.h	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntmarshal.h	Sat Aug 26 12:54:39 2006 +0000
@@ -35,3 +35,10 @@
 										gpointer invocation_hint,
 										gpointer marshal_data);
 
+void gnt_closure_marshal_BOOLEAN__INT_INT_INT(GClosure *closure,
+										GValue *ret_value,
+										guint n_param_values,
+										const GValue *param_values,
+										gpointer invocation_hint,
+										gpointer marshal_data);
+
--- a/console/libgnt/gnttextview.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gnttextview.c	Sat Aug 26 12:54:39 2006 +0000
@@ -135,6 +135,18 @@
 	g_list_free(view->list);
 }
 
+static gboolean
+gnt_text_view_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
+{
+	if (event == GNT_MOUSE_SCROLL_UP) {
+		gnt_text_view_scroll(GNT_TEXT_VIEW(widget), -1);
+	} else if (event == GNT_MOUSE_SCROLL_DOWN) {
+		gnt_text_view_scroll(GNT_TEXT_VIEW(widget), 1);
+	} else
+		return FALSE;
+	return TRUE;
+}
+
 static void
 gnt_text_view_class_init(GntTextViewClass *klass)
 {
@@ -144,6 +156,7 @@
 	parent_class->map = gnt_text_view_map;
 	parent_class->size_request = gnt_text_view_size_request;
 	parent_class->key_pressed = gnt_text_view_key_pressed;
+	parent_class->clicked = gnt_text_view_clicked;
 
 	DEBUG;
 }
--- a/console/libgnt/gnttree.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gnttree.c	Sat Aug 26 12:54:39 2006 +0000
@@ -463,6 +463,34 @@
 	return g_hash_table_lookup(tree->hash, key);
 }
 
+static void
+action_down(GntTree *tree)
+{
+	int dist;
+	GntTreeRow *row = get_next(tree->current);
+	if (row == NULL)
+		return;
+	tree->current = row;
+	if ((dist = get_distance(tree->current, tree->bottom)) < 0)
+		gnt_tree_scroll(tree, -dist);
+	else
+		redraw_tree(tree);
+}
+
+static void
+action_up(GntTree *tree)
+{
+	int dist;
+	GntTreeRow *row = get_prev(tree->current);
+	if (!row)
+		return;
+	tree->current = row;
+	if ((dist = get_distance(tree->current, tree->top)) > 0)
+		gnt_tree_scroll(tree, -dist);
+	else
+		redraw_tree(tree);
+}
+
 static gboolean
 gnt_tree_key_pressed(GntWidget *widget, const char *text)
 {
@@ -473,22 +501,13 @@
 
 	if (text[0] == 27)
 	{
-		if (strcmp(text+1, GNT_KEY_DOWN) == 0 && (row = get_next(tree->current)) != NULL)
+		if (strcmp(text+1, GNT_KEY_DOWN) == 0)
 		{
-			tree->current = row;
-			if ((dist = get_distance(tree->current, tree->bottom)) < 0)
-				gnt_tree_scroll(tree, -dist);
-			else
-				redraw_tree(tree);
+			action_down(tree);
 		}
-		else if (strcmp(text+1, GNT_KEY_UP) == 0 && (row = get_prev(tree->current)) != NULL)
+		else if (strcmp(text+1, GNT_KEY_UP) == 0)
 		{
-			tree->current = row;
-
-			if ((dist = get_distance(tree->current, tree->top)) > 0)
-				gnt_tree_scroll(tree, -dist);
-			else
-				redraw_tree(tree);
+			action_up(tree);
 		}
 		else if (strcmp(text+1, GNT_KEY_PGDOWN) == 0)
 		{
@@ -592,6 +611,39 @@
 	g_free(tree->columns);
 }
 
+static gboolean
+gnt_tree_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
+{
+	if (event == GNT_MOUSE_SCROLL_UP) {
+		action_up(GNT_TREE(widget));
+	} else if (event == GNT_MOUSE_SCROLL_DOWN) {
+		action_down(GNT_TREE(widget));
+	} else if (event == GNT_LEFT_MOUSE_DOWN) {
+		GntTreeRow *row;
+		GntTree *tree = GNT_TREE(widget);
+		int pos = 1;
+		if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER))
+			pos = 0;
+		if (tree->show_title)
+			pos += 2;
+		pos = y - widget->priv.y - pos;
+		row = get_next_n(tree->top, pos);
+		if (row && tree->current != row) {
+			GntTreeRow *old = tree->current;
+			tree->current = row;
+			redraw_tree(tree);
+			tree_selection_changed(tree, old, tree->current);
+		} else if (row == tree->current && row->choice) {
+			row->isselected = !row->isselected;
+			g_signal_emit(tree, signals[SIG_TOGGLED], 0, row->key);
+			redraw_tree(tree);
+		}
+	} else {
+		return FALSE;
+	}
+	return TRUE;
+}
+
 static void
 gnt_tree_class_init(GntTreeClass *klass)
 {
@@ -601,6 +653,7 @@
 	parent_class->map = gnt_tree_map;
 	parent_class->size_request = gnt_tree_size_request;
 	parent_class->key_pressed = gnt_tree_key_pressed;
+	parent_class->clicked = gnt_tree_clicked;
 
 	signals[SIG_SELECTION_CHANGED] = 
 		g_signal_new("selection-changed",
--- a/console/libgnt/gntwidget.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntwidget.c	Sat Aug 26 12:54:39 2006 +0000
@@ -19,6 +19,7 @@
 	SIG_CONFIRM_SIZE,
 	SIG_SIZE_CHANGED,
 	SIG_POSITION,
+	SIG_CLICKED,
 	SIGS
 };
 
@@ -116,6 +117,7 @@
 	
 	klass->key_pressed = NULL;
 	klass->activate = NULL;
+	klass->clicked = NULL;
 	
 	signals[SIG_DESTROY] = 
 		g_signal_new("destroy",
@@ -214,6 +216,15 @@
 					 gnt_closure_marshal_BOOLEAN__STRING,
 					 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
 
+	signals[SIG_CLICKED] = 
+		g_signal_new("clicked",
+					 G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST,
+					 G_STRUCT_OFFSET(GntWidgetClass, clicked),
+					 gnt_boolean_handled_accumulator, NULL,
+					 gnt_closure_marshal_BOOLEAN__INT_INT_INT,
+					 G_TYPE_BOOLEAN, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+
 	DEBUG;
 }
 
@@ -375,6 +386,14 @@
 	return ret;
 }
 
+gboolean
+gnt_widget_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
+{
+	gboolean ret;
+	g_signal_emit(widget, signals[SIG_CLICKED], 0, event, x, y, &ret);
+	return ret;
+}
+
 void
 gnt_widget_expose(GntWidget *widget, int x, int y, int width, int height)
 {
--- a/console/libgnt/gntwidget.h	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntwidget.h	Sat Aug 26 12:54:39 2006 +0000
@@ -45,6 +45,16 @@
 	GNT_WIDGET_TRANSIENT      = 1 << 11,
 } GntWidgetFlags;
 
+typedef enum _GnMouseEvent
+{
+	GNT_LEFT_MOUSE_DOWN = 1,
+	GNT_RIGHT_MOUSE_DOWN,
+	GNT_MIDDLE_MOUSE_DOWN,
+	GNT_MOUSE_UP,
+	GNT_MOUSE_SCROLL_UP,
+	GNT_MOUSE_SCROLL_DOWN
+} GntMouseEvent;
+
 /* XXX: I'll have to ask grim what he's using this for in guifications. */
 typedef enum _GnParamFlags
 {
@@ -96,6 +106,7 @@
 	void (*set_position)(GntWidget *widget, int x, int y);
 	gboolean (*key_pressed)(GntWidget *widget, const char *key);
 	void (*activate)(GntWidget *widget);
+	gboolean (*clicked)(GntWidget *widget, GntMouseEvent event, int x, int y);
 
 	void (*gnt_reserved1)(void);
 	void (*gnt_reserved2)(void);
@@ -121,6 +132,8 @@
 
 gboolean gnt_widget_key_pressed(GntWidget *widget, const char *keys);
 
+gboolean gnt_widget_clicked(GntWidget *widget, GntMouseEvent event, int x, int y);
+
 gboolean gnt_widget_set_focus(GntWidget *widget, gboolean set);
 void gnt_widget_activate(GntWidget *widget);
 
--- a/console/libgnt/gntwm.h	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/gntwm.h	Sat Aug 26 12:54:39 2006 +0000
@@ -10,9 +10,31 @@
 
 struct _GntWM
 {
+	/* This should return a PANEL for the win */
 	PANEL *(*new_window)(GntWidget *win);
-	gboolean (*key_pressed)(const char *key);
-	gboolean (*mouse_clicked)(void);    /* XXX: haven't decided yet */
+
+	/* This is called when a window is being closed */
+	gboolean (*close_window)(GntWidget *win);
+
+	/* This should usually return NULL if the keys were processed by the WM.
+	 * If not, the WM can simply return the original string, which will be
+	 * processed by the default WM. The custom WM can also return a different
+	 * static string for the default WM to process.
+	 */
+	const char *(*key_pressed)(const char *key);
+
+	/* Not decided yet */
+	gboolean (*mouse_clicked)(void);
+
+	/* Whatever the WM wants to do when a window is given focus */
+	void (*give_focus)(GntWidget *widget);
+
+	/* If something needs to be uninited */
 	void (*gntwm_uninit)();
+
+	/* List of windows. Although the WM can keep a list of its own for the windows,
+	 * it'd be better if there was a way to share between the 'core' and the WM.
+	 */
+	const GList *(*window_list)();
 };
 
--- a/console/libgnt/wms/Makefile.am	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/wms/Makefile.am	Sat Aug 26 12:54:39 2006 +0000
@@ -16,6 +16,8 @@
 AM_CPPFLAGS = \
 	-DDATADIR=\"$(datadir)\" \
 	-DVERSION=\"$(VERSION)\" \
+	-I$(top_srcdir)/libgaim \
+	-I$(top_srcdir)/console \
 	-I$(top_srcdir)/console/libgnt \
 	$(DEBUG_CFLAGS) \
 	$(GLIB_CFLAGS) \
--- a/console/libgnt/wms/s.c	Sat Aug 26 07:11:55 2006 +0000
+++ b/console/libgnt/wms/s.c	Sat Aug 26 12:54:39 2006 +0000
@@ -1,8 +1,12 @@
 #include "gntbox.h"
 #include "gntwm.h"
 
+#include "gntblist.h"
+
 #include <string.h>
 
+static GntWM *gwm;
+
 static PANEL *
 s_new_window(GntWidget *win)
 {
@@ -31,12 +35,14 @@
 		mvwin(win->window, y, x);
 
 		gnt_widget_set_size(win, w, h);
+		gnt_widget_draw(win);
 	} else if (name && strcmp(name, "conversation-window") == 0) {
 		/* Put the conversation windows to the far-right */
 		x = maxx - w;
 		y = 0;
 		gnt_widget_set_position(win, x, y);
 		mvwin(win->window, y, x);
+		gnt_widget_draw(win);
 	} else if (!GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_TRANSIENT)) {
 		/* In the middle of the screen */
 		x = (maxx - w) / 2;
@@ -49,8 +55,42 @@
 	return new_panel(win->window);
 }
 
+static GntWidget *
+find_widget(const char *wname)
+{
+	const GList *iter = gwm->window_list();
+	for (; iter; iter = iter->next) {
+		GntWidget *widget = iter->data;
+		const char *name = gnt_widget_get_name(widget);
+		if (name && strcmp(name, wname) == 0) {
+			return widget;
+		}
+	}
+	return NULL;
+}
+
+static const char*
+s_key_pressed(const char *key)
+{
+	/* Alt+b to toggle the buddylist */
+	if (key[0] == 27 && key[1] == 'b' && key[2] == '\0') {
+		GntWidget *w = find_widget("buddylist");
+		if (w == NULL) {
+			gg_blist_show();
+			w = find_widget("buddylist");
+			gwm->give_focus(w);
+		} else {
+			gnt_widget_destroy(w);
+		}
+		return NULL;
+	}
+	return key;
+}
+
 void gntwm_init(GntWM *wm)
 {
+	gwm = wm;
 	wm->new_window = s_new_window;
+	wm->key_pressed = s_key_pressed;
 }