## obt ##
obt_libobt_la_CPPFLAGS = \
+ $(X_CFLAGS) \
$(XINERAMA_CFLAGS) \
$(XKB_CFLAGS) \
$(XRANDR_CFLAGS) \
obt_libobt_la_LDFLAGS = \
-version-info $(OBT_CURRENT):$(OBT_REVISION):$(OBT_AGE)
obt_libobt_la_LIBADD = \
+ $(X_LIBS) \
$(XINERAMA_LIBS) \
$(XKB_LIBS) \
$(XRANDR_LIBS) \
openbox/ping.h \
openbox/place.c \
openbox/place.h \
+ openbox/place_overlap.c \
+ openbox/place_overlap.h \
openbox/prompt.c \
openbox/prompt.h \
openbox/popup.c \
Openbox
-Copyright (C) 2004-2007 Mikael Magnusson
-Copyright (C) 2002-2007 Dana Jansens
+Copyright (C) 2004 Mikael Magnusson
+Copyright (C) 2002 Dana Jansens
----
/bin/sh -c "set -x; $*"
}
-export WANT_AUTOMAKE=1.9
+export WANT_AUTOMAKE=1.11
sh autopoint --force || exit 1 # for GNU gettext
sh libtoolize --copy --force --automake || exit 1
# Set a background color
BG=""
-if which hsetroot >/dev/null; then
+if which hsetroot >/dev/null 2>/dev/null; then
BG=hsetroot
-elif which esetroot >/dev/null; then
+elif which esetroot >/dev/null 2>/dev/null; then
BG=esetroot
-elif which xsetroot >/dev/null; then
+elif which xsetroot >/dev/null 2>/dev/null; then
BG=xsetroot
fi
test -z $BG || $BG -solid "#303030"
from xdg.Exceptions import ParsingError
except ImportError:
print
- print "ERROR:", ME, "requires PyXDG to be installed"
+ print >>sys.stderr, "ERROR:", ME, "requires PyXDG to be installed"
print
sys.exit(1)
[Desktop Entry]
Type=Application
-Encoding=UTF-8
Name=Openbox
Exec=openbox
Icon=openbox
<placement>
<policy>Smart</policy>
<!-- 'Smart' or 'UnderMouse' -->
- <center>yes</center>
- <!-- whether to place windows in the center of the free area found or
- the top left corner -->
<monitor>Primary</monitor>
<!-- with Smart placement on a multi-monitor system, try to place new windows
on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
submenu will not be shown until it is clicked on -->
<submenuHideDelay>400</submenuHideDelay>
<!-- time to delay before hiding a submenu when selecting another
- entry in parent menu -->
+ 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 -->
- <applicationIcons>yes</applicationIcons>
+ <showIcons>yes</showIcons>
<!-- controls if icons appear in the client-list-(combined-)menu -->
<manageDesktops>yes</manageDesktops>
<!-- show the manage desktops section in the client-list-(combined-)menu -->
<application name="the window's _OB_APP_NAME property (see obxprop)"
class="the window's _OB_APP_CLASS property (see obxprop)"
+ groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
+ groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
role="the window's _OB_APP_ROLE property (see obxprop)"
title="the window's _OB_APP_TITLE property (see obxprop)"
type="the window's _OB_APP_TYPE property (see obxprob)..
# applications who refuse to behave
<x>center</x>
# a number like 50, or 'center' to center on screen. use a negative number
- # to start from the right (or bottom for <y>), ie -50 is 50 pixels from the
- # right edge (or bottom).
+ # to start from the right (or bottom for <y>), ie -50 is 50 pixels from
+ # the right edge (or bottom). use 'default' to specify using value
+ # provided by the application, or chosen by openbox, instead.
<y>200</y>
<monitor>1</monitor>
# specifies the monitor in a xinerama setup.
# 1 is the first head, or 'mouse' for wherever the mouse is
</position>
+ <size>
+ # the size to make the window.
+ <width>20</width>
+ # a number like 20, or 'default' to use the size given by the application.
+ # you can use fractions such as 1/2 or percentages such as 75% in which
+ # case the value is relative to the size of the monitor that the window
+ # appears on.
+ <height>30%</height>
+ </size>
+
<focus>yes</focus>
# if the window should try be given focus when it appears. if this is set
# to yes it doesn't guarantee the window will be given focus. some
</xsd:annotation>
<xsd:sequence>
<xsd:element minOccurs="0" name="policy" type="ob:placementpolicy"/>
- <xsd:element minOccurs="0" name="center" type="ob:bool"/>
<xsd:element minOccurs="0" name="monitor" type="ob:placementmonitor"/>
- <xsd:element minOccurs="0" name="monitor" type="ob:primarymonitor"/>
+ <xsd:element minOccurs="0" name="primaryMonitor" type="ob:primarymonitor"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="margins">
</xsd:all>
<xsd:attribute name="force" type="ob:bool"/>
</xsd:complexType>
+ <xsd:complexType name="window_size">
+ <xsd:all>
+ <xsd:element name="width" type="ob:size_value"/>
+ <xsd:element name="height" type="ob:size_value"/>
+ </xsd:all>
+ </xsd:complexType>
<xsd:complexType name="application">
<xsd:all>
<xsd:element minOccurs="0" name="decor" type="ob:bool"/>
<xsd:element minOccurs="0" name="shade" type="ob:bool"/>
<xsd:element minOccurs="0" name="position" type="ob:window_position"/>
+ <xsd:element minOccurs="0" name="size" type="ob:window_size"/>
<xsd:element minOccurs="0" name="focus" type="xsd:string"/>
<xsd:element minOccurs="0" name="desktop" type="xsd:integer"/>
<xsd:element minOccurs="0" name="layer" type="ob:layer"/>
<xsd:enumeration value="InactiveWindow"/>
<xsd:enumeration value="MenuHeader"/>
<xsd:enumeration value="MenuItem"/>
- <xsd:enumeration value="OnScreenDisplay"/>
+ <xsd:enumeration value="ActiveOnScreenDisplay"/>
+ <xsd:enumeration value="InactiveOnScreenDisplay"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="fontweight">
<xsd:restriction base="xsd:string">
<!-- ob: atoi($_) unless $_ eq 'center'; -->
<!-- I think the regexp DTRT WRT atoi. -->
- <xsd:pattern value="center|-?(0|[1-9][0-9]*)"/>
+ <xsd:pattern value="default|center|-?(0|[1-9][0-9]*)(|%|/[1-9][0-9]*)"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="size_value">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="default|([1-9][0-9]*)(|%|/[1-9][0-9]*)"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="mouse_or_int">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Mouse"/>
<xsd:enumeration value="Active"/>
- <xsd:enumeration value="[0-9][0-9][0-9][0-9][0-9]"/>
+ <xsd:pattern value="[0-9][0-9][0-9][0-9][0-9]"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="popupposition">
[Desktop Entry]
-Encoding=UTF-8
Name=GNOME/Openbox
Comment=Use the Openbox window manager inside of the GNOME desktop environment
Exec=@bindir@/openbox-gnome-session
TryExec=gnome-session
-Icon=openbox.png
-Type=XSession
+Icon=openbox
+Type=Application
[Desktop Entry]
-Encoding=UTF-8
Name=KDE/Openbox
Comment=Use the Openbox window manager inside of the K Desktop Environment
Exec=@bindir@/openbox-kde-session
TryExec=startkde
-Icon=openbox.png
-Type=XSession
+Icon=openbox
+Type=Application
[Desktop Entry]
-Encoding=UTF-8
Name=Openbox
Comment=Log in using the Openbox window manager (without a session manager)
Exec=@bindir@/openbox-session
TryExec=@bindir@/openbox-session
-Icon=openbox.png
-Type=XSession
+Icon=openbox
+Type=Application
.PP
These are the possible options that \fBopenbox\fR accepts:
.IP "\fB\-\-help\fP" 10
-Show a summary of the options available.
+Show a summary of the command line options available
+and exit.
.IP "\fB\-\-version\fP" 10
-Show the version of the program.
+Show the version of the program and exit.
.IP "\fB\-\-replace\fP" 10
Replace the currently running window manager.
+.IP "\fB\-\-config-file FILE\fP" 10
+Specify the path to the config file to use.
.IP "\fB\-\-reconfigure\fP" 10
If Openbox is already running on the display, tell it to
reload its configuration.
If Openbox is already running on the display, tell it to
restart. This is useful if you have upgraded Openbox and don't
want to restart X.
+.IP "\fB\-\-exit\fP" 10
+Exit Openbox.
.IP "\fB\-\-sm-disable\fP" 10
Do not connect to the session manager.
.IP "\fB\-\-sync\fP" 10
Run in synchronous mode (for debugging).
+.IP "\fB\-\-startup CMD\fP" 10
+Run CMD after starting.
.IP "\fB\-\-debug\fP" 10
Display debugging output.
.IP "\fB\-\-debug-focus\fP" 10
Display debugging output for focus handling.
+.IP "\fB\-\-debug-session\fP" 10
+Display debugging output for session management.
.IP "\fB\-\-debug-xinerama\fP" 10
Split the display into two fake xinerama regions, if
xinerama is not already enabled. This is for debugging
.PP
Please report bugs to: \fBhttp://bugzilla.icculus.org/
\fP
-.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40
+.\" created by instant / docbook-to-man, Sun 30 Sep 2012, 17:58
<varlistentry>
<term><option>--help</option></term>
<listitem>
- <para>Show a summary of the options available.</para>
+ <para>Show a summary of the command line options available
+ and exit.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--version</option></term>
<listitem>
- <para>Show the version of the program.</para>
+ <para>Show the version of the program and exit.</para>
</listitem>
</varlistentry>
<varlistentry>
<para>Replace the currently running window manager.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--config-file FILE</option></term>
+ <listitem>
+ <para>Specify the path to the config file to use.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>--reconfigure</option></term>
<listitem>
want to restart X.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--exit</option></term>
+ <listitem>
+ <para>Exit Openbox.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>--sm-disable</option></term>
<listitem>
<para>Run in synchronous mode (for debugging).</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--startup CMD</option></term>
+ <listitem>
+ <para>Run CMD after starting.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>--debug</option></term>
<listitem>
<para>Display debugging output for focus handling.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--debug-session</option></term>
+ <listitem>
+ <para>Display debugging output for session management.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>--debug-xinerama</option></term>
<listitem>
"osd.label.text.color",
theme->osd_text_active_color, RrColorCopy(theme->title_focused_color));
- READ_COLOR("osd.inactive.label.text.color", theme->osd_text_inactive_color,
- RrColorNew(inst, 0xff, 0xff, 0xff));
+ READ_COLOR_("osd.inactive.label.text.color",
+ "osd.label.text.color",
+ theme->osd_text_inactive_color, RrColorCopy(theme->title_unfocused_color));
READ_COLOR("window.active.button.unpressed.image.color",
theme->titlebut_focused_unpressed_color,
/* hover */
READ_APPEARANCE_COPY("window.active.button.max.hover.bg",
theme->btn_max->a_hover_focused, TRUE,
- theme->btn_max->a_focused_unpressed);
+ a_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.max.hover.bg",
theme->btn_max->a_hover_unfocused, TRUE,
- theme->btn_max->a_unfocused_unpressed);
+ a_hover_unfocused_tmp);
/* toggled unpressed */
READ_APPEARANCE_("window.active.button.max.toggled.unpressed.bg",
"window.active.button.max.toggled.bg",
theme->btn_max->a_toggled_focused_unpressed, TRUE,
- theme->btn_max->a_focused_pressed);
+ a_toggled_focused_unpressed_tmp);
READ_APPEARANCE_("window.inactive.button.max.toggled.unpressed.bg",
"window.inactive.button.max.toggled.bg",
theme->btn_max->a_toggled_unfocused_unpressed, TRUE,
- theme->btn_max->a_unfocused_pressed);
+ a_toggled_unfocused_unpressed_tmp);
/* toggled pressed */
READ_APPEARANCE_COPY("window.active.button.max.toggled.pressed.bg",
theme->btn_max->a_toggled_focused_pressed, TRUE,
- theme->btn_max->a_focused_pressed);
+ a_toggled_focused_pressed_tmp);
READ_APPEARANCE_COPY("window.inactive.button.max.toggled.pressed.bg",
theme->btn_max->a_toggled_unfocused_pressed, TRUE,
- theme->btn_max->a_unfocused_pressed);
+ a_toggled_unfocused_pressed_tmp);
/* toggled hover */
READ_APPEARANCE_COPY("window.active.button.max.toggled.hover.bg",
theme->btn_max->a_toggled_hover_focused, TRUE,
- theme->btn_max->a_toggled_focused_unpressed);
+ a_toggled_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.max.toggled.hover.bg",
theme->btn_max->a_toggled_hover_unfocused, TRUE,
- theme->btn_max->a_toggled_unfocused_unpressed);
+ a_toggled_hover_unfocused_tmp);
/* close button */
read_button_colors(db, inst, theme, theme->btn_close, "close");
a_disabled_unfocused_tmp);
READ_APPEARANCE_COPY("window.active.button.close.hover.bg",
theme->btn_close->a_hover_focused, TRUE,
- theme->btn_close->a_focused_unpressed);
+ a_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.close.hover.bg",
theme->btn_close->a_hover_unfocused, TRUE,
- theme->btn_close->a_unfocused_unpressed);
+ a_hover_unfocused_tmp);
/* desk button */
read_button_colors(db, inst, theme, theme->btn_desk, "desk");
/* hover */
READ_APPEARANCE_COPY("window.active.button.desk.hover.bg",
theme->btn_desk->a_hover_focused, TRUE,
- theme->btn_desk->a_focused_unpressed);
+ a_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.desk.hover.bg",
theme->btn_desk->a_hover_unfocused, TRUE,
- theme->btn_desk->a_unfocused_unpressed);
+ a_hover_unfocused_tmp);
/* toggled unpressed */
READ_APPEARANCE_("window.active.button.desk.toggled.unpressed.bg",
"window.active.button.desk.toggled.bg",
theme->btn_desk->a_toggled_focused_unpressed, TRUE,
- theme->btn_desk->a_focused_pressed);
+ a_toggled_focused_unpressed_tmp);
READ_APPEARANCE_("window.inactive.button.desk.toggled.unpressed.bg",
"window.inactive.button.desk.toggled.bg",
theme->btn_desk->a_toggled_unfocused_unpressed, TRUE,
- theme->btn_desk->a_unfocused_pressed);
+ a_toggled_unfocused_unpressed_tmp);
/* toggled pressed */
READ_APPEARANCE_COPY("window.active.button.desk.toggled.pressed.bg",
theme->btn_desk->a_toggled_focused_pressed, TRUE,
- theme->btn_desk->a_focused_pressed);
+ a_toggled_focused_pressed_tmp);
READ_APPEARANCE_COPY("window.inactive.button.desk.toggled.pressed.bg",
theme->btn_desk->a_toggled_unfocused_pressed, TRUE,
- theme->btn_desk->a_unfocused_pressed);
+ a_toggled_unfocused_pressed_tmp);
/* toggled hover */
READ_APPEARANCE_COPY("window.active.button.desk.toggled.hover.bg",
theme->btn_desk->a_toggled_hover_focused, TRUE,
- theme->btn_desk->a_toggled_focused_unpressed);
+ a_toggled_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.desk.toggled.hover.bg",
theme->btn_desk->a_toggled_hover_unfocused, TRUE,
- theme->btn_desk->a_toggled_unfocused_unpressed);
+ a_toggled_hover_unfocused_tmp);
/* shade button */
read_button_colors(db, inst, theme, theme->btn_shade, "shade");
/* hover */
READ_APPEARANCE_COPY("window.active.button.shade.hover.bg",
theme->btn_shade->a_hover_focused, TRUE,
- theme->btn_shade->a_focused_unpressed);
+ a_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.shade.hover.bg",
theme->btn_shade->a_hover_unfocused, TRUE,
- theme->btn_shade->a_unfocused_unpressed);
+ a_hover_unfocused_tmp);
/* toggled unpressed */
READ_APPEARANCE_("window.active.button.shade.toggled.unpressed.bg",
"window.active.button.shade.toggled.bg",
theme->btn_shade->a_toggled_focused_unpressed, TRUE,
- theme->btn_shade->a_focused_pressed);
+ a_toggled_focused_unpressed_tmp);
READ_APPEARANCE_("window.inactive.button.shade.toggled.unpressed.bg",
"window.inactive.button.shade.toggled.bg",
theme->btn_shade->a_toggled_unfocused_unpressed, TRUE,
- theme->btn_shade->a_unfocused_pressed);
+ a_toggled_unfocused_unpressed_tmp);
/* toggled pressed */
READ_APPEARANCE_COPY("window.active.button.shade.toggled.pressed.bg",
theme->btn_shade->a_toggled_focused_pressed, TRUE,
- theme->btn_shade->a_focused_pressed);
+ a_toggled_focused_pressed_tmp);
READ_APPEARANCE_COPY("window.inactive.button.shade.toggled.pressed.bg",
theme->btn_shade->a_toggled_unfocused_pressed, TRUE,
- theme->btn_shade->a_unfocused_pressed);
+ a_toggled_unfocused_pressed_tmp);
/* toggled hover */
READ_APPEARANCE_COPY("window.active.button.shade.toggled.hover.bg",
theme->btn_shade->a_toggled_hover_focused, TRUE,
- theme->btn_shade->a_toggled_focused_unpressed);
+ a_toggled_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.shade.toggled.hover.bg",
theme->btn_shade->a_toggled_hover_unfocused, TRUE,
- theme->btn_shade->a_toggled_unfocused_unpressed);
+ a_toggled_hover_unfocused_tmp);
/* iconify button */
read_button_colors(db, inst, theme, theme->btn_iconify, "iconify");
a_disabled_unfocused_tmp);
READ_APPEARANCE_COPY("window.active.button.iconify.hover.bg",
theme->btn_iconify->a_hover_focused, TRUE,
- theme->btn_iconify->a_focused_unpressed);
+ a_hover_focused_tmp);
READ_APPEARANCE_COPY("window.inactive.button.iconify.hover.bg",
theme->btn_iconify->a_hover_unfocused, TRUE,
- theme->btn_iconify->a_unfocused_unpressed);
+ a_hover_unfocused_tmp);
/* osd buttons */
READ_APPEARANCE_COPY("osd.button.unpressed.bg", theme->osd_unpressed_button, TRUE, a_focused_unpressed_tmp);
G_BEGIN_DECLS
-/*! Setup to do a binary search on an array holding elements of type @t */
-#define BSEARCH_SETUP(t) \
- register t l_BSEARCH, r_BSEARCH, out_BSEARCH;
+/*! Setup to do a binary search on an array. */
+#define BSEARCH_SETUP() \
+ register guint l_BSEARCH, r_BSEARCH, out_BSEARCH;
+
+/*! Helper macro that just returns the input */
+#define BSEARCH_IS_T(t) (t)
/*! Search an array @ar holding elements of type @t, starting at index @start,
with @size elements, looking for value @val. */
-#define BSEARCH(t, ar, start, size, val) \
+#define BSEARCH(t, ar, start, size, val) \
+ BSEARCH_CMP(t, ar, start, size, val, BSEARCH_IS_T)
+
+/*! Search an array @ar, starting at index @start,
+ with @size elements, looking for value @val of type @t,
+ using the macro @to_t to convert values in the arrau @ar to type @t.*/
+#define BSEARCH_CMP(t, ar, start, size, val, to_t) \
{ \
l_BSEARCH = (start); \
r_BSEARCH = (start)+(size)-1; \
/* m is in the middle, but to the left if there's an even number \
of elements */ \
out_BSEARCH = l_BSEARCH + (r_BSEARCH - l_BSEARCH)/2; \
- if ((val) == (ar)[out_BSEARCH]) { \
+ if ((val) == to_t((ar)[out_BSEARCH])) { \
break; \
} \
- else if ((val) < (ar)[out_BSEARCH] && out_BSEARCH > 0) { \
+ else if ((val) < to_t((ar)[out_BSEARCH]) && out_BSEARCH > 0) { \
r_BSEARCH = out_BSEARCH-1; /* search to the left side */ \
} \
else \
static XModifierKeymap *modmap;
static KeySym *keymap;
static gint min_keycode, max_keycode, keysyms_per_keycode;
-/* This is a bitmask of the different masks for each modifier key */
+/*! This is a bitmask of the different masks for each modifier key */
static guchar modkeys_keys[OBT_KEYBOARD_NUM_MODKEYS];
static gboolean alt_l = FALSE;
g_free(aname);
}
-ObtModkeysKey obt_keyboard_keyevent_to_modkey(XEvent *e)
+guint obt_keyboard_keyevent_to_modmask(XEvent *e)
{
- KeySym sym;
+ gint i, masknum;
g_return_val_if_fail(e->type == KeyPress || e->type == KeyRelease,
OBT_KEYBOARD_MODKEY_NONE);
- XLookupString(&e->xkey, NULL, 0, &sym, NULL);
-
- switch (sym) {
- case XK_Num_Lock: return OBT_KEYBOARD_MODKEY_NUMLOCK;
- case XK_Scroll_Lock: return OBT_KEYBOARD_MODKEY_SCROLLLOCK;
- case XK_Caps_Lock: return OBT_KEYBOARD_MODKEY_SHIFT;
- case XK_Alt_L:
- case XK_Alt_R: return OBT_KEYBOARD_MODKEY_ALT;
- case XK_Super_L:
- case XK_Super_R: return OBT_KEYBOARD_MODKEY_SUPER;
- case XK_Hyper_L:
- case XK_Hyper_R: return OBT_KEYBOARD_MODKEY_HYPER;
- case XK_Meta_L:
- case XK_Meta_R: return OBT_KEYBOARD_MODKEY_META;
- case XK_Control_L:
- case XK_Control_R: return OBT_KEYBOARD_MODKEY_CONTROL;
- case XK_Shift_L:
- case XK_Shift_R: return OBT_KEYBOARD_MODKEY_SHIFT;
- default: return OBT_KEYBOARD_MODKEY_NONE;
- }
-}
-
-guint obt_keyboard_keyevent_to_modmask(XEvent *e)
-{
- g_return_val_if_fail(e->type == KeyPress || e->type == KeyRelease, 0);
-
- return obt_keyboard_modkey_to_modmask(obt_keyboard_keyevent_to_modkey(e));
+ for (masknum = 0; masknum < NUM_MASKS; ++masknum)
+ for (i = 0; i < modmap->max_keypermod; ++i) {
+ KeyCode c = modmap->modifiermap[masknum*modmap->max_keypermod + i];
+ if (c == e->xkey.keycode)
+ return 1<<masknum;
+ }
+ return 0;
}
guint obt_keyboard_only_modmasks(guint mask)
right keys when there are both. */
guint obt_keyboard_modkey_to_modmask(ObtModkeysKey key);
-/*! Get the modifier key which was pressed or released in a keyboard event */
-ObtModkeysKey obt_keyboard_keyevent_to_modkey(XEvent *e);
-
/*! Convert a KeySym to all the KeyCodes which generate it. */
KeyCode* obt_keyboard_keysym_to_keycode(KeySym sym);
if (!paths)
return NULL;
spl = g_strsplit(paths, ":", -1);
- for (it = spl; *it; ++it)
- list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
+ for (it = spl; *it; ++it) {
+ if ((*it)[0]) /* skip empty strings */
+ list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
+ }
g_free(spl);
return list;
}
const gchar *const path)
{
struct stat st;
- BSEARCH_SETUP(guint);
+ BSEARCH_SETUP();
if (stat(path, &st) != 0)
return FALSE;
GSList *it;
for (it = p->exec_dirs; it; it = g_slist_next(it)) {
- gchar *f = g_strdup_printf(it->data, G_DIR_SEPARATOR_S, path);
+ gchar *f = g_build_filename(it->data, path, NULL);
gboolean e = try_exec(p, f);
g_free(f);
if (e) return TRUE;
CREATE_(OB_APP_TITLE);
CREATE_(OB_APP_NAME);
CREATE_(OB_APP_CLASS);
+ CREATE_(OB_APP_GROUP_NAME);
+ CREATE_(OB_APP_GROUP_CLASS);
CREATE_(OB_APP_TYPE);
}
OBT_PROP_OB_APP_TITLE,
OBT_PROP_OB_APP_NAME,
OBT_PROP_OB_APP_CLASS,
+ OBT_PROP_OB_APP_GROUP_NAME,
+ OBT_PROP_OB_APP_GROUP_CLASS,
OBT_PROP_OB_APP_TYPE,
OBT_PROP_NUM_ATOMS
g_hash_table_insert(i->callbacks, c->tag, c);
}
+void obt_xml_unregister(ObtXmlInst *i, const gchar *tag)
+{
+ g_hash_table_remove(i->callbacks, tag);
+}
+
static gboolean load_file(ObtXmlInst *i,
const gchar *domain,
const gchar *filename,
obt_xml_tree(i, i->root->children);
}
-gchar *obt_xml_node_string(xmlNodePtr node)
+gchar *obt_xml_node_string_unstripped(xmlNodePtr node)
{
xmlChar *c = xmlNodeGetContent(node);
gchar *s;
- if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */
s = g_strdup(c ? (gchar*)c : "");
xmlFree(c);
return s;
}
+gchar *obt_xml_node_string(xmlNodePtr node)
+{
+ gchar* result = obt_xml_node_string_unstripped(node);
+ g_strstrip(result); /* strip leading/trailing whitespace */
+ return result;
+}
+
gint obt_xml_node_int(xmlNodePtr node)
{
xmlChar *c = xmlNodeGetContent(node);
return r;
}
-gboolean obt_xml_attr_string(xmlNodePtr node, const gchar *name,
- gchar **value)
+gboolean obt_xml_attr_string_unstripped(xmlNodePtr node, const gchar *name,
+ gchar **value)
{
xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
gboolean r = FALSE;
if (c) {
- g_strstrip((char*)c); /* strip leading/trailing whitespace */
*value = g_strdup((gchar*)c);
r = TRUE;
}
return r;
}
+gboolean obt_xml_attr_string(xmlNodePtr node, const gchar *name,
+ gchar **value)
+{
+ gboolean result = obt_xml_attr_string_unstripped(node, name, value);
+ if (result)
+ g_strstrip(*value); /* strip leading/trailing whitespace */
+ return result;
+}
+
gboolean obt_xml_attr_contains(xmlNodePtr node, const gchar *name,
const gchar *val)
{
void obt_xml_register(ObtXmlInst *inst, const gchar *tag,
ObtXmlCallback func, gpointer data);
+void obt_xml_unregister(ObtXmlInst *inst, const gchar *tag);
void obt_xml_tree(ObtXmlInst *i, xmlNodePtr node);
void obt_xml_tree_from_root(ObtXmlInst *i);
xmlNodePtr obt_xml_find_node (xmlNodePtr node, const gchar *name);
gboolean obt_xml_node_contains (xmlNodePtr node, const gchar *val);
+gchar *obt_xml_node_string_unstripped(xmlNodePtr node);
gchar *obt_xml_node_string (xmlNodePtr node);
gint obt_xml_node_int (xmlNodePtr node);
gboolean obt_xml_node_bool (xmlNodePtr node);
gboolean obt_xml_attr_contains (xmlNodePtr node, const gchar *name,
const gchar *val);
+gboolean obt_xml_attr_string_unstripped(xmlNodePtr node, const gchar *name,
+ gchar **value);
gboolean obt_xml_attr_string (xmlNodePtr node, const gchar *name,
gchar **value);
gboolean obt_xml_attr_int (xmlNodePtr node, const gchar *name,
ObActionsDataFreeFunc free;
ObActionsRunFunc run;
ObActionsShutdownFunc shutdown;
+ gboolean modifies_focused_window;
};
struct _ObActionsAct {
return NULL;
}
- def = g_slice_new(ObActionsDefinition);
+ def = g_slice_new0(ObActionsDefinition);
def->ref = 1;
def->name = g_strdup(name);
def->free = free;
def->run = run;
def->shutdown = NULL;
+ def->modifies_focused_window = TRUE;
registered = g_slist_prepend(registered, def);
return def;
return FALSE;
}
+gboolean actions_set_modifies_focused_window(const gchar *name,
+ gboolean modifies)
+{
+ GSList *it;
+ ObActionsDefinition *def;
+
+ for (it = registered; it; it = g_slist_next(it)) {
+ def = it->data;
+ if (!g_ascii_strcasecmp(name, def->name)) {
+ def->modifies_focused_window = modifies;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
static void actions_definition_ref(ObActionsDefinition *def)
{
++def->ref;
if (!act->def->run(&data, act->options)) {
if (actions_act_is_interactive(act))
actions_interactive_end_act();
- if (client && client == focus_client)
+ if (client && client == focus_client &&
+ act->def->modifies_focused_window)
+ {
update_user_time = TRUE;
+ }
} else {
/* make sure its interactive if it returned TRUE */
g_assert(act->i_input);
interactive_act = act;
actions_act_ref(interactive_act);
- interactive_initial_state = obt_keyboard_only_modmasks(state);
+ interactive_initial_state = state;
/* if using focus_delay, stop the timer now so that focus doesn't go
moving on us, which would kill the action */
ob_debug_type(OB_DEBUG_FOCUS,
"Generating fake leave because we did a "
"mouse-event action");
- event_enter_client(data->client);
+ event_leave_client(data->client);
}
}
}
gboolean actions_set_shutdown(const gchar *name,
ObActionsShutdownFunc shutdown);
+gboolean actions_set_modifies_focused_window(const gchar *name,
+ gboolean modifies);
ObActionsAct* actions_parse(xmlNodePtr node);
ObActionsAct* actions_parse_string(const gchar *name);
gboolean forward;
gboolean bar;
gboolean raise;
+ gboolean interactive;
ObFocusCyclePopupMode dialog_mode;
GSList *actions;
o = g_slice_new0(Options);
o->bar = TRUE;
o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_LIST;
+ o->interactive = TRUE;
if ((n = obt_xml_find_node(node, "linear")))
o->linear = obt_xml_node_bool(n);
else if (obt_xml_node_contains(n, "icons"))
o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_ICONS;
}
+ if ((n = obt_xml_find_node(node, "interactive")))
+ o->interactive = obt_xml_node_bool(n);
if ((n = obt_xml_find_node(node, "bar")))
o->bar = obt_xml_node_bool(n);
if ((n = obt_xml_find_node(node, "raise")))
Options *o = options;
struct _ObClient *ft;
- ft = focus_cycle(o->forward,
- o->all_desktops,
- !o->only_hilite_windows,
- o->dock_windows,
- o->desktop_windows,
- o->linear,
- TRUE,
- o->bar,
- o->dialog_mode,
- FALSE, FALSE);
+ gboolean done = FALSE;
+ gboolean cancel = FALSE;
+
+ ft = focus_cycle(
+ o->forward,
+ o->all_desktops,
+ !o->only_hilite_windows,
+ o->dock_windows,
+ o->desktop_windows,
+ o->linear,
+ (o->interactive ? o->bar : FALSE),
+ (o->interactive ? o->dialog_mode : OB_FOCUS_CYCLE_POPUP_MODE_NONE),
+ done, cancel);
stacking_restore();
if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
- return TRUE;
+ return o->interactive;
}
static gboolean i_input_func(guint initial_state,
gboolean *used)
{
Options *o = options;
- guint mods;
+ guint mods, initial_mods;
+ initial_mods = obt_keyboard_only_modmasks(initial_state);
mods = obt_keyboard_only_modmasks(e->xkey.state);
if (e->type == KeyRelease) {
/* remove from the state the mask of the modifier key being
}
/* There were no modifiers and they pressed enter */
- else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) {
+ else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods) {
o->cancel = FALSE;
o->state = e->xkey.state;
return FALSE;
}
}
/* They released the modifiers */
- else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
+ else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods))
{
o->cancel = FALSE;
o->state = e->xkey.state;
Options *o = options;
struct _ObClient *ft;
+ gboolean done = TRUE;
+
ft = focus_cycle(o->forward,
o->all_desktops,
!o->only_hilite_windows,
o->dock_windows,
o->desktop_windows,
o->linear,
- TRUE,
o->bar,
o->dialog_mode,
- TRUE, o->cancel);
+ done, o->cancel);
if (ft)
actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
gpointer options,
gboolean *used)
{
- guint mods;
+ guint mods, initial_mods;
+ initial_mods = obt_keyboard_only_modmasks(initial_state);
mods = obt_keyboard_only_modmasks(e->xkey.state);
if (e->type == KeyRelease) {
/* remove from the state the mask of the modifier key being
return FALSE;
/* There were no modifiers and they pressed enter */
- else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state)
+ else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods)
return FALSE;
}
/* They released the modifiers */
- else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
+ else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods))
{
return FALSE;
}
static gboolean i_pre_func(guint initial_state, gpointer options)
{
- if (!initial_state) {
+ guint initial_mods = obt_keyboard_only_modmasks(initial_state);
+ if (!initial_mods) {
Options *o = options;
o->interactive = FALSE;
return FALSE;
gpointer options,
gboolean *used)
{
- guint mods;
+ guint mods, initial_mods;
+ initial_mods = obt_keyboard_only_modmasks(initial_state);
mods = obt_keyboard_only_modmasks(e->xkey.state);
if (e->type == KeyRelease) {
/* remove from the state the mask of the modifier key being
}
/* There were no modifiers and they pressed enter */
- else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) {
+ else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods) {
end_cycle(FALSE, e->xkey.state, options);
return FALSE;
}
}
/* They released the modifiers */
- else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
- {
+ else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods)) {
end_cycle(FALSE, e->xkey.state, options);
return FALSE;
}
{
actions_register("Execute", setup_func, free_func, run_func);
actions_set_shutdown("Execute", shutdown_func);
+ actions_set_modifies_focused_window("Execute", FALSE);
client_add_destroy_notify(client_dest, NULL);
}
free_func(options);
}
+/* Replace occurrences of $variables */
+static gchar* expand_variables(gchar* cmd, ObActionsData* data)
+{
+ gchar *c, *before, *expand;
+
+ expand = NULL;
+ before = cmd;
+
+ while ((c = strchr(before, '$'))) {
+ if ((c[1] == 'p' || c[1] == 'P') &&
+ (c[2] == 'i' || c[2] == 'I') &&
+ (c[3] == 'd' || c[3] == 'D') &&
+ !g_ascii_isalnum(c[4]))
+ {
+ /* found $pid */
+ gchar *tmp;
+
+ *c = '\0';
+ tmp = expand;
+ expand = g_strdup_printf("%s%s%u",
+ (expand ? expand : ""),
+ before,
+ data->client ? data->client->pid : 0);
+ g_free(tmp);
+
+ before = c + 4; /* 4 = strlen("$pid") */
+ }
+ else if ((c[1] == 'w' || c[1] == 'W') &&
+ (c[2] == 'i' || c[2] == 'I') &&
+ (c[3] == 'd' || c[3] == 'D') &&
+ !g_ascii_isalnum(c[4]))
+ {
+ /* found $wid */
+ gchar *tmp;
+
+ *c = '\0';
+ tmp = expand;
+ expand = g_strdup_printf("%s%s%lu",
+ (expand ? expand : ""),
+ before,
+ data->client ? data->client->window : 0);
+ g_free(tmp);
+
+ before = c + 4; /* 4 = strlen("$wid") */
+ }
+ else if ((c[1] == 'p' || c[1] == 'P') &&
+ (c[2] == 'o' || c[2] == 'O') &&
+ (c[3] == 'i' || c[3] == 'I') &&
+ (c[4] == 'n' || c[4] == 'N') &&
+ (c[5] == 't' || c[5] == 'T') &&
+ (c[6] == 'e' || c[6] == 'E') &&
+ (c[7] == 'r' || c[7] == 'R') &&
+ !g_ascii_isalnum(c[8]))
+ {
+ /* found $pointer */
+ gchar *tmp;
+
+ *c = '\0';
+ tmp = expand;
+ expand = g_strdup_printf("%s%s%u %u",
+ (expand ? expand : ""),
+ before,
+ data->x, data->y);
+ g_free(tmp);
+
+ before = c + 8; /* 4 = strlen("$pointer") */
+ }
+ else {
+ /* found something unknown, copy the $ and continue */
+ gchar *tmp;
+
+ *c = '\0';
+ tmp = expand;
+ expand = g_strdup_printf("%s%s$",
+ (expand ? expand : ""),
+ before);
+ g_free(tmp);
+
+ before = c + 1; /* 1 = strlen("$") */
+ }
+ }
+
+ if (expand) {
+ gchar *tmp;
+
+ /* add on the end of the string after the last replacement */
+ tmp = expand;
+ expand = g_strconcat(expand, before, NULL);
+ g_free(tmp);
+
+ /* replace the command with the expanded one */
+ g_free(cmd);
+ cmd = expand;
+ }
+ return cmd;
+}
+
/* Always return FALSE because its not interactive */
static gboolean run_func(ObActionsData *data, gpointer options)
{
return FALSE;
}
- if (data->client) {
- gchar *c, *before, *expand;
-
- /* replace occurrences of $pid and $wid */
-
- expand = NULL;
- before = cmd;
-
- while ((c = strchr(before, '$'))) {
- if ((c[1] == 'p' || c[1] == 'P') &&
- (c[2] == 'i' || c[2] == 'I') &&
- (c[3] == 'd' || c[3] == 'D') &&
- !g_ascii_isalnum(c[4]))
- {
- /* found $pid */
- gchar *tmp;
-
- *c = '\0';
- tmp = expand;
- expand = g_strdup_printf("%s%s%u",
- (expand ? expand : ""),
- before,
- data->client->pid);
- g_free(tmp);
-
- before = c + 4; /* 4 = strlen("$pid") */
- }
- else if ((c[1] == 'w' || c[1] == 'W') &&
- (c[2] == 'i' || c[2] == 'I') &&
- (c[3] == 'd' || c[3] == 'D') &&
- !g_ascii_isalnum(c[4]))
- {
- /* found $wid */
- gchar *tmp;
-
- *c = '\0';
- tmp = expand;
- expand = g_strdup_printf("%s%s%lu",
- (expand ? expand : ""),
- before,
- data->client->window);
- g_free(tmp);
-
- before = c + 4; /* 4 = strlen("$wid") */
- }
- else
- before = c + 1; /* no infinite loops plz */
- }
-
- if (expand) {
- gchar *tmp;
-
- /* add on the end of the string after the last replacement */
- tmp = expand;
- expand = g_strconcat(expand, before, NULL);
- g_free(tmp);
-
- /* replace the command with the expanded one */
- g_free(cmd);
- cmd = expand;
- }
- }
+ cmd = expand_variables(cmd, data);
/* If there is a keyboard grab going on then we need to cancel
it so the application can grab things */
gint h;
gint h_denom;
gint monitor;
+ gboolean w_sets_client_size;
+ gboolean h_sets_client_size;
} Options;
static gpointer setup_func(xmlNodePtr node);
if (g_ascii_strcasecmp(s, "current") != 0)
config_parse_relative_number(s, &o->w, &o->w_denom);
g_free(s);
+
+ obt_xml_attr_bool(n, "client", &o->w_sets_client_size);
}
if ((n = obt_xml_find_node(node, "height"))) {
gchar *s = obt_xml_node_string(n);
if (g_ascii_strcasecmp(s, "current") != 0)
config_parse_relative_number(s, &o->h, &o->h_denom);
g_free(s);
+
+ obt_xml_attr_bool(n, "client", &o->h_sets_client_size);
}
if ((n = obt_xml_find_node(node, "monitor"))) {
area = screen_area(c->desktop, mon, NULL);
carea = screen_area(c->desktop, cmon, NULL);
+ /* find a target size for the client/frame. */
w = o->w;
- if (w == G_MININT) w = c->area.width;
+ if (w == G_MININT) {
+ if (o->w_sets_client_size)
+ w = c->area.width;
+ else
+ w = c->frame->area.width;
+ }
else if (o->w_denom) w = (w * area->width) / o->w_denom;
h = o->h;
- if (h == G_MININT) h = c->area.height;
+ if (h == G_MININT) {
+ if (o->w_sets_client_size)
+ h = c->area.height;
+ else
+ h = c->frame->area.height;
+ }
else if (o->h_denom) h = (h * area->height) / o->h_denom;
+ /* get back to the client's size. */
+ if (!o->w_sets_client_size)
+ w -= c->frame->size.left + c->frame->size.right;
+ if (!o->h_sets_client_size)
+ h -= c->frame->size.top + c->frame->size.bottom;
+
/* it might not be able to resize how they requested, so find out what
it will actually be resized to */
x = c->area.x;
gint left = o->left, right = o->right, top = o->top, bottom = o->bottom;
if (o->left_denom)
- left = (left * c->area.width / c->size_inc.width) / o->left_denom;
+ left = left * c->area.width / o->left_denom;
if (o->right_denom)
- right = (right * c->area.width / c->size_inc.width) / o->right_denom;
+ right = right * c->area.width / o->right_denom;
if (o->top_denom)
- top = (top * c->area.height / c->size_inc.height) / o->top_denom;
+ top = top * c->area.height / o->top_denom;
if (o->bottom_denom)
- bottom = (bottom * c->area.height / c->size_inc.height) / o->bottom_denom;
-
+ bottom = bottom * c->area.height / o->bottom_denom;
+
+ if (left && ABS(left) < c->size_inc.width)
+ left = left < 0 ? -c->size_inc.width : c->size_inc.width;
+ if (right && ABS(right) < c->size_inc.width)
+ right = right < 0 ? -c->size_inc.width : c->size_inc.width;
+ if (top && ABS(top) < c->size_inc.height)
+ top = top < 0 ? -c->size_inc.height : c->size_inc.height;
+ if (bottom && ABS(bottom) < c->size_inc.height)
+ bottom = bottom < 0 ? -c->size_inc.height : c->size_inc.height;
+
+ // When resizing, if the resize has a non-zero value then make sure it
+ // is at least as big as the size increment so the window does actually
+ // resize.
x = c->area.x;
y = c->area.y;
ow = c->area.width;
- xoff = -left * c->size_inc.width;
- nw = ow + right * c->size_inc.width
- + left * c->size_inc.width;
+ xoff = -left;
+ nw = ow + right + left;
oh = c->area.height;
- yoff = -top * c->size_inc.height;
- nh = oh + bottom * c->size_inc.height
- + top * c->size_inc.height;
+ yoff = -top;
+ nh = oh + bottom + top;
client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
xoff = xoff == 0 ? 0 :
}
}
+void client_remove_destroy_notify_data(ObClientCallback func, gpointer data)
+{
+ GSList *it;
+
+ for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ if (d->func == func && d->data == data) {
+ g_slice_free(ClientCallback, d);
+ client_destroy_notifies =
+ g_slist_delete_link(client_destroy_notifies, it);
+ break;
+ }
+ }
+}
+
void client_set_list(void)
{
Window *windows, *win_it;
Time launch_time;
guint32 user_time;
gboolean obplaced;
+ gulong ignore_start;
ob_debug("Managing window: 0x%lx", window);
ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
ob_debug("Window name: %s class: %s role: %s title: %s",
self->name, self->class, self->role, self->title);
+ ob_debug("Window group name: %s group class: %s",
+ self->group_name, self->group_class);
/* per-app settings override stuff from client_get_all, and return the
settings for other uses too. the returned settings is a shallow copy,
"program + user specified" :
"BADNESS !?")))), place.width, place.height);
- obplaced = place_client(self, do_activate, &place.x, &place.y,
- settings);
+ obplaced = place_client(self, do_activate, &place, settings);
/* watch for buggy apps that ask to be placed at (0,0) when there is
a strut there */
/* grab mouse bindings before showing the window */
mouse_grab_for_client(self, TRUE);
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
/* this has to happen before we try focus the window, but we want it to
happen after the client's stacking has been determined or it looks bad
*/
- {
- gulong ignore_start;
- if (!config_focus_under_mouse)
- ignore_start = event_start_ignore_all_enters();
-
- client_show(self);
-
- if (!config_focus_under_mouse)
- event_end_ignore_all_enters(ignore_start);
- }
+ client_show(self);
/* activate/hilight/raise the window */
if (try_activate) {
stacking_raise(CLIENT_AS_WINDOW(self));
}
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
/* add to client list/map */
client_list = g_list_append(client_list, self);
window_add(&self->window, CLIENT_AS_WINDOW(self));
g_free(self->name);
g_free(self->class);
g_free(self->role);
+ g_free(self->group_name);
+ g_free(self->group_class);
g_free(self->client_machine);
g_free(self->sm_client_id);
g_slice_free(ObClient, self);
g_assert(app->name != NULL || app->class != NULL ||
app->role != NULL || app->title != NULL ||
+ app->group_name != NULL || app->group_class != NULL ||
(signed)app->type >= 0);
if (app->name &&
!g_pattern_match(app->name, strlen(self->name), self->name, NULL))
match = FALSE;
+ else if (app->group_name &&
+ !g_pattern_match(app->group_name,
+ strlen(self->group_name), self->group_name, NULL))
+ match = FALSE;
else if (app->class &&
!g_pattern_match(app->class,
strlen(self->class), self->class, NULL))
match = FALSE;
+ else if (app->group_class &&
+ !g_pattern_match(app->group_class,
+ strlen(self->group_class), self->group_class,
+ NULL))
+ match = FALSE;
else if (app->role &&
!g_pattern_match(app->role,
strlen(self->role), self->role, NULL))
from per-app settings */
client_get_session_ids(self);
- /* now we got everything that can affect the decorations */
+ /* get this early so we have it for debugging, also this can be used
+ by app rule matching */
+ client_update_title(self);
+
+ /* now we got everything that can affect the decorations or app rule
+ matching */
if (!real)
return;
- /* get this early so we have it for debugging */
- client_update_title(self);
-
/* save the values of the variables used for app rule matching */
client_save_app_rule_values(self);
if (self->name == NULL) self->name = g_strdup("");
if (self->class == NULL) self->class = g_strdup("");
+ /* get the WM_CLASS (name and class) from the group leader. make them "" if
+ they are not provided */
+ if (leader)
+ got = OBT_PROP_GETSS_TYPE(leader, WM_CLASS, STRING_NO_CC, &ss);
+ else
+ got = FALSE;
+
+ if (got) {
+ if (ss[0]) {
+ self->group_name = g_strdup(ss[0]);
+ if (ss[1])
+ self->group_class = g_strdup(ss[1]);
+ }
+ g_strfreev(ss);
+ }
+
+ if (self->group_name == NULL) self->group_name = g_strdup("");
+ if (self->group_class == NULL) self->group_class = g_strdup("");
+
/* get the WM_WINDOW_ROLE. make it "" if it is not provided */
got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
+ OBT_PROP_SETS(self->window, OB_APP_GROUP_NAME, self->group_name);
+ OBT_PROP_SETS(self->window, OB_APP_GROUP_CLASS, self->group_class);
OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title);
switch (self->type) {
self->type == OB_CLIENT_TYPE_TOOLBAR);
}
+gboolean client_occupies_space(ObClient *self)
+{
+ return !(self->type == OB_CLIENT_TYPE_DESKTOP ||
+ self->type == OB_CLIENT_TYPE_SPLASH);
+}
+
gboolean client_mouse_focusable(ObClient *self)
{
return !(self->type == OB_CLIENT_TYPE_MENU ||
gchar *class;
/*! The specified role of the window, used for identification */
gchar *role;
+ /*! The application that created the window's group. */
+ gchar *group_name;
+ /*! The class of the window's group, can used for grouping */
+ gchar *group_class;
/*! The session client id for the window. *This can be NULL!* */
gchar *sm_client_id;
/*! Get notified when the client is unmanaged */
void client_add_destroy_notify(ObClientCallback func, gpointer data);
void client_remove_destroy_notify(ObClientCallback func);
+void client_remove_destroy_notify_data(ObClientCallback func, gpointer data);
/*! Manages a given window
@param prompt This specifies an ObPrompt which is being managed. It is
(utilty, menu, etc) */
gboolean client_helper(ObClient *self);
+/*! Returns true if the window occupies space in the monitor conceptually, or
+ false if it does not and its presence should be ignored when possible. */
+gboolean client_occupies_space(ObClient *self);
+
/*! Return if the client is a type which should be given focus from mouse
presses on the *client* window. This doesn't affect clicking on the
decorations. This doesn't count for focus cycling, different rules apply to
/*! Determine if the window, using the given client-area, would be considered
as an "oldschool fullscreen" window, that is, if it is filling a whole
monitor. */
-gboolean client_is_oldfullscreen(const ObClient const *self, const Rect *area);
+gboolean client_is_oldfullscreen(const ObClient *self, const Rect *area);
/*! Iconifies or uniconifies the client window
@param iconic true if the window should be iconified; false if it should be
gboolean config_unfocus_leave;
ObPlacePolicy config_place_policy;
-gboolean config_place_center;
ObPlaceMonitor config_place_monitor;
guint config_primary_monitor_index;
dst->position = src->position;
/* monitor is copied above */
}
+
+ if (src->size_given) {
+ dst->size_given = TRUE;
+ dst->width_num = src->width_num;
+ dst->width_denom = src->width_denom;
+ dst->height_num = src->height_num;
+ dst->height_denom = src->height_denom;
+ }
}
void config_parse_relative_number(gchar *s, gint *num, gint *denom)
</applications>
*/
+static void parse_single_per_app_settings(xmlNodePtr app,
+ ObAppSettings *settings)
+{
+ xmlNodePtr n, c;
+ gboolean x_pos_given = FALSE;
+ gboolean width_given = FALSE;
+
+ if ((n = obt_xml_find_node(app->children, "decor")))
+ if (!obt_xml_node_contains(n, "default"))
+ settings->decor = obt_xml_node_bool(n);
+
+ if ((n = obt_xml_find_node(app->children, "shade")))
+ if (!obt_xml_node_contains(n, "default"))
+ settings->shade = obt_xml_node_bool(n);
+
+ if ((n = obt_xml_find_node(app->children, "position"))) {
+ if ((c = obt_xml_find_node(n->children, "x"))) {
+ if (!obt_xml_node_contains(c, "default")) {
+ config_parse_gravity_coord(c, &settings->position.x);
+ x_pos_given = TRUE;
+ }
+ }
+
+ if (x_pos_given && (c = obt_xml_find_node(n->children, "y"))) {
+ if (!obt_xml_node_contains(c, "default")) {
+ config_parse_gravity_coord(c, &settings->position.y);
+ settings->pos_given = TRUE;
+ }
+ }
+
+ /* monitor can be set without setting x or y */
+ if ((c = obt_xml_find_node(n->children, "monitor"))) {
+ if (!obt_xml_node_contains(c, "default")) {
+ gchar *s = obt_xml_node_string(c);
+ if (!g_ascii_strcasecmp(s, "mouse"))
+ settings->monitor_type = OB_PLACE_MONITOR_MOUSE;
+ else if (!g_ascii_strcasecmp(s, "active"))
+ settings->monitor_type = OB_PLACE_MONITOR_ACTIVE;
+ else if (!g_ascii_strcasecmp(s, "primary"))
+ settings->monitor_type = OB_PLACE_MONITOR_PRIMARY;
+ else
+ settings->monitor = obt_xml_node_int(c);
+ g_free(s);
+ }
+ }
+
+ obt_xml_attr_bool(n, "force", &settings->pos_force);
+ }
+
+ if ((n = obt_xml_find_node(app->children, "size"))) {
+ if ((c = obt_xml_find_node(n->children, "width"))) {
+ if (!obt_xml_node_contains(c, "default")) {
+ gchar *s = obt_xml_node_string(c);
+ config_parse_relative_number(s,
+ &settings->width_num,
+ &settings->width_denom);
+ if (settings->width_num > 0 && settings->width_denom >= 0)
+ width_given = TRUE;
+ g_free(s);
+ }
+ }
+
+ if (width_given && (c = obt_xml_find_node(n->children, "height"))) {
+ gchar *s = obt_xml_node_string(c);
+ config_parse_relative_number(s,
+ &settings->height_num,
+ &settings->height_denom);
+ if (settings->height_num > 0 && settings->height_denom >= 0)
+ settings->size_given = TRUE;
+ g_free(s);
+ }
+ }
+
+ if ((n = obt_xml_find_node(app->children, "focus"))) {
+ if (!obt_xml_node_contains(n, "default"))
+ settings->focus = obt_xml_node_bool(n);
+ }
+
+ if ((n = obt_xml_find_node(app->children, "desktop"))) {
+ if (!obt_xml_node_contains(n, "default")) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "all"))
+ settings->desktop = DESKTOP_ALL;
+ else {
+ gint i = obt_xml_node_int(n);
+ if (i > 0)
+ settings->desktop = i;
+ }
+ g_free(s);
+ }
+ }
+
+ if ((n = obt_xml_find_node(app->children, "layer"))) {
+ if (!obt_xml_node_contains(n, "default")) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "above"))
+ settings->layer = 1;
+ else if (!g_ascii_strcasecmp(s, "below"))
+ settings->layer = -1;
+ else
+ settings->layer = 0;
+ g_free(s);
+ }
+ }
+
+ if ((n = obt_xml_find_node(app->children, "iconic")))
+ if (!obt_xml_node_contains(n, "default"))
+ settings->iconic = obt_xml_node_bool(n);
+
+ if ((n = obt_xml_find_node(app->children, "skip_pager")))
+ if (!obt_xml_node_contains(n, "default"))
+ settings->skip_pager = obt_xml_node_bool(n);
+
+ if ((n = obt_xml_find_node(app->children, "skip_taskbar")))
+ if (!obt_xml_node_contains(n, "default"))
+ settings->skip_taskbar = obt_xml_node_bool(n);
+
+ if ((n = obt_xml_find_node(app->children, "fullscreen")))
+ if (!obt_xml_node_contains(n, "default"))
+ settings->fullscreen = obt_xml_node_bool(n);
+
+ if ((n = obt_xml_find_node(app->children, "maximized"))) {
+ if (!obt_xml_node_contains(n, "default")) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "horizontal")) {
+ settings->max_horz = TRUE;
+ settings->max_vert = FALSE;
+ } else if (!g_ascii_strcasecmp(s, "vertical")) {
+ settings->max_horz = FALSE;
+ settings->max_vert = TRUE;
+ } else
+ settings->max_horz = settings->max_vert =
+ obt_xml_node_bool(n);
+ g_free(s);
+ }
+ }
+
+ if ((n = obt_xml_find_node(app->children, "opacity")))
+ if (!obt_xml_node_contains(n, "default"))
+ settings->opacity = obt_xml_node_int(n);
+}
+
/* Manages settings for individual applications.
Some notes: monitor is the screen number in a multi monitor
(Xinerama) setup (starting from 0), or mouse: the monitor the pointer
static void parse_per_app_settings(xmlNodePtr node, gpointer d)
{
xmlNodePtr app = obt_xml_find_node(node->children, "application");
- gchar *name = NULL, *class = NULL, *role = NULL, *title = NULL,
- *type_str = NULL;
- gboolean name_set, class_set, type_set, role_set, title_set;
- ObClientType type;
- gboolean x_pos_given;
+ for (; app; app = obt_xml_find_node(app->next, "application")) {
+ ObAppSettings *settings;
- while (app) {
- x_pos_given = FALSE;
+ gboolean name_set, class_set, role_set, title_set,
+ type_set, group_name_set, group_class_set;
+ gchar *name = NULL, *class = NULL, *role = NULL, *title = NULL,
+ *type_str = NULL, *group_name = NULL, *group_class = NULL;
+ ObClientType type;
class_set = obt_xml_attr_string(app, "class", &class);
name_set = obt_xml_attr_string(app, "name", &name);
+ group_class_set = obt_xml_attr_string(app, "groupclass", &group_class);
+ group_name_set = obt_xml_attr_string(app, "groupname", &group_name);
type_set = obt_xml_attr_string(app, "type", &type_str);
role_set = obt_xml_attr_string(app, "role", &role);
title_set = obt_xml_attr_string(app, "title", &title);
type_set = FALSE; /* not valid! */
}
- if (class_set || name_set || role_set || title_set || type_set) {
- xmlNodePtr n, c;
- ObAppSettings *settings = config_create_app_settings();
-
- if (name_set)
- settings->name = g_pattern_spec_new(name);
-
- if (class_set)
- settings->class = g_pattern_spec_new(class);
-
- if (role_set)
- settings->role = g_pattern_spec_new(role);
-
- if (title_set)
- settings->title = g_pattern_spec_new(title);
-
- if (type_set)
- settings->type = type;
-
- if ((n = obt_xml_find_node(app->children, "decor")))
- if (!obt_xml_node_contains(n, "default"))
- settings->decor = obt_xml_node_bool(n);
-
- if ((n = obt_xml_find_node(app->children, "shade")))
- if (!obt_xml_node_contains(n, "default"))
- settings->shade = obt_xml_node_bool(n);
-
- if ((n = obt_xml_find_node(app->children, "position"))) {
- if ((c = obt_xml_find_node(n->children, "x")))
- if (!obt_xml_node_contains(c, "default")) {
- config_parse_gravity_coord(c, &settings->position.x);
- x_pos_given = TRUE;
- }
-
- if (x_pos_given && (c = obt_xml_find_node(n->children, "y")))
- if (!obt_xml_node_contains(c, "default")) {
- config_parse_gravity_coord(c, &settings->position.y);
- settings->pos_given = TRUE;
- }
-
- /* monitor can be set without setting x or y */
- if ((c = obt_xml_find_node(n->children, "monitor")))
- if (!obt_xml_node_contains(c, "default")) {
- gchar *s = obt_xml_node_string(c);
- if (!g_ascii_strcasecmp(s, "mouse"))
- settings->monitor_type =
- OB_PLACE_MONITOR_MOUSE;
- else if (!g_ascii_strcasecmp(s, "active"))
- settings->monitor_type =
- OB_PLACE_MONITOR_ACTIVE;
- else if (!g_ascii_strcasecmp(s, "primary"))
- settings->monitor_type =
- OB_PLACE_MONITOR_PRIMARY;
- else
- settings->monitor = obt_xml_node_int(c);
- g_free(s);
- }
-
- obt_xml_attr_bool(n, "force", &settings->pos_force);
- }
-
- if ((n = obt_xml_find_node(app->children, "focus")))
- if (!obt_xml_node_contains(n, "default"))
- settings->focus = obt_xml_node_bool(n);
-
- if ((n = obt_xml_find_node(app->children, "desktop"))) {
- if (!obt_xml_node_contains(n, "default")) {
- gchar *s = obt_xml_node_string(n);
- if (!g_ascii_strcasecmp(s, "all"))
- settings->desktop = DESKTOP_ALL;
- else {
- gint i = obt_xml_node_int(n);
- if (i > 0)
- settings->desktop = i;
- }
- g_free(s);
- }
- }
+ if (!(class_set || name_set || role_set || title_set ||
+ type_set || group_class_set || group_name_set))
+ continue;
+
+ settings = config_create_app_settings();
+
+ if (name_set)
+ settings->name = g_pattern_spec_new(name);
+ if (class_set)
+ settings->class = g_pattern_spec_new(class);
+ if (group_name_set)
+ settings->group_name = g_pattern_spec_new(group_name);
+ if (group_class_set)
+ settings->group_class = g_pattern_spec_new(group_class);
+ if (role_set)
+ settings->role = g_pattern_spec_new(role);
+ if (title_set)
+ settings->title = g_pattern_spec_new(title);
+ if (type_set)
+ settings->type = type;
- if ((n = obt_xml_find_node(app->children, "layer")))
- if (!obt_xml_node_contains(n, "default")) {
- gchar *s = obt_xml_node_string(n);
- if (!g_ascii_strcasecmp(s, "above"))
- settings->layer = 1;
- else if (!g_ascii_strcasecmp(s, "below"))
- settings->layer = -1;
- else
- settings->layer = 0;
- g_free(s);
- }
-
- if ((n = obt_xml_find_node(app->children, "iconic")))
- if (!obt_xml_node_contains(n, "default"))
- settings->iconic = obt_xml_node_bool(n);
-
- if ((n = obt_xml_find_node(app->children, "skip_pager")))
- if (!obt_xml_node_contains(n, "default"))
- settings->skip_pager = obt_xml_node_bool(n);
-
- if ((n = obt_xml_find_node(app->children, "skip_taskbar")))
- if (!obt_xml_node_contains(n, "default"))
- settings->skip_taskbar = obt_xml_node_bool(n);
-
- if ((n = obt_xml_find_node(app->children, "fullscreen")))
- if (!obt_xml_node_contains(n, "default"))
- settings->fullscreen = obt_xml_node_bool(n);
-
- if ((n = obt_xml_find_node(app->children, "maximized")))
- if (!obt_xml_node_contains(n, "default")) {
- gchar *s = obt_xml_node_string(n);
- if (!g_ascii_strcasecmp(s, "horizontal")) {
- settings->max_horz = TRUE;
- settings->max_vert = FALSE;
- } else if (!g_ascii_strcasecmp(s, "vertical")) {
- settings->max_horz = FALSE;
- settings->max_vert = TRUE;
- } else
- settings->max_horz = settings->max_vert =
- obt_xml_node_bool(n);
- g_free(s);
- }
-
- if ((n = obt_xml_find_node(app->children, "opacity")))
- if (!obt_xml_node_contains(n, "default"))
- settings->opacity = obt_xml_node_int(n);
-
- config_per_app_settings = g_slist_append(config_per_app_settings,
- (gpointer) settings);
- g_free(name);
- g_free(class);
- g_free(role);
- g_free(title);
- g_free(type_str);
- name = class = role = title = type_str = NULL;
- }
-
- app = obt_xml_find_node(app->next, "application");
+ g_free(name);
+ g_free(class);
+ g_free(group_name);
+ g_free(group_class);
+ g_free(role);
+ g_free(title);
+ g_free(type_str);
+
+ parse_single_per_app_settings(app, settings);
+ config_per_app_settings = g_slist_append(config_per_app_settings,
+ (gpointer)settings);
}
}
if ((n = obt_xml_find_node(node, "policy")))
if (obt_xml_node_contains(n, "UnderMouse"))
config_place_policy = OB_PLACE_POLICY_MOUSE;
- if ((n = obt_xml_find_node(node, "center")))
- config_place_center = obt_xml_node_bool(n);
if ((n = obt_xml_find_node(node, "monitor"))) {
if (obt_xml_node_contains(n, "active"))
config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
obt_xml_register(i, "focus", parse_focus, NULL);
config_place_policy = OB_PLACE_POLICY_SMART;
- config_place_center = TRUE;
config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
config_primary_monitor_index = 1;
for (it = config_per_app_settings; it; it = g_slist_next(it)) {
ObAppSettings *itd = (ObAppSettings *)it->data;
- if (itd->name) g_pattern_spec_free(itd->name);
- if (itd->role) g_pattern_spec_free(itd->role);
+ if (itd->name) g_pattern_spec_free(itd->name);
+ if (itd->role) g_pattern_spec_free(itd->role);
if (itd->title) g_pattern_spec_free(itd->title);
if (itd->class) g_pattern_spec_free(itd->class);
+ if (itd->group_name) g_pattern_spec_free(itd->group_name);
+ if (itd->group_class) g_pattern_spec_free(itd->group_class);
g_slice_free(ObAppSettings, it->data);
}
g_slist_free(config_per_app_settings);
GPatternSpec *class;
GPatternSpec *name;
GPatternSpec *role;
+ GPatternSpec *group_class;
+ GPatternSpec *group_name;
GPatternSpec *title;
ObClientType type;
gboolean pos_given;
gboolean pos_force;
+ gint width_num;
+ gint width_denom;
+ gint height_num;
+ gint height_denom;
+ gboolean size_given;
+
guint desktop;
gint shade;
gint decor;
/*! The algorithm to use for placing new windows */
extern ObPlacePolicy config_place_policy;
-/*! Place windows in the center of the free area */
-extern gboolean config_place_center;
/*! Place windows on the active monitor (unless they are part of an application
already on another monitor) */
extern ObPlaceMonitor config_place_monitor;
if (e->type == ButtonPress || e->type == ButtonRelease) {
ObWindow *w;
static guint pressed = 0;
- static Window pressed_win = None;
event_sourcetime = event_curtime;
if (prompt && !used)
used = event_handle_prompt(prompt, e);
- if (e->type == ButtonPress) {
+ if (e->type == ButtonPress)
pressed = e->xbutton.button;
- pressed_win = e->xbutton.subwindow;
- }
}
}
else if (e->type == KeyPress || e->type == KeyRelease ||
g_assert(config_focus_follow);
if (is_enter_focus_event_ignored(event_curserial)) {
- ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu\n"
+ ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu "
"on client 0x%x", event_curserial, client->window);
return;
}
+ ob_debug_type(OB_DEBUG_FOCUS, "using enter event with serial %lu "
+ "on client 0x%x", event_curserial, client->window);
+
if (client_enter_focusable(client) && client_can_focus(client)) {
if (config_focus_delay) {
ObFocusDelayData *data;
}
else if ((Atom)e->xclient.data.l[2] ==
OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
- moveresize_end(TRUE);
+ if (moveresize_client)
+ moveresize_end(TRUE);
} else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
gint ograv, x, y, w, h;
ObMenuEntryFrame *e;
switch (ev->type) {
+ case MotionNotify:
+ // We need to catch MotionNotify in addition to EnterNotify because
+ // it is possible for the menu to be opened under the mouse cursor, and
+ // moving the mouse should select the item.
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xmotion.window))) {
+ if (e->ignore_enters)
+ --e->ignore_enters;
+ else if (!(f = find_active_menu()) ||
+ f == e->frame ||
+ f->parent == e->frame ||
+ f->child == e->frame)
+ menu_frame_select(e->frame, e, FALSE);
+ }
+ break;
case EnterNotify:
if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
if (e->ignore_enters)
/*! The amount of time before a window appears that is checked for user input
to determine if the user is working in another window */
-#define OB_EVENT_USER_TIME_DELAY (1000) /* 1.0 seconds */
+#define OB_EVENT_USER_TIME_DELAY (1000) /* milliseconds */
/*! The last user-interaction time, as given by the clients */
extern Time event_last_user_time;
gboolean v, s;
v = focus_cycle_valid(c);
- s = focus_cycle_popup_is_showing(c);
+ s = focus_cycle_popup_is_showing(c) || c == focus_cycle_target;
if (v != s)
focus_cycle_reorder();
focus_cycle_update_indicator(focus_cycle_target);
if (!focus_cycle_target)
focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
- TRUE, TRUE, OB_FOCUS_CYCLE_POPUP_MODE_NONE,
+ TRUE, OB_FOCUS_CYCLE_POPUP_MODE_NONE,
TRUE, TRUE);
}
}
ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
gboolean nonhilite_windows,
gboolean dock_windows, gboolean desktop_windows,
- gboolean linear, gboolean interactive,
- gboolean showbar, ObFocusCyclePopupMode mode,
+ gboolean linear, gboolean showbar,
+ ObFocusCyclePopupMode mode,
gboolean done, gboolean cancel)
{
static GList *order = NULL;
ObClient *ft = NULL;
ObClient *ret = NULL;
- if (interactive) {
- if (cancel) {
- focus_cycle_target = NULL;
- goto done_cycle;
- } else if (done)
- goto done_cycle;
+ if (cancel) {
+ focus_cycle_target = NULL;
+ goto done_cycle;
+ } else if (done)
+ goto done_cycle;
- if (!focus_order)
- goto done_cycle;
+ if (!focus_order)
+ goto done_cycle;
- if (linear) list = client_list;
- else list = focus_order;
- } else {
- if (!focus_order)
- goto done_cycle;
- list = client_list;
- }
+ if (linear) list = client_list;
+ else list = focus_order;
if (focus_cycle_target == NULL) {
focus_cycle_linear = linear;
}
ft = it->data;
if (focus_cycle_valid(ft)) {
- if (interactive) {
- if (ft != focus_cycle_target) { /* prevents flicker */
- focus_cycle_target = ft;
- focus_cycle_type = OB_CYCLE_NORMAL;
- focus_cycle_draw_indicator(showbar ? ft : NULL);
- }
- /* same arguments as focus_target_valid */
- focus_cycle_popup_show(ft, mode, focus_cycle_linear);
- return focus_cycle_target;
- } else if (ft != focus_cycle_target) {
+ if (ft != focus_cycle_target) { /* prevents flicker */
focus_cycle_target = ft;
focus_cycle_type = OB_CYCLE_NORMAL;
- done = TRUE;
- break;
+ focus_cycle_draw_indicator(showbar ? ft : NULL);
}
+ /* same arguments as focus_target_valid */
+ focus_cycle_popup_show(ft, mode, focus_cycle_linear);
+ return focus_cycle_target;
}
} while (it != start);
g_list_free(order);
order = NULL;
- if (interactive) {
- focus_cycle_draw_indicator(NULL);
- focus_cycle_popup_hide();
- }
+ focus_cycle_draw_indicator(NULL);
+ focus_cycle_popup_hide();
return ret;
}
struct _ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
gboolean nonhilite_windows,
gboolean dock_windows, gboolean desktop_windows,
- gboolean linear, gboolean interactive,
- gboolean showbar, ObFocusCyclePopupMode mode,
+ gboolean linear, gboolean showbar,
+ ObFocusCyclePopupMode mode,
gboolean done, gboolean cancel);
struct _ObClient* focus_directional_cycle(ObDirection dir,
gboolean dock_windows,
STRUT_SET(self->size,
self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
- self->cbwidth_t + self->bwidth,
+ self->cbwidth_t +
+ (!self->max_horz || !self->max_vert ? self->bwidth : 0),
self->cbwidth_r + (!self->max_horz ? self->bwidth : 0),
self->cbwidth_b +
(!self->max_horz || !self->max_vert ? self->bwidth : 0));
if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
self->size.top += ob_rr_theme->title_height + self->bwidth;
+ else if (self->max_horz && self->max_vert) {
+ /* A maximized and undecorated window needs a small border on the
+ top of the window to let the user still undecorate/unmaximize the
+ window via the client menu. */
+ /* XXX This size should probably be a theme option. */
+ self->size.top += 1;
+ }
+
if (self->decorations & OB_FRAME_DECOR_HANDLE &&
ob_rr_theme->handle_height > 0)
{
#define RECT_RIGHT(r) ((r).x + (r).width - 1)
#define RECT_BOTTOM(r) ((r).y + (r).height - 1)
+#define RECT_AREA(r) ((r).width * (r).height)
+
#define RECT_SET_POINT(r, nx, ny) \
(r).x = (nx), (r).y = (ny)
#define RECT_SET_SIZE(r, w, h) \
(r).height = MIN((a).y + (a).height - 1, \
(b).y + (b).height - 1) - (r).y + 1)
+/* Returns the shortest manhatten distance between two rects, or 0 if they
+ intersect. */
+static inline gint rect_manhatten_distance(Rect r, Rect o)
+{
+ if (RECT_INTERSECTS_RECT(r, o))
+ return 0;
+
+ gint min_distance = G_MAXINT;
+ if (RECT_RIGHT(o) < RECT_LEFT(r))
+ min_distance = MIN(min_distance, RECT_LEFT(r) - RECT_RIGHT(o));
+ if (RECT_LEFT(o) > RECT_RIGHT(r))
+ min_distance = MIN(min_distance, RECT_LEFT(o) - RECT_RIGHT(r));
+ if (RECT_BOTTOM(o) < RECT_TOP(r))
+ min_distance = MIN(min_distance, RECT_TOP(r) - RECT_BOTTOM(o));
+ if (RECT_TOP(o) > RECT_BOTTOM(r))
+ min_distance = MIN(min_distance, RECT_TOP(o) - RECT_BOTTOM(r));
+ return min_distance;
+}
+
typedef struct _Strut {
int left;
int top;
loaded = TRUE;
obt_xml_tree_from_root(menu_parse_inst);
obt_xml_close(menu_parse_inst);
- } else
+ }
+ else if (obt_xml_load_file(menu_parse_inst,
+ it->data,
+ "openbox_menu"))
+ {
+ loaded = TRUE;
+ obt_xml_tree_from_root(menu_parse_inst);
+ obt_xml_close(menu_parse_inst);
+ }
+ else
g_message(_("Unable to find a valid menu file \"%s\""),
(const gchar*)it->data);
}
obt_xml_instance_unref(menu_parse_inst);
menu_parse_inst = NULL;
- client_list_menu_shutdown(reconfig);
+ menu_frame_hide_all();
+
client_list_combined_menu_shutdown(reconfig);
+ client_list_menu_shutdown(reconfig);
- menu_frame_hide_all();
g_hash_table_destroy(menu_hash);
menu_hash = NULL;
}
/* Don't try to extract "icon" attribute if icons in user-defined
menus are not enabled. */
- if (obt_xml_attr_string(node, "label", &label)) {
+ if (obt_xml_attr_string_unstripped(node, "label", &label)) {
xmlNodePtr c;
GSList *acts = NULL;
if (state->parent) {
gchar *label;
- if (!obt_xml_attr_string(node, "label", &label))
+ if (!obt_xml_attr_string_unstripped(node, "label", &label))
label = NULL;
menu_add_separator(state->parent, -1, label);
goto parse_menu_fail;
if (!g_hash_table_lookup(menu_hash, name)) {
- if (!obt_xml_attr_string(node, "label", &title))
+ if (!obt_xml_attr_string_unstripped(node, "label", &title))
goto parse_menu_fail;
if ((menu = menu_new(name, title, TRUE, NULL))) {
self->shortcut = parse_shortcut(title, allow_shortcut_selection,
&self->title, &self->shortcut_position,
&self->shortcut_always_show);
+ self->collate_key = g_utf8_collate_key(self->title, -1);
g_hash_table_replace(menu_hash, self->name, self);
self->more_menu = g_slice_new0(ObMenu);
self->more_menu->name = _("More...");
self->more_menu->title = _("More...");
+ self->more_menu->collate_key = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
self->more_menu->data = data;
self->more_menu->shortcut = g_unichar_tolower(g_utf8_get_char("M"));
menu_clear_entries(self);
g_free(self->name);
g_free(self->title);
+ g_free(self->collate_key);
g_free(self->execute);
g_slice_free(ObMenu, self->more_menu);
case OB_MENU_ENTRY_TYPE_NORMAL:
RrImageUnref(self->data.normal.icon);
g_free(self->data.normal.label);
+ g_free(self->data.normal.collate_key);
while (self->data.normal.actions) {
actions_act_unref(self->data.normal.actions->data);
self->data.normal.actions =
break;
case OB_MENU_ENTRY_TYPE_NORMAL:
g_free(self->data.normal.label);
+ g_free(self->data.normal.collate_key);
self->data.normal.shortcut =
parse_shortcut(label, allow_shortcut, &self->data.normal.label,
&self->data.normal.shortcut_position,
&self->data.normal.shortcut_always_show);
+ self->data.normal.collate_key =
+ g_utf8_collate_key(self->data.normal.label, -1);
break;
default:
g_assert_not_reached();
{
self->show_all_shortcuts = show;
}
+
+static int sort_func(const void *a, const void *b) {
+ const ObMenuEntry *e[2] = {*(ObMenuEntry**)a, *(ObMenuEntry**)b};
+ const gchar *k[2];
+ gint i;
+
+ for (i = 0; i < 2; ++i) {
+ if (e[i]->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ k[i] = e[i]->data.normal.collate_key;
+ else {
+ g_assert(e[i]->type == OB_MENU_ENTRY_TYPE_SUBMENU);
+ if (e[i]->data.submenu.submenu)
+ k[i] = e[i]->data.submenu.submenu->collate_key;
+ else
+ return -1; /* arbitrary really.. the submenu doesn't exist. */
+ }
+ }
+ return strcmp(k[0], k[1]);
+}
+
+/*!
+ @param start The first entry in the range to sort.
+ @param end The last entry in the range to sort.
+*/
+static void sort_range(ObMenu *self, GList *start, GList *end, guint len)
+{
+ ObMenuEntry **ar;
+ GList *it;
+ guint i;
+ if (!len) return;
+
+ ar = g_slice_alloc(sizeof(ObMenuEntry*) * len);
+ for (i = 0, it = start; it != g_list_next(end); ++i, it = g_list_next(it))
+ ar[i] = it->data;
+ qsort(ar, len, sizeof(ObMenuEntry*), sort_func);
+ for (i = 0, it = start; it != g_list_next(end); ++i, it = g_list_next(it))
+ it->data = ar[i];
+ g_slice_free1(sizeof(ObMenuEntry*) * len, ar);
+}
+
+void menu_sort_entries(ObMenu *self)
+{
+ GList *it, *start, *end, *last;
+ guint len;
+
+ /* need the submenus to know their labels for sorting */
+ menu_find_submenus(self);
+
+ start = self->entries;
+ len = 0;
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+ if (e->type == OB_MENU_ENTRY_TYPE_SEPARATOR) {
+ end = g_list_previous(it);
+ sort_range(self, start, end, len);
+
+ it = g_list_next(it); /* skip over the separator */
+ start = it;
+ len = 0;
+ }
+ else
+ len += 1;
+ last = it;
+ }
+ sort_range(self, start, last, len);
+}
gchar *name;
/* Displayed title */
gchar *title;
+ gchar *collate_key;
/*! The shortcut key that would be used to activate this menu if it was
displayed as a submenu */
gunichar shortcut;
gint icon_alpha;
gchar *label;
+ gchar *collate_key;
/*! The shortcut key that would be used to activate this menu entry */
gunichar shortcut;
/*! The shortcut's position in the string */
ObMenuEntry* menu_add_submenu(ObMenu *menu, gint id, const gchar *submenu);
ObMenuEntry* menu_add_separator(ObMenu *menu, gint id, const gchar *label);
-void menu_clear_entries(ObMenu *menu);
+/*! This sorts groups of menu entries between consecutive separators */
+void menu_sort_entries(ObMenu *self);
+
+void menu_clear_entries(ObMenu *self);
void menu_entry_remove(ObMenuEntry *self);
void menu_entry_set_label(ObMenuEntry *self, const gchar *label,
#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
LeaveWindowMask)
#define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
- ButtonPressMask | ButtonReleaseMask)
+ ButtonPressMask | ButtonReleaseMask | \
+ PointerMotionMask)
GList *menu_frame_visible;
GHashTable *menu_frame_map;
parent->child_entry = parent_entry;
}
- if (!menu_frame_show(self))
+ if (!menu_frame_show(self)) {
+ parent->child = NULL;
+ parent->child_entry = NULL;
return FALSE;
+ }
menu_frame_place_submenu(self, &x, &y);
menu_frame_move_on_screen(self, x, y, &dx, &dy);
/* pass our direction on to our child */
f->direction_right = self->frame->direction_right;
- menu_frame_show_submenu(f, self->frame, self);
+ if (!menu_frame_show_submenu(f, self->frame, self))
+ menu_frame_free(f);
}
void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state)
gint *dx, gint *dy);
gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y,
- gint button);
+ gboolean mouse);
gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
ObMenuEntryFrame *parent_entry);
for (i = 0; i < screen_num_monitors; ++i) {
const Rect *a = screen_physical_area_monitor(i);
+
+ if (!RECT_CONTAINS(*a, x, y))
+ continue;
+
if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
{
g_print("Openbox %s\n", PACKAGE_VERSION);
g_print(_("Copyright (c)"));
- g_print(" 2008-2011 Mikael Magnusson\n");
+ g_print(" 2004 Mikael Magnusson\n");
g_print(_("Copyright (c)"));
- g_print(" 2003-2011 Dana Jansens\n\n");
+ g_print(" 2002 Dana Jansens\n\n");
g_print("This program comes with ABSOLUTELY NO WARRANTY.\n");
g_print("This is free software, and you are welcome to redistribute it\n");
g_print("under certain conditions. See the file COPYING for details.\n\n");
#include "config.h"
#include "dock.h"
#include "debug.h"
+#include "place_overlap.h"
extern ObDock *dock;
-static Rect *pick_pointer_head(ObClient *c)
+static Rect *choose_pointer_monitor(ObClient *c)
{
return screen_area(c->desktop, screen_monitor_pointer(), NULL);
}
-/* use the following priority lists for pick_head()
+/* use the following priority lists for choose_monitor()
When a window is being placed in the FOREGROUND, use a monitor chosen in
the following order:
}
/*! Pick a monitor to place a window on. */
-static Rect *pick_head(ObClient *c, gboolean foreground,
- ObAppSettings *settings)
+static Rect* choose_monitor(ObClient *c, gboolean client_to_be_foregrounded,
+ ObAppSettings *settings)
{
Rect *area;
ObPlaceHead *choice;
}
qsort(choice, screen_num_monitors, sizeof(ObPlaceHead),
- foreground ? cmp_foreground : cmp_background);
+ client_to_be_foregrounded ? cmp_foreground : cmp_background);
/* save the areas of the monitors in order of their being chosen */
for (i = 0; i < screen_num_monitors; ++i)
return area;
}
-static gboolean place_random(ObClient *client, Rect *area, gint *x, gint *y)
+static gboolean place_under_mouse(ObClient *client, gint *x, gint *y,
+ Size frame_size)
{
- gint l, r, t, b;
-
- ob_debug("placing randomly");
-
- l = area->x;
- t = area->y;
- r = area->x + area->width - client->frame->area.width;
- b = area->y + area->height - client->frame->area.height;
-
- if (r > l) *x = g_random_int_range(l, r + 1);
- else *x = area->x;
- if (b > t) *y = g_random_int_range(t, b + 1);
- else *y = area->y;
-
- return TRUE;
-}
-
-static GSList* area_add(GSList *list, Rect *a)
-{
- Rect *r = g_slice_new(Rect);
- *r = *a;
- return g_slist_prepend(list, r);
-}
-
-static GSList* area_remove(GSList *list, Rect *a)
-{
- GSList *sit;
- GSList *result = NULL;
-
- for (sit = list; sit; sit = g_slist_next(sit)) {
- Rect *r = sit->data;
-
- if (!RECT_INTERSECTS_RECT(*r, *a)) {
- result = g_slist_prepend(result, r);
- /* dont free r, it's moved to the result list */
- } else {
- Rect isect, extra;
-
- /* Use an intersection of a and r to determine the space
- around r that we can use.
-
- NOTE: the spaces calculated can overlap.
- */
-
- RECT_SET_INTERSECTION(isect, *r, *a);
-
- if (RECT_LEFT(isect) > RECT_LEFT(*r)) {
- RECT_SET(extra, r->x, r->y,
- RECT_LEFT(isect) - r->x, r->height);
- result = area_add(result, &extra);
- }
-
- if (RECT_TOP(isect) > RECT_TOP(*r)) {
- RECT_SET(extra, r->x, r->y,
- r->width, RECT_TOP(isect) - r->y + 1);
- result = area_add(result, &extra);
- }
-
- if (RECT_RIGHT(isect) < RECT_RIGHT(*r)) {
- RECT_SET(extra, RECT_RIGHT(isect) + 1, r->y,
- RECT_RIGHT(*r) - RECT_RIGHT(isect), r->height);
- result = area_add(result, &extra);
- }
-
- if (RECT_BOTTOM(isect) < RECT_BOTTOM(*r)) {
- RECT_SET(extra, r->x, RECT_BOTTOM(isect) + 1,
- r->width, RECT_BOTTOM(*r) - RECT_BOTTOM(isect));
- result = area_add(result, &extra);
- }
-
- /* 'r' is not being added to the result list, so free it */
- g_slice_free(Rect, r);
- }
- }
- g_slist_free(list);
- return result;
-}
-
-enum {
- IGNORE_FULLSCREEN = 1,
- IGNORE_MAXIMIZED = 2,
- IGNORE_MENUTOOL = 3,
- /*IGNORE_SHADED = 3,*/
- IGNORE_NONGROUP = 4,
- IGNORE_BELOW = 5,
- /*IGNORE_NONFOCUS = 1 << 5,*/
- IGNORE_DOCK = 6,
- IGNORE_END = 7
-};
-
-static gboolean place_nooverlap(ObClient *c, Rect *area, gint *x, gint *y)
-{
- gint ignore;
- gboolean ret;
- gint maxsize;
- GSList *spaces = NULL, *sit, *maxit;
-
- ob_debug("placing nonoverlap");
-
- ret = FALSE;
- maxsize = 0;
- maxit = NULL;
-
- /* try ignoring different things to find empty space */
- for (ignore = 0; ignore < IGNORE_END && !ret; ignore++) {
- GList *it;
-
- /* add the whole monitor */
- spaces = area_add(spaces, area);
-
- /* go thru all the windows */
- for (it = client_list; it; it = g_list_next(it)) {
- ObClient *test = it->data;
-
- /* should we ignore this client? */
- if (screen_showing_desktop) continue;
- if (c == test) continue;
- if (test->iconic) continue;
- if (c->desktop != DESKTOP_ALL) {
- if (test->desktop != c->desktop &&
- test->desktop != DESKTOP_ALL) continue;
- } else {
- if (test->desktop != screen_desktop &&
- test->desktop != DESKTOP_ALL) continue;
- }
- if (test->type == OB_CLIENT_TYPE_SPLASH ||
- test->type == OB_CLIENT_TYPE_DESKTOP) continue;
-
-
- if ((ignore >= IGNORE_FULLSCREEN) &&
- test->fullscreen) continue;
- if ((ignore >= IGNORE_MAXIMIZED) &&
- test->max_horz && test->max_vert) continue;
- if ((ignore >= IGNORE_MENUTOOL) &&
- (test->type == OB_CLIENT_TYPE_MENU ||
- test->type == OB_CLIENT_TYPE_TOOLBAR) &&
- client_has_parent(c)) continue;
- /*
- if ((ignore >= IGNORE_SHADED) &&
- test->shaded) continue;
- */
- if ((ignore >= IGNORE_NONGROUP) &&
- client_has_group_siblings(c) &&
- test->group != c->group) continue;
- if ((ignore >= IGNORE_BELOW) &&
- test->layer < c->layer) continue;
- /*
- if ((ignore >= IGNORE_NONFOCUS) &&
- focus_client != test) continue;
- */
- /* don't ignore this window, so remove it from the available
- area */
- spaces = area_remove(spaces, &test->frame->area);
- }
-
- if (ignore < IGNORE_DOCK) {
- Rect a;
- dock_get_area(&a);
- spaces = area_remove(spaces, &a);
- }
-
- for (sit = spaces; sit; sit = g_slist_next(sit)) {
- Rect *r = sit->data;
-
- if (r->width >= c->frame->area.width &&
- r->height >= c->frame->area.height &&
- r->width * r->height > maxsize)
- {
- maxsize = r->width * r->height;
- maxit = sit;
- }
- }
-
- if (maxit) {
- Rect *r = maxit->data;
-
- /* center it in the area */
- *x = r->x;
- *y = r->y;
- if (config_place_center) {
- *x += (r->width - c->frame->area.width) / 2;
- *y += (r->height - c->frame->area.height) / 2;
- }
- ret = TRUE;
- }
-
- while (spaces) {
- g_slice_free(Rect, spaces->data);
- spaces = g_slist_delete_link(spaces, spaces);
- }
- }
-
- return ret;
-}
+ if (config_place_policy != OB_PLACE_POLICY_MOUSE)
+ return FALSE;
-static gboolean place_under_mouse(ObClient *client, gint *x, gint *y)
-{
gint l, r, t, b;
gint px, py;
Rect *area;
if (!screen_pointer_pos(&px, &py))
return FALSE;
- area = pick_pointer_head(client);
+ area = choose_pointer_monitor(client);
l = area->x;
t = area->y;
- r = area->x + area->width - client->frame->area.width;
- b = area->y + area->height - client->frame->area.height;
+ r = area->x + area->width - frame_size.width;
+ b = area->y + area->height - frame_size.height;
- *x = px - client->area.width / 2 - client->frame->size.left;
+ *x = px - frame_size.width / 2;
*x = MIN(MAX(*x, l), r);
- *y = py - client->area.height / 2 - client->frame->size.top;
+ *y = py - frame_size.height / 2;
*y = MIN(MAX(*y, t), b);
g_slice_free(Rect, area);
return TRUE;
}
-static gboolean place_per_app_setting(ObClient *client, Rect *screen,
- gint *x, gint *y,
- ObAppSettings *settings)
+static gboolean place_per_app_setting_position(ObClient *client, Rect *screen,
+ gint *x, gint *y,
+ ObAppSettings *settings,
+ Size frame_size)
{
- if (!settings || (settings && !settings->pos_given))
+ if (!settings || !settings->pos_given)
return FALSE;
ob_debug("placing by per-app settings");
if (settings->position.x.center)
*x = screen->x + screen->width / 2 - client->area.width / 2;
else if (settings->position.x.opposite)
- *x = screen->x + screen->width - client->frame->area.width -
+ *x = screen->x + screen->width - frame_size.width -
settings->position.x.pos;
else
*x = screen->x + settings->position.x.pos;
if (settings->position.y.center)
*y = screen->y + screen->height / 2 - client->area.height / 2;
else if (settings->position.y.opposite)
- *y = screen->y + screen->height - client->frame->area.height -
+ *y = screen->y + screen->height - frame_size.height -
settings->position.y.pos;
else
*y = screen->y + settings->position.y.pos;
return TRUE;
}
+static void place_per_app_setting_size(ObClient *client, Rect *screen,
+ gint *w, gint *h,
+ ObAppSettings *settings)
+{
+ if (!settings || !settings->size_given)
+ return;
+
+ ob_debug("sizing by per-app settings");
+
+ g_assert(settings->width_num > 0);
+ g_assert(settings->width_denom >= 0);
+ g_assert(settings->height_num > 0);
+ g_assert(settings->height_denom >= 0);
+
+ if (!settings->width_denom)
+ *w = settings->width_num;
+ else {
+ *w = screen->width * settings->width_num / settings->width_denom;
+ *w = MIN(*w, screen->width);
+ }
+
+ if (!settings->height_denom)
+ *h = settings->height_num;
+ else {
+ *h = screen->height * settings->height_num / settings->height_denom;
+ *h = MIN(*h, screen->height);
+ }
+}
+
static gboolean place_transient_splash(ObClient *client, Rect *area,
- gint *x, gint *y)
+ gint *x, gint *y, Size frame_size)
{
if (client->type == OB_CLIENT_TYPE_DIALOG) {
GSList *it;
}
}
if (!first) {
- *x = ((r + 1 - l) - client->frame->area.width) / 2 + l;
- *y = ((b + 1 - t) - client->frame->area.height) / 2 + t;
+ *x = ((r + 1 - l) - frame_size.width) / 2 + l;
+ *y = ((b + 1 - t) - frame_size.height) / 2 + t;
return TRUE;
}
}
{
ob_debug("placing dialog or splash");
- *x = (area->width - client->frame->area.width) / 2 + area->x;
- *y = (area->height - client->frame->area.height) / 2 + area->y;
+ *x = (area->width - frame_size.width) / 2 + area->x;
+ *y = (area->height - frame_size.height) / 2 + area->y;
return TRUE;
}
return FALSE;
}
-/*! Return TRUE if openbox chose the position for the window, and FALSE if
- the application chose it */
-gboolean place_client(ObClient *client, gboolean foreground, gint *x, gint *y,
- ObAppSettings *settings)
+static gboolean place_least_overlap(ObClient *c, Rect *head, int *x, int *y,
+ Size frame_size)
+{
+ /* Assemble the list of windows that could overlap with @c in the user's
+ current view. */
+ GSList* potential_overlap_clients = NULL;
+ gint n_client_rects = 0;
+
+ /* if we're "showing desktop", ignore all existing windows */
+ if (!screen_showing_desktop) {
+ GList* it;
+ for (it = client_list; it != NULL; it = g_list_next(it)) {
+ ObClient* maybe_client = (ObClient*)it->data;
+ if (maybe_client == c)
+ continue;
+ if (maybe_client->iconic)
+ continue;
+ if (!client_occupies_space(maybe_client))
+ continue;
+ if (c->desktop != DESKTOP_ALL) {
+ if (maybe_client->desktop != c->desktop &&
+ maybe_client->desktop != DESKTOP_ALL)
+ continue;
+ } else {
+ if (maybe_client->desktop != screen_desktop &&
+ maybe_client->desktop != DESKTOP_ALL)
+ continue;
+ }
+
+ potential_overlap_clients = g_slist_prepend(
+ potential_overlap_clients, maybe_client);
+ n_client_rects += 1;
+ }
+ }
+ Rect client_rects[n_client_rects];
+
+ GSList* it;
+ guint i = 0;
+ for (it = potential_overlap_clients; it != NULL; it = g_slist_next(it)) {
+ ObClient* potential_overlap_client = (ObClient*)it->data;
+ client_rects[i] = potential_overlap_client->frame->area;
+ i += 1;
+ }
+ g_slist_free(potential_overlap_clients);
+
+ Point result;
+ place_overlap_find_least_placement(client_rects, n_client_rects, head,
+ &frame_size, &result);
+ *x = result.x;
+ *y = result.y;
+
+ return TRUE;
+}
+
+static gboolean should_set_client_position(ObClient *client,
+ ObAppSettings *settings)
+{
+ gboolean has_position = settings && settings->pos_given;
+ gboolean has_forced_position = has_position && settings->pos_force;
+
+ gboolean user_positioned = client->positioned & USPosition;
+ if (user_positioned && !has_forced_position)
+ return FALSE;
+
+ gboolean program_positioned = client->positioned & PPosition;
+ if (program_positioned && !has_position)
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean place_client(ObClient *client, gboolean client_to_be_foregrounded,
+ Rect* client_area, ObAppSettings *settings)
{
- Rect *area;
gboolean ret;
+ Rect *monitor_area;
+ int *x, *y, *w, *h;
+ Size frame_size;
+
+ monitor_area = choose_monitor(client, client_to_be_foregrounded, settings);
- /* per-app settings override program specified position
- * but not user specified, unless pos_force is enabled */
- if (((client->positioned & USPosition) &&
- !(settings && settings->pos_given && settings->pos_force)) ||
- ((client->positioned & PPosition) &&
- !(settings && settings->pos_given)))
+ w = &client_area->width;
+ h = &client_area->height;
+ place_per_app_setting_size(client, monitor_area, w, h, settings);
+
+ if (!should_set_client_position(client, settings))
return FALSE;
- area = pick_head(client, foreground, settings);
+ x = &client_area->x;
+ y = &client_area->y;
+
+ SIZE_SET(frame_size,
+ *w + client->frame->size.left + client->frame->size.right,
+ *h + client->frame->size.top + client->frame->size.bottom);
- /* try a number of methods */
- ret = place_per_app_setting(client, area, x, y, settings) ||
- place_transient_splash(client, area, x, y) ||
- (config_place_policy == OB_PLACE_POLICY_MOUSE &&
- place_under_mouse(client, x, y)) ||
- place_nooverlap(client, area, x, y) ||
- place_random(client, area, x, y);
+ ret =
+ place_per_app_setting_position(client, monitor_area, x, y, settings,
+ frame_size) ||
+ place_transient_splash(client, monitor_area, x, y, frame_size) ||
+ place_under_mouse(client, x, y, frame_size) ||
+ place_least_overlap(client, monitor_area, x, y, frame_size);
g_assert(ret);
- g_slice_free(Rect, area);
+ g_slice_free(Rect, monitor_area);
/* get where the client should be */
frame_frame_gravity(client->frame, x, y);
#ifndef ob__place_h
#define ob__place_h
+#include "geom.h"
+
#include <glib.h>
struct _ObClient;
OB_PLACE_MONITOR_PRIMARY
} ObPlaceMonitor;
-gboolean place_client(struct _ObClient *client, gboolean foreground,
- gint *x, gint *y, struct _ObAppSettings *settings);
+/*! Return TRUE if openbox chose the position for the window, and FALSE if
+ the application chose it */
+gboolean place_client(struct _ObClient *client,
+ gboolean client_to_be_foregrounded,
+ Rect* client_area, struct _ObAppSettings *settings);
#endif
--- /dev/null
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ overlap.c for the Openbox window manager
+ Copyright (c) 2011 Ian Zimmerman
+
+ 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 "config.h"
+#include "geom.h"
+#include "place_overlap.h"
+
+#include <stdlib.h>
+
+static void make_grid(const Rect* client_rects, int n_client_rects,
+ const Rect* bound, int* x_edges, int* y_edges,
+ int max_edges);
+
+static int best_direction(const Point* grid_point,
+ const Rect* client_rects, int n_client_rects,
+ const Rect* bound, const Size* req_size,
+ Point* best_top_left);
+
+/* Choose the placement on a grid with least overlap */
+
+void place_overlap_find_least_placement(const Rect* client_rects,
+ int n_client_rects,
+ Rect *const bound,
+ const Size* req_size,
+ Point* result)
+{
+ POINT_SET(*result, 0, 0);
+ int overlap = G_MAXINT;
+ int max_edges = 2 * (n_client_rects + 1);
+
+ int x_edges[max_edges];
+ int y_edges[max_edges];
+ make_grid(client_rects, n_client_rects, bound,
+ x_edges, y_edges, max_edges);
+ int i;
+ for (i = 0; i < max_edges; ++i) {
+ if (x_edges[i] == G_MAXINT)
+ break;
+ int j;
+ for (j = 0; j < max_edges; ++j) {
+ if (y_edges[j] == G_MAXINT)
+ break;
+ Point grid_point = {.x = x_edges[i], .y = y_edges[j]};
+ Point best_top_left;
+ int this_overlap =
+ best_direction(&grid_point, client_rects, n_client_rects,
+ bound, req_size, &best_top_left);
+ if (this_overlap < overlap) {
+ overlap = this_overlap;
+ *result = best_top_left;
+ }
+ if (overlap == 0)
+ break;
+ }
+ if (overlap == 0)
+ break;
+ }
+}
+
+static int compare_ints(const void* a, const void* b)
+{
+ const int* ia = (const int*)a;
+ const int* ib = (const int*)b;
+ return *ia - *ib;
+}
+
+static void uniquify(int* edges, int n_edges)
+{
+ int i = 0;
+ int j = 0;
+
+ while (j < n_edges) {
+ int last = edges[j++];
+ edges[i++] = last;
+ while (j < n_edges && edges[j] == last)
+ ++j;
+ }
+ /* fill the rest with nonsense */
+ for (; i < n_edges ; ++i)
+ edges[i] = G_MAXINT;
+}
+
+static void make_grid(const Rect* client_rects, int n_client_rects,
+ const Rect* bound, int* x_edges, int* y_edges,
+ int max_edges)
+{
+ int i;
+ int n_edges = 0;
+ for (i = 0; i < n_client_rects; ++i) {
+ if (!RECT_INTERSECTS_RECT(client_rects[i], *bound))
+ continue;
+ x_edges[n_edges] = client_rects[i].x;
+ y_edges[n_edges++] = client_rects[i].y;
+ x_edges[n_edges] = client_rects[i].x + client_rects[i].width;
+ y_edges[n_edges++] = client_rects[i].y + client_rects[i].height;
+ }
+ x_edges[n_edges] = bound->x;
+ y_edges[n_edges++] = bound->y;
+ x_edges[n_edges] = bound->x + bound->width;
+ y_edges[n_edges++] = bound->y + bound->height;
+ for (i = n_edges; i < max_edges; ++i)
+ x_edges[i] = y_edges[i] = G_MAXINT;
+ qsort(x_edges, n_edges, sizeof(int), compare_ints);
+ uniquify(x_edges, n_edges);
+ qsort(y_edges, n_edges, sizeof(int), compare_ints);
+ uniquify(y_edges, n_edges);
+}
+
+static int total_overlap(const Rect* client_rects, int n_client_rects,
+ const Rect* proposed_rect)
+{
+ int overlap = 0;
+ int i;
+ for (i = 0; i < n_client_rects; ++i) {
+ if (!RECT_INTERSECTS_RECT(*proposed_rect, client_rects[i]))
+ continue;
+ Rect rtemp;
+ RECT_SET_INTERSECTION(rtemp, *proposed_rect, client_rects[i]);
+ overlap += RECT_AREA(rtemp);
+ }
+ return overlap;
+}
+
+/* Given a list of Rect RECTS, a Point PT and a Size size, determine the
+ direction from PT which results in the least total overlap with RECTS
+ if a rectangle is placed in that direction. Return the top/left
+ Point of such rectangle and the resulting overlap amount. Only
+ consider placements within BOUNDS. */
+
+#define NUM_DIRECTIONS 4
+
+static int best_direction(const Point* grid_point,
+ const Rect* client_rects, int n_client_rects,
+ const Rect* bound, const Size* req_size,
+ Point* best_top_left)
+{
+ static const Size directions[NUM_DIRECTIONS] = {
+ {0, 0}, {0, -1}, {-1, 0}, {-1, -1}
+ };
+ int overlap = G_MAXINT;
+ int i;
+ for (i = 0; i < NUM_DIRECTIONS; ++i) {
+ Point pt = {
+ .x = grid_point->x + (req_size->width * directions[i].width),
+ .y = grid_point->y + (req_size->height * directions[i].height)
+ };
+ Rect r;
+ RECT_SET(r, pt.x, pt.y, req_size->width, req_size->height);
+ if (!RECT_CONTAINS_RECT(*bound, r))
+ continue;
+ int this_overlap = total_overlap(client_rects, n_client_rects, &r);
+ if (this_overlap < overlap) {
+ overlap = this_overlap;
+ *best_top_left = pt;
+ }
+ if (overlap == 0)
+ break;
+ }
+ return overlap;
+}
--- /dev/null
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ overlap.h for the Openbox window manager
+ Copyright (c) 2011 Ian Zimmerman
+
+ 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 "geom.h"
+
+void place_overlap_find_least_placement(const Rect* client_rects,
+ int n_client_rects,
+ Rect *const bounds,
+ const Size* req_size,
+ Point* result);
void prompt_shutdown(gboolean reconfig)
{
- GList *it;
+ GList *it, *next;
if (!reconfig) {
- for (it = prompt_list; it; it = g_list_next(it)) {
+ for (it = prompt_list; it; it = next) {
ObPrompt *p = it->data;
+ next = it->next;
if (p->cleanup) p->cleanup(p, p->data);
}
{
prompt_ref(self);
if (self->func) {
- gboolean clean = self->func(self, self->focus->result, self->data);
+ gboolean clean = self->func(self, result, self->data);
if (clean && self->cleanup)
self->cleanup(self, self->data);
}
supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
g_assert(i == num_support);
b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
}
RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
+
+ for (i = 0; i < *nxin; ++i)
+ ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
+ (*xin_areas)[i].x, (*xin_areas)[i].y,
+ (*xin_areas)[i].width, (*xin_areas)[i].height);
+ ob_debug("Full desktop @ %d,%d %dx%d\n",
+ (*xin_areas)[i].x, (*xin_areas)[i].y,
+ (*xin_areas)[i].width, (*xin_areas)[i].height);
}
void screen_update_areas(void)
return a;
}
+typedef struct {
+ Rect r;
+ gboolean subtract;
+} RectArithmetic;
+
guint screen_find_monitor(const Rect *search)
{
guint i;
- guint most = screen_num_monitors;
- guint mostv = 0;
+ guint mostpx_index = screen_num_monitors;
+ guint mostpx = 0;
+ guint closest_distance_index = screen_num_monitors;
+ guint closest_distance = G_MAXUINT;
+ GSList *counted = NULL;
+
+ /* we want to count the number of pixels search has on each monitor, but not
+ double count. so if a pixel is counted on monitor A then we should not
+ count it again on monitor B. in the end we want to return the monitor
+ that had the most pixels counted under this scheme.
+
+ this assumes that monitors earlier in the list are more desirable to be
+ considered the search area's monitor. we try the configured primary
+ monitor first, so it gets the highest preference.
+
+ if we have counted an area A, then we want to subtract the intersection
+ of A with the area on future monitors.
+ but now consider if we count an area B that intersects A. we want to
+ subtract the area B from that counted on future monitors, but not
+ subtract the intersection of A and B twice! so we would add the
+ intersection of A and B back, to account for it being subtracted both
+ for A and B.
+
+ this is the idea behind the algorithm. we always subtract the full area
+ for monitor M intersected with the search area. we'll call that AREA.
+ but then we go through the list |counted| and for each rectangle in
+ the list that is being subtracted from future monitors, we insert a
+ request to add back the intersection of the subtracted rect with AREA.
+ vice versa for a rect in |counted| that is getting added back.
+ */
+
+ if (config_primary_monitor_index < screen_num_monitors) {
+ const Rect *monitor;
+ Rect on_current_monitor;
+ glong area;
+
+ monitor = screen_physical_area_monitor(config_primary_monitor_index);
+
+ if (RECT_INTERSECTS_RECT(*monitor, *search)) {
+ RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
+ area = RECT_AREA(on_current_monitor);
+
+ if (area > mostpx) {
+ mostpx = area;
+ mostpx_index = config_primary_monitor_index;
+ }
+
+ /* add the intersection rect on the current monitor to the
+ counted list. that's easy for the first one, we just mark it for
+ subtraction */
+ {
+ RectArithmetic *ra = g_slice_new(RectArithmetic);
+ ra->r = on_current_monitor;
+ ra->subtract = TRUE;
+ counted = g_slist_prepend(counted, ra);
+ }
+ }
+ }
for (i = 0; i < screen_num_monitors; ++i) {
- const Rect *area = screen_physical_area_monitor(i);
- if (RECT_INTERSECTS_RECT(*area, *search)) {
- Rect r;
- guint v;
+ const Rect *monitor;
+ Rect on_current_monitor;
+ glong area;
+ GSList *it;
- RECT_SET_INTERSECTION(r, *area, *search);
- v = r.width * r.height;
+ monitor = screen_physical_area_monitor(i);
- if (v > mostv) {
- mostv = v;
- most = i;
+ if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
+ /* If we don't intersect then find the distance between the search
+ rect and the monitor. We'll use the closest monitor from this
+ metric if none of the monitors intersect. */
+ guint distance = rect_manhatten_distance(*monitor, *search);
+
+ if (distance < closest_distance) {
+ closest_distance = distance;
+ closest_distance_index = i;
}
+ continue;
+ }
+
+ if (i == config_primary_monitor_index)
+ continue; /* already did this one */
+
+ RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
+ area = RECT_AREA(on_current_monitor);
+
+ /* remove pixels we already counted on any previous monitors. */
+ for (it = counted; it; it = g_slist_next(it)) {
+ RectArithmetic *ra = it->data;
+ Rect intersection;
+
+ RECT_SET_INTERSECTION(intersection, ra->r, *search);
+ if (ra->subtract) area -= RECT_AREA(intersection);
+ else area += RECT_AREA(intersection);
+ }
+
+ if (area > mostpx) {
+ mostpx = area;
+ mostpx_index = i;
+ }
+
+ /* add the intersection rect on the current monitor I to the counted
+ list.
+ but now we need to compensate for every rectangle R already in the
+ counted list, and add a new rect R' that is the intersection of
+ R and I, but with the reverse subtraction/addition operation.
+ */
+ for (it = counted; it; it = g_slist_next(it)) {
+ RectArithmetic *saved = it->data;
+
+ if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
+ continue;
+ /* we are going to subtract our rect from future monitors, but
+ part of it may already be being subtracted/added, so compensate
+ to not double add/subtract. */
+ RectArithmetic *reverse = g_slice_new(RectArithmetic);
+ RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
+ reverse->subtract = !saved->subtract;
+ /* prepend so we can continue thru the list uninterupted */
+ counted = g_slist_prepend(counted, reverse);
+ }
+ {
+ RectArithmetic *ra = g_slice_new(RectArithmetic);
+ ra->r = on_current_monitor;
+ ra->subtract = TRUE;
+ counted = g_slist_prepend(counted, ra);
}
}
- return most < screen_num_monitors ? most : screen_monitor_primary(FALSE);
+
+ while (counted) {
+ g_slice_free(RectArithmetic, counted->data);
+ counted = g_slist_delete_link(counted, counted);
+ }
+
+ if (mostpx_index < screen_num_monitors)
+ return mostpx_index;
+
+ g_assert(closest_distance_index < screen_num_monitors);
+ return closest_distance_index;
}
const Rect* screen_physical_area_all_monitors(void)
# Peter Schwindt <schwindt@ba-loerrach.de>
# Finn Zirngibl <finn@s23.org>, 2008
# Florian Walch <florian.walch@gmx.at>, 2008
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2011.
#
msgid ""
msgstr ""
-"Project-Id-Version: Openbox 3.4.7\n"
+"Project-Id-Version: Openbox 3.5.0\n"
"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
-"POT-Creation-Date: 2011-08-01 18:20+0200\n"
-"PO-Revision-Date: 2008-03-13 13:38+0100\n"
-"Last-Translator: Florian Walch <florian.walch@gmx.at>\n"
-"Language-Team: <de@li.org>\n"
+"POT-Creation-Date: 2011-11-17 22:54+0100\n"
+"PO-Revision-Date: 2011-11-17 22:54+0100\n"
+"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
+"Language-Team: <gnome-de@gnome.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
#: openbox/actions.c:198
#, c-format
msgid "Invalid action \"%s\" requested. No such action exists."
-msgstr "Unzulässige Aktion \"%s\" angefordert. Diese Aktion existiert nicht."
+msgstr "Unzulässige Aktion »%s« angefordert. Diese Aktion existiert nicht."
#: openbox/actions/execute.c:147
msgid "No"
#: openbox/actions/execute.c:161
#, c-format
msgid "Failed to convert the path \"%s\" from utf8"
-msgstr "Konnte Pfad \"%s\" nicht von UTF-8 konvertieren"
+msgstr "Pfad »%s« konnte nicht aus UTF-8 konvertiert werden"
-#: openbox/actions/exit.c:69 openbox/client.c:3550
+#: openbox/actions/exit.c:69 openbox/client.c:3567
msgid "Cancel"
msgstr "Abbrechen"
#: openbox/actions/exit.c:79
msgid "Exit Openbox"
-msgstr "Beende Openbox"
+msgstr "Openbox beenden"
-#: openbox/client.c:2037
+#: openbox/client.c:2054
msgid "Unnamed Window"
msgstr "Unbenanntes Fenster"
-#: openbox/client.c:2051 openbox/client.c:2082
+#: openbox/client.c:2068 openbox/client.c:2099
msgid "Killing..."
-msgstr "Wird beendet..."
+msgstr "Wird beendet …"
-#: openbox/client.c:2053 openbox/client.c:2084
+#: openbox/client.c:2070 openbox/client.c:2101
msgid "Not Responding"
msgstr "Reagiert nicht"
-#: openbox/client.c:3539
+#: openbox/client.c:3556
#, c-format
msgid ""
"The window \"%s\" does not seem to be responding. Do you want to force it "
"to exit by sending the %s signal?"
msgstr ""
-"Das Fenster \"%s\" scheint nicht zu reagieren. Wollen Sie die Beendigung "
-"durch das Senden des %s-Signals erzwingen?"
+"Das Fenster »%s« scheint nicht zu reagieren. Wollen Sie die Beendigung durch "
+"das Senden des %s-Signals erzwingen?"
-#: openbox/client.c:3541
+#: openbox/client.c:3558
msgid "End Process"
-msgstr "Beende Prozess"
+msgstr "Prozess beenden"
-#: openbox/client.c:3545
+#: openbox/client.c:3562
#, c-format
msgid ""
"The window \"%s\" does not seem to be responding. Do you want to disconnect "
"it from the X server?"
msgstr ""
-"Das Fenster \"%s\" scheint nicht zu reagieren. Soll es vom X-Server getrennt "
+"Das Fenster »%s« scheint nicht zu reagieren. Soll es vom X-Server getrennt "
"werden?"
-#: openbox/client.c:3547
+#: openbox/client.c:3564
msgid "Disconnect"
msgstr "Trennen"
#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
msgid "Go there..."
-msgstr "Hierher wechseln..."
+msgstr "Hierher wechseln …"
#: openbox/client_list_combined_menu.c:100
msgid "Manage desktops"
-msgstr "Desktops verwalten"
+msgstr "Arbeitsflächen verwalten"
#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
msgid "_Add new desktop"
-msgstr "_Neuen Desktop hinzufügen"
+msgstr "_Neue Arbeitsfläche hinzufügen"
#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
msgid "_Remove last desktop"
-msgstr "_Letzten Desktop entfernen"
+msgstr "_Letzte Arbeitsfläche entfernen"
#: openbox/client_list_combined_menu.c:157
msgid "Windows"
#: openbox/client_list_menu.c:214
msgid "Desktops"
-msgstr "Desktops"
+msgstr "Arbeitsflächen"
#: openbox/client_menu.c:259
msgid "All desktops"
-msgstr "Alle Desktops"
+msgstr "Alle Arbeitsflächen"
#: openbox/client_menu.c:371
msgid "_Layer"
-msgstr "_Layer"
+msgstr "_Ebene"
#: openbox/client_menu.c:376
msgid "Always on _top"
#: openbox/client_menu.c:380
msgid "_Send to desktop"
-msgstr "_An Desktop senden"
+msgstr "_An Arbeitsfläche senden"
#: openbox/client_menu.c:384
msgid "Client menu"
-msgstr "Client menu"
+msgstr "Client-Menü"
#: openbox/client_menu.c:394
msgid "R_estore"
-msgstr "Wi_ederherstellen"
+msgstr "_Wiederherstellen"
#: openbox/client_menu.c:398
msgid "_Move"
#: openbox/config.c:503
#, c-format
msgid "Invalid context \"%s\" in mouse binding"
-msgstr "Maus-Binding enthält ungültigen Kontext \"%s\""
+msgstr "Maus-Zuordnung enthält ungültigen Kontext »%s«"
#: openbox/config.c:857
#, c-format
msgid "Invalid button \"%s\" specified in config file"
-msgstr "Unzulässiger Button \"%s\" in der Konfigurationsdatei angegeben"
+msgstr "Unzulässige Taste »%s« in der Konfigurationsdatei angegeben"
#: openbox/config.c:882
msgid ""
"Openbox was compiled without Imlib2 image loading support. Icons in menus "
"will not be loaded."
msgstr ""
+"Openbox wurde ohne die Unterstützung der Imlib2 zum Laden von Grafiken "
+"kompiliert. Symbole in Menüs werden nicht geladen."
-#: openbox/debug.c:55
+#: openbox/debug.c:57
#, c-format
msgid "Unable to make directory '%s': %s"
-msgstr "Das Verzeichnis '%s' konnte nicht angelegt werden: %s"
+msgstr "Der Ordner »%s« konnte nicht angelegt werden: %s"
-#: openbox/debug.c:138 openbox/openbox.c:372
+#: openbox/debug.c:195 openbox/openbox.c:372
msgid "Close"
msgstr "Schließen"
#: openbox/menu.c:94 openbox/menu.c:106
#, c-format
msgid "Unable to find a valid menu file \"%s\""
-msgstr "Konnte keine gültige Menü-Datei \"%s\" finden"
+msgstr "Es konnte keine gültige Menü-Datei »%s« gefunden werden"
#: openbox/menu.c:158
#, c-format
msgid "Failed to execute command for pipe-menu \"%s\": %s"
-msgstr "Konnte Befehl \"%s\" für pipe-menu nicht ausführen: %s"
+msgstr "Befehl »%s« für Pipe-Menü konnte nicht ausgeführt werden: %s"
#: openbox/menu.c:172
#, c-format
msgid "Invalid output from pipe-menu \"%s\""
-msgstr "Ungültige Ausgabe vom pipe-menu \"%s\""
+msgstr "Ungültige Ausgabe vom Pipe-Menü »%s«"
#: openbox/menu.c:185
#, c-format
msgid "Attempted to access menu \"%s\" but it does not exist"
msgstr ""
-"Auf das Menü \"%s\" konnte nicht zugegriffen werden, da es nicht existiert"
+"Auf das Menü »%s« konnte nicht zugegriffen werden, da es nicht existiert"
#: openbox/menu.c:400 openbox/menu.c:401
msgid "More..."
-msgstr "Mehr..."
+msgstr "Mehr …"
-#: openbox/mouse.c:376
+#: openbox/mouse.c:382
#, c-format
msgid "Invalid button \"%s\" in mouse binding"
-msgstr "Maus-Binding enthält ungültigen Button \"%s\""
+msgstr "Maus-Zuordnung enthält ungültige Taste »%s«"
#: openbox/openbox.c:137
#, c-format
msgid "Unable to change to home directory \"%s\": %s"
-msgstr "Konnte nicht in das Heimatverzeichnis \"%s\" wechseln: %s"
+msgstr "Wechsel in den persönlichen Ordner »%s« ist gescheitert: %s"
#: openbox/openbox.c:152
msgid "Failed to open the display from the DISPLAY environment variable."
-msgstr "Konnte das Display aus der Umgebungsvariable DISPLAY nicht öffnen."
+msgstr ""
+"Die Anzeige aus der Umgebungsvariable DISPLAY konnte nicht geöffnet werden."
#: openbox/openbox.c:182
msgid "Failed to initialize the obrender library."
-msgstr "Konnte die obrender-Bibliothek nicht initialisieren."
+msgstr "Die obrender-Bibliothek konnte nicht initialisiert werden."
#: openbox/openbox.c:193
msgid "X server does not support locale."
#: openbox/openbox.c:253
msgid "Unable to find a valid config file, using some simple defaults"
msgstr ""
-"Es wurde keine gültige Konfigurationsdatei gefunden, benutze einfache "
-"Standardwerte"
+"Es wurde keine gültige Konfigurationsdatei gefunden, es werden einfache "
+"Standardwerte verwendet"
#: openbox/openbox.c:286
msgid "Unable to load a theme."
-msgstr "Konnte kein Thema laden."
+msgstr "Es konnte kein Thema geladen werden."
#: openbox/openbox.c:370
#, c-format
"configuration files. See stdout for more information. The last error seen "
"was in file \"%s\" line %d, with message: %s"
msgstr ""
-"Beim Parsen der Openbox-Konfigurationsdateien wurden ein oder mehrere XML-"
-"Syntaxfehler gefunden. Die Standardausgabe enthält weitere Informationen. "
-"Der letzte Fehler wurde in der Datei \"%s\" in Zeile %d festgestellt: %s"
+"Beim Verarbeiten der Openbox-Konfigurationsdateien wurden ein oder mehrere "
+"XML-Syntaxfehler gefunden. Die Standardausgabe enthält weitere "
+"Informationen. Der letzte Fehler wurde in der Datei »%s« in Zeile %d "
+"festgestellt: %s"
#: openbox/openbox.c:372
msgid "Openbox Syntax Error"
#: openbox/openbox.c:438
#, c-format
msgid "Restart failed to execute new executable \"%s\": %s"
-msgstr ""
-"Neustart fehlgeschlagen, um die ausführbare Datei \"%s\" zu starten: %s"
+msgstr "Neustart fehlgeschlagen, um die ausführbare Datei »%s« zu starten: %s"
#: openbox/openbox.c:517 openbox/openbox.c:519
msgid "Copyright (c)"
#: openbox/openbox.c:537
msgid " --sm-disable Disable connection to the session manager\n"
-msgstr " --sm-disable Keine Verbindung zum Sitzungsmanager aufbauen\n"
+msgstr ""
+" --sm-disable Keine Verbindung zur Sitzungsverwaltung aufbauen\n"
#: openbox/openbox.c:538
msgid ""
#: openbox/openbox.c:539
msgid " --reconfigure Reload Openbox's configuration\n"
-msgstr " --reconfigure Openbox's Konfiguration neu laden\n"
+msgstr " --reconfigure Openbox-Konfiguration neu laden\n"
#: openbox/openbox.c:540
msgid " --restart Restart Openbox\n"
#: openbox/openbox.c:541
msgid " --exit Exit Openbox\n"
-msgstr " --exit Beende Openbox\n"
+msgstr " --exit Openbox beenden\n"
#: openbox/openbox.c:542
msgid ""
"Debugging options:\n"
msgstr ""
"\n"
-"Debugging Optionen:\n"
+"Debugging-Optionen:\n"
#: openbox/openbox.c:543
msgid " --sync Run in synchronous mode\n"
#: openbox/openbox.c:544
msgid " --startup CMD Run CMD after starting\n"
-msgstr ""
+msgstr " --startup BEFEHL Befehl nach dem Start ausführen\n"
#: openbox/openbox.c:545
msgid " --debug Display debugging output\n"
#: openbox/openbox.c:546
msgid " --debug-focus Display debugging output for focus handling\n"
msgstr ""
-" --debug-focus Debugging-Informationen für's Fokus-Handling anzeigen\n"
+" --debug-focus Debugging-Informationen für Fokus-Handling anzeigen\n"
#: openbox/openbox.c:547
-#, fuzzy
msgid " --debug-session Display debugging output for session management\n"
msgstr ""
-" --debug-session Debugging-Informationen für's Session-Handling "
+" --debug-session Debugging-Informationen für die Sitzungsverwaltung "
"anzeigen\n"
#: openbox/openbox.c:548
"Please report bugs at %s\n"
msgstr ""
"\n"
-"Bitte melden Sie Bugreports an: %s\n"
+"Bitte melden Sie Fehler an: %s\n"
#: openbox/openbox.c:632 openbox/openbox.c:666
#, c-format
#: openbox/openbox.c:709
#, c-format
msgid "Invalid command line argument \"%s\"\n"
-msgstr "Ungültiges Kommandozeilen Argument \"%s\"\n"
+msgstr "Ungültiges Befehlszeilenargument »%s«\n"
#: openbox/screen.c:106 openbox/screen.c:191
#, c-format
#: openbox/screen.c:127
#, c-format
msgid "Could not acquire window manager selection on screen %d"
-msgstr "Konnte die Fenstermanagerauswahl auf Bildschirm %d nicht reservieren"
+msgstr ""
+"Die Fenstermanagerauswahl auf Bildschirm %d konnte nicht reserviert werden"
#: openbox/screen.c:150
#, c-format
"Openbox is configured for %d desktops, but the current session has %d. "
"Overriding the Openbox configuration."
msgstr[0] ""
-"Openbox wurde für %d Desktop konfiguriert, aber die aktuelle Sitzung hat %d. "
-"Überschreibe die Openbox-Konfiguration."
+"Openbox wurde für %d Arbeitsfläche konfiguriert, aber die aktuelle Sitzung "
+"hat %d. Die Openbox-Konfiguration wird überschrieben."
msgstr[1] ""
-"Openbox wurde für %d Desktops konfiguriert, aber die aktuelle Sitzung hat "
-"%d. Überschreibe die Openbox-Konfiguration."
+"Openbox wurde für %d Arbeitsflächen konfiguriert, aber die aktuelle Sitzung "
+"hat %d. Die Openbox-Konfiguration wird überschrieben."
#: openbox/screen.c:1205
#, c-format
msgid "desktop %i"
-msgstr "Desktop %i"
+msgstr "Arbeitsfläche %i"
#: openbox/startupnotify.c:241
#, c-format
msgid "Running %s"
-msgstr "Starte %s"
+msgstr "%s starten"
#: openbox/translate.c:59
#, c-format
msgid "Invalid modifier key \"%s\" in key/mouse binding"
-msgstr "Ungültige Modifier-Taste \"%s\" in Tastenbelegung/Maus-Binding"
+msgstr "Ungültige Modifier-Taste »%s« in Tastenbelegung/Maus-Zuordnung"
#: openbox/translate.c:138
#, c-format
msgid "Invalid key code \"%s\" in key binding"
-msgstr "Ungültiger Tastencode \"%s\" in Tastenkombination"
+msgstr "Ungültiger Tastencode »%s« in Tastenkombination"
#: openbox/translate.c:145
#, c-format
msgid "Invalid key name \"%s\" in key binding"
-msgstr "Ungültiger Tastenname \"%s\" in Tastenkombination"
+msgstr "Ungültiger Tastenname »%s« in Tastenkombination"
#: openbox/translate.c:151
#, c-format
msgid "Requested key \"%s\" does not exist on the display"
-msgstr "Angeforderte Taste \"%s\" existiert nicht auf dem Display"
+msgstr "Angeforderte Taste »%s« existiert nicht in der Anzeige"
#: openbox/prompt.c:153
msgid "OK"
msgstr "OK"
-
-#, fuzzy
-#~ msgid "Openbox"
-#~ msgstr "Beende Openbox"
-
-#~ msgid "--config-file requires an argument\n"
-#~ msgstr "--config-file erfordert einen Parameter\n"
-
-#~ msgid ""
-#~ "The SessionLogout action is not available since Openbox was built without "
-#~ "session management support"
-#~ msgstr ""
-#~ "Die SessionLogout-Aktion ist nicht verfügbar, da Openbox ohne "
-#~ "Unterstützung für Sitzungsmanagement kompiliert wurde"
-
-#~ msgid "Unable to save the session to \"%s\": %s"
-#~ msgstr "Konnte die Sitzung \"%s\" nicht sichern: %s"
-
-#~ msgid "Error while saving the session to \"%s\": %s"
-#~ msgstr "Fehler beim Speichern der Sitzung nach \"%s\": %s"
-
-#~ msgid "Not connected to a session manager"
-#~ msgstr "Nicht mit einem Sitzungsmanager verbunden"
-
-#~ msgid "X Error: %s"
-#~ msgstr "X-Fehler: %s"
-
-#~ msgid "Failed to execute \"%s\": %s"
-#~ msgstr "Konnte \"%s\" nicht ausführen: %s"
-
-#~ msgid "Invalid use of action \"%s\". Action will be ignored."
-#~ msgstr "Unzulässiger Einsatz der Aktion \"%s\". Aktion wird ignoriert."
--- /dev/null
+#!/bin/sh
+
+help() {
+ echo "Usage: $0 <revision> [lastrelease]"
+ echo
+ echo " <revision> The revision which should be used for release."
+ echo " [lastrelease] The revision of the most recent release made."
+ echo " By default it uses the most recent release-tag."
+ exit 1
+}
+
+error() {
+ echo "error: $1"
+ exit 1
+}
+
+test -e "./openbox/openbox.c" || \
+ error "must be run from the project's top level directory"
+
+REV="$1"
+test -z "$REV" && help
+RELEASE_SHA=$(git rev-parse "$REV")
+test $? = 0 || error "revision $REV not found"
+
+LAST="$2"
+if test -z "$LAST"; then
+ LAST=$(git describe --match 'release-*' $REV)
+ test $? = 0 || \
+ error "unable to find last release"
+fi
+
+#### CHANGELOG #####
+git log --no-merges $LAST..$REV --oneline|egrep '[bB][uU][Gg] #?[0-9]+'
+git log --no-merges $LAST..$REV --oneline | \
+ perl -n -e'/[bB][uU][Gg] #?([0-9]+)/ && print "#$1, "'
+perl -e'print "\b\b \n"'
+
+exit 0
--- /dev/null
+#!/bin/sh
+
+NOW=$(date +%s)
+SRCDIR="$PWD"
+WORKDIR="/tmp/openbox.$NOW"
+TESTDIR="$WORKDIR/.test"
+
+clean() {
+ cd "$SRCDIR"
+ if test -d "$WORKDIR"; then
+ chmod -R +w "$WORKDIR"
+ rm -r -f "$WORKDIR"
+ fi
+}
+
+error() {
+ echo "error: $1"
+ test -n "$2" && echo "$2"
+ clean
+ exit 1
+}
+
+RELEASE_SHA=$(git rev-parse "$REV")
+test $? = 0 || error "revision $REV not found"
+
+#NON_MASTER=$(git rev-list $RELEASE_SHA ^master)
+#test $? = 0 || error "git rev-list failed"
+#test -z $NON_MASTER || error "REVISION contains commits not on master"
+
+git new-workdir "$SRCDIR" "$WORKDIR"
+test $? = 0 || error "git-new-workdir failed"
+
+echo "working in $WORKDIR"
+
+cd "$WORKDIR"
+test $? = 0 || error "cd to $WORKDIR failed"
+
+git checkout -q $RELEASE_SHA
+test $? = 0 || error "git-checkout failed"
+
+# get last release
+if test -z "$LAST"; then
+ LAST=$(git describe --match 'release-*' --abbrev=0 $REV)
+ test $? = 0 || \
+ error "unable to find last release"
+ echo "Using previous release as $LAST"
+fi
+
+H="$(head -1 CHANGELOG|cut -d: -f 1)"
+test $H = $VERSION ||
+ error "CHANGELOG does not contain version $VERSION"
+
+CL="$(cat CHANGELOG|sed -n /^$VERSION:\$/,/^\$/p|tail -n +2|head -n -1)"
+CLNOWRAP=$(echo "$CL" \
+ |sed -e "s/^ \*/*/"|sed -n '1h;1!H;${;g;s/\n / /g;p;}')
+SHORTLOG="$(git shortlog --no-merges $LAST..$REV)"
--- /dev/null
+#!/bin/sh
+
+help() {
+ echo "Usage: $0 <revision> <version> [lastrelease]"
+ echo
+ echo " <revision> The revision which should be used for release."
+ echo " <version> The version of the release."
+ echo " [lastrelease] The revision of the most recent release made."
+ echo " By default it uses the most recent release-tag."
+ exit 1
+}
+
+REV="$1"
+test -z "$REV" && help
+VERSION="$2"
+test -z "$VERSION" && help
+LAST="$3"
+
+. release/common
+
+SUBJECT="[RELEASE] Openbox $VERSION"
+MAILINGLIST=openbox@icculus.org
+MIKACHU=mikachu@icculus.org
+
+cat <<EOF > $WORKDIR/.email
+Hello,
+
+Openbox $VERSION is now available!
+
+Some noteworthy changes are:
+$CLNOWRAP
+
+======== Download ========
+
+Download links are here: http://openbox.org/wiki/Openbox:Download
+
+======== Commits ========
+
+The following is a full list of commits appearing in this release.
+You can see the full commits here: http://git.openbox.org/?p=dana/openbox.git;a=shortlog;h=refs/tags/release-$VERSION
+
+
+$SHORTLOG
+EOF
+
+if test -z $EDITOR; then
+ nano -w $WORKDIR/.email || error "failed to edit email, set \$EDITOR"
+else
+ $EDITOR $WORKDIR/.email || error "failed to edit email with \$EDTIOR"
+fi
+
+test -e $WORKDIR/.email || error "email file disappeared"
+cat $WORKDIR/.email | mail -s "$SUBJECT" "$MAILINGLIST" || \
+ error "mail to $MAILINGLIST failed"
+
+echo "Hi Mikachu,
+
+Please update the freshmeat.net Openbox stuff for $VERSION. Thanks!" | \
+mail -s "Freshmeat.net for Openbox $VERSION" "$MIKACHU" || \
+ error "mail to $MIKACHU failed"
+
+clean
+exit 0
--- /dev/null
+#!/bin/sh
+
+help() {
+ echo "Usage: $0 <revision> <version> [lastrelease]"
+ echo
+ echo " <revision> The revision which should be used for release."
+ echo " <version> The version of the release."
+ echo " [lastrelease] The revision of the most recent release made."
+ echo " By default it uses the most recent release-tag."
+ exit 1
+}
+
+REV="$1"
+test -z "$REV" && help
+VERSION="$2"
+test -z "$VERSION" && help
+LAST="$3"
+
+. release/common
+
+#### CONFIRM SHORTLOG #####
+
+echo Shortlog from previous release:
+echo "$SHORTLOG"
+echo
+echo Shortlog from $LAST contains $(echo "$SHORTLOG"|wc -l) lines
+echo -n "ok? (y/n) "
+read a
+test "$a" = "y" || error "aborted"
+
+#### TEST english po VERSIONS ####
+
+BAD_PO="$(grep Project-Id-Version po/en*.po|grep -v "openbox $VERSION\\\\n")"
+test -z "$BAD_PO" || error "wrong version in po files" "$BAD_PO"
+
+#### TEST COMPILATION ####
+
+# check that it builds
+./bootstrap >/dev/null || "bootstrap failed"
+#CFLAGS="-Werror -isystem /usr/lib/glib-2.0" \
+./configure -C --enable-debug >/dev/null || \
+ error "configure (with debug) failed"
+make || error "make (with debug and Werror) failed"
+git clean -f -x -d -q
+
+# check that it builds with each optional featureset
+./bootstrap >/dev/null || "bootstrap failed"
+
+echo Check compile with all options enabled
+./configure -C >/dev/null || \
+ error "configure failed"
+make >/dev/null 2>/dev/null || \
+ error "make failed"
+grep "XKB 1" config.log >/dev/null || error "missing xkb extension"
+grep "XRANDR 1" config.log >/dev/null || error "missing xrandr extension"
+grep "XINERAMA 1" config.log >/dev/null || error "missing xinerama extension"
+grep "SYNC 1" config.log >/dev/null || error "missing sync extension"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with startup notification disabled
+./configure -C --disable-startup-notification >/dev/null || \
+ error "configure failed"
+make >/dev/null 2>/dev/null || \
+ error "make (with --disable-startup-notification) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xcursor disabled
+./configure -C --disable-xcursor >/dev/null || \
+ error "configure failed"
+make >/dev/null 2>/dev/null || \
+ error "make (with --disable-xcursor) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with imlib2 disabled
+./configure -C --disable-imlib2 >/dev/null || \
+ error "configure failed"
+make >/dev/null 2>/dev/null || \
+ error "make (with --disable-imlib2) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with session management disabled
+./configure -C --disable-session-management >/dev/null || \
+ error "configure failed"
+make >/dev/null 2>/dev/null || \
+ error "make (with --disable-session-management) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xkb disabled
+./configure -C --disable-xkb >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xkb) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xrandr disabled
+./configure -C --disable-xrandr >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xrandr) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xinerama disabled
+./configure -C --disable-xinerama >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xinerama) failed"
+make clean >/dev/null || error "make clean failed"
+
+echo Check compile with xsync disabled
+./configure -C --disable-xsync >/dev/null || error "configure failed"
+make >/dev/null 2>/dev/null || error "make (with --disable-xsync) failed"
+make clean >/dev/null || error "make clean failed"
+
+# check that it installs sanely
+echo Check installation correctness
+./configure -C >/dev/null || \
+ error "configure failed"
+make distcheck >/dev/null || \
+ error "make distcheck failed"
+
+# VERIFY TARBALL
+
+TAR="openbox-$VERSION.tar.gz"
+ASC="openbox-$VERSION.tar.gz.asc"
+
+echo Found Openbox release tarball:
+ls -d openbox-*.tar.gz
+test -e "$TAR" || \
+ error "Specified version does not match configure.am"
+
+# SIGN THE TARBALL
+
+echo Signing the release tarball:
+gpg --sign --detach-sign --armor "$TAR"
+test $? = 0 || \
+ error "Failed to sign release tarball"
+
+echo Tagging the release:
+git tag -s -m "tagging the $VERSION release" "release-$VERSION" $REV || \
+ error "Failed to tag the release"
+
+mv "$TAR" "$ASC" "$SRCDIR"
+
+echo "=$VERSION="
+echo "$CLNOWRAP"
+echo
+echo
+echo Edit download page:
+echo " http://openbox.org/oldwiki/index.php?title=Openbox:Download&action=edit§ion=1"
+echo
+echo Edit changelog:
+echo " http://openbox.org/oldwiki/index.php?title=Openbox:Changelog&action=edit§ion=1"
+echo
+echo Push the tag:
+echo " git push origin tag release-$VERSION"
+echo
+echo Email:
+echo " ./release/email $*"
+echo
+cd "$SRCDIR"
+ls -l "$TAR" "$ASC"
+
+clean
+exit 0