summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexey Korop2015-07-07 19:27:00 +0300
committerAlexey Korop2015-07-07 19:27:00 +0300
commit77c4006495da9dd9dd9a4f561c4775b11a2a8fe0 (patch)
treea57d259a892753460ab1a4ebef38765ee3009cbd
downloadaur-77c4006495da9dd9dd9a4f561c4775b11a2a8fe0.tar.gz
Initial import
-rw-r--r--.SRCINFO74
-rw-r--r--PKGBUILD119
-rw-r--r--clearclick.patch68
-rw-r--r--clm_w_sub6.patch994
-rw-r--r--compact_menu.patch12
-rw-r--r--disable_edges_by_default.patch55
-rw-r--r--edge_mikabox.patch396
-rw-r--r--import-bind.patch107
-rwxr-xr-xinstall14
-rw-r--r--keybind_item.patch44
-rw-r--r--keybind_menu2.patch161
-rw-r--r--menu.xml37
-rw-r--r--menu_w_def9.patch754
-rw-r--r--rc.xml76
-rw-r--r--strict_skip_taskbar.patch53
-rw-r--r--topmenu_kbd_group.patch59
-rw-r--r--topmenu_kbd_group_local.patch63
-rw-r--r--utf8-menu-accelerators.diff166
18 files changed, 3252 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..a7652535e1bb
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,74 @@
+pkgbase = openbox_eui
+ pkgdesc = Highly configurable and lightweight X11 window manager
+ pkgver = 2015.03.31
+ pkgrel = 2
+ url = http://openbox.org
+ install = install
+ arch = i686
+ arch = x86_64
+ groups = lxde
+ license = GPL
+ makedepends = binutils
+ makedepends = git
+ makedepends = patch
+ makedepends = libtool
+ makedepends = automake
+ makedepends = autoconf
+ makedepends = m4
+ makedepends = make
+ makedepends = pkg-config
+ makedepends = gcc
+ makedepends = fakeroot
+ makedepends = docbook-to-man
+ depends = startup-notification
+ depends = libxml2
+ depends = libxinerama
+ depends = libxrandr
+ depends = libxcursor
+ depends = pango
+ depends = imlib2
+ depends = librsvg
+ depends = libsm
+ optdepends = kdebase-workspace: for the KDE/Openbox xsession
+ optdepends = python2-xdg: for the openbox-xdg-autostart script
+ provides = openbox
+ conflicts = openbox
+ backup = etc/xdg/openbox/menu.xml
+ backup = etc/xdg/openbox/rc.xml
+ backup = etc/xdg/openbox/autostart
+ backup = etc/xdg/openbox/environment
+ source = menu_w_def9.patch
+ source = clm_w_sub6.patch
+ source = keybind_item.patch
+ source = keybind_menu2.patch
+ source = clearclick.patch
+ source = topmenu_kbd_group.patch
+ source = import-bind.patch
+ source = edge_mikabox.patch
+ source = disable_edges_by_default.patch
+ source = compact_menu.patch
+ source = utf8-menu-accelerators.diff
+ source = topmenu_kbd_group_local.patch
+ source = strict_skip_taskbar.patch
+ source = menu.xml
+ source = rc.xml
+ source = install
+ md5sums = 90d3238b062f0631988ef788b8bb3dd6
+ md5sums = 36545d5c124f0eb648a46fc2779e3dfe
+ md5sums = 816d283ba870b1c8494691fc3cd41ba2
+ md5sums = 599e87ae3ce608d5f710075b8e59459a
+ md5sums = f42dda01e070fe1f270942c185adefe4
+ md5sums = 68bf6c7ad5a04ac74b9b5744a2197b17
+ md5sums = f10d80373150df6cb08501e6fa6e9c39
+ md5sums = 1b9192e741ac98a1a541c93e137c0a70
+ md5sums = 4d5fbf07973c13aaf86db31dacdbc0ae
+ md5sums = 3718f41d8573d5d2a4cf04536562b93a
+ md5sums = 6579e6898e3195fb6877e356b2092bba
+ md5sums = fabc9f22ebdda17649b7c98fd8fc4d81
+ md5sums = e6a16b0be7eac066976fc56ed0bd1b27
+ md5sums = 1ea5cf52cc72fd7b18f50798d1458baf
+ md5sums = e2e2480a2dfe13e93de5fa88c17eaa77
+ md5sums = da62476c79a186e0313578963b2ee637
+
+pkgname = openbox_eui
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..1f6b59d4e94a
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,119 @@
+# Maintainer: Alexey Korop <akorop@ukr.net>
+
+pkgname=openbox_eui
+pkgver=2015.03.31
+pkgrel=2
+pkgdesc='Highly configurable and lightweight X11 window manager'
+arch=('i686' 'x86_64')
+url='http://openbox.org'
+conflicts=('openbox')
+provides=('openbox')
+license=('GPL')
+makedepends=(binutils git patch libtool automake autoconf m4 make pkg-config gcc fakeroot docbook-to-man)
+depends=('startup-notification' 'libxml2' 'libxinerama' 'libxrandr'
+ 'libxcursor' 'pango' 'imlib2' 'librsvg' 'libsm')
+optdepends=('kdebase-workspace: for the KDE/Openbox xsession'
+ 'python2-xdg: for the openbox-xdg-autostart script')
+groups=('lxde')
+backup=('etc/xdg/openbox/menu.xml' 'etc/xdg/openbox/rc.xml'
+ 'etc/xdg/openbox/autostart' 'etc/xdg/openbox/environment')
+source=(
+'menu_w_def9.patch'
+'clm_w_sub6.patch'
+'keybind_item.patch'
+'keybind_menu2.patch'
+'clearclick.patch'
+'topmenu_kbd_group.patch'
+'import-bind.patch'
+'edge_mikabox.patch'
+'disable_edges_by_default.patch'
+'compact_menu.patch'
+'utf8-menu-accelerators.diff'
+'topmenu_kbd_group_local.patch'
+'strict_skip_taskbar.patch'
+'menu.xml'
+'rc.xml'
+'install'
+)
+md5sums=(
+ '90d3238b062f0631988ef788b8bb3dd6'
+ '36545d5c124f0eb648a46fc2779e3dfe'
+ '816d283ba870b1c8494691fc3cd41ba2'
+ '599e87ae3ce608d5f710075b8e59459a'
+ 'f42dda01e070fe1f270942c185adefe4'
+ '68bf6c7ad5a04ac74b9b5744a2197b17'
+ 'f10d80373150df6cb08501e6fa6e9c39'
+ '1b9192e741ac98a1a541c93e137c0a70'
+ '4d5fbf07973c13aaf86db31dacdbc0ae'
+ '3718f41d8573d5d2a4cf04536562b93a'
+ '6579e6898e3195fb6877e356b2092bba'
+ 'fabc9f22ebdda17649b7c98fd8fc4d81'
+ 'e6a16b0be7eac066976fc56ed0bd1b27'
+ '1ea5cf52cc72fd7b18f50798d1458baf'
+ 'e2e2480a2dfe13e93de5fa88c17eaa77'
+ 'da62476c79a186e0313578963b2ee637'
+)
+
+prepare() {
+ git clone https://github.com/danakj/openbox.git
+ cd openbox
+ git reset --hard 9e8813e111cbe6c1088f6abbc771a29470f05fc2
+ sed -i 's|/usr/bin/env python|/usr/bin/env python2|' \
+ data/autostart/openbox-xdg-autostart
+}
+
+build() {
+ cd openbox
+ echo menu_w_def9.patch
+ patch -p1 < ../menu_w_def9.patch
+ echo clm_w_sub6.patch
+ patch -p1 < ../clm_w_sub6.patch
+ echo keybind_item.patch
+ patch -p1 < ../keybind_item.patch
+ echo keybind_menu2.patch
+ patch -p1 < ../keybind_menu2.patch
+ echo clearclick.patch
+ patch -p1 < ../clearclick.patch
+ echo topmenu_kbd_group.patch
+ patch -p1 < ../topmenu_kbd_group.patch
+ echo import-bind.patch
+ patch -p1 < ../import-bind.patch
+ echo edge_mikabox.patch
+ patch -p1 < ../edge_mikabox.patch
+ echo disable_edges_by_default.patch
+ patch -p1 < ../disable_edges_by_default.patch
+ echo compact_menu.patch
+ patch -p1 < ../compact_menu.patch
+ echo utf8-menu-accelerators.diff
+ patch -p1 < ../utf8-menu-accelerators.diff
+ echo topmenu_kbd_group.patch_local
+ patch -p1 < ../topmenu_kbd_group_local.patch
+ echo strict_skip_taskbar
+ patch -p1 < ../strict_skip_taskbar.patch
+ ./bootstrap
+ ./configure --prefix=/usr \
+ --with-x \
+ --enable-startup-notification \
+ --sysconfdir=/etc \
+ --libexecdir=/usr/lib/openbox
+ make
+}
+
+package() {
+ cd openbox
+ make DESTDIR="$pkgdir" install
+
+ # GNOME Panel is no longer available in the official repositories
+ rm -r "$pkgdir"/usr/bin/{gdm-control,gnome-panel-control,openbox-gnome-session} \
+ "$pkgdir"/usr/share/gnome{,-session} \
+ "$pkgdir"/usr/share/man/man1/openbox-gnome-session.1 \
+ "$pkgdir"/usr/share/xsessions/openbox-gnome.desktop
+
+ sed -i 's:startkde:/usr/bin/\0:' \
+ "$pkgdir"/usr/share/xsessions/openbox-kde.desktop
+ mkdir -p "$pkgdir/etc/$pkgname/samples"
+ install -Dm644 "${srcdir}/rc.xml" "${pkgdir}/etc/$pkgname/samples/rc.xml"
+ install -Dm644 "${srcdir}/menu.xml" "${pkgdir}/etc/$pkgname/samples/menu.xml"
+}
+
+install=install
diff --git a/clearclick.patch b/clearclick.patch
new file mode 100644
index 000000000000..cc506ee79481
--- /dev/null
+++ b/clearclick.patch
@@ -0,0 +1,68 @@
+diff -urwN -X openbox/.gitignore openbox-efc7efc/Makefile.am openbox/Makefile.am
+--- openbox-efc7efc/Makefile.am 2014-12-13 11:53:40.000000000 +0200
++++ openbox/Makefile.am 2014-12-13 10:21:40.000000000 +0200
+@@ -222,6 +222,7 @@
+ openbox/actions/movetoedge.c \
+ openbox/actions/omnipresent.c \
+ openbox/actions/raise.c \
++ openbox/actions/clearclick.c \
+ openbox/actions/raiselower.c \
+ openbox/actions/reconfigure.c \
+ openbox/actions/resize.c \
+diff -urwN -X openbox/.gitignore openbox-efc7efc/openbox/actions/all.c openbox/openbox/actions/all.c
+--- openbox-efc7efc/openbox/actions/all.c 2014-11-07 19:58:40.000000000 +0200
++++ openbox/openbox/actions/all.c 2014-12-13 11:58:08.000000000 +0200
+@@ -15,6 +15,7 @@
+ action_move_startup();
+ action_focus_startup();
+ action_raise_startup();
++ action_clear_click_startup();
+ action_lower_startup();
+ action_raiselower_startup();
+ action_unfocus_startup();
+diff -urwN -X openbox/.gitignore openbox-efc7efc/openbox/actions/all.h openbox/openbox/actions/all.h
+--- openbox-efc7efc/openbox/actions/all.h 2014-11-07 19:58:40.000000000 +0200
++++ openbox/openbox/actions/all.h 2014-12-13 11:57:30.000000000 +0200
+@@ -16,6 +16,7 @@
+ void action_move_startup(void);
+ void action_focus_startup(void);
+ void action_raise_startup(void);
++void action_clear_click_startup(void);
+ void action_lower_startup(void);
+ void action_raiselower_startup(void);
+ void action_unfocus_startup(void);
+diff -urwN -X openbox/.gitignore openbox-efc7efc/openbox/actions/clearclick.c openbox/openbox/actions/clearclick.c
+--- openbox-efc7efc/openbox/actions/clearclick.c 1970-01-01 03:00:00.000000000 +0300
++++ openbox/openbox/actions/clearclick.c 2014-12-13 11:59:15.000000000 +0200
+@@ -0,0 +1,31 @@
++#include "openbox/actions.h"
++#include "openbox/event.h"
++#include "obt/display.h"
++#include "openbox/focus.h"
++
++/* This action prevents the transmission of the mouse event
++to the nonactive window. The typical using is
++
++ <context name="Client">
++ <mousebind action="Press" button="Left">
++ <action name="Focus"/>
++ <action name="Raise"/>
++ <action name="ClearClick"/>
++ </mousebind>
++ */
++
++static gboolean run_func(ObActionsData *data, gpointer options);
++
++void action_clear_click_startup(void)
++{
++ actions_register("ClearClick", NULL, NULL, run_func);
++}
++
++/* Always return FALSE because its not interactive */
++static gboolean run_func(ObActionsData *data, gpointer options)
++{
++ if (data->client && data->client != focus_client)
++ XAllowEvents(obt_display, AsyncPointer , event_time());
++
++ return FALSE;
++}
diff --git a/clm_w_sub6.patch b/clm_w_sub6.patch
new file mode 100644
index 000000000000..3aae888dbb9f
--- /dev/null
+++ b/clm_w_sub6.patch
@@ -0,0 +1,994 @@
+commit 56f62da740419b6edad2cff5f02252bd278d83b0
+Author: Alexey Korop <avkorop@i.ua>
+Date: Sun Mar 15 12:22:50 2015 +0200
+
+ clm_w_sub6.patch
+
+diff --git a/Makefile.am b/Makefile.am
+index f25bf8e..8a3eed6 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -241,8 +241,6 @@ openbox_openbox_SOURCES = \
+ openbox/client.h \
+ openbox/client_list_menu.c \
+ openbox/client_list_menu.h \
+- openbox/client_list_combined_menu.c \
+- openbox/client_list_combined_menu.h \
+ openbox/client_menu.c \
+ openbox/client_menu.h \
+ openbox/config.c \
+diff --git a/openbox/client_list_combined_menu.c b/openbox/client_list_combined_menu.c
+deleted file mode 100644
+index 84eb506..0000000
+--- a/openbox/client_list_combined_menu.c
++++ /dev/null
+@@ -1,167 +0,0 @@
+-/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+-
+- client_list_menu.c for the Openbox window manager
+- Copyright (c) 2006 Mikael Magnusson
+- Copyright (c) 2003-2007 Dana Jansens
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- (at your option) any later version.
+-
+- 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.
+-
+- See the COPYING file for a copy of the GNU General Public License.
+-*/
+-
+-#include "openbox.h"
+-#include "menu.h"
+-#include "menuframe.h"
+-#include "screen.h"
+-#include "client.h"
+-#include "client_list_combined_menu.h"
+-#include "focus.h"
+-#include "config.h"
+-#include "gettext.h"
+-
+-#include <glib.h>
+-
+-#define MENU_NAME "client-list-combined-menu"
+-
+-static ObMenu *combined_menu;
+-
+-#define SEPARATOR -1
+-#define ADD_DESKTOP -2
+-#define REMOVE_DESKTOP -3
+-
+-static void self_cleanup(ObMenu *menu, gpointer data)
+-{
+- menu_clear_entries(menu);
+-}
+-
+-static gboolean self_update(ObMenuFrame *frame, gpointer data)
+-{
+- ObMenu *menu = frame->menu;
+- ObMenuEntry *e;
+- GList *it;
+- guint desktop;
+-
+- menu_clear_entries(menu);
+-
+- for (desktop = 0; desktop < screen_num_desktops; desktop++) {
+- gboolean empty = TRUE;
+- gboolean onlyiconic = TRUE;
+-
+- menu_add_separator(menu, SEPARATOR, screen_desktop_names[desktop]);
+- for (it = focus_order; it; it = g_list_next(it)) {
+- ObClient *c = it->data;
+- if (focus_valid_target(c, desktop,
+- TRUE, TRUE,
+- FALSE, TRUE, FALSE, FALSE, FALSE))
+- {
+- empty = FALSE;
+-
+- if (c->iconic) {
+- gchar *title = g_strdup_printf("(%s)", c->icon_title);
+- e = menu_add_normal(menu, desktop, title, NULL, FALSE);
+- g_free(title);
+- } else {
+- onlyiconic = FALSE;
+- e = menu_add_normal(menu, desktop, c->title, NULL, FALSE);
+- }
+-
+- if (config_menu_show_icons) {
+- e->icon = client_icon(c);
+- RrImageRef(e->icon);
+- e->icon_alpha =
+- c->iconic ? OB_ICONIC_ALPHA : 0xff;
+- }
+-
+- e->data.normal.data = c;
+- }
+- }
+-
+- if (empty || onlyiconic) {
+- /* no entries or only iconified windows, so add a
+- * way to go to this desktop without uniconifying a window */
+- if (!empty)
+- menu_add_separator(menu, SEPARATOR, NULL);
+-
+- e = menu_add_normal(menu, desktop, _("Go there..."), NULL, TRUE);
+- if (desktop == screen_desktop)
+- e->data.normal.enabled = FALSE;
+- }
+- }
+-
+- if (config_menu_manage_desktops) {
+- menu_add_separator(menu, SEPARATOR, _("Manage desktops"));
+- menu_add_normal(menu, ADD_DESKTOP, _("_Add new desktop"), NULL, TRUE);
+- menu_add_normal(menu, REMOVE_DESKTOP, _("_Remove last desktop"),
+- NULL, TRUE);
+- }
+-
+- return TRUE; /* always show the menu */
+-}
+-
+-static void menu_execute(ObMenuEntry *self, ObMenuFrame *f,
+- ObClient *c, guint state, gpointer data)
+-{
+- if (self->id == ADD_DESKTOP) {
+- screen_add_desktop(FALSE);
+- menu_frame_hide_all();
+- }
+- else if (self->id == REMOVE_DESKTOP) {
+- screen_remove_desktop(FALSE);
+- menu_frame_hide_all();
+- }
+- else {
+- ObClient *t = self->data.normal.data;
+- if (t) { /* it's set to NULL if its destroyed */
+- gboolean here = state & ShiftMask;
+-
+- client_activate(t, TRUE, here, TRUE, TRUE, TRUE);
+- /* if the window is omnipresent then we need to go to its
+- desktop */
+- if (!here && t->desktop == DESKTOP_ALL)
+- screen_set_desktop(self->id, FALSE);
+- }
+- else
+- screen_set_desktop(self->id, TRUE);
+- }
+-}
+-
+-static void client_dest(ObClient *client, gpointer data)
+-{
+- /* This concise function removes all references to a closed
+- * client in the client_list_menu, so we don't have to check
+- * in client.c */
+- GList *eit;
+- for (eit = combined_menu->entries; eit; eit = g_list_next(eit)) {
+- ObMenuEntry *meit = eit->data;
+- if (meit->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+- meit->data.normal.data == client)
+- {
+- meit->data.normal.data = NULL;
+- }
+- }
+-}
+-
+-void client_list_combined_menu_startup(gboolean reconfig)
+-{
+- if (!reconfig)
+- client_add_destroy_notify(client_dest, NULL);
+-
+- combined_menu = menu_new(MENU_NAME, _("Windows"), TRUE, NULL);
+- menu_set_update_func(combined_menu, self_update);
+- menu_set_cleanup_func(combined_menu, self_cleanup);
+- menu_set_execute_func(combined_menu, menu_execute);
+-}
+-
+-void client_list_combined_menu_shutdown(gboolean reconfig)
+-{
+- if (!reconfig)
+- client_remove_destroy_notify(client_dest);
+-}
+diff --git a/openbox/client_list_combined_menu.h b/openbox/client_list_combined_menu.h
+deleted file mode 100644
+index 420e898..0000000
+--- a/openbox/client_list_combined_menu.h
++++ /dev/null
+@@ -1,26 +0,0 @@
+-/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+-
+- client_list_menu.h for the Openbox window manager
+- Copyright (c) 2006 Mikael Magnusson
+- Copyright (c) 2003-2007 Dana Jansens
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- (at your option) any later version.
+-
+- 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.
+-
+- See the COPYING file for a copy of the GNU General Public License.
+-*/
+-
+-#ifndef ob__client_list_combined_menu_h
+-#define ob__client_list_combined_menu_h
+-
+-void client_list_combined_menu_startup(gboolean reconfig);
+-void client_list_combined_menu_shutdown(gboolean reconfig);
+-
+-#endif
+diff --git a/openbox/client_list_menu.c b/openbox/client_list_menu.c
+index 4f85935..73f39dc 100644
+--- a/openbox/client_list_menu.c
++++ b/openbox/client_list_menu.c
+@@ -21,8 +21,10 @@
+ #include "menu.h"
+ #include "menuframe.h"
+ #include "screen.h"
++#include "client_menu.h"
+ #include "client.h"
+ #include "client_list_menu.h"
++#include "event.h"
+ #include "focus.h"
+ #include "config.h"
+ #include "gettext.h"
+@@ -30,68 +32,170 @@
+ #include <glib.h>
+
+ #define MENU_NAME "client-list-menu"
++#define COMBINED_MENU_NAME "client-list-combined-menu"
++
++/* Characters, used as first character of submenu name for
++ actions submenus and move-to submenus. These characters are
++ nonprintable for prevent intersection with user defined names.*/
++#define ACTIONS_TAG (gchar)1
++#define SEND_TO_TAG (gchar)2
++
++static ObMenu *clients_menu; // visible client list or NULL
++
++static void clients_list_repaint(ObMenuFrame *f){
++ guint x,y;
++ ObMenuFrame *p;
++ ObMenuEntryFrame *pe;
++ gchar *name;
++ GravityPoint position = { { 0, }, };
++
++ g_assert(f);
++ position.x.pos = f->area.x;
++ position.y.pos = f->area.y;
++ p = f->parent;
++ pe = f->parent_entry;
++ name = g_strdup(f->menu->name);
++ if (config_menu_middle)
++ y += f->area.height / 2;
++ menu_frame_hide(f);
++ if (p)
++ menu_frame_select(p, pe, TRUE); // client-list-menu
++ else
++ menu_show(name, &position, screen_monitor_pointer(), FALSE, FALSE, NULL); // client-list-combined-menu
++ g_free(name);
++}
+
+-static GSList *desktop_menus;
+-
+-typedef struct
++static gboolean act_menu_update(ObMenuFrame *frame, gpointer data)
+ {
+- guint desktop;
+-} DesktopData;
++ frame->client = frame->menu->data; // simulate the client-menu environment
++ return(client_menu_update(frame, data));
++}
+
+-#define SEPARATOR -1
+-#define ADD_DESKTOP -2
+-#define REMOVE_DESKTOP -3
++static void client_list_cleanup(ObMenu *menu, gpointer data)
++{
++ clients_menu = NULL;
++}
+
+-static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data)
++static void client_list_update(ObClient *client, gpointer data)
+ {
+- ObMenu *menu = frame->menu;
+- DesktopData *d = data;
++/* This function recreate menu with list of clients
++ if some listed client is closed or moved to the another desktop */
+ GList *it;
+- gboolean empty = TRUE;
+- gboolean onlyiconic = TRUE;
++ ObMenuFrame *f;
++
++ if (!clients_menu) return;
++ for (it = menu_frame_visible; it; it = g_list_next(it)) {
++ f = it->data;
++ if (f->menu == clients_menu) {
++ clients_list_repaint(f);
++ return;
++ }
++ }
++}
+
+- menu_clear_entries(menu);
++static void send_to_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
++ ObClient *c, guint state, gpointer data)
++{
++ g_assert(c);
+
+- for (it = focus_order; it; it = g_list_next(it)) {
+- ObClient *c = it->data;
+- if (focus_valid_target(c, d->desktop,
+- TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE)) {
+- ObMenuEntry *e;
++ client_set_desktop(c, e->id, FALSE, FALSE);
++ client_list_update(c, NULL);
++}
+
+- empty = FALSE;
++static void add_client_menu(ObMenu *menu, ObClient *c, guint desktop,
++ guint client_number, gboolean *onlyiconic)
++{
++/* Add submenu for one client "c" to menu "menu".
++ This submenu contains the send-to submenu and action items
++(Activate, Close).
++ Created submenus are never freed directly, they will be freed upon
++next time creation the submenus with the same names, and upon
++openbox termination.
++*/
++ ObMenuEntry *e;
++ gchar *name;
++ ObMenu* act_menu, *send_to_menu;
++
++ name = g_strdup_printf("?%d-%d", desktop, client_number);
++ *name = ACTIONS_TAG;
++ if (c->iconic) {
++ gchar *title = g_strdup_printf("(%s)", c->icon_title);
++ act_menu = menu_new(name, title, TRUE, NULL);
++ g_free(title);
++ } else {
++ *onlyiconic = FALSE;
++ act_menu = menu_new(name, c->title, TRUE, NULL);
++ }
++ menu_show_all_shortcuts(act_menu, FALSE);
++ menu_set_update_func(act_menu, act_menu_update);
++ menu_set_execute_func(act_menu, client_menu_execute);
++ act_menu->data = c; // for act_menu_update
++
++ *name = SEND_TO_TAG; // send-to menu name
++ send_to_menu = client_menu_send_to_menu_new(name);
++ menu_set_execute_func(send_to_menu, send_to_menu_execute);
++
++ menu_add_submenu(act_menu, CLIENT_SEND_TO, name);
++ e = menu_add_normal(act_menu, CLIENT_ACTIVATE, _("_Activate"), NULL, TRUE);
++ e->data.normal.data = (gpointer)desktop; // see CLIENT_ACTIVATE in client_menu_execute
++ act_menu->default_entry = e;
++ client_menu_add_actions(act_menu, CA_CLOSE);
++
++ *name = ACTIONS_TAG;
++ e = menu_add_submenu(menu, desktop, name);
++ g_free(name);
++ if (config_menu_show_icons) {
++ e->icon = client_icon(c);
++ RrImageRef(e->icon);
++ e->icon_alpha =
++ c->iconic ? OB_ICONIC_ALPHA : 0xff;
++ menu_set_cleanup_func(menu, client_list_cleanup);
++ clients_menu = menu;
++ }
++}
+
+- if (c->iconic) {
+- gchar *title = g_strdup_printf("(%s)", c->icon_title);
+- e = menu_add_normal(menu, d->desktop, title, NULL, FALSE);
+- g_free(title);
+- } else {
+- onlyiconic = FALSE;
+- e = menu_add_normal(menu, d->desktop, c->title, NULL, FALSE);
+- }
+-
+- if (config_menu_show_icons) {
+- e->icon = client_icon(c);
+- RrImageRef(e->icon);
+- e->icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff;
+- }
+-
+- e->data.normal.data = c;
++/* Add all items about one desktop to the menu */
++void add_clients(ObMenu *menu, guint desktop)
++{
++ ObMenuEntry *e;
++ GList *it;
++ gboolean empty = TRUE;
++ gboolean onlyiconic = TRUE;
++ guint client_number = 0;
++ ObClient *c;
++
++ for (it = focus_order; it; it = g_list_next(it))
++ {
++ c = it->data;
++ if (focus_valid_target(c, desktop,
++ TRUE, TRUE,
++ FALSE, TRUE, FALSE, FALSE, FALSE))
++ {
++ empty = FALSE;
++ add_client_menu(menu, c, desktop, client_number, &onlyiconic);
+ }
++ ++client_number;
+ }
+
+ if (empty || onlyiconic) {
+- ObMenuEntry *e;
+-
+ /* no entries or only iconified windows, so add a
+ * way to go to this desktop without uniconifying a window */
+ if (!empty)
+ menu_add_separator(menu, SEPARATOR, NULL);
+
+- e = menu_add_normal(menu, d->desktop, _("Go there..."), NULL, TRUE);
+- if (d->desktop == screen_desktop)
++ e = menu_add_normal(menu, desktop, _("Go there..."), NULL, TRUE);
++ if (desktop == screen_desktop)
+ e->data.normal.enabled = FALSE;
++ menu_find_submenus(menu); // for client_list_update
+ }
++}
++
++static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data)
++{
++ ObMenu *menu = frame->menu;
+
++ menu_clear_entries(menu);
++ add_clients(menu, (guint)data);
+ return TRUE; /* always show */
+ }
+
+@@ -99,6 +203,7 @@ static void desk_menu_execute(ObMenuEntry *self, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+ {
+ ObClient *t = self->data.normal.data;
++
+ if (t) { /* it's set to NULL if its destroyed */
+ gboolean here = state & ShiftMask;
+
+@@ -108,70 +213,41 @@ static void desk_menu_execute(ObMenuEntry *self, ObMenuFrame *f,
+ if (!here && t->desktop == DESKTOP_ALL)
+ screen_set_desktop(self->id, FALSE);
+ }
+- else
++ else // this case used in client-list-menu only
+ screen_set_desktop(self->id, TRUE);
+ }
+
+-static void desk_menu_destroy(ObMenu *menu, gpointer data)
+-{
+- DesktopData *d = data;
+-
+- g_slice_free(DesktopData, d);
+-
+- desktop_menus = g_slist_remove(desktop_menus, menu);
+-}
+-
+-static void self_cleanup(ObMenu *menu, gpointer data)
+-{
+- menu_clear_entries(menu);
+-
+- while (desktop_menus) {
+- menu_free(desktop_menus->data);
+- desktop_menus = g_slist_delete_link(desktop_menus, desktop_menus);
++void add_desktop_manage(ObMenu *menu){
++ if (config_menu_manage_desktops) {
++ menu_add_separator(menu, SEPARATOR, NULL);
++ menu_add_normal(menu, ADD_DESKTOP, _("_Add new desktop"), NULL, TRUE);
++ menu_add_normal(menu, REMOVE_DESKTOP, _("_Remove last desktop"),
++ NULL, TRUE);
+ }
+ }
+
+-static gboolean self_update(ObMenuFrame *frame, gpointer data)
++static gboolean client_list_menu_update(ObMenuFrame *frame, gpointer data)
+ {
+ ObMenu *menu = frame->menu;
+ guint i;
+
+ menu_clear_entries(menu);
+
+- while (desktop_menus) {
+- menu_free(desktop_menus->data);
+- desktop_menus = g_slist_delete_link(desktop_menus, desktop_menus);
+- }
+-
+ for (i = 0; i < screen_num_desktops; ++i) {
+- ObMenu *submenu;
++ ObMenu *desktop_menu;
+ gchar *name = g_strdup_printf("%s-%u", MENU_NAME, i);
+- DesktopData *ddata = g_slice_new(DesktopData);
+-
+- ddata->desktop = i;
+- submenu = menu_new(name, screen_desktop_names[i], FALSE, ddata);
+- menu_set_update_func(submenu, desk_menu_update);
+- menu_set_execute_func(submenu, desk_menu_execute);
+- menu_set_destroy_func(submenu, desk_menu_destroy);
+-
++ desktop_menu = menu_new(name, screen_desktop_names[i], FALSE, (gpointer)i);
++ menu_set_update_func(desktop_menu, desk_menu_update);
++ menu_set_execute_func(desktop_menu, desk_menu_execute);
+ menu_add_submenu(menu, i, name);
+
+ g_free(name);
+-
+- desktop_menus = g_slist_append(desktop_menus, submenu);
+- }
+-
+- if (config_menu_manage_desktops) {
+- menu_add_separator(menu, SEPARATOR, NULL);
+- menu_add_normal(menu, ADD_DESKTOP, _("_Add new desktop"), NULL, TRUE);
+- menu_add_normal(menu, REMOVE_DESKTOP, _("_Remove last desktop"),
+- NULL, TRUE);
+ }
+-
++ add_desktop_manage(menu);
+ return TRUE; /* always show */
+ }
+
+-static void self_execute(ObMenuEntry *self, ObMenuFrame *f,
++static void root_menu_execute(ObMenuEntry *self, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+ {
+ if (self->id == ADD_DESKTOP) {
+@@ -182,26 +258,8 @@ static void self_execute(ObMenuEntry *self, ObMenuFrame *f,
+ screen_remove_desktop(FALSE);
+ menu_frame_hide_all();
+ }
+-}
+-
+-static void client_dest(ObClient *client, gpointer data)
+-{
+- /* This concise function removes all references to a closed
+- * client in the client_list_menu, so we don't have to check
+- * in client.c */
+- GSList *it;
+- for (it = desktop_menus; it; it = g_slist_next(it)) {
+- ObMenu *mit = it->data;
+- GList *eit;
+- for (eit = mit->entries; eit; eit = g_list_next(eit)) {
+- ObMenuEntry *meit = eit->data;
+- if (meit->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+- meit->data.normal.data == client)
+- {
+- meit->data.normal.data = NULL;
+- }
+- }
+- }
++ else // this case used in client-list-combined-menu only
++ screen_set_desktop(self->id, TRUE);
+ }
+
+ void client_list_menu_startup(gboolean reconfig)
+@@ -209,16 +267,40 @@ void client_list_menu_startup(gboolean reconfig)
+ ObMenu *menu;
+
+ if (!reconfig)
+- client_add_destroy_notify(client_dest, NULL);
++ client_add_destroy_notify(client_list_update, NULL);
+
+ menu = menu_new(MENU_NAME, _("Desktops"), TRUE, NULL);
+- menu_set_update_func(menu, self_update);
+- menu_set_cleanup_func(menu, self_cleanup);
+- menu_set_execute_func(menu, self_execute);
++ menu_set_update_func(menu, client_list_menu_update);
++ menu_set_execute_func(menu, root_menu_execute);
+ }
+
+ void client_list_menu_shutdown(gboolean reconfig)
+ {
+ if (!reconfig)
+- client_remove_destroy_notify(client_dest);
++ client_remove_destroy_notify(client_list_update);
++}
++
++static gboolean combined_menu_update(ObMenuFrame *frame, gpointer data)
++{
++ ObMenu *menu = frame->menu;
++ guint desktop;
++
++ menu_clear_entries(menu);
++
++ for (desktop = 0; desktop < screen_num_desktops; desktop++) {
++ menu_add_separator(menu, SEPARATOR, screen_desktop_names[desktop]);
++ add_clients(menu, desktop);
++ }
++ add_desktop_manage(menu);
++ menu_find_submenus(menu); // for client_list_update
++ return TRUE; /* always show the menu */
++}
++
++void client_list_combined_menu_startup(gboolean reconfig)
++{
++ ObMenu *combined_menu;
++
++ combined_menu = menu_new(COMBINED_MENU_NAME, _("Windows"), TRUE, NULL);
++ menu_set_update_func(combined_menu, combined_menu_update);
++ menu_set_execute_func(combined_menu, root_menu_execute);
+ }
+diff --git a/openbox/client_list_menu.h b/openbox/client_list_menu.h
+index 860cd50..b67c95f 100644
+--- a/openbox/client_list_menu.h
++++ b/openbox/client_list_menu.h
+@@ -20,7 +20,13 @@
+ #ifndef ob__client_list_menu_h
+ #define ob__client_list_menu_h
+
++#define SEPARATOR -1
++#define ADD_DESKTOP -2
++#define REMOVE_DESKTOP -3
++
+ void client_list_menu_startup(gboolean reconfig);
+ void client_list_menu_shutdown(gboolean reconfig);
+-
++void client_list_add_clients(ObMenu *menu, guint desktop);
++void client_list_add_desktop_manage(ObMenu *menu);
++void client_list_combined_menu_startup(gboolean reconfig);
+ #endif
+diff --git a/openbox/client_menu.c b/openbox/client_menu.c
+index 6d5c4c7..e48d810 100644
+--- a/openbox/client_menu.c
++++ b/openbox/client_menu.c
+@@ -42,21 +42,9 @@ enum {
+ LAYER_BOTTOM = -1
+ };
+
+-enum {
+- CLIENT_SEND_TO,
+- CLIENT_LAYER,
+- CLIENT_ICONIFY,
+- CLIENT_RESTORE,
+- CLIENT_MAXIMIZE,
+- CLIENT_SHADE,
+- CLIENT_DECORATE,
+- CLIENT_MOVE,
+- CLIENT_RESIZE,
+- CLIENT_CLOSE
+-};
+-
+-static void set_icon_color(ObMenuEntry *e)
++static void menu_entry_set_mask(ObMenuEntry *e, RrPixmapMask *mask)
+ {
++ e->mask = mask;
+ e->mask_normal_color = ob_rr_theme->menu_color;
+ e->mask_selected_color = ob_rr_theme->menu_selected_color;
+ e->mask_disabled_color = ob_rr_theme->menu_disabled_color;
+@@ -64,7 +52,7 @@ static void set_icon_color(ObMenuEntry *e)
+ ob_rr_theme->menu_disabled_selected_color;
+ }
+
+-static gboolean client_menu_update(ObMenuFrame *frame, gpointer data)
++gboolean client_menu_update(ObMenuFrame *frame, gpointer data)
+ {
+ ObMenu *menu = frame->menu;
+ GList *it;
+@@ -112,11 +100,12 @@ static gboolean client_menu_update(ObMenuFrame *frame, gpointer data)
+ return TRUE; /* show the menu */
+ }
+
+-static void client_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
++void client_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+ {
+ gint x, y;
+ gulong ignore_start;
++ gboolean here;
+
+ if (!c)
+ return;
+@@ -165,6 +154,14 @@ static void client_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
+ case CLIENT_CLOSE:
+ client_close(c);
+ break;
++ case CLIENT_ACTIVATE: // for client list (combined) menu
++ here = state & ShiftMask;
++ client_activate(c, TRUE, here, TRUE, TRUE, TRUE);
++ /* if the window is omnipresent then we need to go to its
++ desktop */
++ if (!here && c->desktop == DESKTOP_ALL)
++ screen_set_desktop((gint)e->data.normal.data, FALSE);
++ break;
+ default:
+ g_assert_not_reached();
+ }
+@@ -233,7 +230,7 @@ static void layer_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
+ }
+ }
+
+-static gboolean send_to_menu_update(ObMenuFrame *frame, gpointer data)
++static gboolean client_menu_send_to_menu_update(ObMenuFrame *frame, gpointer data)
+ {
+ ObMenu *menu = frame->menu;
+ ObClient *c = frame->client;
+@@ -267,18 +264,21 @@ static gboolean send_to_menu_update(ObMenuFrame *frame, gpointer data)
+ }
+ }
+
++ menu->default_entry = NULL;
+ for (it = menu->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+ guint desk = e->id;
+
+ e->data.normal.enabled = c->desktop != desk;
++ if (e->data.normal.enabled && (desk == screen_desktop))
++ { // mark this desktop target as the default item
++ menu->default_entry = e;
++ }
+
+ if ((desk == DESKTOP_ALL && c->desktop != DESKTOP_ALL) ||
+ (c->desktop == DESKTOP_ALL && desk == screen_desktop))
+- {
+- e->mask = ob_rr_theme->btn_desk->unpressed_mask;
+- set_icon_color(e);
+- } else
++ menu_entry_set_mask(e, ob_rr_theme->btn_desk->unpressed_mask);
++ else
+ e->mask = NULL;
+ }
+
+@@ -295,7 +295,7 @@ static void send_to_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
+ /* the client won't even be on the screen anymore, so hide the menu */
+ menu_frame_hide_all();
+ else if (f) {
+- send_to_menu_update(f, (gpointer)1);
++ client_menu_send_to_menu_update(f, (gpointer)1);
+ menu_frame_render(f);
+ }
+ }
+@@ -363,10 +363,59 @@ static void client_menu_place(ObMenuFrame *frame, gint *x, gint *y,
+ }
+ }
+
++void client_menu_add_actions(ObMenu *menu, guint ca_mask)
++{
++ ObMenuEntry *e;
++
++ if (ca_mask & CA_RESTORE){
++ e = menu_add_normal(menu, CLIENT_RESTORE, _("R_estore"), NULL, TRUE);
++ menu_entry_set_mask(e, ob_rr_theme->btn_max->unpressed_toggled_mask);
++ }
++ if (ca_mask & CA_MOVE)
++ menu_add_normal(menu, CLIENT_MOVE, _("_Move"), NULL, TRUE);
++
++ if (ca_mask & CA_RESIZE)
++ menu_add_normal(menu, CLIENT_RESIZE, _("Resi_ze"), NULL, TRUE);
++
++ if (ca_mask & CA_ICONIFY){
++ e = menu_add_normal(menu, CLIENT_ICONIFY, _("Ico_nify"), NULL, TRUE);
++ menu_entry_set_mask(e, ob_rr_theme->btn_iconify->unpressed_mask);
++ }
++
++ if (ca_mask & CA_MAXIMIZE){
++ e = menu_add_normal(menu, CLIENT_MAXIMIZE, _("Ma_ximize"), NULL, TRUE);
++ menu_entry_set_mask(e, ob_rr_theme->btn_max->unpressed_mask);
++ }
++
++ if (ca_mask & CA_SHADE){
++ e = menu_add_normal(menu, CLIENT_SHADE, _("_Roll up/down"), NULL, TRUE);
++ menu_entry_set_mask(e, ob_rr_theme->btn_shade->unpressed_mask);
++ }
++
++ if (ca_mask & CA_DECORATE)
++ menu_add_normal(menu, CLIENT_DECORATE, _("Un/_Decorate"), NULL, TRUE);
++
++ if (ca_mask & CA_CLOSE){
++ menu_add_separator(menu, -1, NULL);
++ e = menu_add_normal(menu, CLIENT_CLOSE, _("_Close"), NULL, TRUE);
++ menu_entry_set_mask(e, ob_rr_theme->btn_close->unpressed_mask);
++ }
++}
++
++ObMenu *client_menu_send_to_menu_new(gchar *name){
++ ObMenu *menu;
++
++ menu = menu_new(name, _("_Send to desktop"), TRUE, NULL);
++ menu_set_update_func(menu, client_menu_send_to_menu_update);
++ return(menu);
++}
++
+ void client_menu_startup(void)
+ {
++ guint const ca_client = CA_SEND_TO | CA_LAYER | CA_ICONIFY |
++ CA_RESTORE | CA_MAXIMIZE | CA_SHADE | CA_DECORATE |
++ CA_MOVE | CA_RESIZE | CA_CLOSE;
+ ObMenu *menu;
+- ObMenuEntry *e;
+
+ menu = menu_new(LAYER_MENU_NAME, _("_Layer"), TRUE, NULL);
+ menu_show_all_shortcuts(menu, TRUE);
+@@ -377,8 +426,7 @@ void client_menu_startup(void)
+ menu_add_normal(menu, LAYER_NORMAL, _("_Normal"), NULL, TRUE);
+ menu_add_normal(menu, LAYER_BOTTOM, _("Always on _bottom"),NULL, TRUE);
+
+- menu = menu_new(SEND_TO_MENU_NAME, _("_Send to desktop"), TRUE, NULL);
+- menu_set_update_func(menu, send_to_menu_update);
++ menu = client_menu_send_to_menu_new(SEND_TO_MENU_NAME);
+ menu_set_execute_func(menu, send_to_menu_execute);
+
+ menu = menu_new(CLIENT_MENU_NAME, _("Client menu"), TRUE, NULL);
+@@ -390,32 +438,5 @@ void client_menu_startup(void)
+ menu_add_submenu(menu, CLIENT_SEND_TO, SEND_TO_MENU_NAME);
+
+ menu_add_submenu(menu, CLIENT_LAYER, LAYER_MENU_NAME);
+-
+- e = menu_add_normal(menu, CLIENT_RESTORE, _("R_estore"), NULL, TRUE);
+- e->mask = ob_rr_theme->btn_max->unpressed_toggled_mask;
+- set_icon_color(e);
+-
+- menu_add_normal(menu, CLIENT_MOVE, _("_Move"), NULL, TRUE);
+-
+- menu_add_normal(menu, CLIENT_RESIZE, _("Resi_ze"), NULL, TRUE);
+-
+- e = menu_add_normal(menu, CLIENT_ICONIFY, _("Ico_nify"), NULL, TRUE);
+- e->mask = ob_rr_theme->btn_iconify->unpressed_mask;
+- set_icon_color(e);
+-
+- e = menu_add_normal(menu, CLIENT_MAXIMIZE, _("Ma_ximize"), NULL, TRUE);
+- e->mask = ob_rr_theme->btn_max->unpressed_mask;
+- set_icon_color(e);
+-
+- e = menu_add_normal(menu, CLIENT_SHADE, _("_Roll up/down"), NULL, TRUE);
+- e->mask = ob_rr_theme->btn_shade->unpressed_mask;
+- set_icon_color(e);
+-
+- menu_add_normal(menu, CLIENT_DECORATE, _("Un/_Decorate"), NULL, TRUE);
+-
+- menu_add_separator(menu, -1, NULL);
+-
+- e = menu_add_normal(menu, CLIENT_CLOSE, _("_Close"), NULL, TRUE);
+- e->mask = ob_rr_theme->btn_close->unpressed_mask;
+- set_icon_color(e);
++ client_menu_add_actions(menu, ca_client);
+ }
+diff --git a/openbox/client_menu.h b/openbox/client_menu.h
+index 5c55516..2c16334 100644
+--- a/openbox/client_menu.h
++++ b/openbox/client_menu.h
+@@ -18,7 +18,38 @@
+
+ #ifndef ob__client_menu_h
+ #define ob__client_menu_h
++#include "client.h"
+
+-void client_menu_startup(void);
++enum {
++ CLIENT_SEND_TO,
++ CLIENT_LAYER,
++ CLIENT_ICONIFY,
++ CLIENT_RESTORE,
++ CLIENT_MAXIMIZE,
++ CLIENT_SHADE,
++ CLIENT_DECORATE,
++ CLIENT_MOVE,
++ CLIENT_RESIZE,
++ CLIENT_CLOSE,
++ CLIENT_ACTIVATE
++};
++
++// client actions bits for menu creation mask
++#define CA_SEND_TO (1<<CLIENT_SEND_TO)
++#define CA_LAYER (1<<CLIENT_LAYER)
++#define CA_ICONIFY (1<<CLIENT_ICONIFY)
++#define CA_RESTORE (1<<CLIENT_RESTORE)
++#define CA_MAXIMIZE (1<<CLIENT_MAXIMIZE)
++#define CA_SHADE (1<<CLIENT_SHADE)
++#define CA_DECORATE (1<<CLIENT_DECORATE)
++#define CA_MOVE (1<<CLIENT_MOVE)
++#define CA_RESIZE (1<<CLIENT_RESIZE)
++#define CA_CLOSE (1<<CLIENT_CLOSE)
+
++void client_menu_startup(void);
++gboolean client_menu_update(ObMenuFrame *frame, gpointer data);
++void client_menu_add_actions(ObMenu *menu, guint ca_mask);
++ObMenu *client_menu_send_to_menu_new(gchar *name);
++void client_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
++ ObClient *c, guint state, gpointer data);
+ #endif
+diff --git a/openbox/menu.c b/openbox/menu.c
+index 6c0cf7e..48c95e2 100644
+--- a/openbox/menu.c
++++ b/openbox/menu.c
+@@ -32,7 +32,6 @@
+ #include "misc.h"
+ #include "client_menu.h"
+ #include "client_list_menu.h"
+-#include "client_list_combined_menu.h"
+ #include "gettext.h"
+ #include "obt/xml.h"
+ #include "obt/paths.h"
+@@ -126,7 +125,6 @@ void menu_shutdown(gboolean reconfig)
+
+ menu_frame_hide_all();
+
+- client_list_combined_menu_shutdown(reconfig);
+ client_list_menu_shutdown(reconfig);
+
+ g_hash_table_destroy(menu_hash);
+diff --git a/openbox/menuframe.c b/openbox/menuframe.c
+index 943ff69..81a55ba 100644
+--- a/openbox/menuframe.c
++++ b/openbox/menuframe.c
+@@ -56,7 +56,6 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
+ static void menu_entry_frame_free(ObMenuEntryFrame *self);
+ static void menu_frame_update(ObMenuFrame *self);
+ static gboolean submenu_show_timeout(gpointer data);
+-static void menu_frame_hide(ObMenuFrame *self);
+
+ static gboolean submenu_hide_timeout(gpointer data);
+
+@@ -1130,7 +1129,7 @@ gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
+ return TRUE;
+ }
+
+-static void menu_frame_hide(ObMenuFrame *self)
++void menu_frame_hide(ObMenuFrame *self)
+ {
+ ObMenu *const menu = self->menu;
+ GList *it = g_list_find(menu_frame_visible, self);
+diff --git a/openbox/menuframe.h b/openbox/menuframe.h
+index ffcedf6..123cadf 100644
+--- a/openbox/menuframe.h
++++ b/openbox/menuframe.h
+@@ -129,6 +129,7 @@ gboolean menu_frame_show_topmenu(ObMenuFrame *self, const GravityPoint *pos,
+ gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
+ ObMenuEntryFrame *parent_entry);
+
++void menu_frame_hide(ObMenuFrame *self);
+ void menu_frame_hide_all(void);
+ void menu_frame_hide_all_client(struct _ObClient *client);
+
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 4509714..decb09e 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -3,7 +3,6 @@ openbox/actions.c
+ openbox/actions/execute.c
+ openbox/actions/exit.c
+ openbox/client.c
+-openbox/client_list_combined_menu.c
+ openbox/client_list_menu.c
+ openbox/client_menu.c
+ openbox/config.c
+diff --git a/po/openbox.pot b/po/openbox.pot
+index 3e5a5a9..aafc288 100644
+--- a/po/openbox.pot
++++ b/po/openbox.pot
+@@ -451,3 +451,6 @@ msgstr ""
+ #: openbox/prompt.c:154
+ msgid "OK"
+ msgstr ""
++
++msgid "_Activate"
++msgstr ""
+diff --git a/po/ru.po b/po/ru.po
+index 62424a3..ccbcb91 100644
+--- a/po/ru.po
++++ b/po/ru.po
+@@ -471,3 +471,6 @@ msgstr "Запрошенного ключа \"%s\" на экране не сущ
+ #: openbox/prompt.c:154
+ msgid "OK"
+ msgstr "OK"
++
++msgid "_Activate"
++msgstr "Активизировать (_A)"
+diff --git a/po/uk.po b/po/uk.po
+index 9d300ef..3642b63 100644
+--- a/po/uk.po
++++ b/po/uk.po
+@@ -474,3 +474,6 @@ msgstr "Потрібної кнопки \"%s\" нема на екрані"
+ #: openbox/prompt.c:154
+ msgid "OK"
+ msgstr "Гаразд"
++
++msgid "_Activate"
++msgstr "Активізувати (_A)"
diff --git a/compact_menu.patch b/compact_menu.patch
new file mode 100644
index 000000000000..e5c5296bd2bd
--- /dev/null
+++ b/compact_menu.patch
@@ -0,0 +1,12 @@
+diff --git a/openbox/menuframe.c b/openbox/menuframe.c
+--- a/openbox/menuframe.c
++++ b/openbox/menuframe.c
+@@ -31,7 +31,7 @@
+ #include "obt/display.h"
+ #include "obrender/theme.h"
+
+-#define PADDING 2
++#define PADDING 0
+ #define MAX_MENU_WIDTH 400
+
+ #define ITEM_HEIGHT (ob_rr_theme->menu_font_height + 2*PADDING)
diff --git a/disable_edges_by_default.patch b/disable_edges_by_default.patch
new file mode 100644
index 000000000000..3bff76d0377e
--- /dev/null
+++ b/disable_edges_by_default.patch
@@ -0,0 +1,55 @@
+diff --git a/openbox/config.c b/openbox/config.c
+--- a/openbox/config.c
++++ b/openbox/config.c
+@@ -27,6 +27,7 @@
+ #include "openbox.h"
+ #include "gettext.h"
+ #include "obt/paths.h"
++#include "edges.h"
+
+ gboolean config_focus_new;
+ gboolean config_focus_follow;
+@@ -565,6 +566,7 @@ static void parse_mouse(xmlNodePtr node, gpointer d)
+ }
+ continue;
+ }
++ enable_edge(cx - OB_FRAME_CONTEXT_EDGE_TOP);
+
+ nbut = obt_xml_find_node(n->children, "mousebind");
+ while (nbut) {
+diff --git a/openbox/edges.c b/openbox/edges.c
+index 2b40619..2aea791 100644
+--- a/openbox/edges.c
++++ b/openbox/edges.c
+@@ -10,7 +10,7 @@
+ /* Array of array of monitors of edges: edge[monitor 2][top edge] */
+ ObEdge ***edge = NULL;
+ #warning put in config.c and parse configs of course
+-gboolean config_edge_enabled[OB_NUM_EDGES] = {1, 1, 1, 1, 1, 1, 1, 1};
++gboolean config_edge_enabled[OB_NUM_EDGES] = {0, 0, 0, 0, 0, 0, 0, 0};
+ /* this could change at runtime, we should hook into that, but for now
+ * don't crash on reconfigure/shutdown */
+ static guint edge_monitors;
+@@ -133,3 +133,10 @@ void edges_configure()
+ edges_shutdown(TRUE);
+ edges_startup(TRUE);
+ }
++
++void enable_edge(int may_be_edge_index)
++{
++ if ((may_be_edge_index >= 0) && (may_be_edge_index < OB_NUM_EDGES))
++ config_edge_enabled[may_be_edge_index] = 1;
++
++}
+diff --git a/openbox/edges.h b/openbox/edges.h
+index 419cba6..1070e9f 100644
+--- a/openbox/edges.h
++++ b/openbox/edges.h
+@@ -25,6 +25,7 @@ struct _ObEdge
+ ObEdgeLocation location;
+ };
+
++void enable_edge(int may_be_edge_index);
+ void edges_startup(gboolean reconfigure);
+ void edges_shutdown(gboolean reconfigure);
+ void edges_configure(void);
diff --git a/edge_mikabox.patch b/edge_mikabox.patch
new file mode 100644
index 000000000000..3bd0180c5a03
--- /dev/null
+++ b/edge_mikabox.patch
@@ -0,0 +1,396 @@
+diff -urN /mnt/i/linux/openbox/openbox/Makefile.am ./Makefile.am
+--- /mnt/i/linux/openbox/openbox/Makefile.am 2014-12-25 11:21:03.000000000 +0200
++++ ./Makefile.am 2015-01-12 21:04:16.301754384 +0200
+@@ -247,6 +247,8 @@
+ openbox/debug.h \
+ openbox/dock.c \
+ openbox/dock.h \
++ openbox/edges.c \
++ openbox/edges.h \
+ openbox/event.c \
+ openbox/event.h \
+ openbox/focus.c \
+diff -urN /mnt/i/linux/openbox/openbox/openbox/edges.c ./openbox/edges.c
+--- /mnt/i/linux/openbox/openbox/openbox/edges.c 1970-01-01 03:00:00.000000000 +0300
++++ ./openbox/edges.c 2015-01-12 21:04:26.662753953 +0200
+@@ -0,0 +1,135 @@
++#include "openbox.h"
++#include "config.h"
++#include "screen.h"
++#include "edges.h"
++#include "frame.h"
++
++#include <X11/Xlib.h>
++#include <glib.h>
++
++/* Array of array of monitors of edges: edge[monitor 2][top edge] */
++ObEdge ***edge = NULL;
++#warning put in config.c and parse configs of course
++gboolean config_edge_enabled[OB_NUM_EDGES] = {1, 1, 1, 1, 1, 1, 1, 1};
++/* this could change at runtime, we should hook into that, but for now
++ * don't crash on reconfigure/shutdown */
++static guint edge_monitors;
++
++#ifdef DEBUG
++#define EDGE_WIDTH 10
++#define CORNER_SIZE 20
++#else
++#define EDGE_WIDTH 1
++#define CORNER_SIZE 2
++#endif
++static void get_position(ObEdgeLocation edge, Rect screen, Rect *rect)
++{
++ switch (edge) {
++ case OB_EDGE_TOP:
++ RECT_SET(*rect, CORNER_SIZE, 0,
++ screen.width - 2 * CORNER_SIZE, EDGE_WIDTH);
++ break;
++ case OB_EDGE_TOPRIGHT:
++ RECT_SET(*rect, screen.width - CORNER_SIZE, 0,
++ CORNER_SIZE, CORNER_SIZE);
++ break;
++ case OB_EDGE_RIGHT:
++ RECT_SET(*rect, screen.width - EDGE_WIDTH, CORNER_SIZE,
++ EDGE_WIDTH, screen.height - 2 * CORNER_SIZE);
++ break;
++ case OB_EDGE_BOTTOMRIGHT:
++ RECT_SET(*rect, screen.width - CORNER_SIZE,
++ screen.height - CORNER_SIZE,
++ CORNER_SIZE, CORNER_SIZE);
++ break;
++ case OB_EDGE_BOTTOM:
++ RECT_SET(*rect, CORNER_SIZE, screen.height - EDGE_WIDTH,
++ screen.width - 2 * CORNER_SIZE, EDGE_WIDTH);
++ break;
++ case OB_EDGE_BOTTOMLEFT:
++ RECT_SET(*rect, 0, screen.height - CORNER_SIZE,
++ CORNER_SIZE, CORNER_SIZE);
++ break;
++ case OB_EDGE_LEFT:
++ RECT_SET(*rect, 0, CORNER_SIZE,
++ EDGE_WIDTH, screen.height - 2 * CORNER_SIZE);
++ break;
++ case OB_EDGE_TOPLEFT:
++ RECT_SET(*rect, 0, 0, CORNER_SIZE, CORNER_SIZE);
++ break;
++ }
++ rect[0].x += screen.x;
++ rect[0].y += screen.y;
++}
++
++void edges_startup(gboolean reconfigure)
++{
++ ObEdgeLocation i;
++ gint m;
++ Rect r;
++ XSetWindowAttributes xswa;
++
++ xswa.override_redirect = True;
++
++ edge_monitors = screen_num_monitors;
++
++ edge = g_slice_alloc(sizeof(ObEdge**) * edge_monitors);
++ for (m = 0; m < edge_monitors; m++) {
++ const Rect *monitor = screen_physical_area_monitor(m);
++ edge[m] = g_slice_alloc(sizeof(ObEdge*) * OB_NUM_EDGES);
++ for (i=0; i < OB_NUM_EDGES; i++) {
++ if (!config_edge_enabled[i])
++ continue;
++
++ edge[m][i] = g_slice_new(ObEdge);
++ edge[m][i]->obwin.type = OB_WINDOW_CLASS_EDGE;
++ edge[m][i]->location = i;
++
++ get_position(i, *monitor, &r);
++ edge[m][i]->win = XCreateWindow(obt_display, obt_root(ob_screen),
++ r.x, r.y, r.width, r.height, 0, 0, InputOnly,
++ CopyFromParent, CWOverrideRedirect, &xswa);
++ XSelectInput(obt_display, edge[m][i]->win, ButtonPressMask | ButtonReleaseMask
++ | EnterWindowMask | LeaveWindowMask);
++ XMapWindow(obt_display, edge[m][i]->win);
++
++ stacking_add(EDGE_AS_WINDOW(edge[m][i]));
++ window_add(&edge[m][i]->win, EDGE_AS_WINDOW(edge[m][i]));
++
++#ifdef DEBUG
++ ob_debug("mapped edge window %i at %03i %03i %02i %02i", i, r.x, r.y, r.width, r.height);
++#endif
++ }
++ }
++
++ XFlush(obt_display);
++}
++
++void edges_shutdown(gboolean reconfigure)
++{
++ gint i, m;
++
++ /* This is in case we get called before startup by screen_resize() */
++ if (!edge)
++ return;
++
++ for (m = 0; m < edge_monitors; m++) {
++ for (i = 0; i < OB_NUM_EDGES; i++) {
++ if (!config_edge_enabled[i])
++ continue;
++
++ window_remove(edge[m][i]->win);
++ stacking_remove(EDGE_AS_WINDOW(edge[m][i]));
++ XDestroyWindow(obt_display, edge[m][i]->win);
++ g_slice_free(ObEdge, edge[m][i]);
++ }
++ g_slice_free1(sizeof(ObEdge*) * OB_NUM_EDGES, edge[m]);
++ }
++ g_slice_free1(sizeof(ObEdge**) * edge_monitors, edge);
++}
++
++void edges_configure()
++{
++ edges_shutdown(TRUE);
++ edges_startup(TRUE);
++}
+diff -urN /mnt/i/linux/openbox/openbox/openbox/edges.h ./openbox/edges.h
+--- /mnt/i/linux/openbox/openbox/openbox/edges.h 1970-01-01 03:00:00.000000000 +0300
++++ ./openbox/edges.h 2015-01-12 21:04:26.664753953 +0200
+@@ -0,0 +1,32 @@
++#ifndef __edges_h
++#define __edges_h
++
++#include "window.h"
++
++typedef enum
++{
++ OB_EDGE_TOP,
++ OB_EDGE_TOPRIGHT,
++ OB_EDGE_RIGHT,
++ OB_EDGE_BOTTOMRIGHT,
++ OB_EDGE_BOTTOM,
++ OB_EDGE_BOTTOMLEFT,
++ OB_EDGE_LEFT,
++ OB_EDGE_TOPLEFT,
++ OB_NUM_EDGES
++} ObEdgeLocation;
++
++typedef struct _ObEdge ObEdge;
++
++struct _ObEdge
++{
++ ObWindow obwin;
++ Window win;
++ ObEdgeLocation location;
++};
++
++void edges_startup(gboolean reconfigure);
++void edges_shutdown(gboolean reconfigure);
++void edges_configure(void);
++
++#endif
+diff -urN /mnt/i/linux/openbox/openbox/openbox/event.c ./openbox/event.c
+--- /mnt/i/linux/openbox/openbox/openbox/event.c 2014-12-25 11:17:52.000000000 +0200
++++ ./openbox/event.c 2015-01-12 21:04:16.302754384 +0200
+@@ -30,6 +30,7 @@
+ #include "grab.h"
+ #include "menu.h"
+ #include "prompt.h"
++#include "edges.h"
+ #include "menuframe.h"
+ #include "keyboard.h"
+ #include "mouse.h"
+@@ -479,9 +480,6 @@
+ case OB_WINDOW_CLASS_MENUFRAME:
+ menu = WINDOW_AS_MENUFRAME(obwin);
+ break;
+- case OB_WINDOW_CLASS_INTERNAL:
+- /* we don't do anything with events directly on these windows */
+- break;
+ case OB_WINDOW_CLASS_PROMPT:
+ prompt = WINDOW_AS_PROMPT(obwin);
+ break;
+@@ -710,7 +708,7 @@
+ /* ...or it if it was physically on an openbox
+ internal window... */
+ ((w = window_find(e->xbutton.subwindow)) &&
+- (WINDOW_IS_INTERNAL(w) || WINDOW_IS_DOCK(w))))
++ (WINDOW_IS_INTERNAL(w) || WINDOW_IS_DOCK(w) || WINDOW_IS_EDGE(w))))
+ /* ...then process the event, otherwise ignore it */
+ {
+ used = event_handle_user_input(client, e);
+diff -urN /mnt/i/linux/openbox/openbox/openbox/frame.c ./openbox/frame.c
+--- /mnt/i/linux/openbox/openbox/openbox/frame.c 2014-11-07 19:58:40.000000000 +0200
++++ ./openbox/frame.c 2015-01-12 21:04:16.302754384 +0200
+@@ -28,6 +28,7 @@
+ #include "focus_cycle_indicator.h"
+ #include "moveresize.h"
+ #include "screen.h"
++#include "edges.h"
+ #include "obrender/theme.h"
+ #include "obt/display.h"
+ #include "obt/xqueue.h"
+@@ -1386,6 +1387,22 @@
+ return OB_FRAME_CONTEXT_MOVE_RESIZE;
+ else if (!g_ascii_strcasecmp("Dock", name))
+ return OB_FRAME_CONTEXT_DOCK;
++ else if (!g_ascii_strcasecmp("ScreenTop", name))
++ return OB_FRAME_CONTEXT_EDGE_TOP;
++ else if (!g_ascii_strcasecmp("ScreenTopRight", name))
++ return OB_FRAME_CONTEXT_EDGE_TOPRIGHT;
++ else if (!g_ascii_strcasecmp("ScreenRight", name))
++ return OB_FRAME_CONTEXT_EDGE_RIGHT;
++ else if (!g_ascii_strcasecmp("ScreenBottomRight", name))
++ return OB_FRAME_CONTEXT_EDGE_BOTTOMRIGHT;
++ else if (!g_ascii_strcasecmp("ScreenBottom", name))
++ return OB_FRAME_CONTEXT_EDGE_BOTTOM;
++ else if (!g_ascii_strcasecmp("ScreenBottomLeft", name))
++ return OB_FRAME_CONTEXT_EDGE_BOTTOMLEFT;
++ else if (!g_ascii_strcasecmp("ScreenLeft", name))
++ return OB_FRAME_CONTEXT_EDGE_LEFT;
++ else if (!g_ascii_strcasecmp("ScreenTopLeft", name))
++ return OB_FRAME_CONTEXT_EDGE_TOPLEFT;
+
+ return OB_FRAME_CONTEXT_NONE;
+ }
+@@ -1397,12 +1414,14 @@
+
+ if (moveresize_in_progress)
+ return OB_FRAME_CONTEXT_MOVE_RESIZE;
+-
+ if (win == obt_root(ob_screen))
+ return OB_FRAME_CONTEXT_ROOT;
+ if ((obwin = window_find(win))) {
+ if (WINDOW_IS_DOCK(obwin)) {
+ return OB_FRAME_CONTEXT_DOCK;
++ } else if (WINDOW_IS_EDGE(obwin)) {
++ ObEdge *edge = (ObEdge *)obwin;
++ return OB_FRAME_CONTEXT_EDGE_TOP + edge->location;
+ }
+ }
+ if (client == NULL) return OB_FRAME_CONTEXT_NONE;
+diff -urN /mnt/i/linux/openbox/openbox/openbox/frame.h ./openbox/frame.h
+--- /mnt/i/linux/openbox/openbox/openbox/frame.h 2014-11-07 19:58:40.000000000 +0200
++++ ./openbox/frame.h 2015-01-12 21:04:16.303754384 +0200
+@@ -54,6 +54,14 @@
+ a move/resize */
+ OB_FRAME_CONTEXT_MOVE_RESIZE,
+ OB_FRAME_CONTEXT_DOCK,
++ OB_FRAME_CONTEXT_EDGE_TOP,
++ OB_FRAME_CONTEXT_EDGE_TOPRIGHT,
++ OB_FRAME_CONTEXT_EDGE_RIGHT,
++ OB_FRAME_CONTEXT_EDGE_BOTTOMRIGHT,
++ OB_FRAME_CONTEXT_EDGE_BOTTOM,
++ OB_FRAME_CONTEXT_EDGE_BOTTOMLEFT,
++ OB_FRAME_CONTEXT_EDGE_LEFT,
++ OB_FRAME_CONTEXT_EDGE_TOPLEFT,
+ OB_FRAME_NUM_CONTEXTS
+ } ObFrameContext;
+
+diff -urN /mnt/i/linux/openbox/openbox/openbox/openbox.c ./openbox/openbox.c
+--- /mnt/i/linux/openbox/openbox/openbox/openbox.c 2014-11-07 19:58:40.000000000 +0200
++++ ./openbox/openbox.c 2015-01-12 21:04:16.303754384 +0200
+@@ -21,6 +21,7 @@
+ #include "openbox.h"
+ #include "session.h"
+ #include "dock.h"
++#include "edges.h"
+ #include "event.h"
+ #include "menu.h"
+ #include "client.h"
+@@ -327,6 +328,7 @@
+ mouse_startup(reconfigure);
+ menu_frame_startup(reconfigure);
+ menu_startup(reconfigure);
++ edges_startup(reconfigure);
+ prompt_startup(reconfigure);
+
+ /* do this after everything is started so no events will get
+@@ -392,6 +394,7 @@
+ window_unmanage_all();
+
+ prompt_shutdown(reconfigure);
++ edges_shutdown(reconfigure);
+ menu_shutdown(reconfigure);
+ menu_frame_shutdown(reconfigure);
+ mouse_shutdown(reconfigure);
+diff -urN /mnt/i/linux/openbox/openbox/openbox/screen.c ./openbox/screen.c
+--- /mnt/i/linux/openbox/openbox/openbox/screen.c 2014-11-07 19:58:40.000000000 +0200
++++ ./openbox/screen.c 2015-01-12 21:04:26.665753953 +0200
+@@ -20,6 +20,7 @@
+ #include "debug.h"
+ #include "openbox.h"
+ #include "dock.h"
++#include "edges.h"
+ #include "grab.h"
+ #include "startupnotify.h"
+ #include "moveresize.h"
+@@ -500,6 +501,7 @@
+
+ /* this calls screen_update_areas(), which we need ! */
+ dock_configure();
++ edges_configure();
+
+ for (it = client_list; it; it = g_list_next(it)) {
+ client_move_onscreen(it->data, FALSE);
+diff -urN /mnt/i/linux/openbox/openbox/openbox/window.c ./openbox/window.c
+--- /mnt/i/linux/openbox/openbox/openbox/window.c 2014-11-07 19:58:40.000000000 +0200
++++ ./openbox/window.c 2015-01-12 21:04:21.164754182 +0200
+@@ -24,6 +24,7 @@
+ #include "frame.h"
+ #include "openbox.h"
+ #include "prompt.h"
++#include "edges.h"
+ #include "debug.h"
+ #include "grab.h"
+ #include "obt/prop.h"
+@@ -62,6 +63,8 @@
+ return WINDOW_AS_INTERNAL(self)->window;
+ case OB_WINDOW_CLASS_PROMPT:
+ return WINDOW_AS_PROMPT(self)->super.window;
++ case OB_WINDOW_CLASS_EDGE:
++ return WINDOW_AS_EDGE(self)->win;
+ }
+ g_assert_not_reached();
+ return None;
+@@ -74,6 +77,7 @@
+ return config_dock_layer;
+ case OB_WINDOW_CLASS_CLIENT:
+ return ((ObClient*)self)->layer;
++ case OB_WINDOW_CLASS_EDGE:
+ case OB_WINDOW_CLASS_MENUFRAME:
+ case OB_WINDOW_CLASS_INTERNAL:
+ return OB_STACKING_LAYER_INTERNAL;
+diff -urN /mnt/i/linux/openbox/openbox/openbox/window.h ./openbox/window.h
+--- /mnt/i/linux/openbox/openbox/openbox/window.h 2014-11-07 19:58:40.000000000 +0200
++++ ./openbox/window.h 2015-01-12 21:04:16.303754384 +0200
+@@ -32,7 +32,8 @@
+ OB_WINDOW_CLASS_DOCK,
+ OB_WINDOW_CLASS_CLIENT,
+ OB_WINDOW_CLASS_INTERNAL,
+- OB_WINDOW_CLASS_PROMPT
++ OB_WINDOW_CLASS_PROMPT,
++ OB_WINDOW_CLASS_EDGE
+ } ObWindowClass;
+
+ /* In order to be an ObWindow, you need to make this struct the top of your
+@@ -51,6 +52,8 @@
+ (((ObWindow*)win)->type == OB_WINDOW_CLASS_INTERNAL)
+ #define WINDOW_IS_PROMPT(win) \
+ (((ObWindow*)win)->type == OB_WINDOW_CLASS_PROMPT)
++#define WINDOW_IS_EDGE(win) \
++ (((ObWindow*)win)->type == OB_WINDOW_CLASS_EDGE)
+
+ struct _ObMenu;
+ struct _ObDock;
+@@ -63,12 +66,14 @@
+ #define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win)
+ #define WINDOW_AS_INTERNAL(win) ((struct _ObInternalWindow*)win)
+ #define WINDOW_AS_PROMPT(win) ((struct _ObPrompt*)win)
++#define WINDOW_AS_EDGE(win) ((struct _ObEdge*)win)
+
+ #define MENUFRAME_AS_WINDOW(menu) ((ObWindow*)menu)
+ #define DOCK_AS_WINDOW(dock) ((ObWindow*)dock)
+ #define CLIENT_AS_WINDOW(client) ((ObWindow*)client)
+ #define INTERNAL_AS_WINDOW(intern) ((ObWindow*)intern)
+ #define PROMPT_AS_WINDOW(prompt) ((ObWindow*)prompt)
++#define EDGE_AS_WINDOW(edge) ((ObWindow*)edge)
+
+ void window_startup (gboolean reconfig);
+ void window_shutdown(gboolean reconfig);
diff --git a/import-bind.patch b/import-bind.patch
new file mode 100644
index 000000000000..89498bce3605
--- /dev/null
+++ b/import-bind.patch
@@ -0,0 +1,107 @@
+commit ce42ade7286fa387ec77d66d79b546ddeae1592b
+Author: Alexey Korop <avkorop@i.ua>
+Date: Sun Mar 15 19:03:24 2015 +0200
+
+ import-bind.patch
+
+diff --git a/openbox/config.c b/openbox/config.c
+index 7a4eaa1..8d50846 100644
+--- a/openbox/config.c
++++ b/openbox/config.c
+@@ -572,6 +572,18 @@ static void parse_mouse(xmlNodePtr node, gpointer d)
+ continue;
+ }
+
++ xmlNodePtr import_node = obt_xml_find_node(n->children, "import");
++ while (import_node) { // import bindings from another context
++ gchar *s = obt_xml_node_string(import_node);
++ if (s) {
++ ObFrameContext import_ctx = frame_context_from_string(s);
++ if (import_ctx != OB_FRAME_CONTEXT_NONE) {
++ mouse_import_bind(cx, import_ctx);
++ }
++ }
++ import_node = obt_xml_find_node(import_node->next, "import");
++ }
++
+ for (nbut = obt_xml_find_node(n->children, "mousebind");
+ nbut;
+ nbut = obt_xml_find_node(nbut->next, "mousebind"))
+diff --git a/openbox/mouse.c b/openbox/mouse.c
+index 4da22f3..08a06d4 100644
+--- a/openbox/mouse.c
++++ b/openbox/mouse.c
+@@ -369,20 +369,14 @@ gboolean mouse_event(ObClient *client, XEvent *e)
+ return used;
+ }
+
+-gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context,
++static gboolean mouse_bind_b(guint state, guint button, ObFrameContext context,
+ ObMouseAction mact, ObActionsAct *action)
+ {
+- guint state = 0, button = 0;
+ ObMouseBinding *b;
+ GSList *it;
+
+ g_assert(context != OB_FRAME_CONTEXT_NONE);
+
+- if (!translate_button(buttonstr, &state, &button)) {
+- g_message(_("Invalid button \"%s\" in mouse binding"), buttonstr);
+- return FALSE;
+- }
+-
+ for (it = bound_contexts[context]; it; it = g_slist_next(it)) {
+ b = it->data;
+ if (b->state == state && b->button == button) {
+@@ -401,6 +395,39 @@ gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context,
+ return TRUE;
+ }
+
++gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context,
++ ObMouseAction mact, ObActionsAct *action)
++{
++ guint state = 0, button = 0;
++ ObMouseBinding *b;
++ GSList *it;
++
++ if (!translate_button(buttonstr, &state, &button)) {
++ g_message(_("Invalid button \"%s\" in mouse binding"), buttonstr);
++ return FALSE;
++ }
++ return(mouse_bind_b(state, button, context, mact, action));
++}
++
++void mouse_import_bind(ObFrameContext context, ObFrameContext import_ctx)
++{
++ GSList *it;
++ ObActionsAct *action;
++ GSList *jt;
++
++ for (it = bound_contexts[import_ctx]; it; it = g_slist_next(it)) {
++ ObMouseBinding *b = it->data;
++ gint j;
++ for (j = 0; j < OB_NUM_MOUSE_ACTIONS; ++j) {
++ for (jt = b->actions[j]; jt; jt = g_slist_next(jt)) {
++ action = jt->data;
++ actions_act_ref(action);
++ mouse_bind_b(b->state, b->button, context, j, action);
++ }
++ }
++ }
++}
++
+ void mouse_startup(gboolean reconfig)
+ {
+ grab_all_clients(TRUE);
+diff --git a/openbox/mouse.h b/openbox/mouse.h
+index de4c0ec..90749b4 100644
+--- a/openbox/mouse.h
++++ b/openbox/mouse.h
+@@ -32,6 +32,7 @@ void mouse_shutdown(gboolean reconfig);
+ gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context,
+ ObMouseAction mact, struct _ObActionsAct *action);
+ void mouse_unbind_all(void);
++void mouse_import_bind(ObFrameContext context, ObFrameContext import_ctx);
+
+ gboolean mouse_event(struct _ObClient *client, XEvent *e);
+
diff --git a/install b/install
new file mode 100755
index 000000000000..4955fa6af71b
--- /dev/null
+++ b/install
@@ -0,0 +1,14 @@
+post_install() {
+ echo "Config samples see in /etc/openbox_eui/samples."
+ echo "If You use obconf and/or lxappearance-obconf You must"
+ echo " rebuild these packages from sources (yaourt -Sb obconf)"
+}
+
+post_upgrade() {
+ post_install
+}
+
+post_remove() {
+ echo "If You use obconf and/or lxappearance-obconf You must"
+ echo "reinstall these packages from official repository"
+}
diff --git a/keybind_item.patch b/keybind_item.patch
new file mode 100644
index 000000000000..64f212e7e115
--- /dev/null
+++ b/keybind_item.patch
@@ -0,0 +1,44 @@
+diff -ruw -X openbox-efc7efc/.gitignore openbox-efc7efc/openbox/menu.c openbox-efc7efc^/openbox/menu.c
+--- openbox-efc7efc/openbox/menu.c 2014-12-06 19:20:47.000000000 +0200
++++ openbox-efc7efc^/openbox/menu.c 2014-12-06 18:46:06.000000000 +0200
+@@ -116,6 +116,7 @@
+ }
+
+ g_assert(menu_parse_state.parent == NULL);
++ keyboard_rebind();
+ }
+
+ void menu_shutdown(gboolean reconfig)
+@@ -282,6 +283,8 @@
+ gboolean is_default = FALSE;
+ gchar *icon;
+ ObMenuEntry *e;
++ GList *keylist;
++ gchar *keystring, **keys, **key;
+
+ if (state->parent) {
+ /* Don't try to extract "icon" attribute if icons in user-defined
+@@ -292,11 +295,22 @@
+ xmlNodePtr c;
+ GSList *acts = NULL;
+
++ keylist = NULL;
++ if (obt_xml_attr_string(node, "key", &keystring)) {
++ keys = g_strsplit(keystring, " ", 0);
++ for (key = keys; *key; ++key)
++ keylist = g_list_append(keylist, *key);
++ }
+ c = obt_xml_find_node(node->children, "action");
+ while (c) {
+ ObActionsAct *action = actions_parse(c);
+- if (action)
++ if (action) {
++ if (keylist) {
++ actions_act_ref(action);
++ keyboard_bind(keylist, action);
++ }
+ acts = g_slist_append(acts, action);
++ }
+ c = obt_xml_find_node(c->next, "action");
+ }
+ e = menu_add_normal(state->parent, -1, label, acts, TRUE);
diff --git a/keybind_menu2.patch b/keybind_menu2.patch
new file mode 100644
index 000000000000..93fae5e86ce4
--- /dev/null
+++ b/keybind_menu2.patch
@@ -0,0 +1,161 @@
+commit d112fc0e8a9d9d5fdd07a9a3698b76d1cfc6eb5c
+Author: Alexey Korop <avkorop@i.ua>
+Date: Sun Mar 15 17:28:50 2015 +0200
+
+ keybind_menu.patch
+
+diff --git a/openbox/actions.c b/openbox/actions.c
+index bf00c6c..994740a 100644
+--- a/openbox/actions.c
++++ b/openbox/actions.c
+@@ -28,6 +28,7 @@
+ #include "debug.h"
+
+ #include "actions/all.h"
++#include "actions/showmenu.h"
+
+ static void actions_definition_ref(ObActionsDefinition *def);
+ static void actions_definition_unref(ObActionsDefinition *def);
+@@ -237,6 +238,16 @@ static ObActionsAct* actions_build_act_from_string(const gchar *name)
+ return act;
+ }
+
++ObActionsAct* actions_build_act_ShowMenu(const gchar *name)
++{
++ ObActionsAct *act;
++
++ act = actions_parse_string(ACTION_SHOW_MENU);
++ act->options = default_show_menu_options(g_strdup(name));
++ return act;
++}
++
++
+ ObActionsAct* actions_parse_string(const gchar *name)
+ {
+ ObActionsAct *act = NULL;
+diff --git a/openbox/actions.h b/openbox/actions.h
+index f8e1ba8..9d25b33 100644
+--- a/openbox/actions.h
++++ b/openbox/actions.h
+@@ -15,6 +15,8 @@
+
+ See the COPYING file for a copy of the GNU General Public License.
+ */
++#ifndef __actions_h
++#define __actions_h
+
+ #include "misc.h"
+ #include "frame.h"
+@@ -121,3 +123,8 @@ gboolean actions_interactive_input_event(XEvent *e);
+
+ /*! Function for actions to call when they are moving a client around */
+ void actions_client_move(ObActionsData *data, gboolean start);
++
++#define ACTION_SHOW_MENU "ShowMenu"
++ObActionsAct* actions_build_act_ShowMenu(const gchar *name);
++
++#endif
+diff --git a/openbox/actions/showmenu.c b/openbox/actions/showmenu.c
+index 7411e98..49047f0 100644
+--- a/openbox/actions/showmenu.c
++++ b/openbox/actions/showmenu.c
+@@ -5,6 +5,7 @@
+ #include "openbox/screen.h"
+ #include "openbox/config.h"
+ #include <glib.h>
++#include "showmenu.h"
+
+ typedef struct {
+ gchar *name;
+@@ -18,9 +19,20 @@ static gpointer setup_func(xmlNodePtr node);
+ static void free_func(gpointer options);
+ static gboolean run_func(ObActionsData *data, gpointer options);
+
++gpointer default_show_menu_options(gchar* name)
++{
++ Options *o;
++
++ o = g_slice_new0(Options);
++ o->monitor = -1;
++ o->name = name;
++ return o;
++}
++
++
+ void action_showmenu_startup(void)
+ {
+- actions_register("ShowMenu", setup_func, free_func, run_func);
++ actions_register(ACTION_SHOW_MENU, setup_func, free_func, run_func);
+ }
+
+ static gpointer setup_func(xmlNodePtr node)
+@@ -29,11 +41,8 @@ static gpointer setup_func(xmlNodePtr node)
+ Options *o;
+ gboolean x_pos_given = FALSE;
+
+- o = g_slice_new0(Options);
+- o->monitor = -1;
+-
+- if ((n = obt_xml_find_node(node, "menu")))
+- o->name = obt_xml_node_string(n);
++ n = obt_xml_find_node(node, "menu");
++ o = (Options*)default_show_menu_options(obt_xml_node_string(n));
+
+ if ((n = obt_xml_find_node(node, "position"))) {
+ if ((c = obt_xml_find_node(n->children, "x"))) {
+diff --git a/openbox/actions/showmenu.h b/openbox/actions/showmenu.h
+new file mode 100644
+index 0000000..217dc90
+--- /dev/null
++++ b/openbox/actions/showmenu.h
+@@ -0,0 +1,2 @@
++/* in fact, return Options* */
++gpointer default_show_menu_options(gchar* name);
+diff --git a/openbox/menu.c b/openbox/menu.c
+index 057e54a..4d461f1 100644
+--- a/openbox/menu.c
++++ b/openbox/menu.c
+@@ -19,6 +19,7 @@
+
+ #include "debug.h"
+ #include "menu.h"
++#include "actions.h"
+ #include "openbox.h"
+ #include "stacking.h"
+ #include "grab.h"
+@@ -353,14 +354,23 @@ static void parse_menu(xmlNodePtr node, gpointer data)
+ ObMenu *menu;
+ ObMenuEntry *e;
+ gchar *icon;
++ GList *keylist = NULL;
++ gchar *keystring, **keys, **key;
+
+ if (!obt_xml_attr_string(node, "id", &name))
+ goto parse_menu_fail;
+
++ if (obt_xml_attr_string(node, "key", &keystring)) { // global keybind
++ keys = g_strsplit(keystring, " ", 0);
++ for (key = keys; *key; ++key)
++ keylist = g_list_append(keylist, *key);
++ }
++
+ if (!g_hash_table_lookup(menu_hash, name)) {
+ if (!obt_xml_attr_string_unstripped(node, "label", &title))
+ goto parse_menu_fail;
+
++
+ if ((menu = menu_new(name, title, TRUE, NULL))) {
+ menu->pipe_creator = state->pipe_creator;
+ if (obt_xml_attr_string(node, "execute", &script)) {
+@@ -373,6 +383,11 @@ static void parse_menu(xmlNodePtr node, gpointer data)
+ obt_xml_tree(menu_parse_inst, node->children);
+ state->parent = old;
+ }
++ if (keylist) { // add keybind to show this menu
++ ObActionsAct *action;
++ action = actions_build_act_ShowMenu(name);
++ keyboard_bind(keylist, action);
++ }
+ }
+ }
+
diff --git a/menu.xml b/menu.xml
new file mode 100644
index 000000000000..e32bae74fe2c
--- /dev/null
+++ b/menu.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openbox_menu xmlns="http://openbox.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://openbox.org/ file:///usr/share/openbox/menu.xsd">
+ <!-- main menu; hotkey - Win+Space -->
+ <menu id="root-menu" label="Openbox 3" key="W-space">
+ <menu id="desktop-app-menu" icon="/usr/share/icons/gnome/16x16/categories/applications-other.png" label="Applications" execute="xdg_menu --format openbox3-pipe" key=""/>
+ <!-- Run dialog; hotkey - Win+r -->
+ <item icon="/usr/share/icons/gnome/16x16/actions/system-run.png" label="Run Program" key="W-r">
+ <action name="Execute">
+ <command>gmrun</command>
+ </action>
+ </item>
+ <!-- "Exit" menu with external scripts; default item - shutdown.
+ For shutdown You may press "Win+Space" (main menu), then "x" (e_Xit menu) -->
+ <menu id="exit" label="e_Xit" icon="/usr/share/icons/gnome/16x16/actions/system-log-out.png" key="W-x">
+ <item icon="/usr/share/icons/gnome/16x16/actions/media-playback-pause.png" label="hibernate">
+ <action name="Execute">
+ <execute>.hibernate</execute>
+ </action>
+ </item>
+ <item icon="/usr/share/icons/gnome/16x16/actions/system-log-out.png" label="Logout">
+ <action name="Execute">
+ <execute>.logoff</execute>
+ </action>
+ </item>
+ <item icon="/usr/share/icons/gnome/16x16/actions/window-close.png" label="Reboot">
+ <action name="Execute">
+ <execute>.reboot</execute>
+ </action>
+ </item>
+ <item icon="/usr/share/icons/gnome/16x16/actions/system-shutdown.png" label="Shutdown" default="yes">
+ <action name="Execute">
+ <execute>.shutdown</execute>
+ </action>
+ </item>
+ </menu>
+ </menu>
+</openbox_menu>
diff --git a/menu_w_def9.patch b/menu_w_def9.patch
new file mode 100644
index 000000000000..dd2f59dfdab2
--- /dev/null
+++ b/menu_w_def9.patch
@@ -0,0 +1,754 @@
+commit 36f80459b6b8f947023ed551dd61cdd3d8d16d0d
+Author: Alexey Korop <avkorop@i.ua>
+Date: Sat Mar 14 21:07:58 2015 +0200
+
+ menu_w_def8.patch
+
+diff --git a/obrender/theme.c b/obrender/theme.c
+index 2a4f6e1..7b1969f 100644
+--- a/obrender/theme.c
++++ b/obrender/theme.c
+@@ -215,6 +215,8 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
+ theme->a_menu_text_disabled_selected = RrAppearanceNew(inst, 1);
+ theme->a_menu_bullet_normal = RrAppearanceNew(inst, 1);
+ theme->a_menu_bullet_selected = RrAppearanceNew(inst, 1);
++ theme->a_menu_bullet_def_normal = RrAppearanceNew(inst, 1);
++ theme->a_menu_bullet_def_selected = RrAppearanceNew(inst, 1);
+ theme->a_clear = RrAppearanceNew(inst, 0);
+ theme->a_clear_tex = RrAppearanceNew(inst, 1);
+ theme->osd_bg = RrAppearanceNew(inst, 0);
+@@ -527,6 +529,13 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
+ theme->menu_bullet_mask = RrPixmapMaskNew(inst, 4, 7, (gchar*)data);
+ }
+
++ /* submenu with default bullet mask */
++ if (!read_mask(inst, path, "bullet_def.xbm", &theme->menu_bullet_def_mask))
++ {
++ guchar data[] = { 0xFF, 0x85, 0x8D, 0x9D, 0xBD, 0x9D, 0x8D, 0x85, 0xFF };
++ theme->menu_bullet_def_mask = RrPixmapMaskNew(inst, 8, 9, (gchar*)data);
++ }
++
+ /* up and down arrows */
+ {
+ guchar data[] = { 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00 };
+@@ -618,6 +627,8 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
+ theme->a_menu_text_disabled_selected->surface.grad =
+ theme->a_menu_bullet_normal->surface.grad =
+ theme->a_menu_bullet_selected->surface.grad = RR_SURFACE_PARENTREL;
++ theme->a_menu_bullet_def_normal->surface.grad =
++ theme->a_menu_bullet_def_selected->surface.grad = RR_SURFACE_PARENTREL;
+
+ /* set up the textures */
+ theme->a_focused_label->texture[0].type = RR_TEXTURE_TEXT;
+@@ -932,13 +943,26 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
+
+ theme->a_menu_bullet_normal->texture[0].type =
+ theme->a_menu_bullet_selected->texture[0].type = RR_TEXTURE_MASK;
++ theme->a_menu_bullet_def_normal->texture[0].type =
++ theme->a_menu_bullet_def_selected->texture[0].type = RR_TEXTURE_MASK;
+ theme->a_menu_bullet_normal->texture[0].data.mask.mask =
+ theme->a_menu_bullet_selected->texture[0].data.mask.mask =
+ theme->menu_bullet_mask;
++ theme->a_menu_bullet_def_normal->texture[0].data.mask.mask =
++ theme->a_menu_bullet_def_selected->texture[0].data.mask.mask =
++ theme->menu_bullet_def_mask;
+ theme->a_menu_bullet_normal->texture[0].data.mask.color =
+ theme->menu_bullet_color;
+ theme->a_menu_bullet_selected->texture[0].data.mask.color =
+ theme->menu_bullet_selected_color;
++ theme->a_menu_bullet_def_normal->texture[0].data.mask.color =
++ theme->menu_bullet_color;
++ theme->a_menu_bullet_def_selected->texture[0].data.mask.color =
++ theme->menu_bullet_selected_color;
++ theme->a_menu_bullet_def_normal->texture[0].data.mask.color =
++ theme->menu_bullet_color;
++ theme->a_menu_bullet_def_selected->texture[0].data.mask.color =
++ theme->menu_bullet_selected_color;
+
+ g_free(path);
+ XrmDestroyDatabase(db);
+@@ -1065,6 +1089,7 @@ void RrThemeFree(RrTheme *theme)
+ g_free(theme->def_win_icon);
+
+ RrPixmapMaskFree(theme->menu_bullet_mask);
++ RrPixmapMaskFree(theme->menu_bullet_def_mask);
+ RrPixmapMaskFree(theme->down_arrow_mask);
+ RrPixmapMaskFree(theme->up_arrow_mask);
+
+@@ -1097,6 +1122,8 @@ void RrThemeFree(RrTheme *theme)
+ RrAppearanceFree(theme->a_menu_text_disabled_selected);
+ RrAppearanceFree(theme->a_menu_bullet_normal);
+ RrAppearanceFree(theme->a_menu_bullet_selected);
++ RrAppearanceFree(theme->a_menu_bullet_def_normal);
++ RrAppearanceFree(theme->a_menu_bullet_def_selected);
+ RrAppearanceFree(theme->a_clear);
+ RrAppearanceFree(theme->a_clear_tex);
+ RrAppearanceFree(theme->osd_bg);
+diff --git a/obrender/theme.h b/obrender/theme.h
+index 8797f0b..2c732fc 100644
+--- a/obrender/theme.h
++++ b/obrender/theme.h
+@@ -123,6 +123,7 @@ struct _RrTheme {
+
+ /* style settings - masks */
+ RrPixmapMask *menu_bullet_mask; /* submenu pointer */
++ RrPixmapMask *menu_bullet_def_mask; /* submenu with default pointer */
+ #if 0
+ RrPixmapMask *menu_toggle_mask; /* menu boolean */
+ #endif
+@@ -161,6 +162,8 @@ struct _RrTheme {
+ RrAppearance *a_menu_text_selected;
+ RrAppearance *a_menu_bullet_normal;
+ RrAppearance *a_menu_bullet_selected;
++ RrAppearance *a_menu_bullet_def_normal;
++ RrAppearance *a_menu_bullet_def_selected;
+ RrAppearance *a_clear; /* clear with no texture */
+ RrAppearance *a_clear_tex; /* clear with a texture */
+
+diff --git a/openbox/client_list_combined_menu.c b/openbox/client_list_combined_menu.c
+index c26b6fa..84eb506 100644
+--- a/openbox/client_list_combined_menu.c
++++ b/openbox/client_list_combined_menu.c
+@@ -74,9 +74,9 @@ static gboolean self_update(ObMenuFrame *frame, gpointer data)
+ }
+
+ if (config_menu_show_icons) {
+- e->data.normal.icon = client_icon(c);
+- RrImageRef(e->data.normal.icon);
+- e->data.normal.icon_alpha =
++ e->icon = client_icon(c);
++ RrImageRef(e->icon);
++ e->icon_alpha =
+ c->iconic ? OB_ICONIC_ALPHA : 0xff;
+ }
+
+diff --git a/openbox/client_list_menu.c b/openbox/client_list_menu.c
+index f3df2a5..4f85935 100644
+--- a/openbox/client_list_menu.c
++++ b/openbox/client_list_menu.c
+@@ -70,9 +70,9 @@ static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data)
+ }
+
+ if (config_menu_show_icons) {
+- e->data.normal.icon = client_icon(c);
+- RrImageRef(e->data.normal.icon);
+- e->data.normal.icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff;
++ e->icon = client_icon(c);
++ RrImageRef(e->icon);
++ e->icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff;
+ }
+
+ e->data.normal.data = c;
+diff --git a/openbox/client_menu.c b/openbox/client_menu.c
+index 4a3b286..6d5c4c7 100644
+--- a/openbox/client_menu.c
++++ b/openbox/client_menu.c
+@@ -57,10 +57,10 @@ enum {
+
+ static void set_icon_color(ObMenuEntry *e)
+ {
+- e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
+- e->data.normal.mask_selected_color = ob_rr_theme->menu_selected_color;
+- e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
+- e->data.normal.mask_disabled_selected_color =
++ e->mask_normal_color = ob_rr_theme->menu_color;
++ e->mask_selected_color = ob_rr_theme->menu_selected_color;
++ e->mask_disabled_color = ob_rr_theme->menu_disabled_color;
++ e->mask_disabled_selected_color =
+ ob_rr_theme->menu_disabled_selected_color;
+ }
+
+@@ -276,10 +276,10 @@ static gboolean send_to_menu_update(ObMenuFrame *frame, gpointer data)
+ if ((desk == DESKTOP_ALL && c->desktop != DESKTOP_ALL) ||
+ (c->desktop == DESKTOP_ALL && desk == screen_desktop))
+ {
+- e->data.normal.mask = ob_rr_theme->btn_desk->unpressed_mask;
++ e->mask = ob_rr_theme->btn_desk->unpressed_mask;
+ set_icon_color(e);
+ } else
+- e->data.normal.mask = NULL;
++ e->mask = NULL;
+ }
+
+ return TRUE; /* show the menu */
+@@ -392,7 +392,7 @@ void client_menu_startup(void)
+ menu_add_submenu(menu, CLIENT_LAYER, LAYER_MENU_NAME);
+
+ e = menu_add_normal(menu, CLIENT_RESTORE, _("R_estore"), NULL, TRUE);
+- e->data.normal.mask = ob_rr_theme->btn_max->unpressed_toggled_mask;
++ e->mask = ob_rr_theme->btn_max->unpressed_toggled_mask;
+ set_icon_color(e);
+
+ menu_add_normal(menu, CLIENT_MOVE, _("_Move"), NULL, TRUE);
+@@ -400,15 +400,15 @@ void client_menu_startup(void)
+ menu_add_normal(menu, CLIENT_RESIZE, _("Resi_ze"), NULL, TRUE);
+
+ e = menu_add_normal(menu, CLIENT_ICONIFY, _("Ico_nify"), NULL, TRUE);
+- e->data.normal.mask = ob_rr_theme->btn_iconify->unpressed_mask;
++ e->mask = ob_rr_theme->btn_iconify->unpressed_mask;
+ set_icon_color(e);
+
+ e = menu_add_normal(menu, CLIENT_MAXIMIZE, _("Ma_ximize"), NULL, TRUE);
+- e->data.normal.mask = ob_rr_theme->btn_max->unpressed_mask;
++ e->mask = ob_rr_theme->btn_max->unpressed_mask;
+ set_icon_color(e);
+
+ e = menu_add_normal(menu, CLIENT_SHADE, _("_Roll up/down"), NULL, TRUE);
+- e->data.normal.mask = ob_rr_theme->btn_shade->unpressed_mask;
++ e->mask = ob_rr_theme->btn_shade->unpressed_mask;
+ set_icon_color(e);
+
+ menu_add_normal(menu, CLIENT_DECORATE, _("Un/_Decorate"), NULL, TRUE);
+@@ -416,6 +416,6 @@ void client_menu_startup(void)
+ menu_add_separator(menu, -1, NULL);
+
+ e = menu_add_normal(menu, CLIENT_CLOSE, _("_Close"), NULL, TRUE);
+- e->data.normal.mask = ob_rr_theme->btn_close->unpressed_mask;
++ e->mask = ob_rr_theme->btn_close->unpressed_mask;
+ set_icon_color(e);
+ }
+diff --git a/openbox/event.c b/openbox/event.c
+index 5774f67..fd3ecb5 100644
+--- a/openbox/event.c
++++ b/openbox/event.c
+@@ -1845,9 +1845,17 @@ static gboolean event_handle_menu_input(XEvent *ev)
+ menu_frame_select(e->frame->child, NULL, TRUE);
+ }
+ menu_frame_select(e->frame, e, TRUE);
+- if (ev->type == ButtonRelease)
++ if ((ev->type == ButtonRelease) && (ev->xbutton.button==1)
++ && (menu_entry_frame_x_not_in_bullet(e->frame, ev->xbutton.x)))
++ {
++ /* execute clicked entry or it's default entry */
++ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
++ if (e->frame->child && e->frame->child->default_entry)
++ e = e->frame->child->default_entry;
++ }
+ menu_entry_frame_execute(e, ev->xbutton.state);
+ }
++ }
+ else
+ menu_frame_hide_all();
+ }
+@@ -1892,24 +1900,15 @@ static gboolean event_handle_menu_input(XEvent *ev)
+ ret = TRUE;
+ }
+
+- else if (sym == XK_Right || sym == XK_Return || sym == XK_KP_Enter)
++ else if (sym == XK_Right) {
++ /* Right goes to the selected submenu */
++ if (frame->selected &&
++ frame->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ {
+- /* Right and enter goes to the selected submenu.
+- Enter executes instead if it's not on a submenu. */
+-
+- if (frame->selected) {
+- const ObMenuEntryType t = frame->selected->entry->type;
+-
+- if (t == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ /* make sure it is visible */
+ menu_frame_select(frame, frame->selected, TRUE);
+- /* move focus to the child menu */
+ menu_frame_select_next(frame->child);
+ }
+- else if (sym != XK_Right) {
+- frame->press_doexec = TRUE;
+- }
+- }
+ ret = TRUE;
+ }
+
+@@ -1933,6 +1932,14 @@ static gboolean event_handle_menu_input(XEvent *ev)
+ ret = TRUE;
+ }
+
++ else if (sym == XK_Return || sym == XK_KP_Enter) {
++ if (frame->selected &&
++ frame->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
++ menu_frame_select(frame, frame->selected, TRUE);
++ frame->press_doexec = TRUE;
++ ret = TRUE;
++ }
++
+ /* keyboard accelerator shortcuts. (if it was a valid key) */
+ else if (frame->entries &&
+ (unikey =
+@@ -1979,13 +1986,14 @@ static gboolean event_handle_menu_input(XEvent *ev)
+ menu_frame_select(frame, found, TRUE);
+
+ if (num_found == 1) {
+- if (found->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
++ if (found->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU &&
++ ! frame->child->default_entry)
++ {
+ /* move focus to the child menu */
+ menu_frame_select_next(frame->child);
+ }
+- else {
++ else
+ frame->press_doexec = TRUE;
+- }
+ }
+ ret = TRUE;
+ }
+@@ -1995,14 +2003,22 @@ static gboolean event_handle_menu_input(XEvent *ev)
+ /* Use KeyRelease events for running things so that the key release
+ doesn't get sent to the focused application.
+
+- Allow ControlMask only, and don't bother if the menu is empty */
+- else if (ev->type == KeyRelease && (mods & ~ControlMask) == 0) {
+- if (frame->press_keycode == ev->xkey.keycode &&
++ Allow all modifiers, and don't bother if the menu is empty */
++ else if (ev->type == KeyRelease) {
++ if ((frame->press_keycode == ev->xkey.keycode) &&
+ frame->got_press &&
+ frame->press_doexec)
+ {
+- if (frame->selected)
+- menu_entry_frame_execute(frame->selected, ev->xkey.state);
++ if (frame->selected){
++ ObMenuEntryFrame *e = frame->selected;
++
++ /* execute selected entry or it's default entry */
++ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
++ if (e->frame->child && e->frame->child->default_entry)
++ e = e->frame->child->default_entry;
++ }
++ menu_entry_frame_execute(e, ev->xkey.state);
++ }
+ }
+ }
+ }
+diff --git a/openbox/menu.c b/openbox/menu.c
+index 8804e12..6c0cf7e 100644
+--- a/openbox/menu.c
++++ b/openbox/menu.c
+@@ -281,6 +281,7 @@ static void parse_menu_item(xmlNodePtr node, gpointer data)
+ {
+ ObMenuParseState *state = data;
+ gchar *label;
++ gboolean is_default = FALSE;
+ gchar *icon;
+ ObMenuEntry *e;
+
+@@ -288,6 +289,7 @@ static void parse_menu_item(xmlNodePtr node, gpointer data)
+ /* Don't try to extract "icon" attribute if icons in user-defined
+ menus are not enabled. */
+
++ obt_xml_attr_bool(node, "default", &is_default);
+ if (obt_xml_attr_string_unstripped(node, "label", &label)) {
+ xmlNodePtr c;
+ GSList *acts = NULL;
+@@ -300,14 +302,15 @@ static void parse_menu_item(xmlNodePtr node, gpointer data)
+ c = obt_xml_find_node(c->next, "action");
+ }
+ e = menu_add_normal(state->parent, -1, label, acts, TRUE);
+-
++ if (is_default && !state->parent->default_entry)
++ state->parent->default_entry = e;
+ if (config_menu_show_icons &&
+ obt_xml_attr_string(node, "icon", &icon))
+ {
+- e->data.normal.icon = RrImageNewFromName(ob_rr_icons, icon);
++ e->icon = RrImageNewFromName(ob_rr_icons, icon);
+
+- if (e->data.normal.icon)
+- e->data.normal.icon_alpha = 0xff;
++ if (e->icon)
++ e->icon_alpha = 0xff;
+
+ g_free(icon);
+ }
+@@ -367,10 +370,10 @@ static void parse_menu(xmlNodePtr node, gpointer data)
+ if (config_menu_show_icons &&
+ obt_xml_attr_string(node, "icon", &icon))
+ {
+- e->data.submenu.icon = RrImageNewFromName(ob_rr_icons, icon);
++ e->icon = RrImageNewFromName(ob_rr_icons, icon);
+
+- if (e->data.submenu.icon)
+- e->data.submenu.icon_alpha = 0xff;
++ if (e->icon)
++ e->icon_alpha = 0xff;
+
+ g_free(icon);
+ }
+@@ -559,7 +562,7 @@ void menu_entry_unref(ObMenuEntry *self)
+ if (self && --self->ref == 0) {
+ switch (self->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+- RrImageUnref(self->data.normal.icon);
++ RrImageUnref(self->icon);
+ g_free(self->data.normal.label);
+ g_free(self->data.normal.collate_key);
+ while (self->data.normal.actions) {
+@@ -570,7 +573,7 @@ void menu_entry_unref(ObMenuEntry *self)
+ }
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+- RrImageUnref(self->data.submenu.icon);
++ RrImageUnref(self->icon);
+ g_free(self->data.submenu.name);
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+diff --git a/openbox/menu.h b/openbox/menu.h
+index 8c2ecd7..04dcf10 100644
+--- a/openbox/menu.h
++++ b/openbox/menu.h
+@@ -78,6 +78,9 @@ struct _ObMenu
+ /* ObMenuEntry list */
+ GList *entries;
+
++ /* default entry */
++ ObMenuEntry *default_entry;
++
+ /* plugin data */
+ gpointer data;
+
+@@ -104,10 +107,6 @@ typedef enum
+ } ObMenuEntryType;
+
+ struct _ObNormalMenuEntry {
+- /* Icon stuff. If you set this, make sure you RrImageRef() it too. */
+- RrImage *icon;
+- gint icon_alpha;
+-
+ gchar *label;
+ gchar *collate_key;
+ /*! The shortcut key that would be used to activate this menu entry */
+@@ -123,21 +122,10 @@ struct _ObNormalMenuEntry {
+ /* List of ObActions */
+ GSList *actions;
+
+- /* Mask icon */
+- RrPixmapMask *mask;
+- RrColor *mask_normal_color;
+- RrColor *mask_selected_color;
+- RrColor *mask_disabled_color;
+- RrColor *mask_disabled_selected_color;
+-
+ gpointer data;
+ };
+
+ struct _ObSubmenuMenuEntry {
+- /* Icon stuff. If you set this, make sure you RrImageRef() it too. */
+- RrImage *icon;
+- gint icon_alpha;
+-
+ gchar *name;
+ ObMenu *submenu;
+
+@@ -157,6 +145,17 @@ struct _ObMenuEntry
+
+ gint id;
+
++ /* Icon stuff. If you set this, make sure you RrImageRef() it too. */
++ RrImage *icon;
++ gint icon_alpha;
++
++ /* Mask icon */
++ RrPixmapMask *mask;
++ RrColor *mask_normal_color;
++ RrColor *mask_selected_color;
++ RrColor *mask_disabled_color;
++ RrColor *mask_disabled_selected_color;
++
+ union u {
+ ObNormalMenuEntry normal;
+ ObSubmenuMenuEntry submenu;
+diff --git a/openbox/menuframe.c b/openbox/menuframe.c
+index 2398031..943ff69 100644
+--- a/openbox/menuframe.c
++++ b/openbox/menuframe.c
+@@ -35,6 +35,9 @@
+
+ #define ITEM_HEIGHT (ob_rr_theme->menu_font_height + 2*PADDING)
+
++ /* offset at which the submenu bullet appears in the items */
++#define BULLET_X (frame->text_x + frame->text_w - ITEM_HEIGHT + PADDING)
++
+ #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
+ LeaveWindowMask)
+ #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
+@@ -187,7 +190,10 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
+ self->icon = createWindow(self->window, 0, NULL);
+ g_hash_table_insert(menu_frame_map, &self->icon, self);
+ }
+- if (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
++ if (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU
++ || (entry->type == OB_MENU_ENTRY_TYPE_NORMAL
++ && entry == entry->menu->default_entry))
++ {
+ self->bullet = createWindow(self->window, 0, NULL);
+ g_hash_table_insert(menu_frame_map, &self->bullet, self);
+ }
+@@ -209,12 +215,11 @@ static void menu_entry_frame_free(ObMenuEntryFrame *self)
+ XDestroyWindow(obt_display, self->window);
+ g_hash_table_remove(menu_frame_map, &self->text);
+ g_hash_table_remove(menu_frame_map, &self->window);
+- if ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+- (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
++ if (self->icon) {
+ XDestroyWindow(obt_display, self->icon);
+ g_hash_table_remove(menu_frame_map, &self->icon);
+ }
+- if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
++ if (self->bullet) {
+ XDestroyWindow(obt_display, self->bullet);
+ g_hash_table_remove(menu_frame_map, &self->bullet);
+ }
+@@ -372,6 +377,7 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
+ gint th; /* temp */
+ ObMenu *sub;
+ ObMenuFrame *frame = self->frame;
++ gboolean has_bullet;
+
+ switch (self->entry->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+@@ -532,14 +538,34 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
+ g_assert_not_reached();
+ }
+
+- if (((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+- (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) &&
+- self->entry->data.normal.icon)
++ has_bullet = FALSE;
++ if ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) &&
++ frame->parent &&
++ (self == self->frame->default_entry))
+ {
++ // show the mark of the default item
++ RrAppearance *bullet_a;
++
++ XMoveResizeWindow(obt_display, self->bullet,
++ PADDING, PADDING,
++ ITEM_HEIGHT - 2*PADDING,
++ ITEM_HEIGHT - 2*PADDING);
++ bullet_a = ((self == self->frame->selected) ?
++ ob_rr_theme->a_menu_bullet_def_selected :
++ ob_rr_theme->a_menu_bullet_def_normal);
++ bullet_a->surface.parent = item_a;
++ bullet_a->surface.parentx = 0;
++ bullet_a->surface.parenty = PADDING;
++ RrPaint(bullet_a, self->bullet,
++ self->frame->icon_x,
++ ITEM_HEIGHT - 2*PADDING);
++ has_bullet = TRUE;
++ }
++ if (self->entry->icon){
+ RrAppearance *clear;
+
+ XMoveResizeWindow(obt_display, self->icon,
+- PADDING, frame->item_margin.top,
++ self->frame->icon_x, frame->item_margin.top,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom,
+ ITEM_HEIGHT - frame->item_margin.top
+@@ -549,9 +575,9 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
+ RrAppearanceClearTextures(clear);
+ clear->texture[0].type = RR_TEXTURE_IMAGE;
+ clear->texture[0].data.image.image =
+- self->entry->data.normal.icon;
++ self->entry->icon;
+ clear->texture[0].data.image.alpha =
+- self->entry->data.normal.icon_alpha;
++ self->entry->icon_alpha;
+ clear->surface.parent = item_a;
+ clear->surface.parentx = PADDING;
+ clear->surface.parenty = frame->item_margin.top;
+@@ -562,13 +588,13 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
+ - frame->item_margin.bottom);
+ XMapWindow(obt_display, self->icon);
+ } else if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+- self->entry->data.normal.mask)
++ self->entry->mask)
+ {
+ RrColor *c;
+ RrAppearance *clear;
+
+ XMoveResizeWindow(obt_display, self->icon,
+- PADDING, frame->item_margin.top,
++ self->frame->icon_x, frame->item_margin.top,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom,
+ ITEM_HEIGHT - frame->item_margin.top
+@@ -578,22 +604,22 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
+ RrAppearanceClearTextures(clear);
+ clear->texture[0].type = RR_TEXTURE_MASK;
+ clear->texture[0].data.mask.mask =
+- self->entry->data.normal.mask;
++ self->entry->mask;
+
+ c = (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ !self->entry->data.normal.enabled ?
+ /* disabled */
+ (self == self->frame->selected ?
+- self->entry->data.normal.mask_disabled_selected_color :
+- self->entry->data.normal.mask_disabled_color) :
++ self->entry->mask_disabled_selected_color :
++ self->entry->mask_disabled_color) :
+ /* enabled */
+ (self == self->frame->selected ?
+- self->entry->data.normal.mask_selected_color :
+- self->entry->data.normal.mask_normal_color));
++ self->entry->mask_selected_color :
++ self->entry->mask_normal_color));
+ clear->texture[0].data.mask.color = c;
+
+ clear->surface.parent = item_a;
+- clear->surface.parentx = PADDING;
++ clear->surface.parentx = self->frame->icon_x;
+ clear->surface.parenty = frame->item_margin.top;
+ RrPaint(clear, self->icon,
+ ITEM_HEIGHT - frame->item_margin.top
+@@ -607,13 +633,17 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
+ if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ RrAppearance *bullet_a;
+ XMoveResizeWindow(obt_display, self->bullet,
+- self->frame->text_x + self->frame->text_w -
+- ITEM_HEIGHT + PADDING, PADDING,
++ BULLET_X, PADDING,
+ ITEM_HEIGHT - 2*PADDING,
+ ITEM_HEIGHT - 2*PADDING);
+- bullet_a = (self == self->frame->selected ?
+- ob_rr_theme->a_menu_bullet_selected :
+- ob_rr_theme->a_menu_bullet_normal);
++ if (sub->default_entry)
++ bullet_a = ((self == self->frame->selected) ?
++ ob_rr_theme->a_menu_bullet_def_selected :
++ ob_rr_theme->a_menu_bullet_def_normal);
++ else
++ bullet_a = ((self == self->frame->selected) ?
++ ob_rr_theme->a_menu_bullet_selected :
++ ob_rr_theme->a_menu_bullet_normal);
+ bullet_a->surface.parent = item_a;
+ bullet_a->surface.parentx =
+ self->frame->text_x + self->frame->text_w - ITEM_HEIGHT + PADDING;
+@@ -621,8 +651,11 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
+ RrPaint(bullet_a, self->bullet,
+ ITEM_HEIGHT - 2*PADDING,
+ ITEM_HEIGHT - 2*PADDING);
++ has_bullet = TRUE;
++ }
++ if (has_bullet)
+ XMapWindow(obt_display, self->bullet);
+- } else
++ else
+ XUnmapWindow(obt_display, self->bullet);
+
+ XFlush(obt_display);
+@@ -767,8 +800,7 @@ void menu_frame_render(ObMenuFrame *self)
+ tw = MIN(tw, MAX_MENU_WIDTH);
+ th = ob_rr_theme->menu_font_height;
+
+- if (e->entry->data.normal.icon ||
+- e->entry->data.normal.mask)
++ if (e->entry->icon || e->entry->mask)
+ has_icon = TRUE;
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+@@ -778,8 +810,8 @@ void menu_frame_render(ObMenuFrame *self)
+ tw = MIN(tw, MAX_MENU_WIDTH);
+ th = ob_rr_theme->menu_font_height;
+
+- if (e->entry->data.normal.icon ||
+- e->entry->data.normal.mask)
++ if (e->entry->icon ||
++ e->entry->mask)
+ has_icon = TRUE;
+
+ tw += ITEM_HEIGHT - PADDING;
+@@ -818,10 +850,17 @@ void menu_frame_render(ObMenuFrame *self)
+ h -= ob_rr_theme->mbwidth;
+ }
+
++ self->icon_x = PADDING;
+ self->text_x = PADDING;
+ self->text_w = w;
+
+ if (self->entries) {
++ gint def_w = ITEM_HEIGHT-2*PADDING;
++ if (self->parent && self->menu->default_entry) {
++ w += def_w;
++ self->icon_x += def_w;
++ self->text_x += def_w;
++ }
+ if (has_icon) {
+ w += ITEM_HEIGHT + PADDING;
+ self->text_x += ITEM_HEIGHT + PADDING;
+@@ -852,6 +891,7 @@ static void menu_frame_update(ObMenuFrame *self)
+ {
+ GList *mit, *fit;
+ const Rect *a;
++ ObMenuEntry *menu_default_entry;
+ gint h;
+
+ menu_pipe_execute(self->menu);
+@@ -871,10 +911,13 @@ static void menu_frame_update(ObMenuFrame *self)
+ f->entry = mit->data;
+ }
+
++ menu_default_entry = self->menu->default_entry;
+ /* if there are more menu entries than in the frame, add them */
+ while (mit) {
+ ObMenuEntryFrame *e = menu_entry_frame_new(mit->data, self);
+ self->entries = g_list_append(self->entries, e);
++ if (menu_default_entry == e->entry)
++ self->default_entry = e;
+ mit = g_list_next(mit);
+ }
+
+@@ -957,7 +1000,7 @@ static gboolean menu_frame_is_visible(ObMenuFrame *self)
+ return !!(g_list_find(menu_frame_visible, self));
+ }
+
+-static gboolean menu_frame_show(ObMenuFrame *self)
++gboolean menu_frame_show(ObMenuFrame *self)
+ {
+ GList *it;
+
+@@ -1178,6 +1221,11 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
+ return ret;
+ }
+
++gboolean menu_entry_frame_x_not_in_bullet(ObMenuFrame* frame, gint x /* local */)
++{
++ return(x <= BULLET_X);
++}
++
+ static gboolean submenu_show_timeout(gpointer data)
+ {
+ g_assert(menu_frame_visible);
+diff --git a/openbox/menuframe.h b/openbox/menuframe.h
+index 7b295b6..ffcedf6 100644
+--- a/openbox/menuframe.h
++++ b/openbox/menuframe.h
+@@ -55,6 +55,8 @@ struct _ObMenuFrame
+
+ GList *entries;
+ ObMenuEntryFrame *selected;
++ /* default entry */
++ ObMenuEntryFrame *default_entry;
+
+ /* show entries from the menu starting at this index */
+ guint show_from;
+@@ -67,6 +69,7 @@ struct _ObMenuFrame
+ Strut item_margin;
+ gint inner_w; /* inside the borders */
+ gint item_h; /* height of all normal items */
++ gint icon_x; /* offset at which the icon or mask appears in the items */
+ gint text_x; /* offset at which the text appears in the items */
+ gint text_w; /* width of the text area in the items */
+ gint text_h; /* height of the items */
+@@ -140,8 +143,10 @@ void menu_frame_select_last(ObMenuFrame *self);
+
+ ObMenuFrame* menu_frame_under(gint x, gint y);
+ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y);
++gboolean menu_entry_frame_x_not_in_bullet(ObMenuFrame* frame, gint x /* local */);
+
+ void menu_entry_frame_show_submenu(ObMenuEntryFrame *self);
++gboolean menu_frame_show(ObMenuFrame *self);
+
+ void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state);
+
diff --git a/rc.xml b/rc.xml
new file mode 100644
index 000000000000..c6db0703d8c9
--- /dev/null
+++ b/rc.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<openbox_config xmlns="http://openbox.org/3.4/rc">
+
+ <theme>
+<!-- By default iconified windows appears in openbox tasklists
+ regardless of _NET_WM_STATE_SKIP_TASKBAR hint.
+ Set "strictSkipTaskbar" to "yes" for override this (nonstandard) behavior. -->
+ <strictSkipTaskbar>no</strictSkipTaskbar>
+ </theme>
+ <mouse>
+ <context name="Desktop">
+ <mousebind action="Click" button="Up">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind action="Click" button="Down">
+ <action name="DesktopNext"/>
+ </mousebind>
+ <mousebind action="Click" button="W-Up">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind action="Click" button="W-Down">
+ <action name="DesktopNext"/>
+ </mousebind>
+ <mousebind action="Click" button="S-Up">
+ <action name="Execute">
+ <command>amixer set Master 1+</command>
+ </action>
+ </mousebind>
+ <mousebind action="Click" button="S-Down">
+ <action name="Execute">
+ <command>amixer set Master 1-</command>
+ </action>
+ </mousebind>
+ </context>
+ <context name="Root">
+ <mousebind action="Press" button="Middle">
+ <action name="ShowMenu">
+ <menu>client-list-combined-menu</menu>
+ </action>
+ </mousebind>
+ <mousebind action="Press" button="Right">
+ <action name="ShowMenu">
+ <menu>root-menu</menu>
+ </action>
+ </mousebind>
+ </context>
+
+<!-- edges -->
+ <context name="ScreenTopLeft"> <import>Root</import> <import>Desktop</import> </context>
+ <context name="ScreenTop"> <import>Root</import> <import>Desktop</import> </context>
+ <context name="ScreenTopRight"> <import>Root</import> <import>Desktop</import> </context>
+ <context name="ScreenRight"> <import>Root</import> <import>Desktop</import> </context>
+ <context name="ScreenBottomRight"> <import>Root</import> <import>Desktop</import> </context>
+ <context name="ScreenBottom"> <import>Root</import> <import>Desktop</import> </context>
+ <context name="ScreenBottomLeft">
+ <import>Root</import> <import>Desktop</import>
+ <mousebind action="Press" button="Left">
+ <action name="ShowMenu">
+ <menu>desktop-app-menu</menu>
+ </action>
+ </mousebind>
+ </context>
+<!-- edges end -->
+
+ </mouse>
+
+ <menu>
+ <file>menu.xml</file>
+<!-- When the topmenu appears to be set first layout -->
+ <TopMenuKbdGroup>0</TopMenuKbdGroup>
+
+<!-- Allow utf8 menu accelerator keys -->
+ <utf8Enabled>yes</utf8Enabled>
+
+ </menu>
+</openbox_config>
diff --git a/strict_skip_taskbar.patch b/strict_skip_taskbar.patch
new file mode 100644
index 000000000000..3ffc16a41042
--- /dev/null
+++ b/strict_skip_taskbar.patch
@@ -0,0 +1,53 @@
+commit 3600f9b81b7987d4f777d6218e23e7b2764878ae
+Author: Alexey Korop <avkorop@i.ua>
+Date: Tue Mar 31 20:30:28 2015 +0300
+
+ strict_skip_taskbar
+
+diff --git a/openbox/config.c b/openbox/config.c
+index 5d2408f..81b542c 100644
+--- a/openbox/config.c
++++ b/openbox/config.c
+@@ -49,6 +49,7 @@ StrutPartial config_margins;
+ gchar *config_theme;
+ gboolean config_theme_keepborder;
+ guint config_theme_window_list_icon_size;
++gboolean config_theme_strict_skip_taskbar;
+
+ gchar *config_title_layout;
+
+@@ -735,6 +736,8 @@ static void parse_theme(xmlNodePtr node, gpointer d)
+ config_theme_window_list_icon_size = 16;
+ else if (config_theme_window_list_icon_size > 96)
+ config_theme_window_list_icon_size = 96;
++ if ((n = obt_xml_find_node(node, "strictSkipTaskbar")))
++ config_theme_strict_skip_taskbar = obt_xml_node_bool(n);
+ }
+
+ for (n = obt_xml_find_node(node, "font");
+diff --git a/openbox/config.h b/openbox/config.h
+index e70ccab..45f8adf 100644
+--- a/openbox/config.h
++++ b/openbox/config.h
+@@ -152,6 +152,8 @@ extern gchar *config_title_layout;
+ extern gboolean config_animate_iconify;
+ /*! Size of icons in focus switching dialogs */
+ extern guint config_theme_window_list_icon_size;
++/*! not show in tasklist iconified windows with _NET_WM_STATE_SKIP_TASKBAR hint */
++extern gboolean config_theme_strict_skip_taskbar;
+
+ /*! The font for the active window's title */
+ extern RrFont *config_font_activewindow;
+diff --git a/openbox/focus.c b/openbox/focus.c
+index a4626bf..257cce6 100644
+--- a/openbox/focus.c
++++ b/openbox/focus.c
+@@ -365,7 +365,7 @@ gboolean focus_valid_target(ObClient *ft,
+ not be long-lasting windows */
+ ok = ok && (!ft->skip_taskbar ||
+ (ft->modal || user_request ||
+- (ft->iconic && !ft->parents) ||
++ (!config_theme_strict_skip_taskbar && ft->iconic && !ft->parents) ||
+ ft->demands_attention ||
+ ft->type == OB_CLIENT_TYPE_DIALOG));
+
diff --git a/topmenu_kbd_group.patch b/topmenu_kbd_group.patch
new file mode 100644
index 000000000000..6b3302ca9cdd
--- /dev/null
+++ b/topmenu_kbd_group.patch
@@ -0,0 +1,59 @@
+diff --git a/data/rc.xsd b/data/rc.xsd
+--- a/data/rc.xsd
++++ b/data/rc.xsd
+@@ -218,6 +218,7 @@
+ <xsd:complexType name="menu">
+ <xsd:sequence>
+ <xsd:element maxOccurs="unbounded" name="file" type="xsd:string"/>
++ <xsd:element minOccurs="0" name="TopMenuKbdGroup" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="hideDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="middle" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="utf8Enabled" type="ob:bool"/>
+diff --git a/openbox/config.c b/openbox/config.c
+--- a/openbox/config.c
++++ b/openbox/config.c
+@@ -92,6 +92,7 @@ gint config_mouse_dclicktime;
+ gint config_mouse_screenedgetime;
+ gboolean config_mouse_screenedgewarp;
+
++guint config_menu_topmenu_kbd_group;
+ guint config_menu_hide_delay;
+ gboolean config_menu_middle;
+ guint config_submenu_show_delay;
+@@ -937,6 +938,8 @@ static void parse_menu(xmlNodePtr node, gpointer d)
+ xmlNodePtr n;
+ node = node->children;
+
++ if ((n = obt_xml_find_node(node, "TopMenuKbdGroup")))
++ config_menu_topmenu_kbd_group = obt_xml_node_int(n) + 1;
+ if ((n = obt_xml_find_node(node, "hideDelay")))
+ config_menu_hide_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "middle")))
+diff --git a/openbox/config.h b/openbox/config.h
+--- a/openbox/config.h
++++ b/openbox/config.h
+@@ -197,6 +197,9 @@ extern gint config_resist_win;
+ /*! Number of pixels to resist while crossing a screen's edge */
+ extern gint config_resist_edge;
+
++/*! Incremented by 1 keyboard layout group that must be set when the toplevel menu show.
++0 if not defined */
++extern guint config_menu_topmenu_kbd_group;
+ /*! Delay for hiding menu when opening in milliseconds */
+ extern guint config_menu_hide_delay;
+ /*! Center menus vertically about the parent entry */
+diff --git a/openbox/menuframe.c b/openbox/menuframe.c
+--- a/openbox/menuframe.c
++++ b/openbox/menuframe.c
+@@ -1056,6 +1056,11 @@ gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y,
+ e->ignore_enters++;
+ }
+
++ /* set the keyboard layout if specified */
++ gint n = config_menu_topmenu_kbd_group;
++ if (n)
++ XkbLockGroup(obt_display, XkbUseCoreKbd, n - 1);
++
+ return TRUE;
+ }
+
diff --git a/topmenu_kbd_group_local.patch b/topmenu_kbd_group_local.patch
new file mode 100644
index 000000000000..0167b71c0fa7
--- /dev/null
+++ b/topmenu_kbd_group_local.patch
@@ -0,0 +1,63 @@
+commit 930d2c1dc7c7ca20defc0ad5540ba628f19adf9f
+Author: Alexey Korop <avkorop@i.ua>
+Date: Tue Mar 17 08:49:45 2015 +0200
+
+ topmenu_kbd_group_local.patch
+
+diff --git a/openbox/menu.c b/openbox/menu.c
+index 57de2fb..6eaf7e9 100644
+--- a/openbox/menu.c
++++ b/openbox/menu.c
+@@ -373,6 +373,7 @@ static void parse_menu(xmlNodePtr node, gpointer data)
+ gchar *name = NULL, *title = NULL, *script = NULL;
+ ObMenu *menu;
+ ObMenuEntry *e;
++ gint n = 0;
+ gchar *icon;
+ GList *keylist = NULL;
+ gchar *keystring, **keys, **key;
+@@ -380,6 +381,9 @@ static void parse_menu(xmlNodePtr node, gpointer data)
+ if (!obt_xml_attr_string(node, "id", &name))
+ goto parse_menu_fail;
+
++ if (obt_xml_attr_int(node, "TopMenuKbdGroup", &n))
++ ++n;
++
+ if (obt_xml_attr_string(node, "key", &keystring)) { // global keybind
+ keys = g_strsplit(keystring, " ", 0);
+ for (key = keys; *key; ++key)
+@@ -393,6 +397,7 @@ static void parse_menu(xmlNodePtr node, gpointer data)
+
+ if ((menu = menu_new(name, title, TRUE, NULL))) {
+ menu->pipe_creator = state->pipe_creator;
++ menu->topmenu_kbd_group = n;
+ if (obt_xml_attr_string(node, "execute", &script)) {
+ menu->execute = obt_paths_expand_tilde(script);
+ } else {
+diff --git a/openbox/menu.h b/openbox/menu.h
+index 04dcf10..c048f65 100644
+--- a/openbox/menu.h
++++ b/openbox/menu.h
+@@ -59,6 +59,9 @@ struct _ObMenu
+ gchar *name;
+ /* Displayed title */
+ gchar *title;
++ /* Incremented by 1 keyboard layout group that must be set when this menu show
++ as root level menu. 0 if not defined. */
++ guint topmenu_kbd_group;
+ gchar *collate_key;
+ /*! The shortcut key that would be used to activate this menu if it was
+ displayed as a submenu */
+diff --git a/openbox/menuframe.c b/openbox/menuframe.c
+index b7ca002..9b438f2 100644
+--- a/openbox/menuframe.c
++++ b/openbox/menuframe.c
+@@ -1071,6 +1071,8 @@ gboolean menu_frame_show_topmenu(ObMenuFrame *self, const GravityPoint *pos,
+
+ /* set the keyboard layout if specified */
+ gint n = config_menu_topmenu_kbd_group;
++ if (self->menu->topmenu_kbd_group)
++ n = self->menu->topmenu_kbd_group; // prefer this menu setting
+ if (n)
+ XkbLockGroup(obt_display, XkbUseCoreKbd, n - 1);
+
diff --git a/utf8-menu-accelerators.diff b/utf8-menu-accelerators.diff
new file mode 100644
index 000000000000..a741eee1743f
--- /dev/null
+++ b/utf8-menu-accelerators.diff
@@ -0,0 +1,166 @@
+diff --git a/data/rc.xml b/data/rc.xml
+index a9a7a35..1f66840 100644
+--- a/data/rc.xml
++++ b/data/rc.xml
+@@ -637,6 +637,14 @@
+ entry in parent menu
+ if this is a negative value, then the delay is infinite and the
+ submenu will not be hidden until a different submenu is opened -->
++ <utf8Enabled>no</utf8Enabled>
++ <!-- set to 'yes' to enable utf8 (unicode) support for menu shortcuts
++ this includes automatic selection of first suitable character and
++ manual setting with a prepended underscore -->
++ <utf8AllowGraph>no</utf8AllowGraph>
++ <!-- set to 'yes' (utf8Enabled must also be set to 'yes') to allow the use of
++ any printable utf8 (unicode) character as a menu shortcut
++ by default ('no'), shortcuts are restricted to alphanumeric characters -->
+ <showIcons>yes</showIcons>
+ <!-- controls if icons appear in the client-list-(combined-)menu -->
+ <manageDesktops>yes</manageDesktops>
+diff --git a/data/rc.xsd b/data/rc.xsd
+index c8f5638..9578b84 100644
+--- a/data/rc.xsd
++++ b/data/rc.xsd
+@@ -221,6 +221,8 @@
+ <xsd:element maxOccurs="unbounded" name="file" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="hideDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="middle" type="ob:bool"/>
++ <xsd:element minOccurs="0" name="utf8Enabled" type="ob:bool"/>
++ <xsd:element minOccurs="0" name="utf8AllowGraph" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="submenuShowDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="showIcons" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="manageDesktops" type="ob:bool"/>
+diff --git a/openbox/config.c b/openbox/config.c
+index 436c555..709c777 100644
+--- a/openbox/config.c
++++ b/openbox/config.c
+@@ -98,6 +98,8 @@ guint config_submenu_show_delay;
+ guint config_submenu_hide_delay;
+ gboolean config_menu_manage_desktops;
+ gboolean config_menu_show_icons;
++gboolean config_menu_utf8_enabled;
++gboolean config_menu_utf8_allow_graph;
+
+ GSList *config_menu_files;
+
+@@ -964,8 +966,13 @@ static void parse_menu(xmlNodePtr node, gpointer d)
+ config_submenu_hide_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "manageDesktops")))
+ config_menu_manage_desktops = obt_xml_node_bool(n);
++ if ((n = obt_xml_find_node(node, "utf8Enabled")))
++ config_menu_utf8_enabled = obt_xml_node_bool(n);
++ if ((n = obt_xml_find_node(node, "utf8AllowGraph")))
++ config_menu_utf8_allow_graph = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "showIcons"))) {
+ config_menu_show_icons = obt_xml_node_bool(n);
++
+ #if !defined(USE_IMLIB2) && !defined(USE_LIBRSVG)
+ if (config_menu_show_icons)
+ g_message(_("Openbox was compiled without image loading support. Icons in menus will not be loaded."));
+@@ -1180,6 +1187,8 @@ void config_startup(ObtXmlInst *i)
+ config_submenu_show_delay = 100;
+ config_submenu_hide_delay = 400;
+ config_menu_manage_desktops = TRUE;
++ config_menu_utf8_enabled = FALSE;
++ config_menu_utf8_allow_graph = FALSE;
+ config_menu_files = NULL;
+ config_menu_show_icons = TRUE;
+
+diff --git a/openbox/config.h b/openbox/config.h
+index 5a694c0..d4f5130 100644
+--- a/openbox/config.h
++++ b/openbox/config.h
+@@ -210,6 +210,10 @@ extern guint config_submenu_show_delay;
+ extern guint config_submenu_hide_delay;
+ /*! Show manage desktops in client_list_menu */
+ extern gboolean config_menu_manage_desktops;
++/*! Enable utf8 support in menus */
++extern gboolean config_menu_utf8_enabled;
++/*! Allow using all graphs as menu shortcuts */
++extern gboolean config_menu_utf8_allow_graph;
+ /*! Load & show icons in user-defined menus */
+ extern gboolean config_menu_show_icons;
+ /*! User-specified menu files */
+diff --git a/openbox/menu.c b/openbox/menu.c
+index 8804e12..a708b38 100644
+--- a/openbox/menu.c
++++ b/openbox/menu.c
+@@ -55,6 +55,7 @@ static void menu_destroy_hash_value(ObMenu *self);
+ static void parse_menu_item(xmlNodePtr node, gpointer data);
+ static void parse_menu_separator(xmlNodePtr node, gpointer data);
+ static void parse_menu(xmlNodePtr node, gpointer data);
++static gboolean is_valid_shortcut(gchar *c);
+ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
+ gchar **strippedlabel, guint *position,
+ gboolean *always_show);
+@@ -201,6 +202,19 @@ static ObMenu* menu_from_name(gchar *name)
+ ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z'))
+
++static gboolean is_valid_shortcut(gchar *c)
++{
++ gunichar uc;
++
++ if(!config_menu_utf8_enabled) // unless utf8 support is explicitly enabled in config
++ return VALID_SHORTCUT(*c); // preserve old behaviour
++
++ uc = g_utf8_get_char_validated(c, MAX(OB_MAX_UTF8_CHAR_SZ, strlen(c)));
++ g_assert(uc > ((gunichar) - 1));
++
++ return config_menu_utf8_allow_graph ? g_unichar_isgraph(uc) : g_unichar_isalnum(uc);
++}
++
+ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
+ gchar **strippedlabel, guint *position,
+ gboolean *always_show)
+@@ -228,7 +242,7 @@ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
+ i = *strippedlabel;
+ do {
+ escape = FALSE;
+- i = strchr(i, '_');
++ i = config_menu_utf8_enabled ? g_utf8_strchr(i, -1, '_') : strchr(i, '_');
+ if (i && *(i+1) == '_') {
+ gchar *j;
+
+@@ -244,10 +258,16 @@ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
+ if (allow_shortcut && i != NULL) {
+ /* there is an underscore in the string */
+
+- /* you have to use a printable ascii character for shortcuts
++ /* without utf8Enabled:
++ you have to use an alphanumeric ascii character for shortcuts
++
++ with utf8Enabled:
++ iff not utf8AllowGraph, you have to use an alphanumeric utf8 character
++ else any graph will do (printable character excluding whitespace/formatting
++
+ don't allow space either, so you can have like "a _ b"
+ */
+- if (VALID_SHORTCUT(*(i+1))) {
++ if (is_valid_shortcut(i+1)) {
+ shortcut = g_unichar_tolower(g_utf8_get_char(i+1));
+ *position = i - *strippedlabel;
+ *always_show = TRUE;
+@@ -267,7 +287,7 @@ static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
+ instead */
+
+ for (i = *strippedlabel; *i != '\0'; ++i)
+- if (VALID_SHORTCUT(*i)) {
++ if (is_valid_shortcut(i)) {
+ *position = i - *strippedlabel;
+ shortcut = g_unichar_tolower(g_utf8_get_char(i));
+ break;
+diff --git a/openbox/misc.h b/openbox/misc.h
+index 750dddd..43b686a 100644
+--- a/openbox/misc.h
++++ b/openbox/misc.h
+@@ -19,6 +19,9 @@
+ #ifndef __ob__misc_h
+ #define __ob__misc_h
+
++
++#define OB_MAX_UTF8_CHAR_SZ 4
++
+ /*! The alpha value to use for icons of iconified windows in various places
+ like the focus cycle popup and client list menus.
+ Give iconic windows 7/16 alpha. A little under 50%.