summaryrefslogtreecommitdiff
path: root/openbox
diff options
context:
space:
mode:
authorDana Jansens <danakj@orodu.net>2003-08-12 07:26:16 +0000
committerDana Jansens <danakj@orodu.net>2003-08-12 07:26:16 +0000
commitc90da6da781932c2d178bfb7e39ec1d5003543b7 (patch)
treee9ed8a17f574701880fe73fcfbd872cdb4868c4d /openbox
parentcb49f853c9b62c4403eb562d39f52c51da292c4f (diff)
move the keyboard and mouse plugins into the kernel for mucho sexiness.
make workspace changing a grabbed/interactive process like focus cycling is, with the popup and all. this is some hot shit.
Diffstat (limited to 'openbox')
-rw-r--r--openbox/.cvsignore3
-rw-r--r--openbox/action.c367
-rw-r--r--openbox/action.h39
-rw-r--r--openbox/client.c12
-rw-r--r--openbox/config.c155
-rw-r--r--openbox/config.h5
-rw-r--r--openbox/event.c24
-rw-r--r--openbox/focus.c5
-rw-r--r--openbox/keyboard.c265
-rw-r--r--openbox/keyboard.h32
-rw-r--r--openbox/keytree.c112
-rw-r--r--openbox/keytree.h25
-rw-r--r--openbox/menu.c4
-rw-r--r--openbox/menu.h4
-rw-r--r--openbox/mouse.c372
-rw-r--r--openbox/mouse.h28
-rw-r--r--openbox/openbox.c6
-rw-r--r--openbox/plugin.c2
-rw-r--r--openbox/screen.c277
-rw-r--r--openbox/screen.h3
-rw-r--r--openbox/translate.c109
-rw-r--r--openbox/translate.h9
22 files changed, 1610 insertions, 248 deletions
diff --git a/openbox/.cvsignore b/openbox/.cvsignore
index e982b462..ab5b29ab 100644
--- a/openbox/.cvsignore
+++ b/openbox/.cvsignore
@@ -29,3 +29,6 @@ xerror.lo
.deps
openbox
.dirstamp
+keyboard.lo
+mouse.lo
+keytree.lo
diff --git a/openbox/action.c b/openbox/action.c
index d59c5514..275ba96a 100644
--- a/openbox/action.c
+++ b/openbox/action.c
@@ -16,18 +16,18 @@
typedef struct ActionString {
char *name;
void (*func)(union ActionData *);
- void (*setup)(Action *);
+ void (*setup)(ObAction *);
} ActionString;
-Action *action_new(void (*func)(union ActionData *data))
+ObAction *action_new(void (*func)(union ActionData *data))
{
- Action *a = g_new0(Action, 1);
+ ObAction *a = g_new0(ObAction, 1);
a->func = func;
return a;
}
-void action_free(Action *a)
+void action_free(ObAction *a)
{
if (a == NULL) return;
@@ -40,137 +40,209 @@ void action_free(Action *a)
g_free(a);
}
-void setup_action_directional_focus_north(Action *a)
+void setup_action_directional_focus_north(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_NORTH;
}
-void setup_action_directional_focus_east(Action *a)
+void setup_action_directional_focus_east(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_EAST;
}
-void setup_action_directional_focus_south(Action *a)
+void setup_action_directional_focus_south(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_SOUTH;
}
-void setup_action_directional_focus_west(Action *a)
+void setup_action_directional_focus_west(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_WEST;
}
-void setup_action_directional_focus_northeast(Action *a)
+void setup_action_directional_focus_northeast(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_NORTHEAST;
}
-void setup_action_directional_focus_southeast(Action *a)
+void setup_action_directional_focus_southeast(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_SOUTHEAST;
}
-void setup_action_directional_focus_southwest(Action *a)
+void setup_action_directional_focus_southwest(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_SOUTHWEST;
}
-void setup_action_directional_focus_northwest(Action *a)
+void setup_action_directional_focus_northwest(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_NORTHWEST;
}
-void setup_action_send_to_desktop(Action *a)
+void setup_action_send_to_desktop(ObAction *a)
{
- a->data.sendto.follow = TRUE;
}
-void setup_action_send_to_desktop_direction(Action *a)
+void setup_action_send_to_desktop_prev(ObAction *a)
{
+ a->data.sendtodir.dir = OB_DIRECTION_WEST;
+ a->data.sendtodir.linear = TRUE;
a->data.sendtodir.wrap = TRUE;
- a->data.sendtodir.follow = TRUE;
}
-void setup_action_desktop_direction(Action *a)
+void setup_action_send_to_desktop_next(ObAction *a)
{
+ a->data.sendtodir.dir = OB_DIRECTION_EAST;
+ a->data.sendtodir.linear = TRUE;
+ a->data.sendtodir.wrap = TRUE;
+}
+
+void setup_action_send_to_desktop_left(ObAction *a)
+{
+ a->data.sendtodir.dir = OB_DIRECTION_WEST;
+ a->data.sendtodir.linear = FALSE;
+ a->data.sendtodir.wrap = TRUE;
+}
+
+void setup_action_send_to_desktop_right(ObAction *a)
+{
+ a->data.sendtodir.dir = OB_DIRECTION_EAST;
+ a->data.sendtodir.linear = FALSE;
+ a->data.sendtodir.wrap = TRUE;
+}
+
+void setup_action_send_to_desktop_up(ObAction *a)
+{
+ a->data.sendtodir.dir = OB_DIRECTION_NORTH;
+ a->data.sendtodir.linear = FALSE;
+ a->data.sendtodir.wrap = TRUE;
+}
+
+void setup_action_send_to_desktop_down(ObAction *a)
+{
+ a->data.sendtodir.dir = OB_DIRECTION_SOUTH;
+ a->data.sendtodir.linear = FALSE;
+ a->data.sendtodir.wrap = TRUE;
+}
+
+void setup_action_desktop_prev(ObAction *a)
+{
+ a->data.desktopdir.dir = OB_DIRECTION_WEST;
+ a->data.desktopdir.linear = TRUE;
+ a->data.desktopdir.wrap = TRUE;
+}
+
+void setup_action_desktop_next(ObAction *a)
+{
+ a->data.desktopdir.dir = OB_DIRECTION_EAST;
+ a->data.desktopdir.linear = TRUE;
+ a->data.desktopdir.wrap = TRUE;
+}
+
+void setup_action_desktop_left(ObAction *a)
+{
+ a->data.desktopdir.dir = OB_DIRECTION_WEST;
+ a->data.desktopdir.linear = FALSE;
+ a->data.desktopdir.wrap = TRUE;
+}
+
+void setup_action_desktop_right(ObAction *a)
+{
+ a->data.desktopdir.dir = OB_DIRECTION_EAST;
+ a->data.desktopdir.linear = FALSE;
+ a->data.desktopdir.wrap = TRUE;
+}
+
+void setup_action_desktop_up(ObAction *a)
+{
+ a->data.desktopdir.dir = OB_DIRECTION_NORTH;
+ a->data.desktopdir.linear = FALSE;
a->data.desktopdir.wrap = TRUE;
}
-void setup_action_move_keyboard(Action *a)
+void setup_action_desktop_down(ObAction *a)
+{
+ a->data.desktopdir.dir = OB_DIRECTION_SOUTH;
+ a->data.desktopdir.linear = FALSE;
+ a->data.desktopdir.wrap = TRUE;
+}
+
+void setup_action_move_keyboard(ObAction *a)
{
a->data.moveresize.corner = prop_atoms.net_wm_moveresize_move_keyboard;
}
-void setup_action_move(Action *a)
+void setup_action_move(ObAction *a)
{
a->data.moveresize.corner = prop_atoms.net_wm_moveresize_move;
}
-void setup_action_resize(Action *a)
+void setup_action_resize(ObAction *a)
{
a->data.moveresize.corner = prop_atoms.net_wm_moveresize_size_topleft;
}
-void setup_action_resize_keyboard(Action *a)
+void setup_action_resize_keyboard(ObAction *a)
{
a->data.moveresize.corner = prop_atoms.net_wm_moveresize_size_keyboard;
}
-void setup_action_cycle_windows_linear_next(Action *a)
+void setup_action_cycle_windows_linear_next(ObAction *a)
{
a->data.cycle.linear = TRUE;
a->data.cycle.forward = TRUE;
}
-void setup_action_cycle_windows_linear_previous(Action *a)
+void setup_action_cycle_windows_linear_previous(ObAction *a)
{
a->data.cycle.linear = TRUE;
a->data.cycle.forward = FALSE;
}
-void setup_action_cycle_windows_next(Action *a)
+void setup_action_cycle_windows_next(ObAction *a)
{
a->data.cycle.linear = FALSE;
a->data.cycle.forward = TRUE;
}
-void setup_action_cycle_windows_previous(Action *a)
+void setup_action_cycle_windows_previous(ObAction *a)
{
a->data.cycle.linear = FALSE;
a->data.cycle.forward = FALSE;
}
-void setup_action_movetoedge_north(Action *a)
+void setup_action_movetoedge_north(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_NORTH;
}
-void setup_action_movetoedge_south(Action *a)
+void setup_action_movetoedge_south(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_SOUTH;
}
-void setup_action_movetoedge_east(Action *a)
+void setup_action_movetoedge_east(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_EAST;
}
-void setup_action_movetoedge_west(Action *a)
+void setup_action_movetoedge_west(ObAction *a)
{
a->data.diraction.direction = OB_DIRECTION_WEST;
}
-void setup_action_top_layer(Action *a)
+void setup_action_top_layer(ObAction *a)
{
a->data.layer.layer = 1;
}
-void setup_action_normal_layer(Action *a)
+void setup_action_normal_layer(ObAction *a)
{
a->data.layer.layer = 0;
}
-void setup_action_bottom_layer(Action *a)
+void setup_action_bottom_layer(ObAction *a)
{
a->data.layer.layer = -1;
}
@@ -358,24 +430,34 @@ ActionString actionstrings[] =
setup_action_send_to_desktop
},
{
+ "sendtodesktopnext",
+ action_send_to_desktop_dir,
+ setup_action_send_to_desktop_next
+ },
+ {
+ "sendtodesktopprevious",
+ action_send_to_desktop_dir,
+ setup_action_send_to_desktop_prev
+ },
+ {
"sendtodesktopright",
- action_send_to_desktop_right,
- setup_action_send_to_desktop_direction
+ action_send_to_desktop_dir,
+ setup_action_send_to_desktop_right
},
{
"sendtodesktopleft",
- action_send_to_desktop_left,
- setup_action_send_to_desktop_direction
+ action_send_to_desktop_dir,
+ setup_action_send_to_desktop_left
},
{
"sendtodesktopup",
- action_send_to_desktop_up,
- setup_action_send_to_desktop_direction
+ action_send_to_desktop_dir,
+ setup_action_send_to_desktop_up
},
{
"sendtodesktopdown",
- action_send_to_desktop_down,
- setup_action_send_to_desktop_direction
+ action_send_to_desktop_dir,
+ setup_action_send_to_desktop_down
},
{
"desktop",
@@ -383,24 +465,34 @@ ActionString actionstrings[] =
NULL
},
{
+ "desktopnext",
+ action_desktop_dir,
+ setup_action_desktop_next
+ },
+ {
+ "desktopprevious",
+ action_desktop_dir,
+ setup_action_desktop_prev
+ },
+ {
"desktopright",
- action_desktop_right,
- setup_action_desktop_direction
+ action_desktop_dir,
+ setup_action_desktop_right
},
{
"desktopleft",
- action_desktop_left,
- setup_action_desktop_direction
+ action_desktop_dir,
+ setup_action_desktop_left
},
{
"desktopup",
- action_desktop_up,
- setup_action_desktop_direction
+ action_desktop_dir,
+ setup_action_desktop_up
},
{
"desktopdown",
- action_desktop_down,
- setup_action_desktop_direction
+ action_desktop_dir,
+ setup_action_desktop_down
},
{
"toggledecorations",
@@ -529,9 +621,9 @@ ActionString actionstrings[] =
}
};
-Action *action_from_string(char *name)
+ObAction *action_from_string(char *name)
{
- Action *a = NULL;
+ ObAction *a = NULL;
int i;
for (i = 0; actionstrings[i].name; i++)
@@ -544,10 +636,10 @@ Action *action_from_string(char *name)
return a;
}
-Action *action_parse(xmlDocPtr doc, xmlNodePtr node)
+ObAction *action_parse(xmlDocPtr doc, xmlNodePtr node)
{
char *actname;
- Action *act = NULL;
+ ObAction *act = NULL;
xmlNodePtr n;
if (parse_attr_string("name", node, &actname)) {
@@ -572,21 +664,13 @@ Action *action_parse(xmlDocPtr doc, xmlNodePtr node)
act->func == action_resize_relative_vert) {
if ((n = parse_find_node("delta", node->xmlChildrenNode)))
act->data.relative.delta = parse_int(doc, n);
- } else if (act->func == action_desktop_right ||
- act->func == action_desktop_left ||
- act->func == action_desktop_up ||
- act->func == action_desktop_down) {
+ } else if (act->func == action_desktop_dir) {
if ((n = parse_find_node("wrap", node->xmlChildrenNode))) {
act->data.desktopdir.wrap = parse_bool(doc, n);
}
- } else if (act->func == action_send_to_desktop_right ||
- act->func == action_send_to_desktop_left ||
- act->func == action_send_to_desktop_up ||
- act->func == action_send_to_desktop_down) {
+ } else if (act->func == action_send_to_desktop_dir) {
if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
act->data.sendtodir.wrap = parse_bool(doc, n);
- if ((n = parse_find_node("follow", node->xmlChildrenNode)))
- act->data.sendtodir.follow = parse_bool(doc, n);
}
}
g_free(actname);
@@ -802,8 +886,8 @@ void action_send_to_desktop(union ActionData *data)
if (data->sendto.desk < screen_num_desktops ||
data->sendto.desk == DESKTOP_ALL) {
- client_set_desktop(c, data->sendto.desk, data->sendto.follow);
- if (data->sendto.follow) screen_set_desktop(data->sendto.desk);
+ client_set_desktop(c, data->sendto.desk, TRUE);
+ screen_set_desktop(data->sendto.desk);
}
}
@@ -814,114 +898,34 @@ void action_desktop(union ActionData *data)
screen_set_desktop(data->desktop.desk);
}
-static void cur_row_col(guint *r, guint *c)
+void action_desktop_dir(union ActionData *data)
{
- switch (screen_desktop_layout.orientation) {
- case OB_ORIENTATION_HORZ:
- switch (screen_desktop_layout.start_corner) {
- case OB_CORNER_TOPLEFT:
- *r = screen_desktop / screen_desktop_layout.columns;
- *c = screen_desktop % screen_desktop_layout.columns;
- break;
- case OB_CORNER_BOTTOMLEFT:
- *r = screen_desktop_layout.rows - 1 -
- screen_desktop / screen_desktop_layout.columns;
- *c = screen_desktop % screen_desktop_layout.columns;
- break;
- case OB_CORNER_TOPRIGHT:
- *r = screen_desktop / screen_desktop_layout.columns;
- *c = screen_desktop_layout.columns - 1 -
- screen_desktop % screen_desktop_layout.columns;
- break;
- case OB_CORNER_BOTTOMRIGHT:
- *r = screen_desktop_layout.rows - 1 -
- screen_desktop / screen_desktop_layout.columns;
- *c = screen_desktop_layout.columns - 1 -
- screen_desktop % screen_desktop_layout.columns;
- break;
- }
- break;
- case OB_ORIENTATION_VERT:
- switch (screen_desktop_layout.start_corner) {
- case OB_CORNER_TOPLEFT:
- *r = screen_desktop % screen_desktop_layout.rows;
- *c = screen_desktop / screen_desktop_layout.rows;
- break;
- case OB_CORNER_BOTTOMLEFT:
- *r = screen_desktop_layout.rows - 1 -
- screen_desktop % screen_desktop_layout.rows;
- *c = screen_desktop / screen_desktop_layout.rows;
- break;
- case OB_CORNER_TOPRIGHT:
- *r = screen_desktop % screen_desktop_layout.rows;
- *c = screen_desktop_layout.columns - 1 -
- screen_desktop / screen_desktop_layout.rows;
- break;
- case OB_CORNER_BOTTOMRIGHT:
- *r = screen_desktop_layout.rows - 1 -
- screen_desktop % screen_desktop_layout.rows;
- *c = screen_desktop_layout.columns - 1 -
- screen_desktop / screen_desktop_layout.rows;
- break;
- }
- break;
- }
+ guint d;
+
+ d = screen_cycle_desktop(data->desktopdir.dir, data->desktopdir.wrap,
+ data->sendtodir.linear,
+ data->desktopdir.final, data->desktopdir.cancel);
+ screen_set_desktop(d);
}
-static guint translate_row_col(guint r, guint c)
-{
- switch (screen_desktop_layout.orientation) {
- case OB_ORIENTATION_HORZ:
- switch (screen_desktop_layout.start_corner) {
- case OB_CORNER_TOPLEFT:
- return r % screen_desktop_layout.rows *
- screen_desktop_layout.columns +
- c % screen_desktop_layout.columns;
- case OB_CORNER_BOTTOMLEFT:
- return (screen_desktop_layout.rows - 1 -
- r % screen_desktop_layout.rows) *
- screen_desktop_layout.columns +
- c % screen_desktop_layout.columns;
- case OB_CORNER_TOPRIGHT:
- return r % screen_desktop_layout.rows *
- screen_desktop_layout.columns +
- (screen_desktop_layout.columns - 1 -
- c % screen_desktop_layout.columns);
- case OB_CORNER_BOTTOMRIGHT:
- return (screen_desktop_layout.rows - 1 -
- r % screen_desktop_layout.rows) *
- screen_desktop_layout.columns +
- (screen_desktop_layout.columns - 1 -
- c % screen_desktop_layout.columns);
- }
- case OB_ORIENTATION_VERT:
- switch (screen_desktop_layout.start_corner) {
- case OB_CORNER_TOPLEFT:
- return c % screen_desktop_layout.columns *
- screen_desktop_layout.rows +
- r % screen_desktop_layout.rows;
- case OB_CORNER_BOTTOMLEFT:
- return c % screen_desktop_layout.columns *
- screen_desktop_layout.rows +
- (screen_desktop_layout.rows - 1 -
- r % screen_desktop_layout.rows);
- case OB_CORNER_TOPRIGHT:
- return (screen_desktop_layout.columns - 1 -
- c % screen_desktop_layout.columns) *
- screen_desktop_layout.rows +
- r % screen_desktop_layout.rows;
- case OB_CORNER_BOTTOMRIGHT:
- return (screen_desktop_layout.columns - 1 -
- c % screen_desktop_layout.columns) *
- screen_desktop_layout.rows +
- (screen_desktop_layout.rows - 1 -
- r % screen_desktop_layout.rows);
- }
- }
- g_assert_not_reached();
- return 0;
+void action_send_to_desktop_dir(union ActionData *data)
+{
+ ObClient *c = data->sendtodir.c;
+ guint d;
+
+ if (!c || !client_normal(c)) return;
+
+ d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
+ data->sendtodir.linear,
+ data->sendtodir.final, data->sendtodir.cancel);
+
+ g_message("sendto %d", d);
+
+ client_set_desktop(c, d, TRUE);
+ screen_set_desktop(d);
}
+#if 0
void action_desktop_right(union ActionData *data)
{
guint r, c, d;
@@ -939,7 +943,8 @@ void action_desktop_right(union ActionData *data)
}
d = translate_row_col(r, c);
if (d < screen_num_desktops)
- screen_set_desktop(d);
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
void action_send_to_desktop_right(union ActionData *data)
@@ -963,7 +968,9 @@ void action_send_to_desktop_right(union ActionData *data)
d = translate_row_col(r, c);
if (d < screen_num_desktops) {
client_set_desktop(cl, d, data->sendtodir.follow);
- if (data->sendtodir.follow) screen_set_desktop(d);
+ if (data->sendtodir.follow)
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
}
@@ -984,7 +991,8 @@ void action_desktop_left(union ActionData *data)
}
d = translate_row_col(r, c);
if (d < screen_num_desktops)
- screen_set_desktop(d);
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
void action_send_to_desktop_left(union ActionData *data)
@@ -1008,7 +1016,9 @@ void action_send_to_desktop_left(union ActionData *data)
d = translate_row_col(r, c);
if (d < screen_num_desktops) {
client_set_desktop(cl, d, data->sendtodir.follow);
- if (data->sendtodir.follow) screen_set_desktop(d);
+ if (data->sendtodir.follow)
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
}
@@ -1029,7 +1039,8 @@ void action_desktop_down(union ActionData *data)
}
d = translate_row_col(r, c);
if (d < screen_num_desktops)
- screen_set_desktop(d);
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
void action_send_to_desktop_down(union ActionData *data)
@@ -1051,7 +1062,9 @@ void action_send_to_desktop_down(union ActionData *data)
d = translate_row_col(r, c);
if (d < screen_num_desktops) {
client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
- if (data->sendtodir.follow) screen_set_desktop(d);
+ if (data->sendtodir.follow)
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
}
}
@@ -1073,7 +1086,8 @@ void action_desktop_up(union ActionData *data)
}
d = translate_row_col(r, c);
if (d < screen_num_desktops)
- screen_set_desktop(d);
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
void action_send_to_desktop_up(union ActionData *data)
@@ -1095,10 +1109,13 @@ void action_send_to_desktop_up(union ActionData *data)
d = translate_row_col(r, c);
if (d < screen_num_desktops) {
client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
- if (data->sendtodir.follow) screen_set_desktop(d);
+ if (data->sendtodir.follow)
+ screen_cycle_desktop(d, data->desktopdir.final,
+ data->desktopdir.cancel);
}
}
}
+#endif
void action_toggle_decorations(union ActionData *data)
{
diff --git a/openbox/action.h b/openbox/action.h
index 3abf7f2b..3ac3ee67 100644
--- a/openbox/action.h
+++ b/openbox/action.h
@@ -4,6 +4,8 @@
#include "misc.h"
#include "parser/parse.h"
+typedef struct _ObAction ObAction;
+
/* These have to all have a Client* at the top even if they don't use it, so
that I can set it blindly later on. So every function will have a Client*
available (possibly NULL though) if it wants it.
@@ -40,8 +42,11 @@ struct SendToDesktop {
struct SendToDesktopDirection {
struct _ObClient *c;
+ ObDirection dir;
gboolean wrap;
- gboolean follow;
+ gboolean linear;
+ gboolean final;
+ gboolean cancel;
};
struct Desktop {
@@ -56,7 +61,11 @@ struct Layer {
struct DesktopDirection {
struct _ObClient *c;
+ ObDirection dir;
gboolean wrap;
+ gboolean linear;
+ gboolean final;
+ gboolean cancel;
};
struct MoveResize {
@@ -98,15 +107,15 @@ union ActionData {
struct Layer layer;
};
-typedef struct {
+struct _ObAction {
/* The func member acts like an enum to tell which one of the structs in
the data union are valid.
*/
void (*func)(union ActionData *data);
union ActionData data;
-} Action;
+};
-Action *action_new(void (*func)(union ActionData *data));
+ObAction *action_new(void (*func)(union ActionData *data));
/* Creates a new Action from the name of the action
A few action types need data set after making this call still. Check if
@@ -120,9 +129,9 @@ Action *action_new(void (*func)(union ActionData *data));
action_resize_relative_vert - the delta
*/
-Action *action_from_string(char *name);
-Action *action_parse(xmlDocPtr doc, xmlNodePtr node);
-void action_free(Action *a);
+ObAction *action_from_string(char *name);
+ObAction *action_parse(xmlDocPtr doc, xmlNodePtr node);
+void action_free(ObAction *a);
/* Execute */
void action_execute(union ActionData *data);
@@ -181,23 +190,11 @@ void action_toggle_maximize_vert(union ActionData *data);
/* SendToDesktop */
void action_send_to_desktop(union ActionData *data);
/* SendToDesktopDirection */
-void action_send_to_desktop_right(union ActionData *data);
-/* SendToDesktopDirection */
-void action_send_to_desktop_left(union ActionData *data);
-/* SendToDesktopDirection */
-void action_send_to_desktop_up(union ActionData *data);
-/* SendToDesktopDirection */
-void action_send_to_desktop_down(union ActionData *data);
+void action_send_to_desktop_dir(union ActionData *data);
/* Desktop */
void action_desktop(union ActionData *data);
/* DesktopDirection */
-void action_desktop_right(union ActionData *data);
-/* DesktopDirection */
-void action_desktop_left(union ActionData *data);
-/* DesktopDirection */
-void action_desktop_up(union ActionData *data);
-/* DesktopDirection */
-void action_desktop_down(union ActionData *data);
+void action_desktop_dir(union ActionData *data);
/* ClientAction */
void action_toggle_decorations(union ActionData *data);
/* MoveResize */
diff --git a/openbox/client.c b/openbox/client.c
index 191fa994..b5e2a3cf 100644
--- a/openbox/client.c
+++ b/openbox/client.c
@@ -18,6 +18,8 @@
#include "group.h"
#include "config.h"
#include "menu.h"
+#include "keyboard.h"
+#include "mouse.h"
#include "render/render.h"
#include <glib.h>
@@ -327,6 +329,9 @@ void client_manage(Window window)
/* update the list hints */
client_set_list();
+ keyboard_grab_for_client(self, TRUE);
+ mouse_grab_for_client(self, TRUE);
+
dispatch_client(Event_Client_Mapped, self, 0, 0);
ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
@@ -355,6 +360,9 @@ void client_unmanage(ObClient *self)
dispatch_client(Event_Client_Destroy, self, 0, 0);
g_assert(self != NULL);
+ keyboard_grab_for_client(self, FALSE);
+ mouse_grab_for_client(self, FALSE);
+
/* remove the window from our save set */
XChangeSaveSet(ob_display, self->window, SetModeDelete);
@@ -1097,7 +1105,7 @@ void client_setup_decor_and_functions(ObClient *self)
/* finally, the user can have requested no decorations, which overrides
everything */
if (!self->decorate)
- self->decorations = 0;
+ self->decorations = OB_FRAME_DECOR_BORDER;
/* if we don't have a titlebar, then we cannot shade! */
if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
@@ -1705,7 +1713,7 @@ void client_configure_full(ObClient *self, ObCorner anchor,
gboolean force_reply)
{
gboolean moved = FALSE, resized = FALSE;
- gint fdecor = self->frame->decorations;
+ guint fdecor = self->frame->decorations;
/* make the frame recalculate its dimentions n shit without changing
anything visible for real, this way the constraints below can work with
diff --git a/openbox/config.c b/openbox/config.c
index 1ffc155a..5444e013 100644
--- a/openbox/config.c
+++ b/openbox/config.c
@@ -1,4 +1,7 @@
#include "config.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "prop.h"
#include "parser/parse.h"
gboolean config_focus_new;
@@ -25,6 +28,151 @@ ObOrientation config_dock_orient;
gboolean config_dock_hide;
guint config_dock_hide_timeout;
+gint config_mouse_threshold;
+gint config_mouse_dclicktime;
+
+/*
+
+<keybind key="C-x">
+ <action name="ChangeDesktop">
+ <desktop>3</desktop>
+ </action>
+</keybind>
+
+*/
+
+static void parse_key(xmlDocPtr doc, xmlNodePtr node, GList *keylist)
+{
+ char *key;
+ ObAction *action;
+ xmlNodePtr n, nact;
+ GList *it;
+
+ n = parse_find_node("keybind", node);
+ while (n) {
+ if (parse_attr_string("key", n, &key)) {
+ keylist = g_list_append(keylist, key);
+
+ parse_key(doc, n->xmlChildrenNode, keylist);
+
+ it = g_list_last(keylist);
+ g_free(it->data);
+ keylist = g_list_delete_link(keylist, it);
+ }
+ n = parse_find_node("keybind", n->next);
+ }
+ if (keylist) {
+ nact = parse_find_node("action", node);
+ while (nact) {
+ if ((action = action_parse(doc, nact))) {
+ /* validate that its okay for a key binding */
+ if (action->func == action_moveresize &&
+ action->data.moveresize.corner !=
+ prop_atoms.net_wm_moveresize_move_keyboard &&
+ action->data.moveresize.corner !=
+ prop_atoms.net_wm_moveresize_size_keyboard) {
+ action_free(action);
+ action = NULL;
+ }
+
+ if (action)
+ keyboard_bind(keylist, action);
+ }
+ nact = parse_find_node("action", nact->next);
+ }
+ }
+}
+
+static void parse_keyboard(xmlDocPtr doc, xmlNodePtr node, void *d)
+{
+ parse_key(doc, node->xmlChildrenNode, NULL);
+}
+
+static int threshold;
+static int dclicktime;
+/*
+
+<context name="Titlebar">
+ <mousebind button="Left" action="Press">
+ <action name="Raise"></action>
+ </mousebind>
+</context>
+
+*/
+
+static void parse_mouse(xmlDocPtr doc, xmlNodePtr node, void *d)
+{
+ xmlNodePtr n, nbut, nact;
+ char *buttonstr;
+ char *contextstr;
+ ObMouseAction mact;
+ ObAction *action;
+
+ node = node->xmlChildrenNode;
+
+ if ((n = parse_find_node("dragThreshold", node)))
+ threshold = parse_int(doc, n);
+ if ((n = parse_find_node("doubleClickTime", node)))
+ dclicktime = parse_int(doc, n);
+
+ n = parse_find_node("context", node);
+ while (n) {
+ if (!parse_attr_string("name", n, &contextstr))
+ goto next_n;
+ nbut = parse_find_node("mousebind", n->xmlChildrenNode);
+ while (nbut) {
+ if (!parse_attr_string("button", nbut, &buttonstr))
+ goto next_nbut;
+ if (parse_attr_contains("press", nbut, "action"))
+ mact = MouseAction_Press;
+ else if (parse_attr_contains("release", nbut, "action"))
+ mact = MouseAction_Release;
+ else if (parse_attr_contains("click", nbut, "action"))
+ mact = MouseAction_Click;
+ else if (parse_attr_contains("doubleclick", nbut,"action"))
+ mact = MouseAction_DClick;
+ else if (parse_attr_contains("drag", nbut, "action"))
+ mact = MouseAction_Motion;
+ else
+ goto next_nbut;
+ nact = parse_find_node("action", nbut->xmlChildrenNode);
+ while (nact) {
+ if ((action = action_parse(doc, nact))) {
+ /* validate that its okay for a mouse binding*/
+ if (mact == MouseAction_Motion) {
+ if (action->func != action_moveresize ||
+ action->data.moveresize.corner ==
+ prop_atoms.net_wm_moveresize_move_keyboard ||
+ action->data.moveresize.corner ==
+ prop_atoms.net_wm_moveresize_size_keyboard) {
+ action_free(action);
+ action = NULL;
+ }
+ } else {
+ if (action->func == action_moveresize &&
+ action->data.moveresize.corner !=
+ prop_atoms.net_wm_moveresize_move_keyboard &&
+ action->data.moveresize.corner !=
+ prop_atoms.net_wm_moveresize_size_keyboard) {
+ action_free(action);
+ action = NULL;
+ }
+ }
+ if (action)
+ mouse_bind(buttonstr, contextstr, mact, action);
+ }
+ nact = parse_find_node("action", nact->next);
+ }
+ g_free(buttonstr);
+ next_nbut:
+ nbut = parse_find_node("mousebind", nbut->next);
+ }
+ g_free(contextstr);
+ next_n:
+ n = parse_find_node("context", n->next);
+ }
+}
+
static void parse_focus(xmlDocPtr doc, xmlNodePtr node, void *d)
{
xmlNodePtr n;
@@ -190,6 +338,13 @@ void config_startup()
config_dock_hide_timeout = 3000;
parse_register("dock", parse_dock, NULL);
+
+ parse_register("keyboard", parse_keyboard, NULL);
+
+ config_mouse_threshold = 3;
+ config_mouse_dclicktime = 200;
+
+ parse_register("mouse", parse_mouse, NULL);
}
void config_shutdown()
diff --git a/openbox/config.h b/openbox/config.h
index d10617d0..413ff184 100644
--- a/openbox/config.h
+++ b/openbox/config.h
@@ -52,6 +52,11 @@ extern int config_desktops_num;
/*! Names for the desktops */
extern GSList *config_desktops_names;
+/*! Number of pixels a drag must go before being considered a drag */
+extern gint config_mouse_threshold;
+/*! Number of milliseconds within which 2 clicks must occur to be a
+ double-click */
+extern gint config_mouse_dclicktime;
void config_startup();
void config_shutdown();
diff --git a/openbox/event.c b/openbox/event.c
index b5e15a3c..97f01fb6 100644
--- a/openbox/event.c
+++ b/openbox/event.c
@@ -8,6 +8,8 @@
#include "screen.h"
#include "frame.h"
#include "menu.h"
+#include "keyboard.h"
+#include "mouse.h"
#include "framerender.h"
#include "focus.h"
#include "moveresize.h"
@@ -541,13 +543,23 @@ static void event_process(XEvent *e)
}
/* user input (action-bound) events */
- /*
if (e->type == ButtonPress || e->type == ButtonRelease ||
- e->type == MotionNotify)
- mouse_event(e, client);
- else if (e->type == KeyPress || e->type == KeyRelease)
- ;
- */
+ e->type == MotionNotify || e->type == KeyPress ||
+ e->type == KeyRelease)
+ {
+ ObFrameContext context;
+
+ context = frame_context(client, e->xany.window);
+
+ if (!keyboard_process_interactive_grab(e, &client, &context)) {
+
+ if (e->type == ButtonPress || e->type == ButtonRelease ||
+ e->type == MotionNotify)
+ mouse_event(client, context, e);
+ else if (e->type == KeyPress)
+ keyboard_event(client, e);
+ }
+ }
/* dispatch the event to registered handlers */
dispatch_x(e, client);
diff --git a/openbox/focus.c b/openbox/focus.c
index 1642261d..9949dea3 100644
--- a/openbox/focus.c
+++ b/openbox/focus.c
@@ -262,7 +262,7 @@ static void popup_cycle(ObClient *c, gboolean show)
}
ObClient *focus_cycle(gboolean forward, gboolean linear, gboolean done,
- gboolean cancel)
+ gboolean cancel)
{
static ObClient *first = NULL;
static ObClient *t = NULL;
@@ -281,8 +281,6 @@ ObClient *focus_cycle(gboolean forward, gboolean linear, gboolean done,
client_activate(focus_cycle_target);
goto done_cycle;
}
- if (!first)
- grab_pointer(TRUE, None);
if (!first) first = focus_client;
if (!focus_cycle_target) focus_cycle_target = focus_client;
@@ -331,7 +329,6 @@ done_cycle:
order = NULL;
popup_cycle(ft, FALSE);
- grab_pointer(FALSE, None);
return NULL;
}
diff --git a/openbox/keyboard.c b/openbox/keyboard.c
new file mode 100644
index 00000000..607941dd
--- /dev/null
+++ b/openbox/keyboard.c
@@ -0,0 +1,265 @@
+#include "focus.h"
+#include "screen.h"
+#include "frame.h"
+#include "openbox.h"
+#include "event.h"
+#include "grab.h"
+#include "client.h"
+#include "action.h"
+#include "prop.h"
+#include "timer.h"
+#include "keytree.h"
+#include "keyboard.h"
+#include "translate.h"
+
+#include <glib.h>
+
+KeyBindingTree *keyboard_firstnode;
+
+static KeyBindingTree *curpos;
+static ObTimer *chain_timer;
+static gboolean interactive_grab;
+static guint grabbed_state;
+static ObClient *grabbed_client;
+static ObAction *grabbed_action;
+static ObFrameContext grabbed_context;
+
+static void grab_for_window(Window win, gboolean grab)
+{
+ KeyBindingTree *p;
+
+ ungrab_all_keys(win);
+
+ if (grab) {
+ p = curpos ? curpos->first_child : keyboard_firstnode;
+ while (p) {
+ grab_key(p->key, p->state, win, GrabModeAsync);
+ p = p->next_sibling;
+ }
+ }
+}
+
+void keyboard_grab_for_client(ObClient *c, gboolean grab)
+{
+ grab_for_window(c->window, grab);
+}
+
+static void grab_keys(gboolean grab)
+{
+ GList *it;
+
+ grab_for_window(screen_support_win, grab);
+ for (it = client_list; it; it = g_list_next(it))
+ grab_for_window(((ObClient*)it->data)->frame->window, grab);
+}
+
+void keyboard_reset_chains()
+{
+ if (chain_timer) {
+ timer_stop(chain_timer);
+ chain_timer = NULL;
+ }
+ if (curpos) {
+ curpos = NULL;
+ grab_keys(TRUE);
+ }
+}
+
+static void chain_timeout(ObTimer *t, void *data)
+{
+ keyboard_reset_chains();
+}
+
+gboolean keyboard_bind(GList *keylist, ObAction *action)
+{
+ KeyBindingTree *tree, *t;
+ gboolean conflict;
+
+ g_assert(keylist != NULL);
+ g_assert(action != NULL);
+
+ if (!(tree = tree_build(keylist)))
+ return FALSE;
+
+ if ((t = tree_find(tree, &conflict)) != NULL) {
+ /* already bound to something, use the existing tree */
+ tree_destroy(tree);
+ tree = NULL;
+ } else
+ t = tree;
+ while (t->first_child) t = t->first_child;
+
+ if (conflict) {
+ g_warning("conflict with binding");
+ tree_destroy(tree);
+ return FALSE;
+ }
+
+ /* set the action */
+ t->actions = g_slist_append(t->actions, action);
+ /* assimilate this built tree into the main tree. assimilation
+ destroys/uses the tree */
+ if (tree) tree_assimilate(tree);
+
+ return TRUE;
+}
+
+void keyboard_interactive_grab(guint state, ObClient *client,
+ ObFrameContext context, ObAction *action)
+{
+ if (!interactive_grab && grab_keyboard(TRUE)) {
+ interactive_grab = TRUE;
+ grabbed_state = state;
+ grabbed_client = client;
+ grabbed_action = action;
+ grabbed_context = context;
+ grab_pointer(TRUE, None);
+ }
+}
+
+gboolean keyboard_process_interactive_grab(const XEvent *e,
+ ObClient **client,
+ ObFrameContext *context)
+{
+ gboolean handled = FALSE;
+ gboolean done = FALSE;
+
+ if (interactive_grab) {
+ *client = grabbed_client;
+ *context = grabbed_context;
+ }
+
+ if ((e->type == KeyRelease &&
+ !(grabbed_state & e->xkey.state)))
+ done = TRUE;
+ else if (e->type == KeyPress) {
+ if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
+ done = TRUE;
+ else if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
+ if (grabbed_action->func == action_cycle_windows) {
+ grabbed_action->data.cycle.cancel = TRUE;
+ }
+ if (grabbed_action->func == action_desktop_dir) {
+ grabbed_action->data.desktopdir.cancel = TRUE;
+ }
+ if (grabbed_action->func == action_send_to_desktop_dir)
+ {
+ grabbed_action->data.sendtodir.cancel = TRUE;
+ }
+ done = TRUE;
+ }
+ }
+ if (done) {
+ if (grabbed_action->func == action_cycle_windows) {
+ grabbed_action->data.cycle.final = TRUE;
+ }
+ if (grabbed_action->func == action_desktop_dir) {
+ grabbed_action->data.desktopdir.final = TRUE;
+ }
+ if (grabbed_action->func == action_send_to_desktop_dir) {
+ grabbed_action->data.sendtodir.final = TRUE;
+ }
+
+ grabbed_action->func(&grabbed_action->data);
+
+ interactive_grab = FALSE;
+ grab_keyboard(FALSE);
+ grab_pointer(FALSE, None);
+ keyboard_reset_chains();
+
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+void keyboard_event(ObClient *client, const XEvent *e)
+{
+ KeyBindingTree *p;
+
+ g_assert(e->type == KeyPress);
+
+ if (curpos == NULL)
+ p = keyboard_firstnode;
+ else
+ p = curpos->first_child;
+ while (p) {
+ if (p->key == e->xkey.keycode &&
+ p->state == e->xkey.state) {
+ if (p->first_child != NULL) { /* part of a chain */
+ if (chain_timer) timer_stop(chain_timer);
+ /* 5 second timeout for chains */
+ chain_timer = timer_start(5000*1000, chain_timeout,
+ NULL);
+ curpos = p;
+ grab_keys(TRUE);
+ } else {
+ GSList *it;
+ for (it = p->actions; it; it = it->next) {
+ ObAction *act = it->data;
+ if (act->func != NULL) {
+ act->data.any.c = client;
+
+ if (act->func == action_cycle_windows)
+ {
+ act->data.cycle.final = FALSE;
+ act->data.cycle.cancel = FALSE;
+ }
+ if (act->func == action_desktop_dir)
+ {
+ act->data.desktopdir.final = FALSE;
+ act->data.desktopdir.cancel = FALSE;
+ }
+ if (act->func == action_send_to_desktop_dir)
+ {
+ act->data.sendtodir.final = FALSE;
+ act->data.sendtodir.cancel = FALSE;
+ }
+
+ if (act->func == action_moveresize)
+ {
+ screen_pointer_pos(&act->data.moveresize.x,
+ &act->data.moveresize.y);
+ }
+
+ if ((act->func == action_cycle_windows ||
+ act->func == action_desktop_dir ||
+ act->func == action_send_to_desktop_dir))
+ {
+ keyboard_interactive_grab(e->xkey.state, client,
+ 0, act);
+ }
+
+ if (act->func == action_showmenu)
+ {
+ act->data.showmenu.x =
+ e->xkey.x_root;
+ act->data.showmenu.y =
+ e->xkey.y_root;
+ }
+
+ act->data.any.c = client;
+ act->func(&act->data);
+ }
+ }
+
+ keyboard_reset_chains();
+ }
+ break;
+ }
+ p = p->next_sibling;
+ }
+}
+
+void keyboard_startup()
+{
+ grab_keys(TRUE);
+}
+
+void keyboard_shutdown()
+{
+ tree_destroy(keyboard_firstnode);
+ keyboard_firstnode = NULL;
+ grab_keys(FALSE);
+}
+
diff --git a/openbox/keyboard.h b/openbox/keyboard.h
new file mode 100644
index 00000000..aa171221
--- /dev/null
+++ b/openbox/keyboard.h
@@ -0,0 +1,32 @@
+#ifndef ob__keybaord_h
+#define ob__keybaord_h
+
+#include "keytree.h"
+#include "frame.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+struct _ObClient;
+struct _ObAction;
+
+extern KeyBindingTree *keyboard_firstnode;
+
+void keyboard_startup();
+void keyboard_shutdown();
+
+gboolean keyboard_bind(GList *keylist, ObAction *action);
+
+void keyboard_event(struct _ObClient *client, const XEvent *e);
+void keyboard_reset_chains();
+
+void keyboard_interactive_grab(guint state, struct _ObClient *client,
+ ObFrameContext context,
+ struct _ObAction *action);
+gboolean keyboard_process_interactive_grab(const XEvent *e,
+ struct _ObClient **client,
+ ObFrameContext *context);
+
+void keyboard_grab_for_client(struct _ObClient *c, gboolean grab);
+
+#endif
diff --git a/openbox/keytree.c b/openbox/keytree.c
new file mode 100644
index 00000000..9aa2ea95
--- /dev/null
+++ b/openbox/keytree.c
@@ -0,0 +1,112 @@
+#include "keyboard.h"
+#include "translate.h"
+#include <glib.h>
+
+void tree_destroy(KeyBindingTree *tree)
+{
+ KeyBindingTree *c;
+
+ while (tree) {
+ tree_destroy(tree->next_sibling);
+ c = tree->first_child;
+ if (c == NULL) {
+ GList *it;
+ GSList *sit;
+ for (it = tree->keylist; it != NULL; it = it->next)
+ g_free(it->data);
+ g_list_free(tree->keylist);
+ for (sit = tree->actions; sit != NULL; sit = sit->next)
+ action_free(sit->data);
+ g_slist_free(tree->actions);
+ }
+ g_free(tree);
+ tree = c;
+ }
+}
+
+KeyBindingTree *tree_build(GList *keylist)
+{
+ GList *it;
+ KeyBindingTree *ret = NULL, *p;
+
+ if (g_list_length(keylist) <= 0)
+ return NULL; /* nothing in the list.. */
+
+ for (it = g_list_last(keylist); it != NULL; it = it->prev) {
+ p = ret;
+ ret = g_new0(KeyBindingTree, 1);
+ if (p == NULL) {
+ GList *it;
+
+ /* this is the first built node, the bottom node of the tree */
+ ret->keylist = g_list_copy(keylist); /* shallow copy */
+ for (it = ret->keylist; it != NULL; it = it->next) /* deep copy */
+ it->data = g_strdup(it->data);
+ }
+ ret->first_child = p;
+ if (!translate_key(it->data, &ret->state, &ret->key)) {
+ tree_destroy(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+void tree_assimilate(KeyBindingTree *node)
+{
+ KeyBindingTree *a, *b, *tmp, *last;
+
+ if (keyboard_firstnode == NULL) {
+ /* there are no nodes at this level yet */
+ keyboard_firstnode = node;
+ } else {
+ a = keyboard_firstnode;
+ last = a;
+ b = node;
+ while (a) {
+ last = a;
+ if (!(a->state == b->state && a->key == b->key)) {
+ a = a->next_sibling;
+ } else {
+ tmp = b;
+ b = b->first_child;
+ g_free(tmp);
+ a = a->first_child;
+ }
+ }
+ if (!(last->state == b->state && last->key == b->key))
+ last->next_sibling = b;
+ else {
+ last->first_child = b->first_child;
+ g_free(b);
+ }
+ }
+}
+
+KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict)
+{
+ KeyBindingTree *a, *b;
+
+ *conflict = FALSE;
+
+ a = keyboard_firstnode;
+ b = search;
+ while (a && b) {
+ if (!(a->state == b->state && a->key == b->key)) {
+ a = a->next_sibling;
+ } else {
+ if ((a->first_child == NULL) == (b->first_child == NULL)) {
+ if (a->first_child == NULL) {
+ /* found it! (return the actual node, not the search's) */
+ return a;
+ }
+ } else {
+ *conflict = TRUE;
+ return NULL; /* the chain status' don't match (conflict!) */
+ }
+ b = b->first_child;
+ a = a->first_child;
+ }
+ }
+ return NULL; /* it just isn't in here */
+}
diff --git a/openbox/keytree.h b/openbox/keytree.h
new file mode 100644
index 00000000..0c61e478
--- /dev/null
+++ b/openbox/keytree.h
@@ -0,0 +1,25 @@
+#ifndef __plugin_keyboard_tree_h
+#define __plugin_keyboard_tree_h
+
+#include "action.h"
+
+#include <glib.h>
+
+typedef struct KeyBindingTree {
+ guint state;
+ guint key;
+ GList *keylist;
+ GSList *actions; /* list of Action pointers */
+
+ /* the next binding in the tree at the same level */
+ struct KeyBindingTree *next_sibling;
+ /* the first child of this binding (next binding in a chained sequence).*/
+ struct KeyBindingTree *first_child;
+} KeyBindingTree;
+
+void tree_destroy(KeyBindingTree *tree);
+KeyBindingTree *tree_build(GList *keylist);
+void tree_assimilate(KeyBindingTree *node);
+KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict);
+
+#endif
diff --git a/openbox/menu.c b/openbox/menu.c
index 00a2bc2b..0f2eff42 100644
--- a/openbox/menu.c
+++ b/openbox/menu.c
@@ -27,7 +27,7 @@ static void parse_menu(xmlDocPtr doc, xmlNodePtr node, void *data)
void parse_menu_full(xmlDocPtr doc, xmlNodePtr node, void *data,
gboolean newmenu)
{
- Action *act;
+ ObAction *act;
xmlNodePtr nact;
gchar *id = NULL, *title = NULL, *label = NULL, *plugin;
@@ -233,7 +233,7 @@ void menu_free(char *name)
g_hash_table_remove(menu_hash, name);
}
-ObMenuEntry *menu_entry_new_full(char *label, Action *action,
+ObMenuEntry *menu_entry_new_full(char *label, ObAction *action,
ObMenuEntryRenderType render_type,
gpointer submenu)
{
diff --git a/openbox/menu.h b/openbox/menu.h
index 3b7c1c38..2ecbb5cd 100644
--- a/openbox/menu.h
+++ b/openbox/menu.h
@@ -103,7 +103,7 @@ struct _ObMenuEntry
char *label;
ObMenu *parent;
- Action *action;
+ ObAction *action;
ObMenuEntryRenderType render_type;
gboolean hilite;
@@ -155,7 +155,7 @@ void menu_hide(ObMenu *self);
void menu_clear(ObMenu *self);
-ObMenuEntry *menu_entry_new_full(char *label, Action *action,
+ObMenuEntry *menu_entry_new_full(char *label, ObAction *action,
ObMenuEntryRenderType render_type,
gpointer submenu);
diff --git a/openbox/mouse.c b/openbox/mouse.c
new file mode 100644
index 00000000..ac0fb1d2
--- /dev/null
+++ b/openbox/mouse.c
@@ -0,0 +1,372 @@
+#include "openbox.h"
+#include "config.h"
+#include "action.h"
+#include "event.h"
+#include "client.h"
+#include "prop.h"
+#include "grab.h"
+#include "frame.h"
+#include "translate.h"
+#include "mouse.h"
+#include "keyboard.h"
+#include <glib.h>
+
+typedef struct {
+ guint state;
+ guint button;
+ GSList *actions[NUM_MOUSEACTION]; /* lists of Action pointers */
+} ObMouseBinding;
+
+/* Array of GSList*s of PointerBinding*s. */
+static GSList *bound_contexts[OB_FRAME_NUM_CONTEXTS];
+
+void mouse_grab_for_client(ObClient *client, gboolean grab)
+{
+ int i;
+ GSList *it;
+
+ for (i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i)
+ for (it = bound_contexts[i]; it != NULL; it = it->next) {
+ /* grab/ungrab the button */
+ ObMouseBinding *b = it->data;
+ Window win;
+ int mode;
+ unsigned int mask;
+
+ if (i == OB_FRAME_CONTEXT_FRAME) {
+ win = client->frame->window;
+ mode = GrabModeAsync;
+ mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
+ } else if (i == OB_FRAME_CONTEXT_CLIENT) {
+ win = client->frame->plate;
+ mode = GrabModeSync; /* this is handled in event */
+ mask = ButtonPressMask; /* can't catch more than this with Sync
+ mode the release event is
+ manufactured in event() */
+ } else continue;
+
+ if (grab)
+ grab_button_full(b->button, b->state, win, mask, mode, None);
+ else
+ ungrab_button(b->button, b->state, win);
+ }
+}
+
+static void grab_all_clients(gboolean grab)
+{
+ GList *it;
+
+ for (it = client_list; it != NULL; it = it->next)
+ mouse_grab_for_client(it->data, grab);
+}
+
+static void clearall()
+{
+ int i;
+ GSList *it;
+
+ for(i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i) {
+ for (it = bound_contexts[i]; it != NULL; it = it->next) {
+ int j;
+
+ ObMouseBinding *b = it->data;
+ for (j = 0; j < NUM_MOUSEACTION; ++j) {
+ GSList *it;
+ for (it = b->actions[j]; it; it = it->next) {
+ action_free(it->data);
+ }
+ g_slist_free(b->actions[j]);
+ }
+ g_free(b);
+ }
+ g_slist_free(bound_contexts[i]);
+ }
+}
+
+static void fire_button(ObMouseAction a, ObFrameContext context,
+ ObClient *c, guint state,
+ guint button, int x, int y)
+{
+ GSList *it;
+ ObMouseBinding *b;
+
+ g_message("%d %d %d", context, state, button);
+
+ for (it = bound_contexts[context]; it != NULL; it = it->next) {
+ b = it->data;
+ if (b->state == state && b->button == button)
+ break;
+ }
+ /* if not bound, then nothing to do! */
+ if (it == NULL) return;
+
+ for (it = b->actions[a]; it; it = it->next) {
+ ObAction *act = it->data;
+ if (act->func != NULL) {
+ act->data.any.c = c;
+
+ g_assert(act->func != action_moveresize);
+
+ if (act->func == action_showmenu) {
+ act->data.showmenu.x = x;
+ act->data.showmenu.y = y;
+ }
+
+ if (act->func == action_desktop_dir)
+ {
+ act->data.desktopdir.final = FALSE;
+ act->data.desktopdir.cancel = FALSE;
+ }
+ if (act->func == action_send_to_desktop_dir)
+ {
+ act->data.sendtodir.final = FALSE;
+ act->data.sendtodir.cancel = FALSE;
+ }
+
+ if ((act->func == action_desktop_dir ||
+ act->func == action_send_to_desktop_dir)) {
+ keyboard_interactive_grab(state, c, context, act);
+ }
+
+ g_message("acting");
+
+ act->func(&act->data);
+ }
+ }
+}
+
+static void fire_motion(ObMouseAction a, ObFrameContext context, ObClient *c,
+ guint state, guint button, int x_root, int y_root,
+ guint32 corner)
+{
+ GSList *it;
+ ObMouseBinding *b;
+
+ for (it = bound_contexts[context]; it != NULL; it = it->next) {
+ b = it->data;
+ if (b->state == state && b->button == button)
+ break;
+ }
+ /* if not bound, then nothing to do! */
+ if (it == NULL) return;
+
+ for (it = b->actions[a]; it; it = it->next) {
+ ObAction *act = it->data;
+ if (act->func != NULL) {
+ act->data.any.c = c;
+
+ if (act->func == action_moveresize) {
+ act->data.moveresize.x = x_root;
+ act->data.moveresize.y = y_root;
+ act->data.moveresize.button = button;
+ if (!(act->data.moveresize.corner ==
+ prop_atoms.net_wm_moveresize_move ||
+ act->data.moveresize.corner ==
+ prop_atoms.net_wm_moveresize_move_keyboard ||
+ act->data.moveresize.corner ==
+ prop_atoms.net_wm_moveresize_size_keyboard))
+ act->data.moveresize.corner = corner;
+ } else
+ g_assert_not_reached();
+
+ act->func(&act->data);
+ }
+ }
+}
+
+static guint32 pick_corner(int x, int y, int cx, int cy, int cw, int ch)
+{
+ if (x - cx < cw / 2) {
+ if (y - cy < ch / 2)
+ return prop_atoms.net_wm_moveresize_size_topleft;
+ else
+ return prop_atoms.net_wm_moveresize_size_bottomleft;
+ } else {
+ if (y - cy < ch / 2)
+ return prop_atoms.net_wm_moveresize_size_topright;
+ else
+ return prop_atoms.net_wm_moveresize_size_bottomright;
+ }
+}
+
+void mouse_event(ObClient *client, ObFrameContext context, XEvent *e)
+{
+ static Time ltime;
+ static guint button = 0, state = 0, lbutton = 0;
+
+ static Window lwindow = None;
+ static int px, py;
+ gboolean click = FALSE;
+ gboolean dclick = FALSE;
+
+ switch (e->type) {
+ case ButtonPress:
+ px = e->xbutton.x_root;
+ py = e->xbutton.y_root;
+ button = e->xbutton.button;
+ state = e->xbutton.state;
+
+ fire_button(MouseAction_Press, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root, e->xbutton.y_root);
+
+ if (context == OB_FRAME_CONTEXT_CLIENT) {
+ /* Replay the event, so it goes to the client*/
+ XAllowEvents(ob_display, ReplayPointer, event_lasttime);
+ /* Fall through to the release case! */
+ } else
+ break;
+
+ case ButtonRelease:
+ if (e->xbutton.button == button) {
+ /* clicks are only valid if its released over the window */
+ int junk1, junk2;
+ Window wjunk;
+ guint ujunk, b, w, h;
+ XGetGeometry(ob_display, e->xbutton.window,
+ &wjunk, &junk1, &junk2, &w, &h, &b, &ujunk);
+ if (e->xbutton.x >= (signed)-b &&
+ e->xbutton.y >= (signed)-b &&
+ e->xbutton.x < (signed)(w+b) &&
+ e->xbutton.y < (signed)(h+b)) {
+ click = TRUE;
+ /* double clicks happen if there were 2 in a row! */
+ if (lbutton == button &&
+ lwindow == e->xbutton.window &&
+ e->xbutton.time - config_mouse_dclicktime <=
+ ltime) {
+ dclick = TRUE;
+ lbutton = 0;
+ } else {
+ lbutton = button;
+ lwindow = e->xbutton.window;
+ }
+ } else {
+ lbutton = 0;
+ lwindow = None;
+ }
+
+ button = 0;
+ state = 0;
+ ltime = e->xbutton.time;
+ }
+ fire_button(MouseAction_Release, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root, e->xbutton.y_root);
+ if (click)
+ fire_button(MouseAction_Click, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root,
+ e->xbutton.y_root);
+ if (dclick)
+ fire_button(MouseAction_DClick, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root,
+ e->xbutton.y_root);
+ break;
+
+ case MotionNotify:
+ if (button) {
+ if (ABS(e->xmotion.x_root - px) >=
+ config_mouse_threshold ||
+ ABS(e->xmotion.y_root - py) >=
+ config_mouse_threshold) {
+ guint32 corner;
+
+ /* You can't drag on buttons */
+ if (context == OB_FRAME_CONTEXT_MAXIMIZE ||
+ context == OB_FRAME_CONTEXT_ALLDESKTOPS ||
+ context == OB_FRAME_CONTEXT_SHADE ||
+ context == OB_FRAME_CONTEXT_ICONIFY ||
+ context == OB_FRAME_CONTEXT_ICON ||
+ context == OB_FRAME_CONTEXT_CLOSE)
+ break;
+
+ if (!client)
+ corner = prop_atoms.net_wm_moveresize_size_bottomright;
+ else
+ corner =
+ pick_corner(e->xmotion.x_root,
+ e->xmotion.y_root,
+ client->frame->area.x,
+ client->frame->area.y,
+ /* use the client size because the frame
+ can be differently sized (shaded
+ windows) and we want this based on the
+ clients size */
+ client->area.width +
+ client->frame->size.left +
+ client->frame->size.right,
+ client->area.height +
+ client->frame->size.top +
+ client->frame->size.bottom);
+ fire_motion(MouseAction_Motion, context,
+ client, state, button, px, py, corner);
+ button = 0;
+ state = 0;
+ }
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+gboolean mouse_bind(char *buttonstr, char *contextstr, ObMouseAction mact,
+ ObAction *action)
+{
+ guint state, button;
+ ObFrameContext context;
+ ObMouseBinding *b;
+ GSList *it;
+
+ if (!translate_button(buttonstr, &state, &button)) {
+ g_warning("invalid button '%s'", buttonstr);
+ return FALSE;
+ }
+
+ contextstr = g_ascii_strdown(contextstr, -1);
+ context = frame_context_from_string(contextstr);
+ if (!context) {
+ g_warning("invalid context '%s'", contextstr);
+ g_free(contextstr);
+ return FALSE;
+ }
+ g_free(contextstr);
+
+ for (it = bound_contexts[context]; it != NULL; it = it->next){
+ b = it->data;
+ if (b->state == state && b->button == button) {
+ b->actions[mact] = g_slist_append(b->actions[mact], action);
+ return TRUE;
+ }
+ }
+
+ grab_all_clients(FALSE);
+
+ /* add the binding */
+ b = g_new0(ObMouseBinding, 1);
+ b->state = state;
+ b->button = button;
+ b->actions[mact] = g_slist_append(NULL, action);
+ bound_contexts[context] = g_slist_append(bound_contexts[context], b);
+
+ grab_all_clients(TRUE);
+
+ return TRUE;
+}
+
+void mouse_startup()
+{
+}
+
+void mouse_shutdown()
+{
+ grab_all_clients(FALSE);
+ clearall();
+}
diff --git a/openbox/mouse.h b/openbox/mouse.h
new file mode 100644
index 00000000..a5e8f9a8
--- /dev/null
+++ b/openbox/mouse.h
@@ -0,0 +1,28 @@
+#ifndef ob__mouse_h
+#define ob__mouse_h
+
+#include "action.h"
+#include "frame.h"
+
+#include <X11/Xlib.h>
+
+typedef enum {
+ MouseAction_Press,
+ MouseAction_Release,
+ MouseAction_Click,
+ MouseAction_DClick,
+ MouseAction_Motion,
+ NUM_MOUSEACTION
+} ObMouseAction;
+
+void mouse_startup();
+void mouse_shutdown();
+
+gboolean mouse_bind(char *buttonstr, char *contextstr, ObMouseAction mact,
+ ObAction *action);
+
+void mouse_event(struct _ObClient *client, ObFrameContext context, XEvent *e);
+
+void mouse_grab_for_client(struct _ObClient *client, gboolean grab);
+
+#endif
diff --git a/openbox/openbox.c b/openbox/openbox.c
index 7f141d6f..631395cc 100644
--- a/openbox/openbox.c
+++ b/openbox/openbox.c
@@ -13,6 +13,8 @@
#include "focus.h"
#include "moveresize.h"
#include "frame.h"
+#include "keyboard.h"
+#include "mouse.h"
#include "extensions.h"
#include "grab.h"
#include "plugin.h"
@@ -243,6 +245,8 @@ int main(int argc, char **argv)
group_startup();
client_startup();
dock_startup();
+ keyboard_startup();
+ mouse_startup();
/* call startup for all the plugins */
plugin_startall();
@@ -259,6 +263,8 @@ int main(int argc, char **argv)
client_unmanage_all();
plugin_shutdown(); /* calls all the plugins' shutdown functions */
+ mouse_shutdown();
+ keyboard_shutdown();
dock_shutdown();
client_shutdown();
group_shutdown();
diff --git a/openbox/plugin.c b/openbox/plugin.c
index cd17d215..e95d03e9 100644
--- a/openbox/plugin.c
+++ b/openbox/plugin.c
@@ -156,8 +156,6 @@ void plugin_loadall()
if (io == NULL) {
/* load the default plugins */
- plugin_open("keyboard");
- plugin_open("mouse");
plugin_open("placement");
plugin_open("resistance");
diff --git a/openbox/screen.c b/openbox/screen.c
index 4f9e6a2c..6c9042a9 100644
--- a/openbox/screen.c
+++ b/openbox/screen.c
@@ -4,6 +4,7 @@
#include "xerror.h"
#include "prop.h"
#include "startup.h"
+#include "grab.h"
#include "timer.h"
#include "config.h"
#include "screen.h"
@@ -25,6 +26,7 @@
# include <sys/types.h>
# include <unistd.h>
#endif
+#include <assert.h>
/*! The event mask to grab on the root window */
#define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
@@ -45,7 +47,6 @@ static Rect **area; /* array of desktop holding array of xinerama areas */
static Rect *monitor_area;
static Popup *desktop_cycle_popup;
-static ObTimer *popup_timer = NULL;
#ifdef USE_LIBSN
static SnMonitorContext *sn_context;
@@ -420,36 +421,6 @@ void screen_set_num_desktops(guint num)
screen_set_desktop(num - 1);
}
-static void popup_cycle_hide(ObTimer *t, void *d)
-{
- timer_stop(t);
- popup_timer = NULL;
-
- popup_hide(desktop_cycle_popup);
-}
-
-static void popup_cycle_show()
-{
- Rect *a;
-
- a = screen_physical_area_monitor(0);
- popup_position(desktop_cycle_popup, CenterGravity,
- a->x + a->width / 2, a->y + a->height / 2);
- /* XXX the size and the font extents need to be related on some level
- */
- popup_size(desktop_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
-
- popup_set_text_align(desktop_cycle_popup, RR_JUSTIFY_CENTER);
-
- popup_show(desktop_cycle_popup,
- screen_desktop_names[screen_desktop], NULL);
-
- g_message("%s", screen_desktop_names[screen_desktop]);
-
- if (popup_timer) timer_stop(popup_timer);
- popup_timer = timer_start(G_USEC_PER_SEC / 2, popup_cycle_hide, NULL);
-}
-
void screen_set_desktop(guint num)
{
GList *it;
@@ -498,12 +469,250 @@ void screen_set_desktop(guint num)
ob_debug("/switch fallback\n");
#endif
- if (ob_state() == OB_STATE_RUNNING)
- popup_cycle_show();
-
dispatch_ob(Event_Ob_Desktop, num, old);
}
+static void get_row_col(guint d, guint *r, guint *c)
+{
+ switch (screen_desktop_layout.orientation) {
+ case OB_ORIENTATION_HORZ:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ *r = d / screen_desktop_layout.columns;
+ *c = d % screen_desktop_layout.columns;
+ break;
+ case OB_CORNER_BOTTOMLEFT:
+ *r = screen_desktop_layout.rows - 1 -
+ d / screen_desktop_layout.columns;
+ *c = d % screen_desktop_layout.columns;
+ break;
+ case OB_CORNER_TOPRIGHT:
+ *r = d / screen_desktop_layout.columns;
+ *c = screen_desktop_layout.columns - 1 -
+ d % screen_desktop_layout.columns;
+ break;
+ case OB_CORNER_BOTTOMRIGHT:
+ *r = screen_desktop_layout.rows - 1 -
+ d / screen_desktop_layout.columns;
+ *c = screen_desktop_layout.columns - 1 -
+ d % screen_desktop_layout.columns;
+ break;
+ }
+ break;
+ case OB_ORIENTATION_VERT:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ *r = d % screen_desktop_layout.rows;
+ *c = d / screen_desktop_layout.rows;
+ break;
+ case OB_CORNER_BOTTOMLEFT:
+ *r = screen_desktop_layout.rows - 1 -
+ d % screen_desktop_layout.rows;
+ *c = d / screen_desktop_layout.rows;
+ break;
+ case OB_CORNER_TOPRIGHT:
+ *r = d % screen_desktop_layout.rows;
+ *c = screen_desktop_layout.columns - 1 -
+ d / screen_desktop_layout.rows;
+ break;
+ case OB_CORNER_BOTTOMRIGHT:
+ *r = screen_desktop_layout.rows - 1 -
+ d % screen_desktop_layout.rows;
+ *c = screen_desktop_layout.columns - 1 -
+ d / screen_desktop_layout.rows;
+ break;
+ }
+ break;
+ }
+}
+
+static guint translate_row_col(guint r, guint c)
+{
+ switch (screen_desktop_layout.orientation) {
+ case OB_ORIENTATION_HORZ:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ return r % screen_desktop_layout.rows *
+ screen_desktop_layout.columns +
+ c % screen_desktop_layout.columns;
+ case OB_CORNER_BOTTOMLEFT:
+ return (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows) *
+ screen_desktop_layout.columns +
+ c % screen_desktop_layout.columns;
+ case OB_CORNER_TOPRIGHT:
+ return r % screen_desktop_layout.rows *
+ screen_desktop_layout.columns +
+ (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns);
+ case OB_CORNER_BOTTOMRIGHT:
+ return (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows) *
+ screen_desktop_layout.columns +
+ (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns);
+ }
+ case OB_ORIENTATION_VERT:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ return c % screen_desktop_layout.columns *
+ screen_desktop_layout.rows +
+ r % screen_desktop_layout.rows;
+ case OB_CORNER_BOTTOMLEFT:
+ return c % screen_desktop_layout.columns *
+ screen_desktop_layout.rows +
+ (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows);
+ case OB_CORNER_TOPRIGHT:
+ return (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns) *
+ screen_desktop_layout.rows +
+ r % screen_desktop_layout.rows;
+ case OB_CORNER_BOTTOMRIGHT:
+ return (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns) *
+ screen_desktop_layout.rows +
+ (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows);
+ }
+ }
+ g_assert_not_reached();
+ return 0;
+}
+
+static void popup_cycle(guint d, gboolean show)
+{
+ Rect *a;
+
+ if (!show) {
+ popup_hide(desktop_cycle_popup);
+ } else {
+ a = screen_physical_area_monitor(0);
+ popup_position(desktop_cycle_popup, CenterGravity,
+ a->x + a->width / 2, a->y + a->height / 2);
+ /* XXX the size and the font extents need to be related on some level
+ */
+ popup_size(desktop_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
+
+ popup_set_text_align(desktop_cycle_popup, RR_JUSTIFY_CENTER);
+
+ popup_show(desktop_cycle_popup,
+ screen_desktop_names[d], NULL);
+ }
+}
+
+guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
+ gboolean done, gboolean cancel)
+{
+ static gboolean first = TRUE;
+ static gboolean lin;
+ static guint origd, d;
+ guint r, c;
+
+ if (cancel) {
+ d = origd;
+ goto done_cycle;
+ } else if (done) {
+ screen_set_desktop(d);
+ goto done_cycle;
+ }
+ if (first) {
+ first = FALSE;
+ lin = linear;
+ d = origd = screen_desktop;
+ }
+
+ get_row_col(d, &r, &c);
+
+ if (lin) {
+ g_message("linear %d", d);
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ if (d < screen_num_desktops - 1)
+ ++d;
+ else if (wrap)
+ d = 0;
+ break;
+ case OB_DIRECTION_WEST:
+ if (d > 0)
+ --d;
+ else if (wrap)
+ d = screen_num_desktops - 1;
+ break;
+ default:
+ assert(0);
+ return screen_desktop;
+ }
+ g_message("linear %d done", d);
+ } else {
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ ++c;
+ if (c >= screen_desktop_layout.columns) {
+ if (!wrap) return d = screen_desktop;
+ c = 0;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (!wrap) return d = screen_desktop;
+ ++c;
+ }
+ break;
+ case OB_DIRECTION_WEST:
+ --c;
+ if (c >= screen_desktop_layout.columns) {
+ if (!wrap) return d = screen_desktop;
+ c = screen_desktop_layout.columns - 1;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (!wrap) return d = screen_desktop;
+ --c;
+ }
+ break;
+ case OB_DIRECTION_SOUTH:
+ ++r;
+ if (r >= screen_desktop_layout.rows) {
+ if (!wrap) return d = screen_desktop;
+ r = 0;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (!wrap) return d = screen_desktop;
+ ++r;
+ }
+ break;
+ case OB_DIRECTION_NORTH:
+ --r;
+ if (r >= screen_desktop_layout.rows) {
+ if (!wrap) return d = screen_desktop;
+ r = screen_desktop_layout.rows - 1;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (!wrap) return d = screen_desktop;
+ --r;
+ }
+ break;
+ default:
+ assert(0);
+ return d = screen_desktop;
+ }
+
+ d = translate_row_col(r, c);
+ }
+
+ popup_cycle(d, TRUE);
+ return d;
+
+done_cycle:
+ first = TRUE;
+
+ popup_cycle(0, FALSE);
+
+ return d = screen_desktop;
+}
+
void screen_update_layout()
{
ObOrientation orient;
diff --git a/openbox/screen.h b/openbox/screen.h
index 1c687036..696423e2 100644
--- a/openbox/screen.h
+++ b/openbox/screen.h
@@ -45,6 +45,9 @@ void screen_resize();
void screen_set_num_desktops(guint num);
/*! Change the current desktop */
void screen_set_desktop(guint num);
+/*! Interactively change desktops */
+guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
+ gboolean done, gboolean cancel);
/*! Shows and focuses the desktop and hides all the client windows, or
returns to the normal state, showing client windows. */
diff --git a/openbox/translate.c b/openbox/translate.c
new file mode 100644
index 00000000..9b4c1406
--- /dev/null
+++ b/openbox/translate.c
@@ -0,0 +1,109 @@
+#include "kernel/openbox.h"
+#include "mouse.h"
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+
+static guint translate_modifier(char *str)
+{
+ if (!g_ascii_strcasecmp("Mod1", str) ||
+ !g_ascii_strcasecmp("A", str)) return Mod1Mask;
+ else if (!g_ascii_strcasecmp("Mod2", str)) return Mod2Mask;
+ else if (!g_ascii_strcasecmp("Mod3", str)) return Mod3Mask;
+ else if (!g_ascii_strcasecmp("Mod4", str) ||
+ !g_ascii_strcasecmp("W", str)) return Mod4Mask;
+ else if (!g_ascii_strcasecmp("Mod5", str)) return Mod5Mask;
+ else if (!g_ascii_strcasecmp("Control", str) ||
+ !g_ascii_strcasecmp("C", str)) return ControlMask;
+ else if (!g_ascii_strcasecmp("Shift", str) ||
+ !g_ascii_strcasecmp("S", str)) return ShiftMask;
+ g_warning("Invalid modifier '%s' in binding.", str);
+ return 0;
+}
+
+gboolean translate_button(char *str, guint *state, guint *button)
+{
+ char **parsed;
+ char *l;
+ int i;
+ gboolean ret = FALSE;
+
+ parsed = g_strsplit(str, "-", -1);
+
+ /* first, find the button (last token) */
+ l = NULL;
+ for (i = 0; parsed[i] != NULL; ++i)
+ l = parsed[i];
+ if (l == NULL)
+ goto translation_fail;
+
+ /* figure out the mod mask */
+ *state = 0;
+ for (i = 0; parsed[i] != l; ++i) {
+ guint m = translate_modifier(parsed[i]);
+ if (!m) goto translation_fail;
+ *state |= m;
+ }
+
+ /* figure out the button */
+ if (!g_ascii_strcasecmp("Left", l)) *button = 1;
+ else if (!g_ascii_strcasecmp("Middle", l)) *button = 2;
+ else if (!g_ascii_strcasecmp("Right", l)) *button = 3;
+ else if (!g_ascii_strcasecmp("Up", l)) *button = 4;
+ else if (!g_ascii_strcasecmp("Down", l)) *button = 5;
+ else if (!g_ascii_strncasecmp("Button", l, 6)) *button = atoi(l+6);
+ if (!*button) {
+ g_warning("Invalid button '%s' in pointer binding.", l);
+ goto translation_fail;
+ }
+
+ ret = TRUE;
+
+translation_fail:
+ g_strfreev(parsed);
+ return ret;
+}
+
+gboolean translate_key(char *str, guint *state, guint *keycode)
+{
+ char **parsed;
+ char *l;
+ int i;
+ gboolean ret = FALSE;
+ KeySym sym;
+
+ parsed = g_strsplit(str, "-", -1);
+
+ /* first, find the key (last token) */
+ l = NULL;
+ for (i = 0; parsed[i] != NULL; ++i)
+ l = parsed[i];
+ if (l == NULL)
+ goto translation_fail;
+
+ /* figure out the mod mask */
+ *state = 0;
+ for (i = 0; parsed[i] != l; ++i) {
+ guint m = translate_modifier(parsed[i]);
+ if (!m) goto translation_fail;
+ *state |= m;
+ }
+
+ /* figure out the keycode */
+ sym = XStringToKeysym(l);
+ if (sym == NoSymbol) {
+ g_warning("Invalid key name '%s' in key binding.", l);
+ goto translation_fail;
+ }
+ *keycode = XKeysymToKeycode(ob_display, sym);
+ if (!*keycode) {
+ g_warning("Key '%s' does not exist on the display.", l);
+ goto translation_fail;
+ }
+
+ ret = TRUE;
+
+translation_fail:
+ g_strfreev(parsed);
+ return ret;
+}
diff --git a/openbox/translate.h b/openbox/translate.h
new file mode 100644
index 00000000..5afab50f
--- /dev/null
+++ b/openbox/translate.h
@@ -0,0 +1,9 @@
+#ifndef ob__translate_h
+#define ob__translate_h
+
+#include <glib.h>
+
+gboolean translate_button(gchar *str, guint *state, guint *keycode);
+gboolean translate_key(gchar *str, guint *state, guint *keycode);
+
+#endif